openclaw-mcp 1.5.0 → 1.6.0

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
@@ -54,6 +54,7 @@ services:
54
54
  - MCP_CLIENT_ID=openclaw
55
55
  - MCP_CLIENT_SECRET=${MCP_CLIENT_SECRET}
56
56
  - MCP_ISSUER_URL=${MCP_ISSUER_URL:-}
57
+ - MCP_REDIRECT_URIS=https://claude.ai/api/mcp/auth_callback,https://claude.com/api/mcp/auth_callback
57
58
  - TRUST_PROXY=1
58
59
  - CORS_ORIGINS=https://claude.ai
59
60
  extra_hosts:
@@ -115,6 +116,8 @@ AUTH_ENABLED=true MCP_CLIENT_ID=openclaw MCP_CLIENT_SECRET=your-secret \
115
116
  > - `MCP_ISSUER_URL` (or `--issuer-url`) to your public HTTPS URL — otherwise OAuth metadata advertises `http://localhost:3000` and clients fail to authenticate.
116
117
  > - `TRUST_PROXY=1` (or `--trust-proxy 1`) — otherwise `express-rate-limit` rejects the proxy's `X-Forwarded-For` header and `/token` crashes with `ERR_ERL_UNEXPECTED_X_FORWARDED_FOR`.
117
118
 
119
+ > **Recommended:** Set `MCP_REDIRECT_URIS=https://claude.ai/api/mcp/auth_callback,https://claude.com/api/mcp/auth_callback` so authorization codes can only be delivered to Claude's callbacks. Note the exact `/api/mcp/auth_callback` path — matching is exact, and getting it wrong fails OAuth with `Unregistered redirect_uri` (see [Troubleshooting](docs/deployment.md#invalid_request--unregistered-redirect_uri-on-authorize)).
120
+
118
121
  See [Installation Guide](docs/installation.md) for details.
119
122
 
120
123
  ## Architecture
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.5.0";
8
+ var SERVER_VERSION = "1.6.0";
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==";
@@ -1175,13 +1175,15 @@ var TOKEN_TTL_MS = 60 * 60 * 1e3;
1175
1175
  var AUTH_CODE_TTL_MS = 10 * 60 * 1e3;
1176
1176
  var REFRESH_TOKEN_TTL_MS = 24 * 60 * 60 * 1e3;
1177
1177
  var REAPER_INTERVAL_MS = 5 * 60 * 1e3;
1178
- var ALLOW_ANY_REDIRECT = new Proxy([], {
1179
- get(target, prop) {
1180
- if (prop === "includes") return () => true;
1181
- if (prop === "length") return 1;
1182
- return Reflect.get(target, prop);
1178
+ var AllowAnyRedirectUris = class extends Array {
1179
+ includes(_searchElement, _fromIndex) {
1180
+ return true;
1183
1181
  }
1184
- });
1182
+ some(_predicate, _thisArg) {
1183
+ return true;
1184
+ }
1185
+ };
1186
+ var ALLOW_ANY_REDIRECT = new AllowAnyRedirectUris();
1185
1187
  var MAX_DYNAMIC_CLIENTS = 100;
1186
1188
  var OpenClawClientsStore = class {
1187
1189
  client;
@@ -170,6 +170,18 @@ When auth is enabled, the server exposes these OAuth 2.1 endpoints:
170
170
 
171
171
  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.
172
172
 
173
+ #### Redirect URI allow-list
174
+
175
+ `MCP_REDIRECT_URIS` restricts which `redirect_uri` values the `/authorize` endpoint accepts for the pre-configured client. When unset, any redirect URI is accepted (the client secret verified during token exchange remains the actual auth gate) — set it in production so authorization codes can only be delivered to callbacks you trust.
176
+
177
+ Claude.ai uses exactly this callback (note the `/api/mcp/auth_callback` path):
178
+
179
+ ```bash
180
+ MCP_REDIRECT_URIS=https://claude.ai/api/mcp/auth_callback,https://claude.com/api/mcp/auth_callback
181
+ ```
182
+
183
+ Matching is exact (scheme, host, path). A common mistake is registering `https://claude.ai/oauth/callback`, which does **not** match and makes Claude.ai fail with `Unregistered redirect_uri`. If you also connect other clients (e.g. MCP Inspector), append their callbacks to the comma-separated list — loopback callbacks (`http://localhost/...`, `http://127.0.0.1/...`) match on any port per RFC 8252.
184
+
173
185
  #### Cursor / Windsurf compatibility (dev only)
174
186
 
175
187
  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).
@@ -24,6 +24,7 @@ services:
24
24
  - MCP_CLIENT_SECRET=${MCP_CLIENT_SECRET:-}
25
25
  - MCP_ISSUER_URL=${MCP_ISSUER_URL:-}
26
26
  - MCP_REDIRECT_URIS=${MCP_REDIRECT_URIS:-}
27
+ - TRUST_PROXY=${TRUST_PROXY:-}
27
28
  - CORS_ORIGINS=${CORS_ORIGINS:-https://claude.ai}
28
29
  - NODE_ENV=production
29
30
  extra_hosts:
@@ -56,6 +57,12 @@ AUTH_ENABLED=true
56
57
  # Public URL (required when behind a reverse proxy)
57
58
  MCP_ISSUER_URL=https://mcp.your-domain.com
58
59
 
60
+ # Trust the reverse proxy's X-Forwarded-For (required behind a reverse proxy)
61
+ TRUST_PROXY=1
62
+
63
+ # Allowed OAuth redirect URIs — Claude.ai callbacks (recommended for production)
64
+ MCP_REDIRECT_URIS=https://claude.ai/api/mcp/auth_callback,https://claude.com/api/mcp/auth_callback
65
+
59
66
  # Allowed CORS origins
60
67
  CORS_ORIGINS=https://claude.ai
61
68
  ```
@@ -79,7 +86,7 @@ docker compose up -d
79
86
  - [ ] `MCP_CLIENT_SECRET` generated securely (`openssl rand -hex 32`, min 32 chars)
80
87
  - [ ] `MCP_ISSUER_URL` set to public HTTPS URL (when behind reverse proxy)
81
88
  - [ ] `TRUST_PROXY` set to the right hop count / CIDR (when behind reverse proxy)
82
- - [ ] `MCP_REDIRECT_URIS` restricted to known callback URLs
89
+ - [ ] `MCP_REDIRECT_URIS` restricted to known callback URLs (for Claude.ai: `https://claude.ai/api/mcp/auth_callback,https://claude.com/api/mcp/auth_callback`)
83
90
  - [ ] CORS restricted to known origins (`CORS_ORIGINS=https://claude.ai`)
84
91
  - [ ] `OPENCLAW_GATEWAY_TOKEN` set for gateway authentication
85
92
  - [ ] Dynamic client registration is disabled (default — no `/register` endpoint)
@@ -205,6 +212,18 @@ You're running behind a reverse proxy but haven't set `MCP_ISSUER_URL`. The OAut
205
212
 
206
213
  Your Claude.ai connector URL is missing the `/mcp` path. The MCP Streamable HTTP transport is mounted at `/mcp`, not at the server root (which is intentional — root is reserved for `/health`, `/.well-known/*`, OAuth endpoints, and legacy SSE endpoints). Update the connector URL in Claude.ai to end with `/mcp`, e.g. `https://mcp.your-domain.com/mcp`.
207
214
 
215
+ ### `invalid_request` / `Unregistered redirect_uri` on `/authorize`
216
+
217
+ The `redirect_uri` the client sent is not on the server's allow-list. Two common causes:
218
+
219
+ 1. **`MCP_REDIRECT_URIS` is set but doesn't contain the client's exact callback.** Claude.ai uses `https://claude.ai/api/mcp/auth_callback` (and `https://claude.com/api/mcp/auth_callback`) — a similar-looking entry like `https://claude.ai/oauth/callback` does **not** match. Matching is exact on scheme, host, and path; only loopback callbacks (`localhost`, `127.0.0.1`, `[::1]`) get port relaxation per RFC 8252.
220
+
221
+ 2. **You're on bridge ≤ 1.5.0 with `MCP_REDIRECT_URIS` unset.** The allow-any fallback in those versions was silently broken by a change in MCP SDK 1.29 (the SDK switched its membership check from `.includes()` to `.some()`), so *every* redirect_uri was rejected — fresh `npx openclaw-mcp@latest` installs picked the new SDK up automatically. Upgrade to bridge ≥ 1.6.0, or set `MCP_REDIRECT_URIS` explicitly (recommended for production anyway):
222
+
223
+ ```bash
224
+ MCP_REDIRECT_URIS=https://claude.ai/api/mcp/auth_callback,https://claude.com/api/mcp/auth_callback
225
+ ```
226
+
208
227
  ### `ValidationError: ERR_ERL_UNEXPECTED_X_FORWARDED_FOR` on `/token`
209
228
 
210
229
  The server is behind a reverse proxy that sets `X-Forwarded-For`, but Express's `trust proxy` is left at its default (`false`). The MCP SDK's OAuth handlers use `express-rate-limit`, which refuses to read the forwarded header in that configuration and crashes the request. Set `TRUST_PROXY=1` (single proxy in front) or `--trust-proxy 1`. Use a higher hop count, a CIDR/IP, or a keyword (`loopback`, `linklocal`, `uniquelocal`) for more complex topologies — see [Server Settings](configuration.md#server-settings-http-transport).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-mcp",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "mcpName": "io.github.freema/openclaw-mcp",
5
5
  "description": "Model Context Protocol (MCP) server for OpenClaw AI assistant integration",
6
6
  "author": "Tomáš Grasl <https://www.tomasgrasl.cz/>",
@@ -40,7 +40,7 @@
40
40
  "node": ">=20.0.0"
41
41
  },
42
42
  "dependencies": {
43
- "@modelcontextprotocol/sdk": "^1.0.0",
43
+ "@modelcontextprotocol/sdk": "^1.29.0",
44
44
  "yargs": "^17.7.2"
45
45
  },
46
46
  "devDependencies": {