> ## Documentation Index
> Fetch the complete documentation index at: https://docs2.openclaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Run OpenClaw as an MCP server

This page covers the `openclaw mcp serve` path: OpenClaw acting as an MCP
server over stdio, its tools, its event model, and its limits.

## OpenClaw as an MCP server

This is the `openclaw mcp serve` path.

### When to use serve

Use `openclaw mcp serve` when:

* 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

Use [`openclaw acp`](https://docs2.openclaw.ai/cli/acp) instead when OpenClaw should host the coding runtime itself and keep the agent session inside OpenClaw.

### 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.

**1. Client spawns the bridge**

The MCP client spawns `openclaw mcp serve`.

**2. Bridge connects to Gateway**

The bridge connects to the OpenClaw Gateway over WebSocket.

**3. Sessions become MCP conversations**

Routed sessions become MCP conversations and transcript/history tools.

**4. Live events queue**

Live events are queued in memory while the bridge is connected.

**5. Optional Claude push**

If Claude channel mode is enabled, the same session can also receive Claude-specific push notifications.

> **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
> * cancelling an `events_wait` request immediately releases its server-side wait and timeout
> * bridge or MCP transport close failures make `openclaw mcp serve` fail instead of reporting a clean shutdown
> * 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

**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`.

> [!NOTE]
>
> `auto` behaves the same as `on`. There is no client capability detection.

### 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:

* `channel`
* recipient or destination metadata
* optional `accountId`
* optional `threadId`

This gives MCP clients one place to:

* list recent routed conversations
* read recent transcript history
* wait for new inbound events
* send a reply back through the same route
* see approval requests that arrive while the bridge is connected

### Usage

**Local Gateway**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw mcp serve
```

**Remote Gateway (token)**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw mcp serve --url wss://gateway-host:18789 --token-file ~/.openclaw/gateway.token
```

**Remote Gateway (password)**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw mcp serve --url wss://gateway-host:18789 --password-file ~/.openclaw/gateway.password
```

**Verbose / Claude off**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw mcp serve --verbose
openclaw mcp serve --claude-channel-mode off
```

### Bridge tools

> **conversations\_list**
>
> Lists recent session-backed conversations that already have route metadata in Gateway session state.
>
> Filters: `limit` (max 500), `search`, `channel`, `includeDerivedTitles`, `includeLastMessage`.

> **conversation\_get**
>
> Returns one conversation by `session_key` using a direct Gateway session lookup.

> **messages\_read**
>
> Reads recent transcript messages for one session-backed conversation. `limit` defaults to 20, max 200.

> **attachments\_fetch**
>
> Extracts non-text message content blocks and canonical persisted media metadata from one transcript message. Looks up `message_id` directly through the Gateway, so the message does not need to appear in the recent history window. Persisted entries use `{ "type": "openclaw_media", "media": { ... } }`, where `media` can include `url`, `contentType`, `kind`, `fileName`, dimensions, duration, or size. This is a metadata view, not a standalone durable attachment blob store.

> **events\_poll**
>
> Reads queued live events since a numeric cursor. `limit` max 200. If the requested cursor predates retained queue history, the result also includes `gap.requested_after_cursor` and `gap.oldest_available_cursor`.

> **events\_wait**
>
> Long-polls until the next matching queued event arrives or a timeout expires (default 30s, max 300s).
>
> Use this when a generic MCP client needs near-real-time delivery without a Claude-specific push protocol.
> A known cursor gap returns immediately with the same additive `gap` metadata, even when no matching event is currently retained.

> **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

The bridge keeps an in-memory event queue while it is connected.

Current event types:

* `message`
* `exec_approval_requested`
* `exec_approval_resolved`
* `plugin_approval_requested`
* `plugin_approval_resolved`
* `claude_permission_request`

> [!WARNING]
>
> * 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
> * the queue is bounded; when `gap` is present, read durable history with `messages_read`, then resume with `after_cursor` set to one less than `gap.oldest_available_cursor`
> * durable backlog should be read with `messages_read`

### 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.

**off**

`--claude-channel-mode off`: standard MCP tools only.

**on**

`--claude-channel-mode on`: enable Claude channel notifications.

**auto (default)**

`--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:

* `notifications/claude/channel`
* `notifications/claude/channel/permission`

Current bridge behavior:

* inbound `user` transcript messages are forwarded as `notifications/claude/channel`
* Claude permission requests received over MCP are tracked in-memory
* if the command owner in the linked conversation later sends `yes <id>` or `no <id>` (`<id>` is the 5-letter request id, excluding `l`), 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.

### MCP client config

Example stdio client config:

```json theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  "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.

### Options

`openclaw mcp serve` supports:

**--url** `string`

Gateway WebSocket URL. Defaults to `gateway.remote.url` when configured.

**--token** `string`

Gateway token.

**--token-file** `string`

Read token from file.

**--password** `string`

Gateway password.

**--password-file** `string`

Read password from file.

**--claude-channel-mode** `"auto" | "on" | "off"`

Claude notification mode. Default `auto`.

**-v, --verbose** `boolean`

Verbose logs on stderr.

> [!TIP]
>
> Prefer `--token-file` or `--password-file` over inline secrets when possible.

### Security and trust boundary

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
* `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

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

OpenClaw ships a deterministic Docker smoke for this bridge:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
pnpm test:docker:mcp-channels
```

That smoke runs a single container: it seeds conversation state, starts the Gateway, then spawns `openclaw mcp serve` as a stdio child process and drives it as an MCP client. It verifies conversation discovery, transcript reads, attachment metadata reads, live event queue behavior, and Claude-style channel and permission notifications over the real stdio MCP bridge. Outbound send routing (`messages_send` reusing the stored conversation route) is covered separately by unit tests in `src/mcp/channel-server.test.ts`.

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](https://docs2.openclaw.ai/help/testing.md).

### 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.

> **events\_poll or events\_wait misses older messages**
>
> The live queue starts when the bridge connects and retains a bounded window. If a result includes `gap`, read durable transcript history with `messages_read`, then resume with `after_cursor` set to one less than `gap.oldest_available_cursor`.

> **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.

## Current limits

The bridge has these limits:

* conversation discovery depends on existing Gateway session route metadata
* no generic push protocol beyond the Claude-specific adapter
* no message edit or react tools
* HTTP/SSE/streamable-http transport connects to a single remote server; upstreams are not multiplexed
* `permissions_list_open` only includes approvals observed while the bridge is connected
