openclaw-mcp 1.3.1 → 1.4.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 +2 -0
- package/dist/index.js +55 -9
- package/docs/configuration.md +13 -1
- package/package.json +2 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ 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.
|
|
8
|
+
var SERVER_VERSION = "1.4.1";
|
|
9
9
|
var DEFAULT_OPENCLAW_URL = "http://127.0.0.1:18789";
|
|
10
10
|
var DEFAULT_MODEL = "openclaw";
|
|
11
11
|
var SERVER_ICON_SVG_BASE64 = "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCIgZmlsbD0ibm9uZSI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzFhMWEyZSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzE2MjEzZSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJjbGF3IiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZmYzMzMzIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjY2MwMDAwIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjEyOCIgaGVpZ2h0PSIxMjgiIHJ4PSIyNCIgZmlsbD0idXJsKCNiZykiLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2NCA2NCkiIHN0cm9rZT0idXJsKCNjbGF3KSIgc3Ryb2tlLXdpZHRoPSI3IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0tMjggLTM4YzAgMCAtMTAgMjAgMCAzMiIvPjxwYXRoIGQ9Ik0tMTIgLTQwYzAgMCAtNiAyMiA0IDM0Ii8+PHBhdGggZD0iTTI4IC0zOGMwIDAgMTAgMjAgMCAzMiIvPjxwYXRoIGQ9Ik0xMiAtNDBjMCAwIDYgMjIgLTQgMzQiLz48Y2lyY2xlIGN4PSIwIiBjeT0iMTAiIHI9IjIwIiBzdHJva2Utd2lkdGg9IjYiLz48cGF0aCBkPSJNLTEwIDR2LTQiIHN0cm9rZS13aWR0aD0iNCIvPjxwYXRoIGQ9Ik0xMCA0di00IiBzdHJva2Utd2lkdGg9IjQiLz48cGF0aCBkPSJNLTggMjBjNCA2IDEyIDYgMTYgMCIgc3Ryb2tlLXdpZHRoPSIzIi8+PC9nPjwvc3ZnPg==";
|
|
@@ -114,6 +114,10 @@ function parseArguments(version) {
|
|
|
114
114
|
type: "string",
|
|
115
115
|
description: "Allowed OAuth redirect URIs (comma-separated)",
|
|
116
116
|
default: process.env.MCP_REDIRECT_URIS || void 0
|
|
117
|
+
}).option("allow-dcr", {
|
|
118
|
+
type: "boolean",
|
|
119
|
+
description: "Allow OAuth Dynamic Client Registration (Cursor/Windsurf compatibility, dev-only)",
|
|
120
|
+
default: process.env.MCP_DANGEROUSLY_ALLOW_DCR === "true"
|
|
117
121
|
}).help().parseSync();
|
|
118
122
|
let instances;
|
|
119
123
|
const instancesEnv = process.env.OPENCLAW_INSTANCES;
|
|
@@ -168,6 +172,7 @@ function parseArguments(version) {
|
|
|
168
172
|
clientSecret: argv["client-secret"],
|
|
169
173
|
issuerUrl: argv["issuer-url"],
|
|
170
174
|
redirectUris: argv["redirect-uris"] ? argv["redirect-uris"].split(",").map((s) => s.trim()).filter(Boolean) : void 0,
|
|
175
|
+
allowDcr: argv["allow-dcr"],
|
|
171
176
|
instances
|
|
172
177
|
};
|
|
173
178
|
}
|
|
@@ -1172,8 +1177,15 @@ var ALLOW_ANY_REDIRECT = new Proxy([], {
|
|
|
1172
1177
|
return Reflect.get(target, prop);
|
|
1173
1178
|
}
|
|
1174
1179
|
});
|
|
1180
|
+
var MAX_DYNAMIC_CLIENTS = 100;
|
|
1175
1181
|
var OpenClawClientsStore = class {
|
|
1176
1182
|
client;
|
|
1183
|
+
dynamicClients = /* @__PURE__ */ new Map();
|
|
1184
|
+
// Assigned conditionally in the constructor. The MCP SDK probes
|
|
1185
|
+
// `clientsStore.registerClient` at router-setup time and only advertises
|
|
1186
|
+
// `/register` when the property is defined, so we must NOT define a no-op
|
|
1187
|
+
// method on the prototype — it has to be instance-level and gated on config.
|
|
1188
|
+
registerClient;
|
|
1177
1189
|
constructor(config) {
|
|
1178
1190
|
if (config.clientId && config.clientSecret) {
|
|
1179
1191
|
const redirectUris = config.redirectUris && config.redirectUris.length > 0 ? config.redirectUris : ALLOW_ANY_REDIRECT;
|
|
@@ -1188,16 +1200,25 @@ var OpenClawClientsStore = class {
|
|
|
1188
1200
|
client_id_issued_at: Math.floor(Date.now() / 1e3)
|
|
1189
1201
|
};
|
|
1190
1202
|
}
|
|
1203
|
+
if (config.allowDynamicRegistration) {
|
|
1204
|
+
this.registerClient = (client) => {
|
|
1205
|
+
if (this.dynamicClients.size >= MAX_DYNAMIC_CLIENTS) {
|
|
1206
|
+
const oldestKey = this.dynamicClients.keys().next().value;
|
|
1207
|
+
if (oldestKey !== void 0) {
|
|
1208
|
+
this.dynamicClients.delete(oldestKey);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
this.dynamicClients.set(client.client_id, client);
|
|
1212
|
+
return client;
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1191
1215
|
}
|
|
1192
1216
|
async getClient(clientId) {
|
|
1193
1217
|
if (this.client && this.client.client_id === clientId) {
|
|
1194
1218
|
return this.client;
|
|
1195
1219
|
}
|
|
1196
|
-
return
|
|
1220
|
+
return this.dynamicClients.get(clientId);
|
|
1197
1221
|
}
|
|
1198
|
-
// No registerClient — dynamic client registration is intentionally disabled.
|
|
1199
|
-
// Only the pre-configured client from MCP_CLIENT_ID + MCP_CLIENT_SECRET can authenticate.
|
|
1200
|
-
// This prevents anyone who knows the server URL from self-registering and bypassing auth.
|
|
1201
1222
|
};
|
|
1202
1223
|
var OpenClawAuthProvider = class {
|
|
1203
1224
|
clientsStore;
|
|
@@ -1336,9 +1357,15 @@ var OpenClawAuthProvider = class {
|
|
|
1336
1357
|
resource: tokenData.resource
|
|
1337
1358
|
};
|
|
1338
1359
|
}
|
|
1339
|
-
async revokeToken(
|
|
1340
|
-
this.tokens.
|
|
1341
|
-
|
|
1360
|
+
async revokeToken(client, request) {
|
|
1361
|
+
const tokenData = this.tokens.get(request.token);
|
|
1362
|
+
if (tokenData && tokenData.clientId === client.client_id) {
|
|
1363
|
+
this.tokens.delete(request.token);
|
|
1364
|
+
}
|
|
1365
|
+
const refreshData = this.refreshTokens.get(request.token);
|
|
1366
|
+
if (refreshData && refreshData.clientId === client.client_id) {
|
|
1367
|
+
this.refreshTokens.delete(request.token);
|
|
1368
|
+
}
|
|
1342
1369
|
}
|
|
1343
1370
|
};
|
|
1344
1371
|
|
|
@@ -1627,7 +1654,8 @@ async function main() {
|
|
|
1627
1654
|
sseConfig.authConfig = {
|
|
1628
1655
|
clientId: args.clientId,
|
|
1629
1656
|
clientSecret: args.clientSecret,
|
|
1630
|
-
redirectUris: args.redirectUris
|
|
1657
|
+
redirectUris: args.redirectUris,
|
|
1658
|
+
allowDynamicRegistration: args.allowDcr
|
|
1631
1659
|
};
|
|
1632
1660
|
log(`OAuth client ID: ${args.clientId}`);
|
|
1633
1661
|
if (!args.redirectUris || args.redirectUris.length === 0) {
|
|
@@ -1635,6 +1663,24 @@ async function main() {
|
|
|
1635
1663
|
"WARNING: MCP_REDIRECT_URIS not set \u2014 any redirect_uri will be accepted. Set MCP_REDIRECT_URIS for production."
|
|
1636
1664
|
);
|
|
1637
1665
|
}
|
|
1666
|
+
if (args.allowDcr) {
|
|
1667
|
+
const isLoopback = args.host === "127.0.0.1" || args.host === "localhost" || args.host === "::1";
|
|
1668
|
+
const publicOptIn = process.env.MCP_DANGEROUSLY_ALLOW_DCR_PUBLIC === "true";
|
|
1669
|
+
if (!isLoopback && !publicOptIn) {
|
|
1670
|
+
logError(
|
|
1671
|
+
`MCP_DANGEROUSLY_ALLOW_DCR=true is set but HOST="${args.host}" is not loopback. Dynamic Client Registration combined with a publicly reachable bind allows anyone on the network to obtain a token. Bind to 127.0.0.1, or set MCP_DANGEROUSLY_ALLOW_DCR_PUBLIC=true to override. Refusing to start.`
|
|
1672
|
+
);
|
|
1673
|
+
process.exit(1);
|
|
1674
|
+
}
|
|
1675
|
+
log(
|
|
1676
|
+
"WARNING: MCP_DANGEROUSLY_ALLOW_DCR is enabled \u2014 OAuth Dynamic Client Registration is open. Any client that can reach this server may self-register and obtain tokens. Use for local development only."
|
|
1677
|
+
);
|
|
1678
|
+
if (publicOptIn) {
|
|
1679
|
+
log(
|
|
1680
|
+
"WARNING: MCP_DANGEROUSLY_ALLOW_DCR_PUBLIC=true \u2014 DCR is exposed on a non-loopback bind. You have explicitly accepted the risk."
|
|
1681
|
+
);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1638
1684
|
} else if (args.authEnabled && !args.clientId) {
|
|
1639
1685
|
logError("AUTH_ENABLED=true but MCP_CLIENT_ID is not set. Refusing to start without auth.");
|
|
1640
1686
|
process.exit(1);
|
package/docs/configuration.md
CHANGED
|
@@ -133,6 +133,8 @@ The server uses the MCP SDK's built-in OAuth 2.1 server with authorization code
|
|
|
133
133
|
| `MCP_CLIENT_SECRET` | OAuth client secret | When auth enabled |
|
|
134
134
|
| `MCP_ISSUER_URL` | OAuth issuer URL override (e.g., `https://mcp.example.com`) | When behind HTTPS proxy |
|
|
135
135
|
| `MCP_REDIRECT_URIS` | Allowed redirect URIs (comma-separated) | Recommended for production |
|
|
136
|
+
| `MCP_DANGEROUSLY_ALLOW_DCR` | Enable Dynamic Client Registration (`true`/`false`) | Dev only (see below) |
|
|
137
|
+
| `MCP_DANGEROUSLY_ALLOW_DCR_PUBLIC` | Escape hatch to allow DCR on non-loopback binds | Never, unless you mean it |
|
|
136
138
|
|
|
137
139
|
**Client ID validation rules:**
|
|
138
140
|
|
|
@@ -153,4 +155,14 @@ When auth is enabled, the server exposes these OAuth 2.1 endpoints:
|
|
|
153
155
|
- `POST /token` — Token exchange (requires client_secret)
|
|
154
156
|
- `POST /revoke` — Token revocation
|
|
155
157
|
|
|
156
|
-
Dynamic client registration is **disabled** — only the pre-configured client (from `MCP_CLIENT_ID` + `MCP_CLIENT_SECRET`) can authenticate. This prevents anyone who knows the server URL from self-registering and bypassing auth.
|
|
158
|
+
Dynamic client registration is **disabled by default** — only the pre-configured client (from `MCP_CLIENT_ID` + `MCP_CLIENT_SECRET`) can authenticate. This prevents anyone who knows the server URL from self-registering and bypassing auth.
|
|
159
|
+
|
|
160
|
+
#### Cursor / Windsurf compatibility (dev only)
|
|
161
|
+
|
|
162
|
+
Cursor and Windsurf only support MCP servers that expose OAuth 2.0 Dynamic Client Registration (RFC 7591). To let them connect, set `MCP_DANGEROUSLY_ALLOW_DCR=true`. The server will then advertise a `/register` endpoint and accept ad-hoc client registrations (kept in an in-memory FIFO store, capped at 100 entries).
|
|
163
|
+
|
|
164
|
+
`MCP_CLIENT_ID` and `MCP_CLIENT_SECRET` are still required — DCR augments the pre-configured client, it does not replace it. If you are only running Cursor locally you can use any valid values for them; they simply remain unused.
|
|
165
|
+
|
|
166
|
+
**This is dev-only.** With DCR enabled alongside the server's auto-approve authorization flow, any client that can reach the server can register itself and obtain a token. To prevent accidental exposure the server refuses to start when `MCP_DANGEROUSLY_ALLOW_DCR=true` and `HOST` is not loopback (`127.0.0.1`, `localhost`, or `::1`). If you genuinely need DCR on a non-loopback bind (e.g., inside a trusted private network), also set `MCP_DANGEROUSLY_ALLOW_DCR_PUBLIC=true`.
|
|
167
|
+
|
|
168
|
+
For production with Claude.ai, keep DCR disabled and use the pre-configured `MCP_CLIENT_ID` / `MCP_CLIENT_SECRET`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
|
+
"mcpName": "io.github.freema/openclaw-mcp",
|
|
4
5
|
"description": "Model Context Protocol (MCP) server for OpenClaw AI assistant integration",
|
|
5
6
|
"author": "Tomáš Grasl <https://www.tomasgrasl.cz/>",
|
|
6
7
|
"license": "MIT",
|