openclaw-mcp 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Tomáš Grasl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -4,8 +4,18 @@
4
4
  [![CI](https://github.com/freema/openclaw-mcp/workflows/CI/badge.svg)](https://github.com/freema/openclaw-mcp/actions/workflows/ci.yml)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
+ <a href="https://glama.ai/mcp/servers/@freema/openclaw-mcp">
8
+ <img width="380" height="200" src="https://glama.ai/mcp/servers/@freema/openclaw-mcp/badge" />
9
+ </a>
10
+
7
11
  🦞 Model Context Protocol (MCP) server for [OpenClaw](https://github.com/openclaw/openclaw) AI assistant integration.
8
12
 
13
+ ## Demo
14
+
15
+ <p align="center">
16
+ <img src="docs/assets/claude-ai-demo.gif" alt="OpenClaw MCP in Claude.ai" width="720" />
17
+ </p>
18
+
9
19
  ## Why I Built This
10
20
 
11
21
  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.
@@ -41,10 +51,13 @@ Add to your Claude Desktop config:
41
51
 
42
52
  ```bash
43
53
  AUTH_ENABLED=true MCP_CLIENT_ID=openclaw MCP_CLIENT_SECRET=your-secret \
54
+ MCP_ISSUER_URL=https://mcp.your-domain.com \
44
55
  CORS_ORIGINS=https://claude.ai OPENCLAW_GATEWAY_TOKEN=your-gateway-token \
45
56
  npx openclaw-mcp --transport sse --port 3000
46
57
  ```
47
58
 
59
+ > **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.
60
+
48
61
  Then in Claude.ai add a custom connector with your `MCP_CLIENT_ID` and `MCP_CLIENT_SECRET`.
49
62
 
50
63
  See [Installation Guide](docs/installation.md) for details.
@@ -124,7 +137,11 @@ See [Configuration](docs/configuration.md) for all security options.
124
137
  ## Requirements
125
138
 
126
139
  - Node.js ≥ 20
127
- - OpenClaw gateway running
140
+ - OpenClaw gateway running with HTTP API enabled:
141
+ ```json5
142
+ // openclaw.json
143
+ { "gateway": { "http": { "endpoints": { "chatCompletions": { "enabled": true } } } } }
144
+ ```
128
145
 
129
146
  ## License
130
147
 
Binary file
@@ -20,6 +20,8 @@ services:
20
20
  - AUTH_ENABLED=${AUTH_ENABLED:-true}
21
21
  - MCP_CLIENT_ID=${MCP_CLIENT_ID:-openclaw}
22
22
  - MCP_CLIENT_SECRET=${MCP_CLIENT_SECRET:-}
23
+ - MCP_ISSUER_URL=${MCP_ISSUER_URL:-}
24
+ - MCP_REDIRECT_URIS=${MCP_REDIRECT_URIS:-}
23
25
  - CORS_ORIGINS=${CORS_ORIGINS:-https://claude.ai}
24
26
  - NODE_ENV=production
25
27
  extra_hosts:
@@ -49,6 +51,9 @@ MCP_CLIENT_SECRET=your-client-secret
49
51
  # Enable OAuth (required for production SSE)
50
52
  AUTH_ENABLED=true
51
53
 
54
+ # Public URL (required when behind a reverse proxy)
55
+ MCP_ISSUER_URL=https://mcp.your-domain.com
56
+
52
57
  # Allowed CORS origins
53
58
  CORS_ORIGINS=https://claude.ai
54
59
  ```
@@ -76,3 +81,104 @@ docker compose up -d
76
81
  - [ ] `OPENCLAW_GATEWAY_TOKEN` set for gateway authentication
77
82
  - [ ] Dynamic client registration is disabled (default — no `/register` endpoint)
78
83
  - [ ] Container runs read-only with no-new-privileges
84
+
85
+ ## Reverse Proxy (HTTPS)
86
+
87
+ The MCP bridge must be served over HTTPS for production use. Use a reverse proxy that handles TLS termination.
88
+
89
+ > **Important:** You **must** set `MCP_ISSUER_URL` to your public HTTPS URL. Without this, OAuth metadata endpoints will advertise `http://localhost:3000` and MCP clients (including Claude.ai) will fail to authenticate with the error: `Protected resource http://localhost:3000/mcp does not match expected https://your-domain.com/mcp`.
90
+
91
+ ### Caddy (recommended)
92
+
93
+ Caddy automatically provisions Let's Encrypt certificates.
94
+
95
+ ```
96
+ mcp.your-domain.com {
97
+ reverse_proxy openclaw-mcp:3000
98
+ }
99
+ ```
100
+
101
+ Add to your `docker-compose.yml`:
102
+
103
+ ```yaml
104
+ services:
105
+ caddy:
106
+ image: caddy:2-alpine
107
+ restart: unless-stopped
108
+ ports:
109
+ - "80:80"
110
+ - "443:443"
111
+ - "443:443/udp"
112
+ volumes:
113
+ - ./Caddyfile:/etc/caddy/Caddyfile:ro
114
+ - caddy-data:/data
115
+ - caddy-config:/config
116
+
117
+ mcp-bridge:
118
+ # ... (same as above, but remove the ports section)
119
+ expose:
120
+ - "3000"
121
+ environment:
122
+ - MCP_ISSUER_URL=https://mcp.your-domain.com
123
+ # ... other env vars
124
+
125
+ volumes:
126
+ caddy-data:
127
+ caddy-config:
128
+ ```
129
+
130
+ ### nginx
131
+
132
+ ```nginx
133
+ server {
134
+ listen 443 ssl http2;
135
+ server_name mcp.your-domain.com;
136
+
137
+ ssl_certificate /path/to/cert.pem;
138
+ ssl_certificate_key /path/to/key.pem;
139
+
140
+ location / {
141
+ proxy_pass http://localhost:3000;
142
+ proxy_http_version 1.1;
143
+ proxy_set_header Upgrade $http_upgrade;
144
+ proxy_set_header Connection "upgrade";
145
+ proxy_set_header Host $host;
146
+ proxy_set_header X-Forwarded-Proto $scheme;
147
+ }
148
+ }
149
+ ```
150
+
151
+ ## OpenClaw Gateway Prerequisites
152
+
153
+ The MCP bridge communicates with the OpenClaw gateway via its OpenAI-compatible HTTP API (`/v1/chat/completions`). This endpoint is **disabled by default** — you must enable it in your OpenClaw config:
154
+
155
+ ```json5
156
+ // openclaw.json
157
+ {
158
+ "gateway": {
159
+ "http": {
160
+ "endpoints": {
161
+ "chatCompletions": {
162
+ "enabled": true
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ Without this, the MCP bridge will receive `405 Method Not Allowed` from the gateway.
171
+
172
+ ## Troubleshooting
173
+
174
+ ### `405 Method Not Allowed` from gateway
175
+
176
+ The OpenClaw gateway's HTTP chat completions endpoint is disabled by default. Enable it in `openclaw.json` — see [Gateway Prerequisites](#openclaw-gateway-prerequisites) above.
177
+
178
+ ### `Protected resource http://localhost:3000/mcp does not match expected https://...`
179
+
180
+ You're running behind a reverse proxy but haven't set `MCP_ISSUER_URL`. The OAuth metadata endpoints are advertising `http://localhost:3000` instead of your public HTTPS URL. Set `MCP_ISSUER_URL` to your public URL (e.g., `https://mcp.your-domain.com`) or pass `--issuer-url` on the CLI.
181
+
182
+ ### `fetch failed` / MCP bridge can't reach gateway
183
+
184
+ When both services run in Docker, the MCP bridge must connect via the Docker network hostname (e.g., `http://openclaw-gateway:18789`), not `localhost`. Make sure both containers are on the same Docker network.
@@ -97,6 +97,10 @@ Options:
97
97
  --auth Enable OAuth [default: false]
98
98
  --client-id MCP OAuth client ID [env: MCP_CLIENT_ID]
99
99
  --client-secret MCP OAuth client secret [env: MCP_CLIENT_SECRET]
100
+ --issuer-url OAuth issuer URL [env: MCP_ISSUER_URL]
101
+ --redirect-uris Allowed redirect URIs [env: MCP_REDIRECT_URIS]
100
102
  --version Show version number
101
103
  --help Show help
102
104
  ```
105
+
106
+ > **Note:** `--issuer-url` is required when running behind a reverse proxy (Caddy, nginx, etc.) so that OAuth metadata endpoints return the correct public HTTPS URL instead of `http://localhost:3000`.
@@ -0,0 +1,102 @@
1
+ # Logging
2
+
3
+ ## Overview
4
+
5
+ The MCP server logs operational events to **stderr** using the `[openclaw-mcp]` prefix. Stderr is used (instead of stdout) because the stdio MCP transport uses stdout for protocol messages.
6
+
7
+ ## Log Levels
8
+
9
+ | Level | Prefix | When |
10
+ |-------|--------|------|
11
+ | Info | `[openclaw-mcp]` | Normal operations — startup, connections, shutdown |
12
+ | Error | `[openclaw-mcp] ERROR:` | Failures — connection errors, invalid config, fatal errors |
13
+ | Debug | `[openclaw-mcp] DEBUG:` | Verbose output — only when `DEBUG=true` or `NODE_ENV=development` |
14
+
15
+ ## What Gets Logged
16
+
17
+ ### Startup
18
+
19
+ - Server name and version
20
+ - OpenClaw gateway URL (host only, no tokens)
21
+ - Transport type (stdio or SSE)
22
+ - Whether a gateway token is configured (yes/no, not the token itself)
23
+ - OAuth client ID (when auth is enabled)
24
+ - Listening address and port (SSE mode)
25
+ - CORS origins configuration
26
+
27
+ ### Connections (SSE/HTTP transport)
28
+
29
+ - SSE session connected/disconnected (with session ID)
30
+ - Streamable HTTP session initialized/closed (with session ID)
31
+
32
+ ### Errors
33
+
34
+ - Gateway connection failures
35
+ - Request timeouts
36
+ - Invalid client configuration (missing secrets, bad client ID format)
37
+ - Session errors
38
+
39
+ ### What Is NOT Logged
40
+
41
+ - **Message content** — user messages and OpenClaw responses are never logged
42
+ - **Authentication tokens** — Bearer tokens, client secrets, gateway tokens
43
+ - **Request/response bodies** — only error messages, not full payloads
44
+ - **User-identifiable information** — no IPs, user agents, or personal data
45
+
46
+ ## Sensitive Data Redaction
47
+
48
+ The logger automatically redacts patterns that look like credentials:
49
+
50
+ - `Bearer <token>` -> `[REDACTED]`
51
+ - `api_key=<value>` -> `[REDACTED]`
52
+ - `token=<value>` -> `[REDACTED]`
53
+ - `secret=<value>` -> `[REDACTED]`
54
+ - `password=<value>` -> `[REDACTED]`
55
+
56
+ This is a safety net — the code avoids logging sensitive values in the first place, but the redaction layer catches accidental exposure.
57
+
58
+ ## Log Destination
59
+
60
+ | Transport | Destination | Notes |
61
+ |-----------|-------------|-------|
62
+ | stdio | stderr | Cannot use stdout (reserved for MCP protocol) |
63
+ | SSE/HTTP | stderr | Same format, same destination |
64
+ | Docker | `docker logs openclaw-mcp` | stderr is captured by Docker's log driver |
65
+
66
+ ## Docker Log Management
67
+
68
+ When running in Docker, logs are managed by the Docker log driver:
69
+
70
+ ```bash
71
+ # View logs
72
+ docker logs openclaw-mcp
73
+
74
+ # Follow logs
75
+ docker logs -f openclaw-mcp
76
+
77
+ # Last 100 lines
78
+ docker logs --tail 100 openclaw-mcp
79
+ ```
80
+
81
+ To configure log rotation in `docker-compose.yml`:
82
+
83
+ ```yaml
84
+ services:
85
+ mcp-bridge:
86
+ logging:
87
+ driver: json-file
88
+ options:
89
+ max-size: "10m"
90
+ max-file: "3"
91
+ ```
92
+
93
+ ## Enabling Debug Logs
94
+
95
+ Set either environment variable:
96
+
97
+ ```bash
98
+ DEBUG=true # Explicit debug flag
99
+ NODE_ENV=development # Development mode
100
+ ```
101
+
102
+ Debug logs include additional operational detail but still never log message content or credentials.
@@ -0,0 +1,116 @@
1
+ # Threat Model
2
+
3
+ ## Architecture Overview
4
+
5
+ ```
6
+ Claude (Desktop / Claude.ai)
7
+ |
8
+ | MCP Protocol (stdio or SSE/Streamable HTTP)
9
+ v
10
+ OpenClaw MCP Server (this project)
11
+ |
12
+ | OpenAI-compatible REST API (POST /v1/chat/completions)
13
+ v
14
+ OpenClaw Gateway (user-controlled)
15
+ ```
16
+
17
+ The MCP server is a **stateless proxy** — it translates MCP tool calls into OpenAI-compatible API requests and returns the response. It does not execute code, access the filesystem, or modify any external state on its own.
18
+
19
+ ## What Claude Can Do (via MCP tools)
20
+
21
+ | Tool | Action | Side Effects |
22
+ |------|--------|--------------|
23
+ | `openclaw_chat` | Send a text message to OpenClaw, receive a text response | None — read-only query |
24
+ | `openclaw_status` | Check if the OpenClaw gateway is reachable | None — health check only |
25
+ | `openclaw_chat_async` | Queue a message for async processing, receive a task ID | Creates an in-memory task |
26
+ | `openclaw_task_status` | Check the status of an async task by ID | None — read-only |
27
+ | `openclaw_task_list` | List all async tasks and their statuses | None — read-only |
28
+ | `openclaw_task_cancel` | Cancel a pending async task by ID | Removes an in-memory task |
29
+
30
+ **Key point:** All tools either read data or send text messages to OpenClaw. The MCP server itself has **no write access** to any filesystem, database, or external service beyond the OpenClaw gateway.
31
+
32
+ ## What Claude Cannot Do
33
+
34
+ - **Execute shell commands** — the server has no shell execution capability
35
+ - **Read or write files** — no filesystem access (Docker enforces `read_only: true`)
36
+ - **Access the network** — can only reach the configured OpenClaw gateway URL
37
+ - **Modify server configuration** — environment variables are set at startup, not changeable at runtime
38
+ - **Bypass authentication** — OAuth tokens are validated per-request when auth is enabled
39
+ - **Access other users' sessions** — sessions are isolated by MCP session ID
40
+
41
+ ## Trust Boundaries
42
+
43
+ ### Boundary 1: Claude <-> MCP Server
44
+
45
+ - **stdio transport (local):** Trusted — communication stays on the local machine. No authentication required.
46
+ - **SSE/HTTP transport (remote):** Untrusted network — requires OAuth 2.1 authentication, HTTPS (via reverse proxy), and CORS restrictions.
47
+
48
+ ### Boundary 2: MCP Server <-> OpenClaw Gateway
49
+
50
+ - The MCP server authenticates to the gateway using a Bearer token (`OPENCLAW_GATEWAY_TOKEN`).
51
+ - The server validates the gateway URL at startup and blocks requests to private IP ranges (SSRF protection).
52
+ - Responses from the gateway are size-limited (10 MB max) and parsed as JSON — no raw pass-through.
53
+
54
+ ### Boundary 3: User <-> Claude
55
+
56
+ - Claude decides which MCP tools to call and with what arguments. The MCP server validates all tool inputs (string length, type, format) but **cannot control Claude's intent**.
57
+ - If OpenClaw can perform actions with real-world consequences (e.g., sending emails, modifying data), those consequences are ultimately triggered by Claude's tool calls through the gateway.
58
+
59
+ ## Attack Surfaces
60
+
61
+ ### 1. Malicious MCP Tool Input
62
+
63
+ **Risk:** Crafted tool arguments could exploit the OpenClaw gateway.
64
+
65
+ **Mitigations:**
66
+ - All tool inputs are validated: type checks, string length limits, control character rejection
67
+ - The MCP server does not interpret message content — it passes validated strings to the gateway
68
+
69
+ ### 2. Compromised OpenClaw Gateway
70
+
71
+ **Risk:** A compromised gateway could return malicious responses.
72
+
73
+ **Mitigations:**
74
+ - Response size limit (10 MB) prevents memory exhaustion
75
+ - Responses are parsed as JSON — no script execution
76
+ - Error messages from the gateway are sanitized before being returned to Claude
77
+
78
+ ### 3. Network-Level Attacks (SSE transport)
79
+
80
+ **Risk:** Man-in-the-middle, replay attacks, unauthorized access.
81
+
82
+ **Mitigations:**
83
+ - OAuth 2.1 with client credentials (required for production)
84
+ - HTTPS via reverse proxy (Caddy or nginx)
85
+ - CORS restricted to configured origins (default: `https://claude.ai`)
86
+ - DNS rebinding protection via MCP SDK's Express middleware
87
+
88
+ ### 4. Server-Side Request Forgery (SSRF)
89
+
90
+ **Risk:** Attacker could trick the server into making requests to internal services.
91
+
92
+ **Mitigations:**
93
+ - Only one outbound destination: the configured `OPENCLAW_URL`
94
+ - Private IP ranges are blocked at the client level
95
+ - URL is set at startup via environment variable, not controllable via tool input
96
+
97
+ ### 5. Denial of Service
98
+
99
+ **Risk:** Resource exhaustion through excessive requests or large payloads.
100
+
101
+ **Mitigations:**
102
+ - Request timeout: 30 seconds per gateway call
103
+ - Response size limit: 10 MB
104
+ - Docker memory limit: 256 MB (configurable)
105
+ - Async task queue is in-memory with bounded processing
106
+
107
+ ## Production Recommendations
108
+
109
+ If you expose this server beyond localhost:
110
+
111
+ 1. **Enable OAuth 2.1** — set `AUTH_ENABLED=true` with strong client credentials
112
+ 2. **Use HTTPS** — terminate TLS at a reverse proxy (Caddy recommended)
113
+ 3. **Restrict CORS** — set `CORS_ORIGINS=https://claude.ai` (or your specific origin)
114
+ 4. **Run in Docker** — use the provided `docker-compose.yml` with `read_only` and `no-new-privileges`
115
+ 5. **Review gateway permissions** — the MCP server is only as safe as what the OpenClaw gateway allows. If OpenClaw can perform destructive actions, consider adding tool allowlists and human approval on the gateway side
116
+ 6. **Monitor logs** — see [Logging](./logging.md) for what gets logged and where
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Model Context Protocol (MCP) server for OpenClaw AI assistant integration",
5
5
  "author": "Tomáš Grasl <https://www.tomasgrasl.cz/>",
6
6
  "license": "MIT",