openclaw-mcp 1.2.0 → 1.3.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
@@ -4,6 +4,7 @@
4
4
  [![CI](https://github.com/freema/openclaw-mcp/workflows/CI/badge.svg)](https://github.com/freema/openclaw-mcp/actions/workflows/ci.yml)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![GHCR](https://img.shields.io/badge/GHCR-ghcr.io%2Ffreema%2Fopenclaw--mcp-blue?logo=github)](https://github.com/freema/openclaw-mcp/pkgs/container/openclaw-mcp)
7
+ [![Website](https://img.shields.io/badge/Website-openclaw--mcp.cloud-e24b4a)](https://openclaw-mcp.cloud)
7
8
 
8
9
  <a href="https://glama.ai/mcp/servers/@freema/openclaw-mcp">
9
10
  <img width="380" height="200" src="https://glama.ai/mcp/servers/@freema/openclaw-mcp/badge" />
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.0.0";
8
+ var SERVER_VERSION = "1.3.0";
9
9
  var DEFAULT_OPENCLAW_URL = "http://127.0.0.1:18789";
10
10
  var SERVER_ICON_SVG_BASE64 = "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCIgZmlsbD0ibm9uZSI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzFhMWEyZSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzE2MjEzZSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJjbGF3IiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZmYzMzMzIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjY2MwMDAwIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjEyOCIgaGVpZ2h0PSIxMjgiIHJ4PSIyNCIgZmlsbD0idXJsKCNiZykiLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2NCA2NCkiIHN0cm9rZT0idXJsKCNjbGF3KSIgc3Ryb2tlLXdpZHRoPSI3IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0tMjggLTM4YzAgMCAtMTAgMjAgMCAzMiIvPjxwYXRoIGQ9Ik0tMTIgLTQwYzAgMCAtNiAyMiA0IDM0Ii8+PHBhdGggZD0iTTI4IC0zOGMwIDAgMTAgMjAgMCAzMiIvPjxwYXRoIGQ9Ik0xMiAtNDBjMCAwIDYgMjIgLTQgMzQiLz48Y2lyY2xlIGN4PSIwIiBjeT0iMTAiIHI9IjIwIiBzdHJva2Utd2lkdGg9IjYiLz48cGF0aCBkPSJNLTEwIDR2LTQiIHN0cm9rZS13aWR0aD0iNCIvPjxwYXRoIGQ9Ik0xMCA0di00IiBzdHJva2Utd2lkdGg9IjQiLz48cGF0aCBkPSJNLTggMjBjNCA2IDEyIDYgMTYgMCIgc3Ryb2tlLXdpZHRoPSIzIi8+PC9nPjwvc3ZnPg==";
11
11
 
@@ -91,6 +91,45 @@ function parseArguments(version) {
91
91
  description: "Allowed OAuth redirect URIs (comma-separated)",
92
92
  default: process.env.MCP_REDIRECT_URIS || void 0
93
93
  }).help().parseSync();
94
+ let instances;
95
+ const instancesEnv = process.env.OPENCLAW_INSTANCES;
96
+ if (instancesEnv) {
97
+ try {
98
+ const parsed = JSON.parse(instancesEnv);
99
+ if (!Array.isArray(parsed) || parsed.length === 0) {
100
+ throw new Error("OPENCLAW_INSTANCES must be a non-empty JSON array");
101
+ }
102
+ for (const item of parsed) {
103
+ if (!item || typeof item.name !== "string" || !item.name.trim()) {
104
+ throw new Error(
105
+ 'Each instance in OPENCLAW_INSTANCES must have a non-empty string "name"'
106
+ );
107
+ }
108
+ if (typeof item.url !== "string" || !item.url.trim()) {
109
+ throw new Error(`Instance "${item.name}": must have a non-empty string "url"`);
110
+ }
111
+ }
112
+ instances = parsed.map((cfg) => ({
113
+ ...cfg,
114
+ timeout: cfg.timeout ?? argv.timeout
115
+ }));
116
+ } catch (error) {
117
+ if (error instanceof SyntaxError) {
118
+ throw new Error(`OPENCLAW_INSTANCES contains invalid JSON: ${error.message}`);
119
+ }
120
+ throw error;
121
+ }
122
+ } else {
123
+ instances = [
124
+ {
125
+ name: "default",
126
+ url: argv["openclaw-url"],
127
+ token: argv["gateway-token"],
128
+ timeout: argv.timeout,
129
+ default: true
130
+ }
131
+ ];
132
+ }
94
133
  return {
95
134
  openclawUrl: argv["openclaw-url"],
96
135
  gatewayToken: argv["gateway-token"],
@@ -102,7 +141,8 @@ function parseArguments(version) {
102
141
  clientId: argv["client-id"],
103
142
  clientSecret: argv["client-secret"],
104
143
  issuerUrl: argv["issuer-url"],
105
- redirectUris: argv["redirect-uris"] ? argv["redirect-uris"].split(",").map((s) => s.trim()).filter(Boolean) : void 0
144
+ redirectUris: argv["redirect-uris"] ? argv["redirect-uris"].split(",").map((s) => s.trim()).filter(Boolean) : void 0,
145
+ instances
106
146
  };
107
147
  }
108
148
 
@@ -261,6 +301,120 @@ var OpenClawClient = class {
261
301
  }
262
302
  };
263
303
 
304
+ // src/openclaw/registry.ts
305
+ var INSTANCE_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
306
+ var InstanceRegistry = class {
307
+ instances = /* @__PURE__ */ new Map();
308
+ defaultName;
309
+ constructor(configs) {
310
+ if (configs.length === 0) {
311
+ throw new Error("At least one OpenClaw instance must be configured");
312
+ }
313
+ const names = /* @__PURE__ */ new Set();
314
+ let explicitDefault;
315
+ for (const config of configs) {
316
+ if (!INSTANCE_NAME_RE.test(config.name)) {
317
+ throw new Error(
318
+ `Invalid instance name "${config.name}": must be 1-64 chars, alphanumeric/dashes/underscores, start with alphanumeric`
319
+ );
320
+ }
321
+ try {
322
+ const parsed = new URL(config.url);
323
+ if (!["http:", "https:"].includes(parsed.protocol)) {
324
+ throw new Error(
325
+ `Instance "${config.name}": URL must use http or https (got ${parsed.protocol})`
326
+ );
327
+ }
328
+ } catch (error) {
329
+ if (error instanceof TypeError) {
330
+ throw new Error(`Instance "${config.name}": invalid URL "${config.url}"`);
331
+ }
332
+ throw error;
333
+ }
334
+ if (names.has(config.name)) {
335
+ throw new Error(`Duplicate instance name: "${config.name}"`);
336
+ }
337
+ names.add(config.name);
338
+ if (config.default) {
339
+ if (explicitDefault) {
340
+ throw new Error(
341
+ `Multiple default instances: "${explicitDefault}" and "${config.name}". Only one default is allowed.`
342
+ );
343
+ }
344
+ explicitDefault = config.name;
345
+ }
346
+ const client = new OpenClawClient(config.url, config.token, config.timeout);
347
+ this.instances.set(config.name, { config, client });
348
+ }
349
+ this.defaultName = explicitDefault ?? configs[0].name;
350
+ }
351
+ /**
352
+ * Get client by instance name. Returns undefined if not found.
353
+ */
354
+ get(name) {
355
+ return this.instances.get(name)?.client;
356
+ }
357
+ /**
358
+ * Get the default client.
359
+ */
360
+ getDefault() {
361
+ const entry = this.instances.get(this.defaultName);
362
+ if (!entry) {
363
+ throw new Error(`Default instance "${this.defaultName}" not found`);
364
+ }
365
+ return entry.client;
366
+ }
367
+ /**
368
+ * Get the default instance name.
369
+ */
370
+ getDefaultName() {
371
+ return this.defaultName;
372
+ }
373
+ /**
374
+ * Resolve an optional instance name to a concrete client.
375
+ * Falls back to default when name is undefined.
376
+ */
377
+ resolve(name) {
378
+ if (!name) {
379
+ return { name: this.defaultName, client: this.getDefault() };
380
+ }
381
+ const client = this.get(name);
382
+ if (!client) {
383
+ const available = this.listNames().join(", ");
384
+ throw new Error(`Unknown instance "${name}". Available: ${available}`);
385
+ }
386
+ return { name, client };
387
+ }
388
+ /**
389
+ * List instance names.
390
+ */
391
+ listNames() {
392
+ return Array.from(this.instances.keys());
393
+ }
394
+ /**
395
+ * List instances with safe metadata (never exposes tokens).
396
+ */
397
+ list() {
398
+ return Array.from(this.instances.entries()).map(([name, { config }]) => ({
399
+ name,
400
+ url: config.url,
401
+ isDefault: name === this.defaultName
402
+ }));
403
+ }
404
+ /**
405
+ * Number of registered instances.
406
+ */
407
+ get size() {
408
+ return this.instances.size;
409
+ }
410
+ /**
411
+ * Check if this is a single-instance (backward-compat) setup.
412
+ */
413
+ get isSingleInstance() {
414
+ return this.instances.size === 1;
415
+ }
416
+ };
417
+
264
418
  // src/server/tools-registration.ts
265
419
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
266
420
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
@@ -329,12 +483,16 @@ var openclawChatTool = {
329
483
  session_id: {
330
484
  type: "string",
331
485
  description: "Optional session ID for conversation context"
486
+ },
487
+ instance: {
488
+ type: "string",
489
+ description: "Target OpenClaw instance name. Use openclaw_instances to list available instances. Defaults to the default instance."
332
490
  }
333
491
  },
334
492
  required: ["message"]
335
493
  }
336
494
  };
337
- async function handleOpenclawChat(client2, input) {
495
+ async function handleOpenclawChat(registry2, input) {
338
496
  if (!validateInputIsObject(input)) {
339
497
  return errorResponse("Invalid input: expected an object");
340
498
  }
@@ -350,8 +508,17 @@ async function handleOpenclawChat(client2, input) {
350
508
  }
351
509
  sessionId = sidResult.value;
352
510
  }
511
+ let instanceName;
512
+ if (input.instance !== void 0) {
513
+ const instResult = validateId(input.instance, "instance");
514
+ if (instResult.valid === false) {
515
+ return errorResponse(instResult.error);
516
+ }
517
+ instanceName = instResult.value;
518
+ }
353
519
  try {
354
- const response = await client2.chat(msgResult.value, sessionId);
520
+ const { client } = registry2.resolve(instanceName);
521
+ const response = await client.chat(msgResult.value, sessionId);
355
522
  return successResponse(response.response);
356
523
  } catch (error) {
357
524
  return errorResponse(error instanceof Error ? error.message : "Failed to chat with OpenClaw");
@@ -364,16 +531,33 @@ var openclawStatusTool = {
364
531
  description: "Get OpenClaw gateway status and health information",
365
532
  inputSchema: {
366
533
  type: "object",
367
- properties: {}
534
+ properties: {
535
+ instance: {
536
+ type: "string",
537
+ description: "Target OpenClaw instance name. Defaults to the default instance."
538
+ }
539
+ }
368
540
  }
369
541
  };
370
- async function handleOpenclawStatus(client2, input) {
542
+ async function handleOpenclawStatus(registry2, input) {
371
543
  if (!validateInputIsObject(input)) {
372
544
  return errorResponse("Invalid input: expected an object");
373
545
  }
546
+ let instanceName;
547
+ if (input.instance !== void 0) {
548
+ const instResult = validateId(input.instance, "instance");
549
+ if (instResult.valid === false) {
550
+ return errorResponse(instResult.error);
551
+ }
552
+ instanceName = instResult.value;
553
+ }
374
554
  try {
375
- const response = await client2.health();
376
- return jsonResponse(response);
555
+ const resolved = registry2.resolve(instanceName);
556
+ const response = await resolved.client.health();
557
+ return jsonResponse({
558
+ ...response,
559
+ instance: resolved.name
560
+ });
377
561
  } catch (error) {
378
562
  return errorResponse(
379
563
  error instanceof Error ? error.message : "Failed to get status from OpenClaw"
@@ -381,6 +565,22 @@ async function handleOpenclawStatus(client2, input) {
381
565
  }
382
566
  }
383
567
 
568
+ // src/mcp/tools/instances.ts
569
+ var openclawInstancesTool = {
570
+ name: "openclaw_instances",
571
+ description: "List all configured OpenClaw instances. Shows instance names, URLs, and which is the default. Use instance names in other tools to target a specific OpenClaw gateway.",
572
+ inputSchema: {
573
+ type: "object",
574
+ properties: {}
575
+ }
576
+ };
577
+ async function handleOpenclawInstances(registry2, _input) {
578
+ return jsonResponse({
579
+ instances: registry2.list(),
580
+ total: registry2.size
581
+ });
582
+ }
583
+
384
584
  // src/mcp/tasks/manager.ts
385
585
  var MAX_TASKS = 1e3;
386
586
  var CLEANUP_INTERVAL_MS = 10 * 60 * 1e3;
@@ -421,6 +621,7 @@ var TaskManager = class {
421
621
  input: options.input,
422
622
  createdAt: /* @__PURE__ */ new Date(),
423
623
  sessionId: options.sessionId,
624
+ instanceId: options.instanceId,
424
625
  priority: options.priority ?? 0
425
626
  };
426
627
  this.tasks.set(id, task);
@@ -444,6 +645,9 @@ var TaskManager = class {
444
645
  if (filter?.sessionId) {
445
646
  tasks = tasks.filter((t) => t.sessionId === filter.sessionId);
446
647
  }
648
+ if (filter?.instanceId) {
649
+ tasks = tasks.filter((t) => t.instanceId === filter.instanceId);
650
+ }
447
651
  return tasks.sort((a, b) => {
448
652
  if (b.priority !== a.priority) return b.priority - a.priority;
449
653
  return a.createdAt.getTime() - b.createdAt.getTime();
@@ -551,6 +755,10 @@ var openclawChatAsyncTool = {
551
755
  priority: {
552
756
  type: "number",
553
757
  description: "Task priority (higher = processed first). Default: 0"
758
+ },
759
+ instance: {
760
+ type: "string",
761
+ description: "Target OpenClaw instance name. Defaults to the default instance."
554
762
  }
555
763
  },
556
764
  required: ["message"]
@@ -572,7 +780,7 @@ var openclawTaskStatusTool = {
572
780
  };
573
781
  var openclawTaskListTool = {
574
782
  name: "openclaw_task_list",
575
- description: "List all tasks. Optionally filter by status or session.",
783
+ description: "List all tasks. Optionally filter by status, session, or instance.",
576
784
  inputSchema: {
577
785
  type: "object",
578
786
  properties: {
@@ -584,6 +792,10 @@ var openclawTaskListTool = {
584
792
  session_id: {
585
793
  type: "string",
586
794
  description: "Filter by session ID"
795
+ },
796
+ instance: {
797
+ type: "string",
798
+ description: "Filter by instance name"
587
799
  }
588
800
  },
589
801
  required: []
@@ -604,12 +816,20 @@ var openclawTaskCancelTool = {
604
816
  }
605
817
  };
606
818
  var processorRunning = false;
607
- var processorClient = null;
608
- async function processTask(task, client2) {
819
+ var processorRegistry = null;
820
+ async function processTask(task, registry2) {
609
821
  taskManager.updateStatus(task.id, "running");
822
+ let client;
823
+ try {
824
+ client = registry2.resolve(task.instanceId).client;
825
+ } catch (error) {
826
+ const errorMsg = error instanceof Error ? error.message : "Instance not available";
827
+ taskManager.updateStatus(task.id, "failed", void 0, errorMsg);
828
+ return;
829
+ }
610
830
  try {
611
831
  const input = task.input;
612
- const response = await client2.chat(input.message, input.session_id);
832
+ const response = await client.chat(input.message, input.session_id);
613
833
  taskManager.updateStatus(task.id, "completed", response.response);
614
834
  } catch (error) {
615
835
  const errorMsg = error instanceof Error ? error.message : "Unknown error";
@@ -617,26 +837,26 @@ async function processTask(task, client2) {
617
837
  }
618
838
  }
619
839
  async function taskProcessor() {
620
- if (!processorClient) return;
840
+ if (!processorRegistry) return;
621
841
  while (processorRunning) {
622
842
  const task = taskManager.getNextPending();
623
843
  if (task) {
624
- await processTask(task, processorClient);
844
+ await processTask(task, processorRegistry);
625
845
  } else {
626
846
  await new Promise((resolve) => setTimeout(resolve, 100));
627
847
  }
628
848
  }
629
849
  }
630
- function startTaskProcessor(client2) {
850
+ function startTaskProcessor(registry2) {
631
851
  if (processorRunning) return;
632
- processorClient = client2;
852
+ processorRegistry = registry2;
633
853
  processorRunning = true;
634
854
  taskProcessor().catch(() => {
635
855
  processorRunning = false;
636
856
  });
637
857
  log("Task processor started");
638
858
  }
639
- async function handleOpenclawChatAsync(client2, input) {
859
+ async function handleOpenclawChatAsync(registry2, input) {
640
860
  if (!validateInputIsObject(input)) {
641
861
  return errorResponse("Invalid input: expected an object");
642
862
  }
@@ -659,18 +879,35 @@ async function handleOpenclawChatAsync(client2, input) {
659
879
  }
660
880
  priority = input.priority;
661
881
  }
662
- startTaskProcessor(client2);
882
+ let instanceId;
883
+ try {
884
+ let instanceName;
885
+ if (input.instance !== void 0) {
886
+ const instResult = validateId(input.instance, "instance");
887
+ if (instResult.valid === false) {
888
+ return errorResponse(instResult.error);
889
+ }
890
+ instanceName = instResult.value;
891
+ }
892
+ const resolved = registry2.resolve(instanceName);
893
+ instanceId = resolved.name;
894
+ } catch (error) {
895
+ return errorResponse(error instanceof Error ? error.message : "Invalid instance");
896
+ }
897
+ startTaskProcessor(registry2);
663
898
  const task = taskManager.create({
664
899
  type: "chat",
665
900
  input: { message: msgResult.value, session_id: sessionId },
666
901
  sessionId,
667
- priority
902
+ priority,
903
+ instanceId
668
904
  });
669
905
  return successResponse(
670
906
  JSON.stringify(
671
907
  {
672
908
  task_id: task.id,
673
909
  status: task.status,
910
+ instance: instanceId,
674
911
  message: "Task queued. Use openclaw_task_status to check progress."
675
912
  },
676
913
  null,
@@ -678,7 +915,7 @@ async function handleOpenclawChatAsync(client2, input) {
678
915
  )
679
916
  );
680
917
  }
681
- async function handleOpenclawTaskStatus(_client, input) {
918
+ async function handleOpenclawTaskStatus(_registry, input) {
682
919
  if (!validateInputIsObject(input)) {
683
920
  return errorResponse("Invalid input: expected an object");
684
921
  }
@@ -695,6 +932,7 @@ async function handleOpenclawTaskStatus(_client, input) {
695
932
  task_id: task.id,
696
933
  type: task.type,
697
934
  status: task.status,
935
+ instance: task.instanceId,
698
936
  created_at: task.createdAt.toISOString()
699
937
  };
700
938
  if (task.startedAt) {
@@ -718,7 +956,7 @@ var VALID_TASK_STATUSES = [
718
956
  "failed",
719
957
  "cancelled"
720
958
  ];
721
- async function handleOpenclawTaskList(_client, input) {
959
+ async function handleOpenclawTaskList(_registry, input) {
722
960
  if (!validateInputIsObject(input)) {
723
961
  return errorResponse("Invalid input: expected an object");
724
962
  }
@@ -737,12 +975,21 @@ async function handleOpenclawTaskList(_client, input) {
737
975
  }
738
976
  session_id = sidResult.value;
739
977
  }
740
- const tasks = taskManager.list({ status, sessionId: session_id });
978
+ let instanceFilter;
979
+ if (input.instance !== void 0) {
980
+ const instResult = validateId(input.instance, "instance");
981
+ if (instResult.valid === false) {
982
+ return errorResponse(instResult.error);
983
+ }
984
+ instanceFilter = instResult.value;
985
+ }
986
+ const tasks = taskManager.list({ status, sessionId: session_id, instanceId: instanceFilter });
741
987
  const stats = taskManager.stats();
742
988
  const taskList = tasks.map((t) => ({
743
989
  task_id: t.id,
744
990
  type: t.type,
745
991
  status: t.status,
992
+ instance: t.instanceId,
746
993
  priority: t.priority,
747
994
  created_at: t.createdAt.toISOString(),
748
995
  has_result: t.status === "completed" && !!t.result
@@ -758,7 +1005,7 @@ async function handleOpenclawTaskList(_client, input) {
758
1005
  )
759
1006
  );
760
1007
  }
761
- async function handleOpenclawTaskCancel(_client, input) {
1008
+ async function handleOpenclawTaskCancel(_registry, input) {
762
1009
  if (!validateInputIsObject(input)) {
763
1010
  return errorResponse("Invalid input: expected an object");
764
1011
  }
@@ -813,14 +1060,15 @@ function createMcpServer(deps2) {
813
1060
  return server;
814
1061
  }
815
1062
  function registerTools(server, deps2) {
816
- const { client: client2 } = deps2;
1063
+ const { registry: registry2 } = deps2;
817
1064
  const toolHandlers = /* @__PURE__ */ new Map([
818
- ["openclaw_chat", (input) => handleOpenclawChat(client2, input)],
819
- ["openclaw_status", (input) => handleOpenclawStatus(client2, input)],
820
- ["openclaw_chat_async", (input) => handleOpenclawChatAsync(client2, input)],
821
- ["openclaw_task_status", (input) => handleOpenclawTaskStatus(client2, input)],
822
- ["openclaw_task_list", (input) => handleOpenclawTaskList(client2, input)],
823
- ["openclaw_task_cancel", (input) => handleOpenclawTaskCancel(client2, input)]
1065
+ ["openclaw_chat", (input) => handleOpenclawChat(registry2, input)],
1066
+ ["openclaw_status", (input) => handleOpenclawStatus(registry2, input)],
1067
+ ["openclaw_chat_async", (input) => handleOpenclawChatAsync(registry2, input)],
1068
+ ["openclaw_task_status", (input) => handleOpenclawTaskStatus(registry2, input)],
1069
+ ["openclaw_task_list", (input) => handleOpenclawTaskList(registry2, input)],
1070
+ ["openclaw_task_cancel", (input) => handleOpenclawTaskCancel(registry2, input)],
1071
+ ["openclaw_instances", (input) => handleOpenclawInstances(registry2, input)]
824
1072
  ]);
825
1073
  const allTools = [
826
1074
  openclawChatTool,
@@ -828,7 +1076,8 @@ function registerTools(server, deps2) {
828
1076
  openclawChatAsyncTool,
829
1077
  openclawTaskStatusTool,
830
1078
  openclawTaskListTool,
831
- openclawTaskCancelTool
1079
+ openclawTaskCancelTool,
1080
+ openclawInstancesTool
832
1081
  ];
833
1082
  server.setRequestHandler(ListToolsRequestSchema, async () => {
834
1083
  return { tools: allTools };
@@ -938,9 +1187,9 @@ var OpenClawAuthProvider = class {
938
1187
  /**
939
1188
  * Auto-approve: generate auth code and redirect immediately.
940
1189
  */
941
- async authorize(client2, params, res) {
1190
+ async authorize(client, params, res) {
942
1191
  const code = randomUUID();
943
- this.codes.set(code, { client: client2, params, createdAt: Date.now() });
1192
+ this.codes.set(code, { client, params, createdAt: Date.now() });
944
1193
  const searchParams = new URLSearchParams({ code });
945
1194
  if (params.state !== void 0) {
946
1195
  searchParams.set("state", params.state);
@@ -957,13 +1206,13 @@ var OpenClawAuthProvider = class {
957
1206
  }
958
1207
  return codeData.params.codeChallenge;
959
1208
  }
960
- async exchangeAuthorizationCode(client2, authorizationCode, _codeVerifier, _redirectUri, resource) {
1209
+ async exchangeAuthorizationCode(client, authorizationCode, _codeVerifier, _redirectUri, resource) {
961
1210
  const codeData = this.codes.get(authorizationCode);
962
1211
  if (!codeData || Date.now() - codeData.createdAt > AUTH_CODE_TTL_MS) {
963
1212
  if (codeData) this.codes.delete(authorizationCode);
964
1213
  throw new InvalidRequestError("Invalid authorization code");
965
1214
  }
966
- if (codeData.client.client_id !== client2.client_id) {
1215
+ if (codeData.client.client_id !== client.client_id) {
967
1216
  throw new InvalidRequestError("Authorization code was not issued to this client");
968
1217
  }
969
1218
  this.codes.delete(authorizationCode);
@@ -972,13 +1221,13 @@ var OpenClawAuthProvider = class {
972
1221
  const scopes = codeData.params.scopes || [];
973
1222
  this.tokens.set(accessToken, {
974
1223
  token: accessToken,
975
- clientId: client2.client_id,
1224
+ clientId: client.client_id,
976
1225
  scopes,
977
1226
  expiresAt: Date.now() + TOKEN_TTL_MS,
978
1227
  resource: resource || codeData.params.resource
979
1228
  });
980
1229
  this.refreshTokens.set(refreshToken, {
981
- clientId: client2.client_id,
1230
+ clientId: client.client_id,
982
1231
  scopes,
983
1232
  expiresAt: Date.now() + REFRESH_TOKEN_TTL_MS,
984
1233
  resource: resource || codeData.params.resource
@@ -991,13 +1240,13 @@ var OpenClawAuthProvider = class {
991
1240
  scope: scopes.join(" ")
992
1241
  };
993
1242
  }
994
- async exchangeRefreshToken(client2, refreshToken, scopes, resource) {
1243
+ async exchangeRefreshToken(client, refreshToken, scopes, resource) {
995
1244
  const data = this.refreshTokens.get(refreshToken);
996
1245
  if (!data || data.expiresAt < Date.now()) {
997
1246
  if (data) this.refreshTokens.delete(refreshToken);
998
1247
  throw new InvalidRequestError("Invalid refresh token");
999
1248
  }
1000
- if (data.clientId !== client2.client_id) {
1249
+ if (data.clientId !== client.client_id) {
1001
1250
  throw new InvalidRequestError("Refresh token was not issued to this client");
1002
1251
  }
1003
1252
  this.refreshTokens.delete(refreshToken);
@@ -1006,13 +1255,13 @@ var OpenClawAuthProvider = class {
1006
1255
  const tokenScopes = scopes || data.scopes;
1007
1256
  this.tokens.set(accessToken, {
1008
1257
  token: accessToken,
1009
- clientId: client2.client_id,
1258
+ clientId: client.client_id,
1010
1259
  scopes: tokenScopes,
1011
1260
  expiresAt: Date.now() + TOKEN_TTL_MS,
1012
1261
  resource: resource || data.resource
1013
1262
  });
1014
1263
  this.refreshTokens.set(newRefreshToken, {
1015
- clientId: client2.client_id,
1264
+ clientId: client.client_id,
1016
1265
  scopes: tokenScopes,
1017
1266
  expiresAt: Date.now() + REFRESH_TOKEN_TTL_MS,
1018
1267
  resource: resource || data.resource
@@ -1281,18 +1530,20 @@ async function createSSEServer(config, deps2) {
1281
1530
 
1282
1531
  // src/index.ts
1283
1532
  var args = parseArguments(SERVER_VERSION);
1284
- var client = new OpenClawClient(args.openclawUrl, args.gatewayToken, args.timeout);
1533
+ var registry = new InstanceRegistry(args.instances);
1285
1534
  var deps = {
1286
- client,
1535
+ registry,
1287
1536
  serverName: SERVER_NAME,
1288
1537
  serverVersion: SERVER_VERSION
1289
1538
  };
1290
1539
  async function main() {
1291
1540
  log(`Starting ${SERVER_NAME} v${SERVER_VERSION}`);
1292
- log(`OpenClaw URL: ${args.openclawUrl}`);
1293
1541
  log(`Transport: ${args.transport}`);
1294
- log(`Gateway token: ${args.gatewayToken ? "configured" : "not set"}`);
1295
1542
  log(`Request timeout: ${args.timeout}ms`);
1543
+ for (const instance of registry.list()) {
1544
+ const defaultLabel = instance.isDefault ? " (default)" : "";
1545
+ log(`Instance "${instance.name}": ${instance.url}${defaultLabel}`);
1546
+ }
1296
1547
  if (args.transport === "sse") {
1297
1548
  const sseConfig = {
1298
1549
  port: args.port,
package/docs/CNAME ADDED
@@ -0,0 +1 @@
1
+ openclaw-mcp.cloud
Binary file