Configuration Reference

September 18, 2026 ยท View on GitHub

This document describes all configuration options for opencode-chat-bridge.

Quick Setup

  1. Copy chat-bridge.json.example to chat-bridge.json and edit local bridge settings
  2. Create opencode.json with the chat-bridge agent
  3. Run bun src/cli.ts

chat-bridge.json is intentionally ignored by git. Keep deployment-specific connector settings, local paths, and credentials there. Commit safe defaults or examples to chat-bridge.json.example instead.

ACP backend

The bridge can launch any ACP v1 stdio agent. Configure the executable, argument vector, stable backend identity, and optional workspace profile in chat-bridge.json:

{
  "sessionStorePath": "./.opencode/chat-sessions.json",
  "acp": {
    "command": "/usr/local/bin/ferrum",
    "args": ["acp"],
    "backendId": "ferrum",
    "profileDir": "./profiles/ferrum-chat"
  }
}

Each connector/thread receives a deterministic working directory. The profile is copied into that directory before the ACP process starts, so it can provide backend-specific policy, instructions, and skills. The bridge persists sessionId, canonical cwd, and backendId in sessionStorePath; it resumes matching sessions after restart and deletes the mapping and backend session on /clear.

backendId must change when the configured backend or its incompatible session format changes. ACP processes inherit the bridge environment; keep credentials outside profiles and the session store.

Ferrum backend notes

Ferrum is a small Rust-native Linux coding agent. It can be used directly as an ACP v1 stdio backend:

ferrum acp

Ferrum is useful for chat bot deployments because it starts quickly, has low runtime overhead, persists durable JSONL sessions, and has explicit tool policy. The bridge has been validated with Ferrum for executable/argument selection, per-thread workspaces, text and thought streaming, command discovery, cancellation, process restart, session resume/delete, and restrictive workspace policy.

A typical Ferrum chat profile keeps broad local authority off by default and exposes specific capabilities through MCP:

mcp_enabled = true

[tools]
deny = ["write", "edit", "bash", "wait"]
writable_roots = ["."]

[[mcp.servers]]
name = "search"
command = "/usr/local/bin/search-mcp"
args = []
enabled = true

Useful Ferrum features for this setup:

  • AGENTS.md context loading and Agent Skills-style instruction packages
  • configurable providers, models, thinking, safety, and context budget
  • OpenAI-compatible providers and ChatGPT/Codex OAuth
  • bounded native tools for file access, search, editing, and shell execution
  • allow/deny tool exposure policy
  • stdio MCP client support with namespaced tools
  • project-local .ferrum/config.toml restrictions for tools, roots, skills, MCP access, safety, and tool-round limits

Ferrum is not a sandbox: an allowed tool or MCP server runs with the Unix permissions of the Ferrum process. Its safety model is still meaningful. Native tools and shell execution are bounded and policy-checked; writable roots and safety tiers limit mutation; MCP servers get a filtered environment, explicit env allowlists, bounded protocol frames/schema/output, stderr withholding from model-visible errors, sanitized tool-name collision checks, transport quarantine on framing failure, and Linux process-tree cleanup through delegated cgroup-v2 when available.

For production chat bots, configure only trusted MCP commands, pass only required environment variables, keep credentials outside copied profiles and session stores, and prefer narrow MCP tools over enabling general shell access.

Session picker

The project/session picker is disabled by default because it exposes saved ACP working directories, session titles, and optional recent history previews.

Enable it explicitly:

{
  "sessionPicker": {
    "enabled": true,
    "connectors": ["whatsapp"],
    "mirrorIntervalSeconds": 60
  }
}

Commands when enabled:

/p, /p <n>      list/select saved working directories
/s, /s <n>      list/select saved sessions interactively
/m, /m <n>      stop/start read-only mirror mode
/r              reload current selected session
/d              detach without deleting the backend session

connectors limits where the picker is available. If empty, all connectors may use it. Keep it disabled for shared rooms unless the session paths and history are safe to expose there.

opencode.json (OpenCode backend)

The opencode.json file defines the secure OpenCode agent configuration:

{
  "$schema": "https://opencode.ai/config.json",
  "default_agent": "chat-bridge",
  "agent": {
    "chat-bridge": {
      "description": "Secure chat assistant",
      "mode": "primary",
      "prompt": "You are a helpful assistant. You can search the web and check time.",
      "permission": {
        "read": "deny",
        "edit": "deny",
        "bash": "deny",
        "glob": "deny",
        "grep": "deny",
        "task": "deny",
        "todowrite": "deny",
        "todoread": "deny",
        "webfetch": "deny",
        "codesearch": "deny",
        "question": "allow",
        "time_*": "allow",
        "weather_*": "allow",
        "web-search_*": "allow"
      }
    }
  }
}

Key Settings

SettingDescription
default_agentAgent used by default for new sessions
agent.*.mode"primary" for main agents, "subagent" for helpers
agent.*.promptSystem prompt for the agent
agent.*.permissionTool permissions (allow/deny/ask)

Permission Configuration

Permission Actions

ActionBehavior
"allow"Tool executes immediately
"deny"Tool blocked, error returned to LLM
"ask"Requires user confirmation

For chat bots, use "allow" or "deny". The "ask" action requires interactive confirmation.

Tool Names

Built-in tools:

  • read - Read files
  • edit - Edit files
  • bash - Execute commands
  • glob - Find files
  • grep - Search file contents
  • task - Spawn subagents
  • todowrite - Write todos
  • todoread - Read todos
  • webfetch - Fetch URLs
  • codesearch - Search code
  • question - Ask user questions

MCP Tool Permissions

MCP tools use the pattern <server>_<tool>:

{
  "permission": {
    "time_*": "allow",
    "weather_*": "allow",
    "web-search_*": "allow"
  }
}

Wildcard matching: * matches any characters, so weather_* allows all weather tools.

Available MCP Servers

Check installed MCP servers:

opencode mcp list

Common servers:

ServerTools
timetime_get_current_time, time_convert_time
weatherweather_get_weather, weather_get_forecast, weather_search_location
web-searchweb-search_full-web-search, web-search_get-web-search-summaries, web-search_get-single-web-page-content
chrome-devtoolsBrowser automation (deny for chat bots)

Any MCP server can be used. Check available servers with opencode mcp list.

Disabling MCP Servers Locally

Your global OpenCode config (~/.config/opencode/opencode.json) may have MCP servers enabled that you don't want the chat bot to use. You can disable them in your local project config.

Problem: Global config has chrome-devtools enabled, but you don't want the chat bot to use it.

Solution: Add an mcp section to your project's opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "model": "anthropic/claude-sonnet-4-5",
  "default_agent": "chat-bridge",
  
  "mcp": {
    "chrome-devtools": {
      "enabled": false
    }
  },
  
  "agent": {
    "chat-bridge": {
      ...
    }
  }
}

How it works:

  • Local config overrides global config
  • You only need to specify "enabled": false" - not the full server definition
  • Other MCP servers (time, weather, web-search) remain enabled from global config

Important: Disabling the MCP server prevents tools from being loaded. But the model may still "think" it has access if tools were visible in a previous session. For complete blocking, combine with permission denials:

{
  "mcp": {
    "chrome-devtools": { "enabled": false }
  },
  
  "agent": {
    "chat-bridge": {
      "permission": {
        "chrome-devtools_*": "deny"
      }
    }
  }
}

The wildcard chrome-devtools_* denies ALL tools from that MCP server.

Disabling a Whole MCP Server vs Single Functions

There are two ways to block MCP tools:

Method 1: Disable the Entire MCP Server

Use the mcp section to prevent the server from loading at all:

{
  "mcp": {
    "chrome-devtools": {
      "enabled": false
    }
  }
}

This completely disables ALL tools from that server. The tools won't be loaded or visible to the model.

Method 2: Deny Individual Functions

Use the permission section to block specific tools while keeping others:

{
  "agent": {
    "chat-bridge": {
      "permission": {
        "weather_set_default_location": "deny"
      }
    }
  }
}

This allows most weather tools but blocks the one that saves state.

Method 3: Wildcard Deny All Functions from an MCP

Use * to deny all tools from a server without disabling it:

{
  "agent": {
    "chat-bridge": {
      "permission": {
        "chrome-devtools_*": "deny"
      }
    }
  }
}

Comparison

GoalMethodConfig
Block entire MCP serverMCP disable"mcp": { "server": { "enabled": false } }
Block all tools from MCPWildcard deny"permission": { "server_*": "deny" }
Block one specific toolSingle deny"permission": { "server_tool": "deny" }
Allow one tool, block restSelective"server_*": "deny" + "server_tool": "allow"

Example: Allow Only Some web-search Tools

{
  "permission": {
    "web-search_*": "deny",
    "web-search_get-web-search-summaries": "allow"
  }
}

This denies all web-search tools by default, then explicitly allows only the lightweight summary search.

Why Both MCP Disable AND Permission Deny?

MethodWhat it does
mcp.enabled: falseServer not loaded, tools don't appear
permission: denyTools blocked at execution time

Using both provides defense in depth:

  1. MCP disable prevents tools from loading
  2. Permission deny blocks execution if tools somehow load
  3. The model won't list capabilities it can't use

Skills

Skills are custom instruction sets that the AI can load via the skill tool. They provide domain-specific context, formatting preferences, or personas.

Creating Skills

Skills live in .opencode/skills/<name>/SKILL.md:

.opencode/
  skills/
    weather/
      SKILL.md
    assistant/
      SKILL.md

Example skill (.opencode/skills/weather/SKILL.md):

# Weather Skill

When reporting weather, always include:
- Temperature in Celsius
- Wind speed and direction
- Humidity percentage

Format as a markdown table for chat readability.

Enabling Skills

  1. Add skill: allow to agent permissions in opencode.json:
{
  "agent": {
    "chat-bridge": {
      "permission": {
        "skill": "allow"
      }
    }
  }
}
  1. The bridge automatically symlinks .opencode/ to session directories

  2. Users can invoke skills via chat:

!oc /weather    # Loads the weather skill

Skills vs Commands vs Tools

TypeLocationPurpose
Skills.opencode/skills/Instructions loaded by AI via skill tool
Commands.opencode/commands/Prompts invoked with /name, shown in /help
Tools.opencode/tools/Code the AI can execute

Notes

  • Skills are local config - don't commit secrets to git
  • The .opencode/ directory is gitignored by default
  • Skills require both skill: allow permission and the symlink setup

Tool Messages

toolMessages controls which tool activity the bridge returns to the chat channel. Tool authorization remains part of the ACP backend security profile; these settings affect presentation only.

{
  "toolMessages": {
    "mode": "events",
    "showArguments": false,
    "showOutputFor": ["bash"],
    "maxTraceEntries": 20
  }
}
  • mode selects the tool-call presentation:
    • off hides tool calls.
    • events sends one immutable message per call and is the default.
    • status maintains one editable message with the current tool and totals.
    • trace maintains one editable cumulative tool-call ledger.
  • showCalls is a legacy compatibility switch and is no longer needed in new configurations. If present, false always resolves to mode: "off".
  • showArguments adds up to three compact arguments to each call notice. Long path-like values retain their filename/suffix; other values retain their beginning. Arguments can contain local paths, queries, URLs, or other sensitive input, so the default is false.
  • showOutputFor lists tool-name substrings whose output is returned to chat. The default ["bash"] provides progress from shell commands. Use ["*"] to return output from every tool, or an empty list to suppress all direct tool output. Forwarding all output can expose sensitive tool results and create substantial chat traffic. This setting is independent from mode; mode: "off" can still forward selected output.
  • maxTraceEntries bounds each trace message. Longer traces continue in additional editable messages without discarding earlier calls.

The mode is global across WhatsApp, Telegram, Slack, Discord, Mattermost, Matrix, and Web. Editable modes correlate progressive ACP updates by tool-call ID. If a message can no longer be edited, the bridge continues in a replacement message. The CLI retains event-style output.

For an editable audit trail with arguments:

{
  "toolMessages": {
    "mode": "trace",
    "showArguments": true,
    "showOutputFor": [],
    "maxTraceEntries": 20
  }
}

For example, to show individual tool calls and output from Bash and time MCP tools:

{
  "toolMessages": {
    "mode": "events",
    "showArguments": false,
    "showOutputFor": ["bash", "mcp__time"]
  }
}

To show only the agent's final response:

{
  "toolMessages": {
    "mode": "off",
    "showArguments": false,
    "showOutputFor": []
  }
}

Verbose Error Messages

verboseErrors controls whether operational error messages surfaced to chat users include the underlying backend error detail. It currently applies to ACP session listing, loading, and reload errors (the catch blocks that used to return a fixed friendly string).

{
  "verboseErrors": false
}
  • verboseErrors: false (default) keeps the existing friendly UX. Users see only the generic message such as Could not list saved ACP sessions.; the underlying error is still written to the bridge logs.
  • verboseErrors: true appends the backend error detail on a new line, for example Could not list saved ACP sessions.\nbackend unreachable. Whitespace in the detail is collapsed, empty details are dropped, and the detail is bounded to 500 characters so ACP/backend errors cannot exceed connector message limits (e.g. Discord's 2000-character cap).

Security warning. Backend error messages may expose internal paths, endpoints, hostnames, stack-trace fragments, or other sensitive operational details to anyone in the chat room. Only enable verboseErrors in trusted environments (private DMs, locked-down channels) and never in shared public channels where untrusted users can read the bot's replies. The detail is bounded but not redacted.

Matrix HTML Formatting

By default, bot responses are sent as plain text. When formatHtml is enabled, the Matrix connector converts markdown responses to HTML before sending, using the Matrix formatted_body field. Matrix clients render the HTML while plain text clients (IRC bridges, etc.) see the unformatted fallback.

Enable in chat-bridge.json

{
  "matrix": {
    "formatHtml": true
  }
}

What it does

With formatHtml: false (default)With formatHtml: true
sendText() - plain text onlysendMessage() with format: org.matrix.custom.html
Markdown syntax visible as raw textTables, bold, lists rendered natively
Works on all clients equallyHTML for Matrix, plain text fallback for others

When to use it

  • Enable when your primary audience uses Matrix/Element clients
  • Leave disabled for IRC-bridged rooms or plain text environments
  • The plain text body is always included as a fallback

CLI Options

# Interactive mode
bun src/cli.ts

# Single prompt
bun src/cli.ts "What time is it?"

Interactive Commands

CommandDescription
exitExit the CLI
quitExit the CLI

Agent Configuration

Multiple Agents

Define multiple agents for different purposes:

{
  "agent": {
    "chat-bridge": {
      "description": "Secure chat assistant",
      "mode": "primary",
      "permission": {
        "read": "deny",
        "bash": "deny"
      }
    },
    "researcher": {
      "description": "Deep research mode",
      "mode": "primary",
      "permission": {
        "web-search_*": "allow"
      }
    },
    "coder": {
      "description": "Code-focused assistant",
      "mode": "primary",
      "permission": {
        "read": "allow",
        "glob": "allow",
        "grep": "allow",
        "edit": "deny"
      }
    }
  }
}

Agent Settings

SettingDescriptionDefault
mode"primary" or "subagent""all"
descriptionHuman-readable description-
promptSystem promptOpenCode default
modelLLM model overrideConfig default
temperatureResponse randomnessModel default
permissionTool permissionsConfig default

Model Configuration

Default Model

IMPORTANT: You MUST set the model field in opencode.json. Without it, OpenCode defaults to opencode/big-pickle (a free but less capable model).

Set in opencode.json:

{
  "model": "anthropic/claude-sonnet-4-5"
}

Per-Agent Model

Override for specific agents:

{
  "agent": {
    "fast-helper": {
      "model": "anthropic/claude-haiku-4-5",
      "permission": { "read": "deny" }
    }
  }
}

Available Models

Check available models:

opencode model list

Common models:

  • anthropic/claude-sonnet-4-* - Good balance
  • anthropic/claude-opus-4-* - Most capable
  • anthropic/claude-haiku-4-* - Fastest
  • openai/gpt-4o - OpenAI
  • google/gemini-* - Google

Environment Variables

Trigger Prefix

The trigger prefix can be set globally in chat-bridge.json or overridden per connector via environment variables:

Env varConnectorFallback
SLACK_TRIGGERSlackchat-bridge.json trigger
DISCORD_TRIGGERDiscordchat-bridge.json trigger
MATRIX_TRIGGERMatrixchat-bridge.json trigger
MATTERMOST_TRIGGERMattermostchat-bridge.json trigger
WHATSAPP_TRIGGERWhatsAppchat-bridge.json trigger
WEB_TRIGGERWebchat-bridge.json trigger

This lets you run multiple connectors with different triggers from the same config.

Other Variables

# OpenCode configuration
OPENCODE_MODEL="anthropic/claude-sonnet-4-20250514"
OPENCODE_CONFIG="/path/to/opencode.json"

# For chat connectors
MATRIX_ACCESS_TOKEN="syt_..."
DISCORD_TOKEN="..."

User Allowlists

Each chat connector can restrict access to a connector-specific list of user IDs. Empty list means all users are allowed. For WhatsApp, allowedUsers filters non-owner senders; the linked WhatsApp account itself is always allowed.

These IDs are platform-native identifiers, not always human-friendly usernames or phone numbers. For example, Slack uses member IDs like U01ABC123, while WhatsApp may use a sender ID shown in connector logs.

WhatsApp also supports personal linked-account mode:

{
  "whatsapp": {
    "respondToOthers": false
  }
}

When respondToOthers is false, only messages sent by the linked WhatsApp account can trigger the bot. Messages from other people are ignored even if they use the trigger. WhatsApp self-chat plain text is always accepted as a prompt from the owner; other chats still require the configured trigger.

For WhatsApp personal deployments, prefer whatsapp.respondToOthers: false over allowedUsers; WhatsApp sender IDs may be LID-style identifiers rather than phone numbers and can be hard to map to contacts.

In chat-bridge.json:

{
  "slack": { "allowedUsers": ["U01ABC123", "U02DEF456"] },
  "whatsapp": { "allowedUsers": ["34600111222"] },
  "matrix": { "allowedUsers": ["@alice:matrix.org"] },
  "discord": { "allowedUsers": ["123456789012345678"] },
  "mattermost": { "allowedUsers": ["user-id-1"] }
}

Environment variable overrides:

Env varConnector
SLACK_ALLOWED_USERSSlack
WHATSAPP_ALLOWED_USERSWhatsApp
WHATSAPP_RESPOND_TO_OTHERSWhatsApp owner-only mode (false = ignore other people)
MATRIX_ALLOWED_USERSMatrix
DISCORD_ALLOWED_USERSDiscord
MATTERMOST_ALLOWED_USERSMattermost

Breaking change: WhatsApp now uses allowedUsers / WHATSAPP_ALLOWED_USERS. The older allowedNumbers / WHATSAPP_ALLOWED_NUMBERS names were removed.

Example Configurations

Minimal (CLI Only)

{
  "$schema": "https://opencode.ai/config.json",
  "default_agent": "chat-bridge",
  "agent": {
    "chat-bridge": {
      "mode": "primary",
      "permission": {
        "read": "deny",
        "edit": "deny",
        "bash": "deny",
        "time_*": "allow",
        "web-search_*": "allow"
      }
    }
  }
}

Full Production

{
  "$schema": "https://opencode.ai/config.json",
  "model": "anthropic/claude-sonnet-4-5",
  "default_agent": "chat-bridge",
  
  "mcp": {
    "chrome-devtools": { "enabled": false },
    "antigravity-img": { "enabled": false }
  },
  
  "agent": {
    "chat-bridge": {
      "description": "Secure chat assistant",
      "mode": "primary",
      "prompt": "You are a helpful assistant in a chat interface.\n\nYOUR CAPABILITIES (only these):\n1) Web search and fetching web pages\n2) Time and timezone queries\n3) Document library access\n\nYOU DO NOT HAVE:\n- Browser automation or Chrome DevTools\n- Image generation\n- Filesystem access\n- Code execution\n\nDo NOT mention capabilities you don't have.",
      "permission": {
        "read": "deny",
        "edit": "deny",
        "bash": "deny",
        "glob": "deny",
        "grep": "deny",
        "task": "deny",
        "todowrite": "deny",
        "todoread": "deny",
        "webfetch": "deny",
        "codesearch": "deny",
        "question": "allow",
        
        "chrome-devtools_*": "deny",
        "generate_image": "deny",
        "image_quota": "deny",
        
        "time_*": "allow",
        "weather_*": "allow",
        "web-search_*": "allow"
      }
    }
  }
}

This configuration allows time, weather, and web-search MCP tools while denying all filesystem and dangerous tools.

Session Management

Bot sessions are stored outside the project git repo to prevent them from cluttering your OpenCode session list.

Why outside the git repo? OpenCode uses the git root to create a unique project hash. If session directories are inside the repo, all bot sessions end up in the same project hash as your dev sessions. By storing them outside (in ~/.cache/), OpenCode assigns them to the global project instead, keeping them completely separate from your development sessions.

Session Storage Location

Default: ~/.cache/opencode-chat-bridge/sessions/<connector>/<channel-id>/

Override: Set SESSION_BASE_DIR environment variable:

SESSION_BASE_DIR=/path/to/sessions

Session Directory Structure

~/.cache/opencode-chat-bridge/sessions/
  slack/
    C0ABC123_1710000000_111/  # Slack thread session (channel:threadTs)
    C0ABC123_1710000000_222/  # Another thread in same channel
  matrix/
    _room1_server.org/  # Matrix room session (special chars sanitized)
  mattermost/
    abc123def456/  # Mattermost channel session
  whatsapp/
    1234567890/   # WhatsApp chat session

Session Cleanup

Sessions are cleaned up in two ways:

1. Startup cleanup (days-based): Old on-disk session directories are deleted when a connector starts.

# .env
SESSION_RETENTION_DAYS=7  # Default: 7 days

2. Runtime expiry (minutes-based): A background sweep expires inactive in-memory sessions, removes their persisted ACP mappings, and deletes their on-disk cache.

# .env
SESSION_RETENTION_MINS=30  # Default: 30 for Slack, unset for others

Sessions with active in-flight queries are never evicted. Both mechanisms coexist -- startup cleanup handles long-dead sessions, runtime expiry handles sessions that go stale while the bot is running.

Session Commands

Users can manage their sessions via chat:

Slack:

  • !oc /status - Show session info and directory location
  • !oc /clear or !oc /reset - Clear session history
  • !oc /help - Show available commands

Matrix:

  • !oc /status - Show session info
  • !oc /clear or !oc /reset - Clear session history
  • !oc /help - Show available commands

Mattermost:

  • !oc /status - Show session info
  • !oc /clear or !oc /reset - Clear session history
  • !oc /help - Show available commands

WhatsApp:

  • !oc /status - Show session info
  • !oc /clear or !oc /reset - Clear session history
  • !oc /help - Show available commands

Session Continuity

Each conversation thread maintains its own session. Users can reference previous messages as long as:

  1. The session hasn't been manually cleared (/clear)
  2. The session hasn't expired due to SESSION_RETENTION_MINS inactivity
  3. The session is less than SESSION_RETENTION_DAYS old (on-disk cleanup at startup)
  4. The connector hasn't been restarted (in-memory sessions are lost on restart)

For Slack specifically, sessions are per-thread. Other connectors use per-channel/room sessions.

Debugging Sessions

To inspect a session directory:

ls -la ~/.cache/opencode-chat-bridge/sessions/slack/C0ABC123/

OpenCode stores session data in ~/.local/share/opencode/storage/session/<project-hash>/. Since bot sessions are outside any git repo, they go to the global project (~/.local/share/opencode/storage/session/global/) instead of your dev project's hash.

To view session files for a specific bot session:

cd ~/.cache/opencode-chat-bridge/sessions/slack/C0ABC123
opencode session list

This won't pollute your main project session list since it's a different "project" (directory outside git).

Troubleshooting

"Agent not found"

Ensure default_agent matches an agent name:

{
  "default_agent": "chat-bridge",
  "agent": {
    "chat-bridge": { ... }
  }
}

"Tool blocked"

Check permission configuration. Tool calls are blocked if:

  • Tool is set to "deny"
  • Tool not explicitly allowed and no default "*": "allow"

"No response"

  1. Check OpenCode is installed: opencode --version
  2. Check opencode.json exists in working directory
  3. Check for errors in output

"Wrong model being used" / "Big Pickle"

If the bot uses opencode/big-pickle instead of your intended model:

  1. Add "model": "anthropic/claude-sonnet-4-5" to opencode.json
  2. Without the model field, OpenCode defaults to free models
  3. Verify with: opencode models to list available models

"Images not displaying in Matrix"

If MCP tool images aren't showing in Matrix chat:

  1. Tool results contain image path markers (e.g., [DOCLIBRARY_IMAGE]...[/DOCLIBRARY_IMAGE])
  2. These come in tool_result events, not in response text chunks
  3. The Matrix connector captures tool results via update events
  4. Check logs for [IMAGE] messages