Skip to main content
Glama
wenyue

OpenClaw MCP Proxy

by wenyue

OpenClaw MCP Proxy

OpenClaw MCP Proxy is a small FastAPI + FastMCP service that exposes app-registered tools as an MCP server for OpenClaw, while forwarding actual tool execution back to the app over a WebSocket bridge.

It has two responsibilities:

  • Manage chat-scoped proxy sessions over HTTP and WebSocket.

  • Expose the registered tool set as MCP over either stateless HTTP or stdio.

Architecture

sequenceDiagram
    participant AppClient
    participant ProxyServer
    participant OpenClawClient

    AppClient->>ProxyServer: POST /v1/chat/sessions
    ProxyServer-->>AppClient: mcpSessionId, bridge_url, mcp_url
    AppClient->>ProxyServer: WS /v1/chat/sessions/{session_id}/bridge
    OpenClawClient->>ProxyServer: POST /v1/mcp/{session_id}
    ProxyServer->>AppClient: invoke_tool
    AppClient-->>ProxyServer: invoke_result
    ProxyServer-->>OpenClawClient: MCP tool result
    AppClient->>ProxyServer: DELETE /v1/chat/sessions/{session_id}

Related MCP server: MCP Host RPC Bridge

Endpoints

POST /v1/chat/sessions

Creates a session and returns the bridge and MCP endpoints for that session.

Request body:

{
  "device_id": "device-1",
  "device_name": "desktop",
  "app_version": "1.0.0",
  "chat_id": "chat-1",
  "tools": [
    {
      "name": "echo_text",
      "path": "/tools/echo_text",
      "description": "Echo text.",
      "input_schema": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string"
          }
        }
      }
    }
  ]
}

Response body:

{
  "mcpSessionId": "session-id",
  "bridge_url": "ws://127.0.0.1:8000/v1/chat/sessions/session-id/bridge",
  "mcp_url": "http://127.0.0.1:8000/v1/mcp/session-id"
}

DELETE /v1/chat/sessions/{session_id}

Deletes a session.

Response body:

{
  "ok": true
}

WS /v1/chat/sessions/{session_id}/bridge

Connects the app-side execution bridge for a registered session.

Bridge messages:

  • Proxy -> app: invoke_tool

  • App -> proxy: invoke_result

  • Proxy -> app: ping

  • App -> proxy: pong

Proxy -> app:

{
  "type": "invoke_tool",
  "mcpSessionId": "session-id",
  "request_id": "session-id:1",
  "tool_name": "echo_text",
  "arguments": {
    "text": "hello"
  }
}

App -> proxy:

{
  "type": "invoke_result",
  "mcpSessionId": "session-id",
  "request_id": "session-id:1",
  "ok": true,
  "content": {
    "echoed_text": "hello"
  }
}

POST /v1/mcp/{session_id}

Exposes the registered tools for a specific session as a stateless HTTP MCP endpoint.

This is the simplest way to connect OpenClaw to a specific registered session.

POST /v1/mcp/ with MCP-Session-Id

The MCP endpoint also supports header-based session routing:

  • Header: MCP-Session-Id: <session_id>

This is useful when the MCP client configuration prefers a stable URL and injects the session ID through headers.

MCP Transports

The proxy now supports two MCP-facing transports:

  • HTTP: multi-session, routed by path or MCP-Session-Id

  • stdio: single-session, bound during MCP initialize via mcpSessionId

The stdio transport is implemented as a local proxy process in front of the HTTP MCP endpoint. The actual tool execution path is unchanged:

  1. The app registers a session over HTTP.

  2. The app connects the WebSocket bridge.

  3. The stdio process stores initialize.params.mcpSessionId and proxies MCP traffic to POST /v1/mcp with header MCP-Session-Id.

  4. The HTTP proxy forwards tool execution to the app over the existing bridge.

GET /health

Returns plain text ok.

Authentication

The proxy uses two independent bearer tokens:

  • OPENCLAW_PROXY_APP_TOKEN Used by:

    • POST /v1/chat/sessions

    • DELETE /v1/chat/sessions/{session_id}

    • WS /v1/chat/sessions/{session_id}/bridge

  • OPENCLAW_PROXY_OPENCLAW_TOKEN Used by:

    • /v1/mcp/...

Important:

  • If OPENCLAW_PROXY_APP_TOKEN is empty, app-facing endpoints accept requests without authentication.

  • If OPENCLAW_PROXY_OPENCLAW_TOKEN is empty, MCP-facing endpoints accept requests without authentication.

Do not leave either token empty outside local development.

WebSocket close codes:

  • 4401: invalid app token

  • 4404: unknown session_id

Configuration

Environment variables:

Variable

Default

Description

OPENCLAW_PROXY_APP_TOKEN

""

Bearer token for app registration and bridge endpoints.

OPENCLAW_PROXY_OPENCLAW_TOKEN

""

Bearer token for MCP requests from OpenClaw.

OPENCLAW_PROXY_SERVER_URL

http://127.0.0.1:8000

Base URL used by the stdio proxy process to reach the HTTP proxy.

OPENCLAW_PROXY_SESSION_TTL_SECONDS

300

Session time-to-live in seconds.

OPENCLAW_PROXY_TOOL_TIMEOUT_SECONDS

120

Tool call timeout in seconds.

Example .env:

OPENCLAW_PROXY_APP_TOKEN=replace-me
OPENCLAW_PROXY_OPENCLAW_TOKEN=replace-me
OPENCLAW_PROXY_SERVER_URL=http://127.0.0.1:8000
OPENCLAW_PROXY_SESSION_TTL_SECONDS=300
OPENCLAW_PROXY_TOOL_TIMEOUT_SECONDS=120

Run Locally

Requirements

  • Python 3.11+ recommended

  • pip

Install dependencies

pip install -r requirements.txt

Start the server

uvicorn app.main:app --host 0.0.0.0 --port 8000

Start the stdio MCP proxy

After creating a session and connecting the app bridge, you can expose that session over stdio:

python -m app.stdio_main

Optional flags:

python -m app.stdio_main \
  --proxy-base-url http://127.0.0.1:8000

The stdio client must send mcpSessionId in initialize.params, for example:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-03-26",
    "capabilities": {},
    "clientInfo": {
      "name": "your-client",
      "version": "1.0.0"
    },
    "mcpSessionId": "<session_id>"
  }
}

Check health

curl http://127.0.0.1:8000/health

Expected output:

ok

OpenClaw MCP Configuration

Header-routed example:

{
  "mcpServers": {
    "otakuroom-chat-mcp": {
      "transport": "http",
      "url": "https://your-proxy-host.example.com/v1/mcp",
      "headers": {
        "Authorization": "Bearer ${OPENCLAW_PROXY_OPENCLAW_TOKEN}",
        "MCP-Session-Id": "${SESSION_ID}"
      }
    }
  }
}

You can also connect directly to the session-specific URL returned by session creation, for example:

https://your-proxy-host.example.com/v1/mcp/<session_id>

Stdio example:

{
  "mcpServers": {
    "otakuroom-chat-mcp": {
      "transport": "stdio",
      "command": "python",
      "args": [
        "-m",
        "app.stdio_main"
      ],
      "env": {
        "OPENCLAW_PROXY_SERVER_URL": "http://127.0.0.1:8000",
        "OPENCLAW_PROXY_OPENCLAW_TOKEN": "${OPENCLAW_PROXY_OPENCLAW_TOKEN}"
      }
    }
  }
}

How Tool Forwarding Works

  1. The app creates a session and sends its available tool definitions.

  2. The proxy stores the session in memory.

  3. OpenClaw calls the session MCP endpoint.

  4. The proxy dynamically builds a FastMCP server for that session and tool set.

  5. When OpenClaw invokes a tool, the proxy sends invoke_tool over the WebSocket bridge.

  6. The app executes the tool locally and sends invoke_result.

  7. The proxy returns the tool result to the MCP caller.

Testing

Run the proxy integration tests:

python -m unittest tests.test_proxy_integration

Current coverage includes:

  • session-specific MCP routing via mcp_url

  • header-based MCP routing via MCP-Session-Id

  • normal WebSocket bridge disconnect without error-level logging

  • shared MCP server construction and stdio proxy bootstrap behavior

Operational Notes

  • Sessions are stored in memory only. The proxy is not currently designed for stateless multi-instance deployment without sticky routing or shared session state.

  • Tool calls require an active bridge connection. If the session exists but the bridge is disconnected, tool calls fail with Bridge is not connected.

  • Sessions expire automatically. A background cleanup loop runs every 15 seconds and removes expired sessions.

  • Registered tool names must be unique within a session.

  • Reverse proxies must support WebSocket upgrade and must forward:

    • Authorization

    • MCP-Session-Id

  • HTTP MCP is served directly from the in-process session registry.

  • stdio MCP runs as a separate proxy process and forwards to the HTTP MCP endpoint.

  • Tool schemas are registered dynamically from the provided input_schema. Only trusted app clients should be allowed to register tool definitions.

Limitations

  • No persistent session storage.

  • No built-in rate limiting.

  • No container or deployment manifests are included in this directory.

  • Audit logging is plain application logging, not a full security audit pipeline.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A bridge server that connects MCP-compatible clients to the OpenClaw (Jarvis) AI assistant for asynchronous task processing and real-time WebSocket communication. It enables users to send intents, execute tasks, and query results using a suite of tools including process_intent, ask_jarvis, and execute_task.
    3
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Exposes any stdio-based MCP server to the internet via HTTP/SSE transport, enabling remote agents to access MCP tools over a network.
    4 npm
    MIT