openclaw-mcp 1.6.0 → 1.7.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
@@ -161,9 +161,15 @@ See [Installation Guide](docs/installation.md) for details.
161
161
  |------|-------------|
162
162
  | `openclaw_chat_async` | Queue a message, get task_id immediately |
163
163
  | `openclaw_task_status` | Check task progress and get results |
164
- | `openclaw_task_list` | List all tasks with filtering |
164
+ | `openclaw_task_list` | List your tasks with filtering |
165
165
  | `openclaw_task_cancel` | Cancel a pending task |
166
166
 
167
+ Tasks are scoped to the MCP connection that created them. In HTTP mode, where
168
+ one process serves many clients, a client can only see and cancel its own
169
+ tasks — another client's `task_id` reads as "not found" even if it is known.
170
+ Reconnecting starts a fresh scope, so poll a task on the connection that
171
+ queued it.
172
+
167
173
  ## Multi-Instance Mode
168
174
 
169
175
  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).
@@ -261,7 +267,8 @@ AUTH_ENABLED=true MCP_CLIENT_ID=openclaw MCP_CLIENT_SECRET=$MCP_CLIENT_SECRET \
261
267
  openclaw-mcp --transport http
262
268
  ```
263
269
 
264
- Configure CORS to restrict access:
270
+ CORS is disabled unless you opt in. Set `CORS_ORIGINS` only when a browser
271
+ client needs to reach the server directly:
265
272
 
266
273
  ```bash
267
274
  CORS_ORIGINS=https://claude.ai,https://your-app.com
@@ -269,6 +276,19 @@ CORS_ORIGINS=https://claude.ai,https://your-app.com
269
276
 
270
277
  See [Configuration](docs/configuration.md) for all security options.
271
278
 
279
+ ### Upgrading to 1.7.0
280
+
281
+ Two defaults changed for security. Both only affect HTTP mode; stdio is
282
+ unchanged.
283
+
284
+ - **CORS is now off by default.** Previously an unset `CORS_ORIGINS` sent
285
+ `Access-Control-Allow-Origin: *`. If a browser client depends on that, set
286
+ the origins explicitly (`CORS_ORIGINS=https://your-app.com`), or
287
+ `CORS_ORIGINS=*` to restore the old behaviour.
288
+ - **Async tasks are scoped to the connection that created them.** A client
289
+ that used to poll a `task_id` queued by a different connection will now get
290
+ "not found".
291
+
272
292
  ## Migrating from SSE to HTTP transport
273
293
 
274
294
  Starting with v1.5.0, the primary transport is **Streamable HTTP** (`--transport http`). The legacy SSE transport (`--transport sse`) is deprecated but still works for backward compatibility.
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.6.0";
8
+ var SERVER_VERSION = "1.7.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==";
@@ -475,6 +475,7 @@ var InstanceRegistry = class {
475
475
  };
476
476
 
477
477
  // src/server/tools-registration.ts
478
+ import { randomUUID as randomUUID2 } from "crypto";
478
479
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
479
480
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
480
481
 
@@ -641,12 +642,12 @@ async function handleOpenclawInstances(registry2, _input) {
641
642
  }
642
643
 
643
644
  // src/mcp/tasks/manager.ts
645
+ import { randomUUID } from "crypto";
644
646
  var MAX_TASKS = 1e3;
645
647
  var CLEANUP_INTERVAL_MS = 10 * 60 * 1e3;
646
648
  var CLEANUP_MAX_AGE_MS = 60 * 60 * 1e3;
647
649
  var TaskManager = class {
648
650
  tasks = /* @__PURE__ */ new Map();
649
- taskCounter = 0;
650
651
  cleanupInterval;
651
652
  constructor() {
652
653
  this.cleanupInterval = setInterval(() => this.cleanup(CLEANUP_MAX_AGE_MS), CLEANUP_INTERVAL_MS);
@@ -655,16 +656,17 @@ var TaskManager = class {
655
656
  }
656
657
  }
657
658
  /**
658
- * Generate unique task ID
659
+ * Generate an unguessable task ID.
660
+ *
661
+ * Deliberately not sequential: a predictable counter would let a caller
662
+ * enumerate IDs and probe for tasks it does not own. Ownership checks are
663
+ * the real defence, this just removes the oracle.
659
664
  */
660
665
  generateId() {
661
- this.taskCounter++;
662
- const timestamp = Date.now().toString(36);
663
- const counter = this.taskCounter.toString(36).padStart(4, "0");
664
- return `task_${timestamp}_${counter}`;
666
+ return `task_${randomUUID()}`;
665
667
  }
666
668
  /**
667
- * Create a new task
669
+ * Create a new task owned by `options.ownerId`.
668
670
  */
669
671
  create(options) {
670
672
  if (this.tasks.size >= MAX_TASKS) {
@@ -679,6 +681,7 @@ var TaskManager = class {
679
681
  status: "pending",
680
682
  input: options.input,
681
683
  createdAt: /* @__PURE__ */ new Date(),
684
+ ownerId: options.ownerId,
682
685
  sessionId: options.sessionId,
683
686
  instanceId: options.instanceId,
684
687
  priority: options.priority ?? 0
@@ -688,16 +691,21 @@ var TaskManager = class {
688
691
  return task;
689
692
  }
690
693
  /**
691
- * Get task by ID
694
+ * Get a task by ID, but only if `ownerId` owns it.
695
+ *
696
+ * Returns undefined both for unknown IDs and for tasks owned by someone
697
+ * else, so callers cannot distinguish the two.
692
698
  */
693
- get(id) {
694
- return this.tasks.get(id);
699
+ get(id, ownerId) {
700
+ const task = this.tasks.get(id);
701
+ if (!task || task.ownerId !== ownerId) return void 0;
702
+ return task;
695
703
  }
696
704
  /**
697
- * List all tasks, optionally filtered by status
705
+ * List the tasks owned by `ownerId`, optionally filtered further.
698
706
  */
699
- list(filter) {
700
- let tasks = Array.from(this.tasks.values());
707
+ list(ownerId, filter) {
708
+ let tasks = Array.from(this.tasks.values()).filter((t) => t.ownerId === ownerId);
701
709
  if (filter?.status) {
702
710
  tasks = tasks.filter((t) => t.status === filter.status);
703
711
  }
@@ -713,7 +721,10 @@ var TaskManager = class {
713
721
  });
714
722
  }
715
723
  /**
716
- * Update task status
724
+ * Update task status.
725
+ *
726
+ * Internal worker path only — never reachable from a tool handler, so it
727
+ * takes no ownerId.
717
728
  */
718
729
  updateStatus(id, status, result, error) {
719
730
  const task = this.tasks.get(id);
@@ -731,10 +742,10 @@ var TaskManager = class {
731
742
  return true;
732
743
  }
733
744
  /**
734
- * Cancel a pending task
745
+ * Cancel a pending task owned by `ownerId`.
735
746
  */
736
- cancel(id) {
737
- const task = this.tasks.get(id);
747
+ cancel(id, ownerId) {
748
+ const task = this.get(id, ownerId);
738
749
  if (!task) return false;
739
750
  if (task.status !== "pending") {
740
751
  return false;
@@ -745,16 +756,22 @@ var TaskManager = class {
745
756
  return true;
746
757
  }
747
758
  /**
748
- * Delete a task (cleanup)
759
+ * Delete a task (cleanup). Internal only.
749
760
  */
750
761
  delete(id) {
751
762
  return this.tasks.delete(id);
752
763
  }
753
764
  /**
754
- * Get next pending task (for workers)
765
+ * Get next pending task across all owners (for the background worker).
766
+ *
767
+ * Internal only — the worker has to see every queue, which is exactly why
768
+ * tool handlers must go through the owner-scoped methods instead.
755
769
  */
756
770
  getNextPending() {
757
- const pending = this.list({ status: "pending" });
771
+ const pending = Array.from(this.tasks.values()).filter((t) => t.status === "pending").sort((a, b) => {
772
+ if (b.priority !== a.priority) return b.priority - a.priority;
773
+ return a.createdAt.getTime() - b.createdAt.getTime();
774
+ });
758
775
  return pending[0];
759
776
  }
760
777
  /**
@@ -775,9 +792,12 @@ var TaskManager = class {
775
792
  return cleaned;
776
793
  }
777
794
  /**
778
- * Get statistics
795
+ * Get statistics for the tasks owned by `ownerId`.
796
+ *
797
+ * Scoped like everything else: a global total would leak how busy other
798
+ * connections are.
779
799
  */
780
- stats() {
800
+ stats(ownerId) {
781
801
  const byStatus = {
782
802
  pending: 0,
783
803
  running: 0,
@@ -785,13 +805,13 @@ var TaskManager = class {
785
805
  failed: 0,
786
806
  cancelled: 0
787
807
  };
808
+ let total = 0;
788
809
  for (const task of this.tasks.values()) {
810
+ if (task.ownerId !== ownerId) continue;
789
811
  byStatus[task.status]++;
812
+ total++;
790
813
  }
791
- return {
792
- total: this.tasks.size,
793
- byStatus
794
- };
814
+ return { total, byStatus };
795
815
  }
796
816
  };
797
817
  var taskManager = new TaskManager();
@@ -915,7 +935,7 @@ function startTaskProcessor(registry2) {
915
935
  });
916
936
  log("Task processor started");
917
937
  }
918
- async function handleOpenclawChatAsync(registry2, input) {
938
+ async function handleOpenclawChatAsync(registry2, input, ownerId) {
919
939
  if (!validateInputIsObject(input)) {
920
940
  return errorResponse("Invalid input: expected an object");
921
941
  }
@@ -957,6 +977,7 @@ async function handleOpenclawChatAsync(registry2, input) {
957
977
  const task = taskManager.create({
958
978
  type: "chat",
959
979
  input: { message: msgResult.value, session_id: sessionId },
980
+ ownerId,
960
981
  sessionId,
961
982
  priority,
962
983
  instanceId
@@ -974,7 +995,7 @@ async function handleOpenclawChatAsync(registry2, input) {
974
995
  )
975
996
  );
976
997
  }
977
- async function handleOpenclawTaskStatus(_registry, input) {
998
+ async function handleOpenclawTaskStatus(_registry, input, ownerId) {
978
999
  if (!validateInputIsObject(input)) {
979
1000
  return errorResponse("Invalid input: expected an object");
980
1001
  }
@@ -983,7 +1004,7 @@ async function handleOpenclawTaskStatus(_registry, input) {
983
1004
  return errorResponse(tidResult.error);
984
1005
  }
985
1006
  const task_id = tidResult.value;
986
- const task = taskManager.get(task_id);
1007
+ const task = taskManager.get(task_id, ownerId);
987
1008
  if (!task) {
988
1009
  return errorResponse(`Task not found: ${task_id}`);
989
1010
  }
@@ -1015,7 +1036,7 @@ var VALID_TASK_STATUSES = [
1015
1036
  "failed",
1016
1037
  "cancelled"
1017
1038
  ];
1018
- async function handleOpenclawTaskList(_registry, input) {
1039
+ async function handleOpenclawTaskList(_registry, input, ownerId) {
1019
1040
  if (!validateInputIsObject(input)) {
1020
1041
  return errorResponse("Invalid input: expected an object");
1021
1042
  }
@@ -1042,8 +1063,12 @@ async function handleOpenclawTaskList(_registry, input) {
1042
1063
  }
1043
1064
  instanceFilter = instResult.value;
1044
1065
  }
1045
- const tasks = taskManager.list({ status, sessionId: session_id, instanceId: instanceFilter });
1046
- const stats = taskManager.stats();
1066
+ const tasks = taskManager.list(ownerId, {
1067
+ status,
1068
+ sessionId: session_id,
1069
+ instanceId: instanceFilter
1070
+ });
1071
+ const stats = taskManager.stats(ownerId);
1047
1072
  const taskList = tasks.map((t) => ({
1048
1073
  task_id: t.id,
1049
1074
  type: t.type,
@@ -1064,7 +1089,7 @@ async function handleOpenclawTaskList(_registry, input) {
1064
1089
  )
1065
1090
  );
1066
1091
  }
1067
- async function handleOpenclawTaskCancel(_registry, input) {
1092
+ async function handleOpenclawTaskCancel(_registry, input, ownerId) {
1068
1093
  if (!validateInputIsObject(input)) {
1069
1094
  return errorResponse("Invalid input: expected an object");
1070
1095
  }
@@ -1073,7 +1098,7 @@ async function handleOpenclawTaskCancel(_registry, input) {
1073
1098
  return errorResponse(tidResult.error);
1074
1099
  }
1075
1100
  const task_id = tidResult.value;
1076
- const task = taskManager.get(task_id);
1101
+ const task = taskManager.get(task_id, ownerId);
1077
1102
  if (!task) {
1078
1103
  return errorResponse(`Task not found: ${task_id}`);
1079
1104
  }
@@ -1082,7 +1107,7 @@ async function handleOpenclawTaskCancel(_registry, input) {
1082
1107
  `Cannot cancel task with status: ${task.status}. Only pending tasks can be cancelled.`
1083
1108
  );
1084
1109
  }
1085
- const cancelled = taskManager.cancel(task_id);
1110
+ const cancelled = taskManager.cancel(task_id, ownerId);
1086
1111
  if (!cancelled) {
1087
1112
  return errorResponse("Failed to cancel task");
1088
1113
  }
@@ -1120,13 +1145,14 @@ function createMcpServer(deps2) {
1120
1145
  }
1121
1146
  function registerTools(server, deps2) {
1122
1147
  const { registry: registry2 } = deps2;
1148
+ const ownerId = randomUUID2();
1123
1149
  const toolHandlers = /* @__PURE__ */ new Map([
1124
1150
  ["openclaw_chat", (input) => handleOpenclawChat(registry2, input)],
1125
1151
  ["openclaw_status", (input) => handleOpenclawStatus(registry2, input)],
1126
- ["openclaw_chat_async", (input) => handleOpenclawChatAsync(registry2, input)],
1127
- ["openclaw_task_status", (input) => handleOpenclawTaskStatus(registry2, input)],
1128
- ["openclaw_task_list", (input) => handleOpenclawTaskList(registry2, input)],
1129
- ["openclaw_task_cancel", (input) => handleOpenclawTaskCancel(registry2, input)],
1152
+ ["openclaw_chat_async", (input) => handleOpenclawChatAsync(registry2, input, ownerId)],
1153
+ ["openclaw_task_status", (input) => handleOpenclawTaskStatus(registry2, input, ownerId)],
1154
+ ["openclaw_task_list", (input) => handleOpenclawTaskList(registry2, input, ownerId)],
1155
+ ["openclaw_task_cancel", (input) => handleOpenclawTaskCancel(registry2, input, ownerId)],
1130
1156
  ["openclaw_instances", (input) => handleOpenclawInstances(registry2, input)]
1131
1157
  ]);
1132
1158
  const allTools = [
@@ -1158,7 +1184,7 @@ function registerTools(server, deps2) {
1158
1184
  }
1159
1185
 
1160
1186
  // src/server/http.ts
1161
- import { randomUUID as randomUUID2 } from "crypto";
1187
+ import { randomUUID as randomUUID4 } from "crypto";
1162
1188
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
1163
1189
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
1164
1190
  import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
@@ -1166,7 +1192,7 @@ import { mcpAuthRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
1166
1192
  import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
1167
1193
 
1168
1194
  // src/auth/provider.ts
1169
- import { randomUUID } from "crypto";
1195
+ import { randomUUID as randomUUID3 } from "crypto";
1170
1196
  import {
1171
1197
  InvalidRequestError,
1172
1198
  InvalidTokenError
@@ -1265,7 +1291,7 @@ var OpenClawAuthProvider = class {
1265
1291
  * Auto-approve: generate auth code and redirect immediately.
1266
1292
  */
1267
1293
  async authorize(client, params, res) {
1268
- const code = randomUUID();
1294
+ const code = randomUUID3();
1269
1295
  this.codes.set(code, { client, params, createdAt: Date.now() });
1270
1296
  const searchParams = new URLSearchParams({ code });
1271
1297
  if (params.state !== void 0) {
@@ -1293,8 +1319,8 @@ var OpenClawAuthProvider = class {
1293
1319
  throw new InvalidRequestError("Authorization code was not issued to this client");
1294
1320
  }
1295
1321
  this.codes.delete(authorizationCode);
1296
- const accessToken = randomUUID();
1297
- const refreshToken = randomUUID();
1322
+ const accessToken = randomUUID3();
1323
+ const refreshToken = randomUUID3();
1298
1324
  const scopes = codeData.params.scopes || [];
1299
1325
  this.tokens.set(accessToken, {
1300
1326
  token: accessToken,
@@ -1327,8 +1353,8 @@ var OpenClawAuthProvider = class {
1327
1353
  throw new InvalidRequestError("Refresh token was not issued to this client");
1328
1354
  }
1329
1355
  this.refreshTokens.delete(refreshToken);
1330
- const accessToken = randomUUID();
1331
- const newRefreshToken = randomUUID();
1356
+ const accessToken = randomUUID3();
1357
+ const newRefreshToken = randomUUID3();
1332
1358
  const tokenScopes = scopes || data.scopes;
1333
1359
  this.tokens.set(accessToken, {
1334
1360
  token: accessToken,
@@ -1395,12 +1421,12 @@ function parseTrustProxy(value) {
1395
1421
  }
1396
1422
  function loadCorsConfig() {
1397
1423
  const corsOrigins = process.env.CORS_ORIGINS;
1398
- if (!corsOrigins || corsOrigins === "*") {
1399
- return { origins: ["*"], enabled: true };
1400
- }
1401
- if (corsOrigins.toLowerCase() === "none" || corsOrigins === "") {
1424
+ if (!corsOrigins || corsOrigins.toLowerCase() === "none") {
1402
1425
  return { origins: [], enabled: false };
1403
1426
  }
1427
+ if (corsOrigins === "*") {
1428
+ return { origins: ["*"], enabled: true };
1429
+ }
1404
1430
  return {
1405
1431
  origins: corsOrigins.split(",").map((s) => s.trim()).filter(Boolean),
1406
1432
  enabled: true
@@ -1551,7 +1577,7 @@ async function createHttpServer(config, deps2) {
1551
1577
  return;
1552
1578
  }
1553
1579
  const transport = new StreamableHTTPServerTransport({
1554
- sessionIdGenerator: () => randomUUID2(),
1580
+ sessionIdGenerator: () => randomUUID4(),
1555
1581
  onsessioninitialized: (newSessionId) => {
1556
1582
  streamableSessions.set(newSessionId, { transport, server });
1557
1583
  log(`Streamable session initialized: ${newSessionId}`);
@@ -1586,6 +1612,11 @@ async function createHttpServer(config, deps2) {
1586
1612
  log(`HTTP server listening on ${config.host}:${config.port}`);
1587
1613
  log(`Auth enabled: ${authEnabled}`);
1588
1614
  log(`CORS origins: ${corsConfig.enabled ? corsConfig.origins.join(", ") : "disabled"}`);
1615
+ if (corsConfig.origins.includes("*")) {
1616
+ logError(
1617
+ "WARNING: CORS_ORIGINS=* allows any web page to call this server from a browser. List explicit origins instead."
1618
+ );
1619
+ }
1589
1620
  if (authEnabled) {
1590
1621
  log("OAuth 2.1 authentication is REQUIRED for all connections");
1591
1622
  log("Endpoints:");
@@ -123,17 +123,24 @@ When `TRUST_PROXY` is unset and a reverse proxy injects an `X-Forwarded-For` hea
123
123
 
124
124
  ### CORS Configuration
125
125
 
126
- | Variable | Description | Default |
127
- | -------------- | --------------------------------- | ------- |
128
- | `CORS_ORIGINS` | Allowed origins (comma-separated) | `*` |
126
+ | Variable | Description | Default |
127
+ | -------------- | --------------------------------- | ------------------ |
128
+ | `CORS_ORIGINS` | Allowed origins (comma-separated) | _(CORS disabled)_ |
129
+
130
+ CORS is **off unless you opt in**. With it off the server sends no
131
+ `Access-Control-Allow-Origin` header, so browsers refuse cross-origin calls to
132
+ it. Non-browser clients — Claude.ai, Claude Desktop, and anything speaking
133
+ stdio — are unaffected; set this only when a web page has to reach the server
134
+ directly.
129
135
 
130
136
  **CORS_ORIGINS examples:**
131
137
 
132
- - `*`Allow all origins (not recommended for production)
133
- - `none` — Disable CORS entirely
138
+ - _(unset)_ or `none` CORS disabled (default)
134
139
  - `https://claude.ai` — Single origin
135
140
  - `https://claude.ai,https://your-app.com` — Multiple origins
136
141
  - `*.example.com` — Wildcard subdomain
142
+ - `*` — Allow all origins. Lets any web page drive this server through a
143
+ visitor's browser; do not use in production.
137
144
 
138
145
  ### Authentication (OAuth 2.1)
139
146
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-mcp",
3
- "version": "1.6.0",
3
+ "version": "1.7.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/>",