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 +77 -0
- package/dist/index.js +66 -6
- package/docs/configuration.md +52 -3
- package/docs/deployment.md +18 -0
- package/docs/index.html +122 -2
- package/docs/installation.md +2 -0
- package/docs/logging.md +13 -2
- package/package.json +2 -2
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.
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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}`);
|
package/docs/configuration.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
- 3
|
|
139
|
+
- 3-64 characters
|
|
91
140
|
- Alphanumeric, dashes, underscores only
|
|
92
141
|
- Must start with a letter or digit
|
|
93
142
|
|
package/docs/deployment.md
CHANGED
|
@@ -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
|
-
/* ===
|
|
292
|
-
|
|
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() {
|
package/docs/installation.md
CHANGED
|
@@ -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
|
|
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.
|
|
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": "
|
|
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",
|