docs/cli/mcp.md
openclaw mcp has two jobs:
openclaw mcp servelist, show, set, and unsetIn other words:
serve is OpenClaw acting as an MCP serverlist / show / set / unset is OpenClaw acting as an MCP client-side registry for other MCP servers its runtimes may consume laterUse openclaw acp when OpenClaw should host a coding harness session itself and route that runtime through ACP.
This is the openclaw mcp serve path.
serveUse openclaw mcp serve when:
Use openclaw acp instead when OpenClaw should host the coding runtime itself and keep the agent session inside OpenClaw.
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.
Use the same bridge in two different ways:
<Tabs> <Tab title="Generic MCP clients"> Standard MCP tools only. Use `conversations_list`, `messages_read`, `events_poll`, `events_wait`, `messages_send`, and the approval tools. </Tab> <Tab title="Claude Code"> Standard MCP tools plus the Claude-specific channel adapter. Enable `--claude-channel-mode on` or leave the default `auto`. </Tab> </Tabs> <Note> Today, `auto` behaves the same as `on`. There is no client capability detection yet. </Note>serve exposesThe 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:
channelaccountIdthreadIdThis gives MCP clients one place to:
The current bridge exposes these MCP tools:
<AccordionGroup> <Accordion title="conversations_list"> Lists recent session-backed conversations that already have route metadata in Gateway session state.Useful filters:
- `limit`
- `search`
- `channel`
- `includeDerivedTitles`
- `includeLastMessage`
Use this when a generic MCP client needs near-real-time delivery without a Claude-specific push protocol.
Current behavior:
- requires an existing conversation route
- uses the session's channel, recipient, account id, and thread id
- sends text only
- `allow-once`
- `allow-always`
- `deny`
The bridge keeps an in-memory event queue while it is connected.
Current event types:
messageexec_approval_requestedexec_approval_resolvedplugin_approval_requestedplugin_approval_resolvedclaude_permission_requestThe 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.
<Tabs> <Tab title="off"> `--claude-channel-mode off`: standard MCP tools only. </Tab> <Tab title="on"> `--claude-channel-mode on`: enable Claude channel notifications. </Tab> <Tab title="auto (default)"> `--claude-channel-mode auto`: current default; same bridge behavior as `on`. </Tab> </Tabs>When Claude channel mode is enabled, the server advertises Claude experimental capabilities and can emit:
notifications/claude/channelnotifications/claude/channel/permissionCurrent bridge behavior:
user transcript messages are forwarded as notifications/claude/channelyes abcde or no abcde, the bridge converts that to notifications/claude/channel/permissionThis is intentionally client-specific. Generic MCP clients should rely on the standard polling tools.
Example stdio client config:
{
"mcpServers": {
"openclaw": {
"command": "openclaw",
"args": [
"mcp",
"serve",
"--url",
"wss://gateway-host:18789",
"--token-file",
"/path/to/gateway.token"
]
}
}
}
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.
openclaw mcp serve supports:
The bridge does not invent routing. It only exposes conversations that Gateway already knows how to route.
That means:
messages_send can only reply through an existing stored routeIf 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.
OpenClaw ships a deterministic Docker smoke for this bridge:
pnpm test:docker:mcp-channels
That smoke:
openclaw mcp serveThis 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.
- 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
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.
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.
<AccordionGroup> <Accordion title="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 </Accordion> </AccordionGroup>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.
OpenClaw also stores a lightweight MCP server registry in config for surfaces that want OpenClaw-managed MCP definitions.
Commands:
openclaw mcp listopenclaw mcp show [name]openclaw mcp set <name> <json>openclaw mcp unset <name>Notes:
list sorts server names.show without a name prints the full configured MCP server object.set expects one JSON object value on the command line.transport: "streamable-http" for Streamable HTTP MCP servers. openclaw mcp set also normalizes CLI-native type: "http" to the same canonical config shape for compatibility.unset fails if the named server does not exist.Examples:
openclaw mcp list
openclaw mcp show context7 --json
openclaw mcp set context7 '{"command":"uvx","args":["context7-mcp"]}'
openclaw mcp set docs '{"url":"https://mcp.example.com","transport":"streamable-http"}'
openclaw mcp unset context7
Example config shape:
{
"mcp": {
"servers": {
"context7": {
"command": "uvx",
"args": ["context7-mcp"]
},
"docs": {
"url": "https://mcp.example.com",
"transport": "streamable-http"
}
}
}
}
Launches a local child process and communicates over stdin/stdout.
| Field | Description |
|---|---|
command | Executable to spawn (required) |
args | Array of command-line arguments |
env | Extra environment variables |
cwd / workingDirectory | Working directory for the process |
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.
</Warning>
Connects to a remote MCP server over HTTP Server-Sent Events.
| Field | Description |
|---|---|
url | HTTP or HTTPS URL of the remote server (required) |
headers | Optional key-value map of HTTP headers (for example auth tokens) |
connectionTimeoutMs | Per-server connection timeout in ms (optional) |
Example:
{
"mcp": {
"servers": {
"remote-tools": {
"url": "https://mcp.example.com",
"headers": {
"Authorization": "Bearer <token>"
}
}
}
}
}
Sensitive values in url (userinfo) and headers are redacted in logs and status output.
streamable-http is an additional transport option alongside sse and stdio. It uses HTTP streaming for bidirectional communication with remote MCP servers.
| Field | Description |
|---|---|
url | HTTP or HTTPS URL of the remote server (required) |
transport | Set to "streamable-http" to select this transport; when omitted, OpenClaw uses sse |
headers | Optional key-value map of HTTP headers (for example auth tokens) |
connectionTimeoutMs | Per-server connection timeout in ms (optional) |
OpenClaw config uses transport: "streamable-http" as the canonical spelling. CLI-native MCP type: "http" values are accepted when saved through openclaw mcp set and repaired by openclaw doctor --fix in existing config, but transport is what embedded Pi consumes directly.
Example:
{
"mcp": {
"servers": {
"streaming-tools": {
"url": "https://mcp.example.com/stream",
"transport": "streamable-http",
"connectionTimeoutMs": 10000,
"headers": {
"Authorization": "Bearer <token>"
}
}
}
}
}
This page documents the bridge as shipped today.
Current limits:
permissions_list_open only includes approvals observed while the bridge is connected