freema/openclaw-mcp

freema/openclaw-mcp

πŸ“‡ ☁️ 🏠 - MCP server for [OpenClaw](https://github.com/openclaw/openclaw) AI assistant integration. Enables Claude to delegate tasks to OpenClaw agents with sync/async tools, OAuth 2.1 auth, and SSE transport for Claude.ai.

CLAUDE.md

OpenClaw MCP Server

Project Overview

OpenClaw MCP (Model Context Protocol) server provides a bridge between Claude (Desktop & Claude.ai) and the OpenClaw AI assistant. It implements the MCP specification to expose OpenClaw capabilities as tools that Claude can invoke.

Security Policy (CRITICAL)

This is a security-critical MCP server. Follow these rules:

Docker-First Deployment (MANDATORY)

  • Users MUST run this server via Docker in production
  • Never run directly on bare metal in production environments
  • Docker provides: process isolation, network segmentation, resource limits, reproducible builds
  • Use the provided docker-compose.yml for the complete stack

Authentication

  • OAuth 2.1 MUST be enabled in production (AUTH_ENABLED=true)
  • Set MCP_CLIENT_ID and MCP_CLIENT_SECRET env vars
  • Generate secrets with: openssl rand -hex 32
  • Never commit secrets to the repository
  • Uses MCP SDK's built-in OAuth server (mcpAuthRouter + requireBearerAuth)

Input Validation

  • All MCP tool inputs MUST be validated before processing
  • Validate string lengths, types, and formats
  • Never pass unsanitized input to system calls or APIs

Error Handling

  • Never expose stack traces, internal paths, or credentials in error responses
  • Log errors server-side, return generic messages to clients

Architecture

Claude Desktop/Claude.ai
    ↕ (MCP Protocol - stdio or SSE)
OpenClaw MCP Server (this project)
    ↕ (OpenAI-compatible REST API: POST /v1/chat/completions)
OpenClaw Gateway (http://localhost:18789)

Gateway API

The OpenClaw gateway exposes an OpenAI-compatible endpoint at POST /v1/chat/completions. Authentication uses a Bearer token (OPENCLAW_GATEWAY_TOKEN).

Transports

  • stdio - For local Claude Desktop integration (default)
  • SSE - For remote Claude.ai integration (requires OAuth + HTTPS)

Tools

Tool Type Description
openclaw_chat sync Send message to OpenClaw
openclaw_status sync Health check
openclaw_chat_async async Queue message, get task_id
openclaw_task_status async Check task progress
openclaw_task_list async List all tasks
openclaw_task_cancel async Cancel pending task

Development

Commands

npm run dev          # Watch mode (tsx)
npm run build        # Production build (tsup)
npm run typecheck    # TypeScript type checking
npm run lint         # ESLint
npm run lint:fix     # ESLint with auto-fix
npm run format       # Prettier formatting
npm run test         # Vitest (watch mode)
npm run test:run     # Vitest (single run)
npm run check:all    # Full validation pipeline

Tech Stack

  • TypeScript (ES2022 target, ESM modules)
  • Node.js >=20
  • Build: tsup (bundles to single dist/index.js)
  • Test: vitest
  • Lint: eslint + prettier

File Structure

src/
β”œβ”€β”€ index.ts              # Entry point, MCP server setup
β”œβ”€β”€ cli.ts                # CLI argument parsing (yargs)
β”œβ”€β”€ auth/provider.ts      # OAuth 2.1 server provider (MCP SDK)
β”œβ”€β”€ config/constants.ts   # Server constants
β”œβ”€β”€ mcp/tools/            # MCP tool definitions & handlers
β”œβ”€β”€ mcp/tasks/            # Async task manager
β”œβ”€β”€ openclaw/client.ts    # OpenClaw API client (OpenAI-compatible)
β”œβ”€β”€ openclaw/types.ts     # TypeScript type definitions
β”œβ”€β”€ server/sse.ts         # SSE transport (remote access)
└── utils/                # Logger, errors, response helpers

Conventions

  • ESM imports with .js extensions (required for ESM compatibility)
  • Underscore prefix for unused variables (_var)
  • Single quotes, semicolons, 2-space indent, 100 char width
  • Custom error classes extend OpenClawError
  • All async operations use async/await (no raw promises)

Docker Deployment (Required for Production)

# Quick start
docker compose up -d

# With custom config
cp .env.example .env
# Edit .env with your settings
docker compose up -d

See docs/deployment.md for full production setup including TLS, OAuth, and monitoring.

README.md

OpenClaw MCP Server

npm version CI License: MIT GHCR Website

🦞 Model Context Protocol (MCP) server for OpenClaw AI assistant integration.

Demo

Why I Built This

Hey! I created this MCP server because I didn't want to rely solely on messaging channels to communicate with OpenClaw. What really excites me is the ability to connect OpenClaw to the Claude web UI. Essentially, my chat can delegate tasks to my Claw bot, which then handles everything else β€” like spinning up Claude Code to fix issues for me.

Think of it as an AI assistant orchestrating another AI assistant. Pretty cool, right?

Quick Start

Docker (Recommended)

Pre-built images are published to GitHub Container Registry on every release.

docker pull ghcr.io/freema/openclaw-mcp:latest

Create a docker-compose.yml:

services:
  mcp-bridge:
    image: ghcr.io/freema/openclaw-mcp:latest
    container_name: openclaw-mcp
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - OPENCLAW_URL=http://host.docker.internal:18789
      - OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN}
      - OPENCLAW_MODEL=openclaw
      - AUTH_ENABLED=true
      - MCP_CLIENT_ID=openclaw
      - MCP_CLIENT_SECRET=${MCP_CLIENT_SECRET}
      - MCP_ISSUER_URL=${MCP_ISSUER_URL:-}
      - CORS_ORIGINS=https://claude.ai
    extra_hosts:
      - "host.docker.internal:host-gateway"
    read_only: true
    security_opt:
      - no-new-privileges

Generate secrets and start:

export MCP_CLIENT_SECRET=$(openssl rand -hex 32)
export OPENCLAW_GATEWAY_TOKEN=your-gateway-token
docker compose up -d

Then in Claude.ai add a custom MCP connector pointing to your server with MCP_CLIENT_ID=openclaw and your MCP_CLIENT_SECRET.

Tip: Pin a specific version instead of latest for production: ghcr.io/freema/openclaw-mcp:1.1.0

Local (Claude Desktop)

npx openclaw-mcp

Add to your Claude Desktop config:

{
  "mcpServers": {
    "openclaw": {
      "command": "npx",
      "args": ["openclaw-mcp"],
      "env": {
        "OPENCLAW_URL": "http://127.0.0.1:18789",
        "OPENCLAW_GATEWAY_TOKEN": "your-gateway-token",
        "OPENCLAW_MODEL": "openclaw",
        "OPENCLAW_TIMEOUT_MS": "300000"
      }
    }
  }
}

Remote (Claude.ai) without Docker

AUTH_ENABLED=true MCP_CLIENT_ID=openclaw MCP_CLIENT_SECRET=your-secret \
  MCP_ISSUER_URL=https://mcp.your-domain.com \
  CORS_ORIGINS=https://claude.ai OPENCLAW_GATEWAY_TOKEN=your-gateway-token \
  npx openclaw-mcp --transport sse --port 3000

Important: When running behind a reverse proxy (Caddy, nginx, etc.), you must set MCP_ISSUER_URL (or --issuer-url) to your public HTTPS URL. Without this, OAuth metadata will advertise http://localhost:3000 and clients will fail to authenticate.

See Installation Guide for details.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Your Server                             β”‚
β”‚                                                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚
β”‚  β”‚   OpenClaw      β”‚      β”‚    OpenClaw MCP         β”‚          β”‚
β”‚  β”‚   Gateway       │◄────►│    Bridge Server        β”‚          β”‚
β”‚  β”‚   :18789        β”‚      β”‚    :3000                β”‚          β”‚
β”‚  β”‚                 β”‚      β”‚                         β”‚          β”‚
β”‚  β”‚  OpenAI-compat  β”‚      β”‚  - OAuth 2.1 auth       β”‚          β”‚
β”‚  β”‚  /v1/chat/...   β”‚      β”‚  - CORS protection      β”‚          β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚  - Input validation     β”‚          β”‚
β”‚                           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚
β”‚                                      β”‚                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                       β”‚ HTTPS + OAuth 2.1
                                       β–Ό
                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                              β”‚   Claude.ai     β”‚
                              β”‚   (MCP Client)  β”‚
                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Available Tools

Sync Tools

Tool Description
openclaw_chat Send messages to OpenClaw and get responses
openclaw_status Check OpenClaw gateway health
openclaw_instances List all configured OpenClaw instances

Async Tools (for long-running operations)

Tool Description
openclaw_chat_async Queue a message, get task_id immediately
openclaw_task_status Check task progress and get results
openclaw_task_list List all tasks with filtering
openclaw_task_cancel Cancel a pending task

Multi-Instance Mode

Orchestrate multiple OpenClaw gateways from a single MCP server. One bridge, many claws β€” route requests to prod, staging, dev, or whatever you name them (lobster-supreme and the-claw-abides are perfectly valid names).

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Claude.ai / Claude Desktop                    β”‚
β”‚                              (MCP Client)                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     OpenClaw MCP Bridge Server                        β”‚
β”‚                                                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”‚
β”‚  β”‚  Instance     β”‚  β”‚  Instance     β”‚  β”‚  Instance     β”‚              β”‚
β”‚  β”‚  Registry     β”‚  β”‚  Resolver     β”‚  β”‚  Validator    β”‚              β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚
β”‚         β”‚                 β”‚                  β”‚                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”               β”‚
β”‚  β”‚              Per-Instance OpenClaw Clients          β”‚              β”‚
β”‚  β”‚     (separate auth, timeout, URL per instance)     β”‚              β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚              β”‚              β”‚
            β–Ό              β–Ό              β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  🦞 prod     β”‚ β”‚  🦞 staging  β”‚ β”‚  🦞 dev      β”‚
   β”‚  (default)   β”‚ β”‚              β”‚ β”‚              β”‚
   β”‚  :18789      β”‚ β”‚  :18789      β”‚ β”‚  :18789      β”‚
   β”‚  OpenClaw GW β”‚ β”‚  OpenClaw GW β”‚ β”‚  OpenClaw GW β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Setup

OPENCLAW_INSTANCES='[
  {"name": "prod", "url": "http://prod:18789", "token": "tok1", "default": true},
  {"name": "staging", "url": "http://staging:18789", "token": "tok2"},
  {"name": "dev", "url": "http://dev:18789", "token": "tok3"}
]'

Usage

All tools accept an optional instance parameter to target a specific gateway:

# Chat with staging instance
openclaw_chat message="Deploy status?" instance="staging"

# Check health of prod
openclaw_status instance="prod"

# List all configured instances
openclaw_instances

# Async task targeting dev
openclaw_chat_async message="Run tests" instance="dev"

When instance is omitted, the default instance is used. Each instance has its own auth token, timeout, and URL β€” fully isolated.

Key Features

  • Zero-migration upgrade β€” existing single-instance deployments work without any config change
  • Per-instance isolation β€” separate auth tokens, timeouts, and URLs
  • Dynamic routing β€” Claude picks the right instance per request
  • Task tracking β€” async tasks remember which instance they target
  • Security β€” tokens are never exposed via openclaw_instances

See Configuration β€” Multi-Instance Mode for the full reference.

Documentation

  • Installation β€” Setup for Claude Desktop & Claude.ai
  • Configuration β€” Environment variables & options
  • Deployment β€” Docker & production setup
  • Threat Model β€” What Claude can/can't trigger, trust boundaries & attack surfaces
  • Logging β€” What gets logged, where, and what is never logged
  • Development β€” Contributing & adding tools
  • Security β€” Security policy & best practices

Security

⚠️ Always enable authentication in production!

# Generate secure client secret
export MCP_CLIENT_SECRET=$(openssl rand -hex 32)

# Run with auth enabled
AUTH_ENABLED=true MCP_CLIENT_ID=openclaw MCP_CLIENT_SECRET=$MCP_CLIENT_SECRET \
  openclaw-mcp --transport sse

Configure CORS to restrict access:

CORS_ORIGINS=https://claude.ai,https://your-app.com

See Configuration for all security options.

Requirements

  • Node.js β‰₯ 20
  • OpenClaw gateway running with HTTP API enabled:
    // openclaw.json
    { "gateway": { "http": { "endpoints": { "chatCompletions": { "enabled": true } } } } }
    

License

MIT

Author

Created by TomΓ‘Ε‘ Grasl

Related Projects