diff --git a/docs/cli/mcp.md b/docs/cli/mcp.md
index 312af52e46f..5baec8598cd 100644
--- a/docs/cli/mcp.md
+++ b/docs/cli/mcp.md
@@ -5,91 +5,89 @@ read_when:
- Running `openclaw mcp serve`
- Managing OpenClaw-saved MCP server definitions
title: "MCP"
+sidebarTitle: "MCP"
---
`openclaw mcp` has two jobs:
- run OpenClaw as an MCP server with `openclaw mcp serve`
-- manage OpenClaw-owned outbound MCP server definitions with `list`, `show`,
- `set`, and `unset`
+- manage OpenClaw-owned outbound MCP server definitions with `list`, `show`, `set`, and `unset`
In other words:
- `serve` is OpenClaw acting as an MCP server
-- `list` / `show` / `set` / `unset` is OpenClaw acting as an MCP client-side
- registry for other MCP servers its runtimes may consume later
+- `list` / `show` / `set` / `unset` is OpenClaw acting as an MCP client-side registry for other MCP servers its runtimes may consume later
-Use [`openclaw acp`](/cli/acp) when OpenClaw should host a coding harness
-session itself and route that runtime through ACP.
+Use [`openclaw acp`](/cli/acp) when OpenClaw should host a coding harness session itself and route that runtime through ACP.
## OpenClaw as an MCP server
This is the `openclaw mcp serve` path.
-## When to use `serve`
+### When to use `serve`
Use `openclaw mcp serve` when:
-- Codex, Claude Code, or another MCP client should talk directly to
- OpenClaw-backed channel conversations
+- Codex, Claude Code, or another MCP client should talk directly to OpenClaw-backed channel conversations
- you already have a local or remote OpenClaw Gateway with routed sessions
-- you want one MCP server that works across OpenClaw's channel backends instead
- of running separate per-channel bridges
+- you want one MCP server that works across OpenClaw's channel backends instead of running separate per-channel bridges
-Use [`openclaw acp`](/cli/acp) instead when OpenClaw should host the coding
-runtime itself and keep the agent session inside OpenClaw.
+Use [`openclaw acp`](/cli/acp) instead when OpenClaw should host the coding runtime itself and keep the agent session inside OpenClaw.
-## How it works
+### How it works
-`openclaw mcp serve` starts a stdio MCP server. The MCP client owns that
-process. While the client keeps the stdio session open, the bridge connects to a
-local or remote OpenClaw Gateway over WebSocket and exposes routed channel
-conversations over MCP.
+`openclaw mcp serve` starts a stdio MCP server. The MCP client owns that process. While the client keeps the stdio session open, the bridge connects to a local or remote OpenClaw Gateway over WebSocket and exposes routed channel conversations over MCP.
-Lifecycle:
+
+
+ The MCP client spawns `openclaw mcp serve`.
+
+
+ The bridge connects to the OpenClaw Gateway over WebSocket.
+
+
+ Routed sessions become MCP conversations and transcript/history tools.
+
+
+ Live events are queued in memory while the bridge is connected.
+
+
+ If Claude channel mode is enabled, the same session can also receive Claude-specific push notifications.
+
+
-1. the MCP client spawns `openclaw mcp serve`
-2. the bridge connects to Gateway
-3. routed sessions become MCP conversations and transcript/history tools
-4. live events are queued in memory while the bridge is connected
-5. if Claude channel mode is enabled, the same session can also receive
- Claude-specific push notifications
+
+
+ - live queue state starts when the bridge connects
+ - older transcript history is read with `messages_read`
+ - Claude push notifications only exist while the MCP session is alive
+ - when the client disconnects, the bridge exits and the live queue is gone
+ - one-shot agent entry points such as `openclaw agent` and `openclaw infer model run` retire any bundled MCP runtimes they open when the reply completes, so repeated scripted runs do not accumulate stdio MCP child processes
+ - stdio MCP servers launched by OpenClaw (bundled or user-configured) are torn down as a process tree on shutdown, so child subprocesses started by the server do not survive after the parent stdio client exits
+ - deleting or resetting a session disposes that session's MCP clients through the shared runtime cleanup path, so there are no lingering stdio connections tied to a removed session
+
+
-Important behavior:
-
-- live queue state starts when the bridge connects
-- older transcript history is read with `messages_read`
-- Claude push notifications only exist while the MCP session is alive
-- when the client disconnects, the bridge exits and the live queue is gone
-- one-shot agent entry points such as `openclaw agent` and
- `openclaw infer model run` retire any bundled MCP runtimes they open when the
- reply completes, so repeated scripted runs do not accumulate stdio MCP child
- processes
-- stdio MCP servers launched by OpenClaw (bundled or user-configured) are torn
- down as a process tree on shutdown, so child subprocesses started by the
- server do not survive after the parent stdio client exits
-- deleting or resetting a session disposes that session's MCP clients through
- the shared runtime cleanup path, so there are no lingering stdio connections
- tied to a removed session
-
-## Choose a client mode
+### Choose a client mode
Use the same bridge in two different ways:
-- Generic MCP clients: standard MCP tools only. Use `conversations_list`,
- `messages_read`, `events_poll`, `events_wait`, `messages_send`, and the
- approval tools.
-- Claude Code: standard MCP tools plus the Claude-specific channel adapter.
- Enable `--claude-channel-mode on` or leave the default `auto`.
+
+
+ Standard MCP tools only. Use `conversations_list`, `messages_read`, `events_poll`, `events_wait`, `messages_send`, and the approval tools.
+
+
+ Standard MCP tools plus the Claude-specific channel adapter. Enable `--claude-channel-mode on` or leave the default `auto`.
+
+
-Today, `auto` behaves the same as `on`. There is no client capability detection
-yet.
+
+Today, `auto` behaves the same as `on`. There is no client capability detection yet.
+
-## What `serve` exposes
+### What `serve` exposes
-The bridge uses existing Gateway session route metadata to expose channel-backed
-conversations. A conversation appears when OpenClaw already has session state
-with a known route such as:
+The bridge uses existing Gateway session route metadata to expose channel-backed conversations. A conversation appears when OpenClaw already has session state with a known route such as:
- `channel`
- recipient or destination metadata
@@ -104,101 +102,91 @@ This gives MCP clients one place to:
- send a reply back through the same route
- see approval requests that arrive while the bridge is connected
-## Usage
+### Usage
-```bash
-# Local Gateway
-openclaw mcp serve
+
+
+ ```bash
+ openclaw mcp serve
+ ```
+
+
+ ```bash
+ openclaw mcp serve --url wss://gateway-host:18789 --token-file ~/.openclaw/gateway.token
+ ```
+
+
+ ```bash
+ openclaw mcp serve --url wss://gateway-host:18789 --password-file ~/.openclaw/gateway.password
+ ```
+
+
+ ```bash
+ openclaw mcp serve --verbose
+ openclaw mcp serve --claude-channel-mode off
+ ```
+
+
-# Remote Gateway
-openclaw mcp serve --url wss://gateway-host:18789 --token-file ~/.openclaw/gateway.token
-
-# Remote Gateway with password auth
-openclaw mcp serve --url wss://gateway-host:18789 --password-file ~/.openclaw/gateway.password
-
-# Enable verbose bridge logs
-openclaw mcp serve --verbose
-
-# Disable Claude-specific push notifications
-openclaw mcp serve --claude-channel-mode off
-```
-
-## Bridge tools
+### Bridge tools
The current bridge exposes these MCP tools:
-- `conversations_list`
-- `conversation_get`
-- `messages_read`
-- `attachments_fetch`
-- `events_poll`
-- `events_wait`
-- `messages_send`
-- `permissions_list_open`
-- `permissions_respond`
+
+
+ Lists recent session-backed conversations that already have route metadata in Gateway session state.
-### `conversations_list`
+ Useful filters:
-Lists recent session-backed conversations that already have route metadata in
-Gateway session state.
+ - `limit`
+ - `search`
+ - `channel`
+ - `includeDerivedTitles`
+ - `includeLastMessage`
-Useful filters:
+
+
+ Returns one conversation by `session_key`.
+
+
+ Reads recent transcript messages for one session-backed conversation.
+
+
+ Extracts non-text message content blocks from one transcript message. This is a metadata view over transcript content, not a standalone durable attachment blob store.
+
+
+ Reads queued live events since a numeric cursor.
+
+
+ Long-polls until the next matching queued event arrives or a timeout expires.
-- `limit`
-- `search`
-- `channel`
-- `includeDerivedTitles`
-- `includeLastMessage`
+ Use this when a generic MCP client needs near-real-time delivery without a Claude-specific push protocol.
-### `conversation_get`
+
+
+ Sends text back through the same route already recorded on the session.
-Returns one conversation by `session_key`.
+ Current behavior:
-### `messages_read`
+ - requires an existing conversation route
+ - uses the session's channel, recipient, account id, and thread id
+ - sends text only
-Reads recent transcript messages for one session-backed conversation.
+
+
+ Lists pending exec/plugin approval requests the bridge has observed since it connected to the Gateway.
+
+
+ Resolves one pending exec/plugin approval request with:
-### `attachments_fetch`
+ - `allow-once`
+ - `allow-always`
+ - `deny`
-Extracts non-text message content blocks from one transcript message. This is a
-metadata view over transcript content, not a standalone durable attachment blob
-store.
+
+
-### `events_poll`
-
-Reads queued live events since a numeric cursor.
-
-### `events_wait`
-
-Long-polls until the next matching queued event arrives or a timeout expires.
-
-Use this when a generic MCP client needs near-real-time delivery without a
-Claude-specific push protocol.
-
-### `messages_send`
-
-Sends text back through the same route already recorded on the session.
-
-Current behavior:
-
-- requires an existing conversation route
-- uses the session's channel, recipient, account id, and thread id
-- sends text only
-
-### `permissions_list_open`
-
-Lists pending exec/plugin approval requests the bridge has observed since it
-connected to the Gateway.
-
-### `permissions_respond`
-
-Resolves one pending exec/plugin approval request with:
-
-- `allow-once`
-- `allow-always`
-- `deny`
-
-## Event model
+### Event model
The bridge keeps an in-memory event queue while it is connected.
@@ -211,46 +199,43 @@ Current event types:
- `plugin_approval_resolved`
- `claude_permission_request`
-Important limits:
-
+
- the queue is live-only; it starts when the MCP bridge starts
-- `events_poll` and `events_wait` do not replay older Gateway history by
- themselves
+- `events_poll` and `events_wait` do not replay older Gateway history by themselves
- durable backlog should be read with `messages_read`
+
-## Claude channel notifications
+### Claude channel notifications
-The bridge can also expose Claude-specific channel notifications. This is the
-OpenClaw equivalent of a Claude Code channel adapter: standard MCP tools remain
-available, but live inbound messages can also arrive as Claude-specific MCP
-notifications.
+The bridge can also expose Claude-specific channel notifications. This is the OpenClaw equivalent of a Claude Code channel adapter: standard MCP tools remain available, but live inbound messages can also arrive as Claude-specific MCP notifications.
-Flags:
+
+
+ `--claude-channel-mode off`: standard MCP tools only.
+
+
+ `--claude-channel-mode on`: enable Claude channel notifications.
+
+
+ `--claude-channel-mode auto`: current default; same bridge behavior as `on`.
+
+
-- `--claude-channel-mode off`: standard MCP tools only
-- `--claude-channel-mode on`: enable Claude channel notifications
-- `--claude-channel-mode auto`: current default; same bridge behavior as `on`
-
-When Claude channel mode is enabled, the server advertises Claude experimental
-capabilities and can emit:
+When Claude channel mode is enabled, the server advertises Claude experimental capabilities and can emit:
- `notifications/claude/channel`
- `notifications/claude/channel/permission`
Current bridge behavior:
-- inbound `user` transcript messages are forwarded as
- `notifications/claude/channel`
+- inbound `user` transcript messages are forwarded as `notifications/claude/channel`
- Claude permission requests received over MCP are tracked in-memory
-- if the linked conversation later sends `yes abcde` or `no abcde`, the bridge
- converts that to `notifications/claude/channel/permission`
-- these notifications are live-session only; if the MCP client disconnects,
- there is no push target
+- if the linked conversation later sends `yes abcde` or `no abcde`, the bridge converts that to `notifications/claude/channel/permission`
+- these notifications are live-session only; if the MCP client disconnects, there is no push target
-This is intentionally client-specific. Generic MCP clients should rely on the
-standard polling tools.
+This is intentionally client-specific. Generic MCP clients should rely on the standard polling tools.
-## MCP client config
+### MCP client config
Example stdio client config:
@@ -272,43 +257,52 @@ Example stdio client config:
}
```
-For most generic MCP clients, start with the standard tool surface and ignore
-Claude mode. Turn Claude mode on only for clients that actually understand the
-Claude-specific notification methods.
+For most generic MCP clients, start with the standard tool surface and ignore Claude mode. Turn Claude mode on only for clients that actually understand the Claude-specific notification methods.
-## Options
+### Options
`openclaw mcp serve` supports:
-- `--url `: Gateway WebSocket URL
-- `--token `: Gateway token
-- `--token-file `: read token from file
-- `--password `: Gateway password
-- `--password-file `: read password from file
-- `--claude-channel-mode `: Claude notification mode
-- `-v`, `--verbose`: verbose logs on stderr
+
+ Gateway WebSocket URL.
+
+
+ Gateway token.
+
+
+ Read token from file.
+
+
+ Gateway password.
+
+
+ Read password from file.
+
+
+ Claude notification mode.
+
+
+ Verbose logs on stderr.
+
+
Prefer `--token-file` or `--password-file` over inline secrets when possible.
+
-## Security and trust boundary
+### Security and trust boundary
-The bridge does not invent routing. It only exposes conversations that Gateway
-already knows how to route.
+The bridge does not invent routing. It only exposes conversations that Gateway already knows how to route.
That means:
-- sender allowlists, pairing, and channel-level trust still belong to the
- underlying OpenClaw channel configuration
+- sender allowlists, pairing, and channel-level trust still belong to the underlying OpenClaw channel configuration
- `messages_send` can only reply through an existing stored route
- approval state is live/in-memory only for the current bridge session
-- bridge auth should use the same Gateway token or password controls you would
- trust for any other remote Gateway client
+- bridge auth should use the same Gateway token or password controls you would trust for any other remote Gateway client
-If a conversation is missing from `conversations_list`, the usual cause is not
-MCP configuration. It is missing or incomplete route metadata in the underlying
-Gateway session.
+If a conversation is missing from `conversations_list`, the usual cause is not MCP configuration. It is missing or incomplete route metadata in the underlying Gateway session.
-## Testing
+### Testing
OpenClaw ships a deterministic Docker smoke for this bridge:
@@ -320,79 +314,60 @@ That smoke:
- starts a seeded Gateway container
- starts a second container that spawns `openclaw mcp serve`
-- verifies conversation discovery, transcript reads, attachment metadata reads,
- live event queue behavior, and outbound send routing
-- validates Claude-style channel and permission notifications over the real
- stdio MCP bridge
+- verifies conversation discovery, transcript reads, attachment metadata reads, live event queue behavior, and outbound send routing
+- validates Claude-style channel and permission notifications over the real stdio MCP bridge
-This is the fastest way to prove the bridge works without wiring a real
-Telegram, Discord, or iMessage account into the test run.
+This is the fastest way to prove the bridge works without wiring a real Telegram, Discord, or iMessage account into the test run.
For broader testing context, see [Testing](/help/testing).
-## Troubleshooting
+### Troubleshooting
-### No conversations returned
+
+
+ Usually means the Gateway session is not already routable. Confirm that the underlying session has stored channel/provider, recipient, and optional account/thread route metadata.
+
+
+ Expected. The live queue starts when the bridge connects. Read older transcript history with `messages_read`.
+
+
+ Check all of these:
-Usually means the Gateway session is not already routable. Confirm that the
-underlying session has stored channel/provider, recipient, and optional
-account/thread route metadata.
+ - the client kept the stdio MCP session open
+ - `--claude-channel-mode` is `on` or `auto`
+ - the client actually understands the Claude-specific notification methods
+ - the inbound message happened after the bridge connected
-### `events_poll` or `events_wait` misses older messages
-
-Expected. The live queue starts when the bridge connects. Read older transcript
-history with `messages_read`.
-
-### Claude notifications do not show up
-
-Check all of these:
-
-- the client kept the stdio MCP session open
-- `--claude-channel-mode` is `on` or `auto`
-- the client actually understands the Claude-specific notification methods
-- the inbound message happened after the bridge connected
-
-### Approvals are missing
-
-`permissions_list_open` only shows approval requests observed while the bridge
-was connected. It is not a durable approval history API.
+
+
+ `permissions_list_open` only shows approval requests observed while the bridge was connected. It is not a durable approval history API.
+
+
## OpenClaw as an MCP client registry
This is the `openclaw mcp list`, `show`, `set`, and `unset` path.
-These commands do not expose OpenClaw over MCP. They manage OpenClaw-owned MCP
-server definitions under `mcp.servers` in OpenClaw config.
+These commands do not expose OpenClaw over MCP. They manage OpenClaw-owned MCP server definitions under `mcp.servers` in OpenClaw config.
-Those saved definitions are for runtimes that OpenClaw launches or configures
-later, such as embedded Pi and other runtime adapters. OpenClaw stores the
-definitions centrally so those runtimes do not need to keep their own duplicate
-MCP server lists.
+Those saved definitions are for runtimes that OpenClaw launches or configures later, such as embedded Pi and other runtime adapters. OpenClaw stores the definitions centrally so those runtimes do not need to keep their own duplicate MCP server lists.
-Important behavior:
+
+
+ - these commands only read or write OpenClaw config
+ - they do not connect to the target MCP server
+ - they do not validate whether the command, URL, or remote transport is reachable right now
+ - runtime adapters decide which transport shapes they actually support at execution time
+ - embedded Pi exposes configured MCP tools in normal `coding` and `messaging` tool profiles; `minimal` still hides them, and `tools.deny: ["bundle-mcp"]` disables them explicitly
+ - session-scoped bundled MCP runtimes are reaped after `mcp.sessionIdleTtlMs` milliseconds of idle time (default 10 minutes; set `0` to disable) and one-shot embedded runs clean them up at run end
+
+
-- these commands only read or write OpenClaw config
-- they do not connect to the target MCP server
-- they do not validate whether the command, URL, or remote transport is
- reachable right now
-- runtime adapters decide which transport shapes they actually support at
- execution time
-- embedded Pi exposes configured MCP tools in normal `coding` and `messaging`
- tool profiles; `minimal` still hides them, and `tools.deny: ["bundle-mcp"]`
- disables them explicitly
-- session-scoped bundled MCP runtimes are reaped after `mcp.sessionIdleTtlMs`
- milliseconds of idle time (default 10 minutes; set `0` to disable) and
- one-shot embedded runs clean them up at run end
+Runtime adapters may normalize this shared registry into the shape their downstream client expects. For example, embedded Pi consumes OpenClaw `transport` values directly, while Claude Code and Gemini receive CLI-native `type` values such as `http`, `sse`, or `stdio`.
-Runtime adapters may normalize this shared registry into the shape their
-downstream client expects. For example, embedded Pi consumes OpenClaw
-`transport` values directly, while Claude Code and Gemini receive CLI-native
-`type` values such as `http`, `sse`, or `stdio`.
+### Saved MCP server definitions
-## Saved MCP server definitions
-
-OpenClaw also stores a lightweight MCP server registry in config for surfaces
-that want OpenClaw-managed MCP definitions.
+OpenClaw also stores a lightweight MCP server registry in config for surfaces that want OpenClaw-managed MCP definitions.
Commands:
@@ -447,11 +422,13 @@ Launches a local child process and communicates over stdin/stdout.
| `env` | Extra environment variables |
| `cwd` / `workingDirectory` | Working directory for the process |
-#### Stdio env safety filter
+
+**Stdio env safety filter**
OpenClaw rejects interpreter-startup env keys that can alter how a stdio MCP server starts up before the first RPC, even if they appear in a server's `env` block. Blocked keys include `NODE_OPTIONS`, `PYTHONSTARTUP`, `PYTHONPATH`, `PERL5OPT`, `RUBYOPT`, `SHELLOPTS`, `PS4`, and similar runtime-control variables. Startup rejects these with a configuration error so they cannot inject an implicit prelude, swap the interpreter, or enable a debugger against the stdio process. Ordinary credential, proxy, and server-specific env vars (`GITHUB_TOKEN`, `HTTP_PROXY`, custom `*_API_KEY`, etc.) are unaffected.
If your MCP server genuinely needs one of the blocked variables, set it on the gateway host process instead of under the stdio server's `env`.
+
### SSE / HTTP transport
@@ -480,8 +457,7 @@ Example:
}
```
-Sensitive values in `url` (userinfo) and `headers` are redacted in logs and
-status output.
+Sensitive values in `url` (userinfo) and `headers` are redacted in logs and status output.
### Streamable HTTP transport
@@ -513,8 +489,9 @@ Example:
}
```
-These commands manage saved config only. They do not start the channel bridge,
-open a live MCP client session, or prove the target server is reachable.
+
+These commands manage saved config only. They do not start the channel bridge, open a live MCP client session, or prove the target server is reachable.
+
## Current limits
@@ -526,8 +503,7 @@ Current limits:
- no generic push protocol beyond the Claude-specific adapter
- no message edit or react tools yet
- HTTP/SSE/streamable-http transport connects to a single remote server; no multiplexed upstream yet
-- `permissions_list_open` only includes approvals observed while the bridge is
- connected
+- `permissions_list_open` only includes approvals observed while the bridge is connected
## Related