openclaw-mcp 1.3.0 → 1.3.1

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/README.md CHANGED
@@ -47,6 +47,7 @@ services:
47
47
  environment:
48
48
  - OPENCLAW_URL=http://host.docker.internal:18789
49
49
  - OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN}
50
+ - OPENCLAW_MODEL=openclaw
50
51
  - AUTH_ENABLED=true
51
52
  - MCP_CLIENT_ID=openclaw
52
53
  - MCP_CLIENT_SECRET=${MCP_CLIENT_SECRET}
@@ -88,6 +89,7 @@ Add to your Claude Desktop config:
88
89
  "env": {
89
90
  "OPENCLAW_URL": "http://127.0.0.1:18789",
90
91
  "OPENCLAW_GATEWAY_TOKEN": "your-gateway-token",
92
+ "OPENCLAW_MODEL": "openclaw",
91
93
  "OPENCLAW_TIMEOUT_MS": "300000"
92
94
  }
93
95
  }
@@ -141,6 +143,7 @@ See [Installation Guide](docs/installation.md) for details.
141
143
  |------|-------------|
142
144
  | `openclaw_chat` | Send messages to OpenClaw and get responses |
143
145
  | `openclaw_status` | Check OpenClaw gateway health |
146
+ | `openclaw_instances` | List all configured OpenClaw instances |
144
147
 
145
148
  ### Async Tools (for long-running operations)
146
149
 
@@ -151,6 +154,80 @@ See [Installation Guide](docs/installation.md) for details.
151
154
  | `openclaw_task_list` | List all tasks with filtering |
152
155
  | `openclaw_task_cancel` | Cancel a pending task |
153
156
 
157
+ ## Multi-Instance Mode
158
+
159
+ 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).
160
+
161
+ ```
162
+ ┌──────────────────────────────────────────────────────────────────────┐
163
+ │ Claude.ai / Claude Desktop │
164
+ │ (MCP Client) │
165
+ └──────────────────────┬───────────────────────────────────────────────┘
166
+
167
+
168
+ ┌──────────────────────────────────────────────────────────────────────┐
169
+ │ OpenClaw MCP Bridge Server │
170
+ │ │
171
+ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
172
+ │ │ Instance │ │ Instance │ │ Instance │ │
173
+ │ │ Registry │ │ Resolver │ │ Validator │ │
174
+ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
175
+ │ │ │ │ │
176
+ │ ┌──────┴─────────────────┴──────────────────┴───────┐ │
177
+ │ │ Per-Instance OpenClaw Clients │ │
178
+ │ │ (separate auth, timeout, URL per instance) │ │
179
+ │ └────────┬──────────────┬──────────────┬────────────┘ │
180
+ └───────────┼──────────────┼──────────────┼────────────────────────────┘
181
+ │ │ │
182
+ ▼ ▼ ▼
183
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
184
+ │ 🦞 prod │ │ 🦞 staging │ │ 🦞 dev │
185
+ │ (default) │ │ │ │ │
186
+ │ :18789 │ │ :18789 │ │ :18789 │
187
+ │ OpenClaw GW │ │ OpenClaw GW │ │ OpenClaw GW │
188
+ └──────────────┘ └──────────────┘ └──────────────┘
189
+ ```
190
+
191
+ ### Setup
192
+
193
+ ```bash
194
+ OPENCLAW_INSTANCES='[
195
+ {"name": "prod", "url": "http://prod:18789", "token": "tok1", "default": true},
196
+ {"name": "staging", "url": "http://staging:18789", "token": "tok2"},
197
+ {"name": "dev", "url": "http://dev:18789", "token": "tok3"}
198
+ ]'
199
+ ```
200
+
201
+ ### Usage
202
+
203
+ All tools accept an optional `instance` parameter to target a specific gateway:
204
+
205
+ ```
206
+ # Chat with staging instance
207
+ openclaw_chat message="Deploy status?" instance="staging"
208
+
209
+ # Check health of prod
210
+ openclaw_status instance="prod"
211
+
212
+ # List all configured instances
213
+ openclaw_instances
214
+
215
+ # Async task targeting dev
216
+ openclaw_chat_async message="Run tests" instance="dev"
217
+ ```
218
+
219
+ When `instance` is omitted, the default instance is used. Each instance has its own auth token, timeout, and URL — fully isolated.
220
+
221
+ ### Key Features
222
+
223
+ - **Zero-migration upgrade** — existing single-instance deployments work without any config change
224
+ - **Per-instance isolation** — separate auth tokens, timeouts, and URLs
225
+ - **Dynamic routing** — Claude picks the right instance per request
226
+ - **Task tracking** — async tasks remember which instance they target
227
+ - **Security** — tokens are never exposed via `openclaw_instances`
228
+
229
+ See [Configuration — Multi-Instance Mode](docs/configuration.md#multi-instance-mode) for the full reference.
230
+
154
231
  ## Documentation
155
232
 
156
233
  - [Installation](docs/installation.md) — Setup for Claude Desktop & Claude.ai
package/dist/index.js CHANGED
@@ -5,11 +5,13 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
5
5
 
6
6
  // src/config/constants.ts
7
7
  var SERVER_NAME = "openclaw-mcp";
8
- var SERVER_VERSION = "1.3.0";
8
+ var SERVER_VERSION = "1.3.1";
9
9
  var DEFAULT_OPENCLAW_URL = "http://127.0.0.1:18789";
10
+ var DEFAULT_MODEL = "openclaw";
10
11
  var SERVER_ICON_SVG_BASE64 = "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCIgZmlsbD0ibm9uZSI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzFhMWEyZSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzE2MjEzZSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJjbGF3IiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZmYzMzMzIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjY2MwMDAwIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjEyOCIgaGVpZ2h0PSIxMjgiIHJ4PSIyNCIgZmlsbD0idXJsKCNiZykiLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2NCA2NCkiIHN0cm9rZT0idXJsKCNjbGF3KSIgc3Ryb2tlLXdpZHRoPSI3IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0tMjggLTM4YzAgMCAtMTAgMjAgMCAzMiIvPjxwYXRoIGQ9Ik0tMTIgLTQwYzAgMCAtNiAyMiA0IDM0Ii8+PHBhdGggZD0iTTI4IC0zOGMwIDAgMTAgMjAgMCAzMiIvPjxwYXRoIGQ9Ik0xMiAtNDBjMCAwIDYgMjIgLTQgMzQiLz48Y2lyY2xlIGN4PSIwIiBjeT0iMTAiIHI9IjIwIiBzdHJva2Utd2lkdGg9IjYiLz48cGF0aCBkPSJNLTEwIDR2LTQiIHN0cm9rZS13aWR0aD0iNCIvPjxwYXRoIGQ9Ik0xMCA0di00IiBzdHJva2Utd2lkdGg9IjQiLz48cGF0aCBkPSJNLTggMjBjNCA2IDEyIDYgMTYgMCIgc3Ryb2tlLXdpZHRoPSIzIi8+PC9nPjwvc3ZnPg==";
11
12
 
12
13
  // src/utils/logger.ts
14
+ var debugEnabled = false;
13
15
  var SENSITIVE_PATTERNS = [
14
16
  /Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi,
15
17
  /api[_-]?key["\s:=]+[A-Za-z0-9\-._~+/]{8,}/gi,
@@ -27,6 +29,19 @@ function sanitizeLogMessage(message) {
27
29
  function log(message) {
28
30
  console.error(`[openclaw-mcp] ${sanitizeLogMessage(message)}`);
29
31
  }
32
+ function setDebugEnabled(enabled) {
33
+ debugEnabled = enabled;
34
+ }
35
+ function isDebugEnabled() {
36
+ return debugEnabled;
37
+ }
38
+ function logDebug(messageOrFactory) {
39
+ if (!debugEnabled) {
40
+ return;
41
+ }
42
+ const message = typeof messageOrFactory === "function" ? messageOrFactory() : messageOrFactory;
43
+ console.error(`[openclaw-mcp] DEBUG: ${sanitizeLogMessage(message)}`);
44
+ }
30
45
  function logError(message, error) {
31
46
  console.error(`[openclaw-mcp] ERROR: ${sanitizeLogMessage(message)}`);
32
47
  if (error) {
@@ -51,6 +66,11 @@ function parseArguments(version) {
51
66
  type: "string",
52
67
  description: "Bearer token for OpenClaw gateway authentication",
53
68
  default: process.env.OPENCLAW_GATEWAY_TOKEN || void 0
69
+ }).option("model", {
70
+ alias: "m",
71
+ type: "string",
72
+ description: "Model name for chat completions",
73
+ default: process.env.OPENCLAW_MODEL || DEFAULT_MODEL
54
74
  }).option("transport", {
55
75
  alias: "t",
56
76
  type: "string",
@@ -70,6 +90,10 @@ function parseArguments(version) {
70
90
  type: "number",
71
91
  description: "Request timeout in milliseconds",
72
92
  default: parseInt(process.env.OPENCLAW_TIMEOUT_MS || "120000", 10)
93
+ }).option("debug", {
94
+ type: "boolean",
95
+ description: "Enable debug logging",
96
+ default: process.env.DEBUG === "true" || process.env.NODE_ENV === "development"
73
97
  }).option("auth", {
74
98
  type: "boolean",
75
99
  description: "Enable OAuth authentication (SSE mode)",
@@ -133,10 +157,12 @@ function parseArguments(version) {
133
157
  return {
134
158
  openclawUrl: argv["openclaw-url"],
135
159
  gatewayToken: argv["gateway-token"],
160
+ model: argv.model,
136
161
  transport: argv.transport,
137
162
  port: argv.port,
138
163
  host: argv.host,
139
164
  timeout: argv.timeout,
165
+ debug: argv.debug,
140
166
  authEnabled: argv.auth,
141
167
  clientId: argv["client-id"],
142
168
  clientSecret: argv["client-secret"],
@@ -171,14 +197,17 @@ var OpenClawApiError = class extends OpenClawError {
171
197
  // src/openclaw/client.ts
172
198
  var DEFAULT_TIMEOUT_MS = 12e4;
173
199
  var MAX_RESPONSE_SIZE_BYTES = 10 * 1024 * 1024;
200
+ var MAX_DEBUG_BODY_LENGTH = 4096;
174
201
  var OpenClawClient = class {
175
202
  baseUrl;
176
203
  gatewayToken;
177
204
  timeoutMs;
178
- constructor(baseUrl, gatewayToken, timeoutMs = DEFAULT_TIMEOUT_MS) {
205
+ model;
206
+ constructor(baseUrl, gatewayToken, timeoutMs = DEFAULT_TIMEOUT_MS, model = "openclaw") {
179
207
  this.baseUrl = baseUrl.replace(/\/$/, "");
180
208
  this.gatewayToken = gatewayToken;
181
209
  this.timeoutMs = timeoutMs;
210
+ this.model = model;
182
211
  }
183
212
  buildHeaders() {
184
213
  const headers = {
@@ -189,8 +218,16 @@ var OpenClawClient = class {
189
218
  }
190
219
  return headers;
191
220
  }
221
+ truncateForLog(value) {
222
+ if (value.length <= MAX_DEBUG_BODY_LENGTH) return value;
223
+ return value.slice(0, MAX_DEBUG_BODY_LENGTH) + `... (truncated, ${value.length} chars total)`;
224
+ }
192
225
  async request(path, options = {}) {
193
226
  const url = `${this.baseUrl}${path}`;
227
+ logDebug(() => `Request: ${options.method ?? "GET"} ${url}`);
228
+ if (options.body) {
229
+ logDebug(() => `Request body: ${this.truncateForLog(options.body)}`);
230
+ }
194
231
  const controller = new AbortController();
195
232
  const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
196
233
  try {
@@ -203,11 +240,23 @@ var OpenClawClient = class {
203
240
  }
204
241
  });
205
242
  if (!response.ok) {
243
+ if (isDebugEnabled()) {
244
+ const contentLength2 = response.headers.get("content-length");
245
+ if (!contentLength2 || parseInt(contentLength2, 10) <= MAX_RESPONSE_SIZE_BYTES) {
246
+ const errorBody = await response.text();
247
+ if (errorBody.length <= MAX_RESPONSE_SIZE_BYTES) {
248
+ logDebug(
249
+ () => `Response error (${response.status}): ${this.truncateForLog(errorBody)}`
250
+ );
251
+ }
252
+ }
253
+ }
206
254
  throw new OpenClawApiError(
207
255
  `API request failed: ${response.status} ${response.statusText}`,
208
256
  response.status
209
257
  );
210
258
  }
259
+ logDebug(() => `Response: ${response.status} ${response.statusText}`);
211
260
  const contentLength = response.headers.get("content-length");
212
261
  if (contentLength && parseInt(contentLength, 10) > MAX_RESPONSE_SIZE_BYTES) {
213
262
  throw new OpenClawApiError("Response exceeds maximum allowed size (10MB)", 413);
@@ -276,7 +325,7 @@ var OpenClawClient = class {
276
325
  */
277
326
  async chat(message, sessionId) {
278
327
  const body = {
279
- model: "claude-opus-4-5",
328
+ model: this.model,
280
329
  messages: [{ role: "user", content: message }],
281
330
  max_tokens: 4096
282
331
  };
@@ -306,7 +355,7 @@ var INSTANCE_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
306
355
  var InstanceRegistry = class {
307
356
  instances = /* @__PURE__ */ new Map();
308
357
  defaultName;
309
- constructor(configs) {
358
+ constructor(configs, model) {
310
359
  if (configs.length === 0) {
311
360
  throw new Error("At least one OpenClaw instance must be configured");
312
361
  }
@@ -343,7 +392,7 @@ var InstanceRegistry = class {
343
392
  }
344
393
  explicitDefault = config.name;
345
394
  }
346
- const client = new OpenClawClient(config.url, config.token, config.timeout);
395
+ const client = new OpenClawClient(config.url, config.token, config.timeout, model);
347
396
  this.instances.set(config.name, { config, client });
348
397
  }
349
398
  this.defaultName = explicitDefault ?? configs[0].name;
@@ -1530,7 +1579,14 @@ async function createSSEServer(config, deps2) {
1530
1579
 
1531
1580
  // src/index.ts
1532
1581
  var args = parseArguments(SERVER_VERSION);
1533
- var registry = new InstanceRegistry(args.instances);
1582
+ setDebugEnabled(args.debug);
1583
+ var trimmedModel = args.model.trim();
1584
+ if (!trimmedModel) {
1585
+ logError('OPENCLAW_MODEL / --model must be a non-empty string. Default is "openclaw".');
1586
+ process.exit(1);
1587
+ }
1588
+ args.model = trimmedModel;
1589
+ var registry = new InstanceRegistry(args.instances, args.model);
1534
1590
  var deps = {
1535
1591
  registry,
1536
1592
  serverName: SERVER_NAME,
@@ -1538,8 +1594,12 @@ var deps = {
1538
1594
  };
1539
1595
  async function main() {
1540
1596
  log(`Starting ${SERVER_NAME} v${SERVER_VERSION}`);
1597
+ log(`Model: ${args.model}`);
1541
1598
  log(`Transport: ${args.transport}`);
1542
1599
  log(`Request timeout: ${args.timeout}ms`);
1600
+ if (args.debug) {
1601
+ log("Debug logging: enabled");
1602
+ }
1543
1603
  for (const instance of registry.list()) {
1544
1604
  const defaultLabel = instance.isDefault ? " (default)" : "";
1545
1605
  log(`Instance "${instance.name}": ${instance.url}${defaultLabel}`);
@@ -11,11 +11,27 @@ All configuration can be done via environment variables. Copy `.env.example` to
11
11
  | `OPENCLAW_URL` | OpenClaw gateway URL | `http://127.0.0.1:18789` |
12
12
  | `OPENCLAW_GATEWAY_TOKEN` | Bearer token for gateway authentication | (none) |
13
13
  | `OPENCLAW_TIMEOUT_MS` | Request timeout in milliseconds | `120000` (2 min) |
14
+ | `OPENCLAW_MODEL` | Model name for chat completions | `openclaw` |
14
15
 
15
16
  ### Multi-Instance Mode
16
17
 
17
18
  Orchestrate multiple OpenClaw gateways from a single MCP server. Set `OPENCLAW_INSTANCES` as a JSON array — when present, it takes precedence over `OPENCLAW_URL` / `OPENCLAW_GATEWAY_TOKEN`.
18
19
 
20
+ ```
21
+ ┌─────────────────┐
22
+ │ Claude Client │
23
+ └────────┬────────┘
24
+
25
+ ┌─────────────▼─────────────┐
26
+ │ OpenClaw MCP Bridge │
27
+ │ │
28
+ │ instance="prod" ──────────────► OpenClaw GW (prod)
29
+ │ instance="staging" ────────────► OpenClaw GW (staging)
30
+ │ instance="dev" ───────────────► OpenClaw GW (dev)
31
+ │ (no instance) ──► default ──► OpenClaw GW (prod)
32
+ └────────────────────────────┘
33
+ ```
34
+
19
35
  | Variable | Description | Default |
20
36
  | -------------------- | ------------------------------ | ----------------------------- |
21
37
  | `OPENCLAW_INSTANCES` | JSON array of instance configs | (none — single-instance mode) |
@@ -45,11 +61,44 @@ Each instance object supports:
45
61
  All gateway-facing tools (`openclaw_chat`, `openclaw_status`, `openclaw_chat_async`) accept an optional `instance` parameter. When omitted, the default instance is used.
46
62
 
47
63
  ```
64
+ # Target a specific instance
48
65
  openclaw_chat message="Hello" instance="staging"
49
- openclaw_instances # list all available instances
66
+
67
+ # Check health of a specific gateway
68
+ openclaw_status instance="prod"
69
+
70
+ # List all available instances (names, URLs, default — tokens are never exposed)
71
+ openclaw_instances
72
+
73
+ # Async tasks also support instance targeting
74
+ openclaw_chat_async message="Run migration" instance="dev"
75
+
76
+ # Filter task list by instance
77
+ openclaw_task_list instance="staging"
78
+ ```
79
+
80
+ **How instance resolution works:**
81
+
82
+ 1. If `instance` parameter is provided → use that instance
83
+ 2. If `instance` is omitted → use the instance marked as `default`
84
+ 3. If no instance is marked as default → the first instance in the array is used
85
+
86
+ Each instance gets its own isolated HTTP client with independent auth token, timeout, and base URL. Async tasks store the target instance ID so results are always routed correctly.
87
+
88
+ **Docker Compose with multi-instance:**
89
+
90
+ ```yaml
91
+ services:
92
+ mcp-bridge:
93
+ image: ghcr.io/freema/openclaw-mcp:latest
94
+ environment:
95
+ - OPENCLAW_INSTANCES=[{"name":"prod","url":"http://prod-gw:18789","token":"tok1","default":true},{"name":"staging","url":"http://staging-gw:18789","token":"tok2"}]
96
+ - AUTH_ENABLED=true
97
+ - MCP_CLIENT_ID=openclaw
98
+ - MCP_CLIENT_SECRET=${MCP_CLIENT_SECRET}
50
99
  ```
51
100
 
52
- **Backward compatibility:** When `OPENCLAW_INSTANCES` is not set, the server creates a single `"default"` instance from `OPENCLAW_URL` + `OPENCLAW_GATEWAY_TOKEN`. Existing deployments work without any configuration change.
101
+ **Backward compatibility:** When `OPENCLAW_INSTANCES` is not set, the server creates a single `"default"` instance from `OPENCLAW_URL` + `OPENCLAW_GATEWAY_TOKEN`. Existing deployments work without any configuration change — zero migration required.
53
102
 
54
103
  ### Server Settings (SSE transport)
55
104
 
@@ -87,7 +136,7 @@ The server uses the MCP SDK's built-in OAuth 2.1 server with authorization code
87
136
 
88
137
  **Client ID validation rules:**
89
138
 
90
- - 364 characters
139
+ - 3-64 characters
91
140
  - Alphanumeric, dashes, underscores only
92
141
  - Must start with a letter or digit
93
142
 
@@ -17,6 +17,8 @@ services:
17
17
  environment:
18
18
  - OPENCLAW_URL=${OPENCLAW_URL:-http://host.docker.internal:18789}
19
19
  - OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN:-}
20
+ - OPENCLAW_MODEL=${OPENCLAW_MODEL:-openclaw}
21
+ - DEBUG=${DEBUG:-false}
20
22
  - AUTH_ENABLED=${AUTH_ENABLED:-true}
21
23
  - MCP_CLIENT_ID=${MCP_CLIENT_ID:-openclaw}
22
24
  - MCP_CLIENT_SECRET=${MCP_CLIENT_SECRET:-}
@@ -169,8 +171,24 @@ The MCP bridge communicates with the OpenClaw gateway via its OpenAI-compatible
169
171
 
170
172
  Without this, the MCP bridge will receive `405 Method Not Allowed` from the gateway.
171
173
 
174
+ ## Bridge / Gateway Compatibility
175
+
176
+ | MCP Bridge | Gateway | Result |
177
+ |------------|---------|--------|
178
+ | ≤ 1.2.2 | ≥ 2026.3.24 | `400 Bad Request` — bridge sends `model: "claude-opus-4-5"`, gateway rejects it |
179
+ | ≥ 1.3.0 | ≥ 2026.3.24 | Works — bridge defaults to `model: "openclaw"` |
180
+ | ≥ 1.3.0 | older | Works — set `OPENCLAW_MODEL` to whatever the older gateway expects |
181
+
182
+ If you're running a non-standard gateway setup with custom agent routing, set `OPENCLAW_MODEL=openclaw/<agentId>` to match your configuration.
183
+
172
184
  ## Troubleshooting
173
185
 
186
+ ### `400 Bad Request` from gateway on `openclaw_chat`
187
+
188
+ Gateway versions 2026.3.24+ require `model: "openclaw"` (or `"openclaw/<agentId>"`). The MCP bridge defaults to `"openclaw"` since v1.3.0. If you're using an older bridge version, upgrade or set `OPENCLAW_MODEL=openclaw`. If you need custom model routing, set `OPENCLAW_MODEL` to the value your gateway expects.
189
+
190
+ To diagnose, enable debug logging (`DEBUG=true`) which logs the outgoing request body and gateway error responses.
191
+
174
192
  ### `405 Method Not Allowed` from gateway
175
193
 
176
194
  The OpenClaw gateway's HTTP chat completions endpoint is disabled by default. Enable it in `openclaw.json` — see [Gateway Prerequisites](#openclaw-gateway-prerequisites) above.
package/docs/index.html CHANGED
@@ -129,6 +129,7 @@ pre, code { font-family: 'JetBrains Mono', monospace; }
129
129
  display: none; gap: 2rem; font-size: 0.875rem;
130
130
  text-transform: uppercase; letter-spacing: 0.05em; font-weight: 500; color: var(--text);
131
131
  }
132
+ .hamburger { display: none; }
132
133
  .nav-links a:hover { color: var(--accent); transition: color 0.2s; }
133
134
  .nav-gh { display: flex; align-items: center; gap: 0; font-size: 0.75rem; font-weight: 700; }
134
135
  .nav-gh-btn {
@@ -288,8 +289,106 @@ pre, code { font-family: 'JetBrains Mono', monospace; }
288
289
  text-align: center; font-size: 10px; color: rgba(160,160,160,0.5);
289
290
  }
290
291
 
291
- /* === Desktop (640px+) === */
292
- @media (min-width: 640px) {
292
+ /* === Mobile Hamburger Menu === */
293
+ .hamburger {
294
+ display: flex; flex-direction: column; gap: 5px; padding: 0.5rem;
295
+ cursor: pointer; z-index: 60; background: none; border: none;
296
+ }
297
+ .hamburger span {
298
+ display: block; width: 22px; height: 2px; background: #fff;
299
+ transition: transform 0.3s, opacity 0.3s;
300
+ }
301
+ .hamburger.active span:nth-child(1) { transform: rotate(45deg) translate(5px, 5px); }
302
+ .hamburger.active span:nth-child(2) { opacity: 0; }
303
+ .hamburger.active span:nth-child(3) { transform: rotate(-45deg) translate(5px, -5px); }
304
+
305
+ .mobile-menu {
306
+ display: none; position: fixed; inset: 0; z-index: 55;
307
+ background: rgba(8,8,8,0.97); backdrop-filter: blur(16px);
308
+ -webkit-backdrop-filter: blur(16px);
309
+ flex-direction: column; align-items: center; justify-content: center; gap: 2rem;
310
+ font-size: 1.25rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 700;
311
+ }
312
+ .mobile-menu.active { display: flex; }
313
+ .mobile-menu a { color: var(--text); transition: color 0.2s; padding: 0.5rem 1rem; }
314
+ .mobile-menu a:hover, .mobile-menu a:active { color: var(--accent); }
315
+
316
+ /* === Mobile-first fixes === */
317
+ @media (max-width: 639px) {
318
+ .hamburger { display: flex; }
319
+ .nav-gh { display: none; }
320
+
321
+ .hero { padding: 7rem 0 3rem; }
322
+ .hero h1 { font-size: 2.25rem; }
323
+ .hero-sub { font-size: 1rem; margin-top: 1.25rem; }
324
+ .hero-buttons { margin-top: 2rem; gap: 0.75rem; }
325
+ .btn-primary, .btn-secondary { padding: 0.875rem 1.5rem; font-size: 1rem; }
326
+ .stats-bar { margin-top: 2.5rem; gap: 1rem; font-size: 0.75rem; }
327
+
328
+ .how-section, .features-section, .tools-section, .quickstart-section, .cta-section {
329
+ padding: 4rem 0;
330
+ }
331
+ .how-section h2, .features-section h2, .tools-section h2,
332
+ .quickstart-section h2, .cta-section h2 {
333
+ font-size: 1.75rem; margin-bottom: 2rem;
334
+ }
335
+
336
+ .how-step { padding: 1.5rem; }
337
+ .how-step-num { font-size: 1.5rem; }
338
+ .how-step h3 { font-size: 1.1rem; }
339
+
340
+ .features-grid { gap: 1rem; }
341
+ .feature-card { padding: 1.5rem; }
342
+ .feature-card h3 { font-size: 1.1rem; }
343
+
344
+ .tools-table { font-size: 0.75rem; }
345
+ .tools-table th, .tools-table td { padding: 0.75rem 0.5rem; }
346
+ .tools-col-header h3 { font-size: 1.25rem; }
347
+
348
+ .tab-btn { padding: 0.75rem 1rem; font-size: 0.65rem; }
349
+ .tab-content { padding: 1rem; }
350
+ .tab-content pre code { font-size: 0.7rem; word-break: break-all; white-space: pre-wrap; }
351
+
352
+ .cta-btn-primary, .cta-btn-secondary {
353
+ padding: 1rem 2rem; font-size: 1rem; width: 100%; text-align: center;
354
+ }
355
+
356
+ .footer { padding: 2.5rem 0; }
357
+ .footer-links { gap: 1.25rem; }
358
+ .footer-bottom { margin-top: 2rem; }
359
+
360
+ .plugin-cards { gap: 0.75rem !important; }
361
+
362
+ /* Prevent horizontal overflow */
363
+ .terminal-body { font-size: 0.75rem; padding: 1rem; }
364
+ .container { padding: 0 1rem; }
365
+ }
366
+
367
+ /* === Tablet (640px - 1023px) === */
368
+ @media (min-width: 640px) and (max-width: 1023px) {
369
+ .hamburger { display: flex; }
370
+ .nav-links { display: none; }
371
+ .hero h1 { font-size: 4rem; }
372
+ .hero-sub { font-size: 1.25rem; }
373
+ .hero-buttons { flex-direction: row; }
374
+ .btn-primary, .btn-secondary { width: auto; }
375
+ .how-grid { grid-template-columns: repeat(2, 1fr); }
376
+ .how-step:nth-child(2) { border-right: none; }
377
+ .features-grid { grid-template-columns: repeat(2, 1fr); }
378
+ .tools-grid { grid-template-columns: repeat(2, 1fr); }
379
+ .cta-section h2 { font-size: 2.5rem; }
380
+ .cta-buttons { flex-direction: row; }
381
+ .footer-inner { flex-direction: row; justify-content: space-between; align-items: center; }
382
+ .plugin-cards { grid-template-columns: repeat(2, 1fr) !important; }
383
+
384
+ .how-section, .features-section, .tools-section, .quickstart-section, .cta-section {
385
+ padding: 5rem 0;
386
+ }
387
+ }
388
+
389
+ /* === Desktop (1024px+) === */
390
+ @media (min-width: 1024px) {
391
+ .hamburger { display: none; }
293
392
  .nav-links { display: flex; }
294
393
  .hero h1 { font-size: 6rem; }
295
394
  .hero-sub { font-size: 1.5rem; }
@@ -322,6 +421,9 @@ pre, code { font-family: 'JetBrains Mono', monospace; }
322
421
  <a href="#quickstart">Quick start</a>
323
422
  <a href="#plugin">Plugin</a>
324
423
  </div>
424
+ <button class="hamburger" aria-label="Menu" onclick="document.querySelector('.mobile-menu').classList.toggle('active');this.classList.toggle('active')">
425
+ <span></span><span></span><span></span>
426
+ </button>
325
427
  <div class="nav-gh">
326
428
  <a class="nav-gh-btn" href="https://github.com/freema/openclaw-mcp" target="_blank" rel="noopener">
327
429
  <svg viewBox="0 0 16 16" width="16" height="16" fill="white"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
@@ -339,6 +441,16 @@ pre, code { font-family: 'JetBrains Mono', monospace; }
339
441
  </div>
340
442
  </nav>
341
443
 
444
+ <!-- Mobile Menu Overlay -->
445
+ <div class="mobile-menu" id="mobile-menu">
446
+ <a href="#how" onclick="closeMobileMenu()">How it works</a>
447
+ <a href="#features" onclick="closeMobileMenu()">Features</a>
448
+ <a href="#tools" onclick="closeMobileMenu()">Tools</a>
449
+ <a href="#quickstart" onclick="closeMobileMenu()">Quick start</a>
450
+ <a href="#plugin" onclick="closeMobileMenu()">Plugin</a>
451
+ <a href="https://github.com/freema/openclaw-mcp" target="_blank" rel="noopener" style="color:var(--accent)">GitHub</a>
452
+ </div>
453
+
342
454
  <!-- Hero -->
343
455
  <header class="hero">
344
456
  <div class="container">
@@ -610,6 +722,14 @@ npx openclaw-mcp --transport sse --port 3000</code></pre>
610
722
  </div>
611
723
  </footer>
612
724
 
725
+ <!-- Mobile Menu Script -->
726
+ <script>
727
+ function closeMobileMenu() {
728
+ document.querySelector('.mobile-menu').classList.remove('active');
729
+ document.querySelector('.hamburger').classList.remove('active');
730
+ }
731
+ </script>
732
+
613
733
  <!-- Tab Switching Script -->
614
734
  <script>
615
735
  (function() {
@@ -91,9 +91,11 @@ openclaw-mcp --help
91
91
  Options:
92
92
  --openclaw-url, -u OpenClaw gateway URL [default: "http://127.0.0.1:18789"]
93
93
  --gateway-token Bearer token for gateway [default: none]
94
+ --model, -m Model name for chat [default: "openclaw"]
94
95
  --transport, -t Transport mode [choices: "stdio", "sse"] [default: "stdio"]
95
96
  --port, -p Port for SSE server [default: 3000]
96
97
  --host Host for SSE server [default: "0.0.0.0"]
98
+ --debug Enable debug logging [default: false]
97
99
  --auth Enable OAuth [default: false]
98
100
  --client-id MCP OAuth client ID [env: MCP_CLIENT_ID]
99
101
  --client-secret MCP OAuth client secret [env: MCP_CLIENT_SECRET]
package/docs/logging.md CHANGED
@@ -36,13 +36,24 @@ The MCP server logs operational events to **stderr** using the `[openclaw-mcp]`
36
36
  - Invalid client configuration (missing secrets, bad client ID format)
37
37
  - Session errors
38
38
 
39
- ### What Is NOT Logged
39
+ ### What Is NOT Logged (Info/Error levels)
40
40
 
41
41
  - **Message content** — user messages and OpenClaw responses are never logged
42
42
  - **Authentication tokens** — Bearer tokens, client secrets, gateway tokens
43
43
  - **Request/response bodies** — only error messages, not full payloads
44
44
  - **User-identifiable information** — no IPs, user agents, or personal data
45
45
 
46
+ ### Debug Level (`DEBUG=true`)
47
+
48
+ > **Warning:** Debug mode is a diagnostic tool. It logs request and response payloads which may contain user message content. Do not enable in production under normal operation — use it only for active troubleshooting, then disable it.
49
+
50
+ When debug logging is enabled, the following **are** logged for troubleshooting:
51
+
52
+ - **Request bodies** — outgoing payloads sent to the gateway (truncated to 4096 chars)
53
+ - **Error response bodies** — full error responses from the gateway (truncated to 4096 chars)
54
+
55
+ Credentials are still redacted by the sanitization layer. Headers (including Authorization) are never logged, even in debug mode.
56
+
46
57
  ## Sensitive Data Redaction
47
58
 
48
59
  The logger automatically redacts patterns that look like credentials:
@@ -99,4 +110,4 @@ DEBUG=true # Explicit debug flag
99
110
  NODE_ENV=development # Development mode
100
111
  ```
101
112
 
102
- Debug logs include additional operational detail but still never log message content or credentials.
113
+ Debug logs include request/response bodies for troubleshooting (truncated to 4096 chars). Credentials are still redacted, and headers (including Authorization) are never logged.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-mcp",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
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",
@@ -14,7 +14,7 @@
14
14
  "dev": "tsx watch src/index.ts",
15
15
  "build": "tsup",
16
16
  "start": "node dist/index.js",
17
- "clean": "rm -rf dist",
17
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
18
18
  "typecheck": "tsc --noEmit",
19
19
  "lint": "eslint src --ext .ts",
20
20
  "lint:fix": "eslint src --ext .ts --fix",