openclaw-mcp 1.2.1 → 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
@@ -4,11 +4,8 @@
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
 
6
6
  // src/config/constants.ts
7
- import { createRequire } from "module";
8
- var require2 = createRequire(import.meta.url);
9
- var pkg = require2("../../package.json");
10
7
  var SERVER_NAME = "openclaw-mcp";
11
- var SERVER_VERSION = pkg.version;
8
+ var SERVER_VERSION = "1.3.0";
12
9
  var DEFAULT_OPENCLAW_URL = "http://127.0.0.1:18789";
13
10
  var SERVER_ICON_SVG_BASE64 = "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCIgZmlsbD0ibm9uZSI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzFhMWEyZSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzE2MjEzZSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJjbGF3IiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZmYzMzMzIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjY2MwMDAwIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjEyOCIgaGVpZ2h0PSIxMjgiIHJ4PSIyNCIgZmlsbD0idXJsKCNiZykiLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2NCA2NCkiIHN0cm9rZT0idXJsKCNjbGF3KSIgc3Ryb2tlLXdpZHRoPSI3IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0tMjggLTM4YzAgMCAtMTAgMjAgMCAzMiIvPjxwYXRoIGQ9Ik0tMTIgLTQwYzAgMCAtNiAyMiA0IDM0Ii8+PHBhdGggZD0iTTI4IC0zOGMwIDAgMTAgMjAgMCAzMiIvPjxwYXRoIGQ9Ik0xMiAtNDBjMCAwIDYgMjIgLTQgMzQiLz48Y2lyY2xlIGN4PSIwIiBjeT0iMTAiIHI9IjIwIiBzdHJva2Utd2lkdGg9IjYiLz48cGF0aCBkPSJNLTEwIDR2LTQiIHN0cm9rZS13aWR0aD0iNCIvPjxwYXRoIGQ9Ik0xMCA0di00IiBzdHJva2Utd2lkdGg9IjQiLz48cGF0aCBkPSJNLTggMjBjNCA2IDEyIDYgMTYgMCIgc3Ryb2tlLXdpZHRoPSIzIi8+PC9nPjwvc3ZnPg==";
14
11
 
@@ -94,6 +91,45 @@ function parseArguments(version) {
94
91
  description: "Allowed OAuth redirect URIs (comma-separated)",
95
92
  default: process.env.MCP_REDIRECT_URIS || void 0
96
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
+ }
97
133
  return {
98
134
  openclawUrl: argv["openclaw-url"],
99
135
  gatewayToken: argv["gateway-token"],
@@ -105,7 +141,8 @@ function parseArguments(version) {
105
141
  clientId: argv["client-id"],
106
142
  clientSecret: argv["client-secret"],
107
143
  issuerUrl: argv["issuer-url"],
108
- 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
109
146
  };
110
147
  }
111
148
 
@@ -264,6 +301,120 @@ var OpenClawClient = class {
264
301
  }
265
302
  };
266
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
+
267
418
  // src/server/tools-registration.ts
268
419
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
269
420
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
@@ -332,12 +483,16 @@ var openclawChatTool = {
332
483
  session_id: {
333
484
  type: "string",
334
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."
335
490
  }
336
491
  },
337
492
  required: ["message"]
338
493
  }
339
494
  };
340
- async function handleOpenclawChat(client2, input) {
495
+ async function handleOpenclawChat(registry2, input) {
341
496
  if (!validateInputIsObject(input)) {
342
497
  return errorResponse("Invalid input: expected an object");
343
498
  }
@@ -353,8 +508,17 @@ async function handleOpenclawChat(client2, input) {
353
508
  }
354
509
  sessionId = sidResult.value;
355
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
+ }
356
519
  try {
357
- const response = await client2.chat(msgResult.value, sessionId);
520
+ const { client } = registry2.resolve(instanceName);
521
+ const response = await client.chat(msgResult.value, sessionId);
358
522
  return successResponse(response.response);
359
523
  } catch (error) {
360
524
  return errorResponse(error instanceof Error ? error.message : "Failed to chat with OpenClaw");
@@ -367,16 +531,33 @@ var openclawStatusTool = {
367
531
  description: "Get OpenClaw gateway status and health information",
368
532
  inputSchema: {
369
533
  type: "object",
370
- properties: {}
534
+ properties: {
535
+ instance: {
536
+ type: "string",
537
+ description: "Target OpenClaw instance name. Defaults to the default instance."
538
+ }
539
+ }
371
540
  }
372
541
  };
373
- async function handleOpenclawStatus(client2, input) {
542
+ async function handleOpenclawStatus(registry2, input) {
374
543
  if (!validateInputIsObject(input)) {
375
544
  return errorResponse("Invalid input: expected an object");
376
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
+ }
377
554
  try {
378
- const response = await client2.health();
379
- 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
+ });
380
561
  } catch (error) {
381
562
  return errorResponse(
382
563
  error instanceof Error ? error.message : "Failed to get status from OpenClaw"
@@ -384,6 +565,22 @@ async function handleOpenclawStatus(client2, input) {
384
565
  }
385
566
  }
386
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
+
387
584
  // src/mcp/tasks/manager.ts
388
585
  var MAX_TASKS = 1e3;
389
586
  var CLEANUP_INTERVAL_MS = 10 * 60 * 1e3;
@@ -424,6 +621,7 @@ var TaskManager = class {
424
621
  input: options.input,
425
622
  createdAt: /* @__PURE__ */ new Date(),
426
623
  sessionId: options.sessionId,
624
+ instanceId: options.instanceId,
427
625
  priority: options.priority ?? 0
428
626
  };
429
627
  this.tasks.set(id, task);
@@ -447,6 +645,9 @@ var TaskManager = class {
447
645
  if (filter?.sessionId) {
448
646
  tasks = tasks.filter((t) => t.sessionId === filter.sessionId);
449
647
  }
648
+ if (filter?.instanceId) {
649
+ tasks = tasks.filter((t) => t.instanceId === filter.instanceId);
650
+ }
450
651
  return tasks.sort((a, b) => {
451
652
  if (b.priority !== a.priority) return b.priority - a.priority;
452
653
  return a.createdAt.getTime() - b.createdAt.getTime();
@@ -554,6 +755,10 @@ var openclawChatAsyncTool = {
554
755
  priority: {
555
756
  type: "number",
556
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."
557
762
  }
558
763
  },
559
764
  required: ["message"]
@@ -575,7 +780,7 @@ var openclawTaskStatusTool = {
575
780
  };
576
781
  var openclawTaskListTool = {
577
782
  name: "openclaw_task_list",
578
- description: "List all tasks. Optionally filter by status or session.",
783
+ description: "List all tasks. Optionally filter by status, session, or instance.",
579
784
  inputSchema: {
580
785
  type: "object",
581
786
  properties: {
@@ -587,6 +792,10 @@ var openclawTaskListTool = {
587
792
  session_id: {
588
793
  type: "string",
589
794
  description: "Filter by session ID"
795
+ },
796
+ instance: {
797
+ type: "string",
798
+ description: "Filter by instance name"
590
799
  }
591
800
  },
592
801
  required: []
@@ -607,12 +816,20 @@ var openclawTaskCancelTool = {
607
816
  }
608
817
  };
609
818
  var processorRunning = false;
610
- var processorClient = null;
611
- async function processTask(task, client2) {
819
+ var processorRegistry = null;
820
+ async function processTask(task, registry2) {
612
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
+ }
613
830
  try {
614
831
  const input = task.input;
615
- const response = await client2.chat(input.message, input.session_id);
832
+ const response = await client.chat(input.message, input.session_id);
616
833
  taskManager.updateStatus(task.id, "completed", response.response);
617
834
  } catch (error) {
618
835
  const errorMsg = error instanceof Error ? error.message : "Unknown error";
@@ -620,26 +837,26 @@ async function processTask(task, client2) {
620
837
  }
621
838
  }
622
839
  async function taskProcessor() {
623
- if (!processorClient) return;
840
+ if (!processorRegistry) return;
624
841
  while (processorRunning) {
625
842
  const task = taskManager.getNextPending();
626
843
  if (task) {
627
- await processTask(task, processorClient);
844
+ await processTask(task, processorRegistry);
628
845
  } else {
629
846
  await new Promise((resolve) => setTimeout(resolve, 100));
630
847
  }
631
848
  }
632
849
  }
633
- function startTaskProcessor(client2) {
850
+ function startTaskProcessor(registry2) {
634
851
  if (processorRunning) return;
635
- processorClient = client2;
852
+ processorRegistry = registry2;
636
853
  processorRunning = true;
637
854
  taskProcessor().catch(() => {
638
855
  processorRunning = false;
639
856
  });
640
857
  log("Task processor started");
641
858
  }
642
- async function handleOpenclawChatAsync(client2, input) {
859
+ async function handleOpenclawChatAsync(registry2, input) {
643
860
  if (!validateInputIsObject(input)) {
644
861
  return errorResponse("Invalid input: expected an object");
645
862
  }
@@ -662,18 +879,35 @@ async function handleOpenclawChatAsync(client2, input) {
662
879
  }
663
880
  priority = input.priority;
664
881
  }
665
- 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);
666
898
  const task = taskManager.create({
667
899
  type: "chat",
668
900
  input: { message: msgResult.value, session_id: sessionId },
669
901
  sessionId,
670
- priority
902
+ priority,
903
+ instanceId
671
904
  });
672
905
  return successResponse(
673
906
  JSON.stringify(
674
907
  {
675
908
  task_id: task.id,
676
909
  status: task.status,
910
+ instance: instanceId,
677
911
  message: "Task queued. Use openclaw_task_status to check progress."
678
912
  },
679
913
  null,
@@ -681,7 +915,7 @@ async function handleOpenclawChatAsync(client2, input) {
681
915
  )
682
916
  );
683
917
  }
684
- async function handleOpenclawTaskStatus(_client, input) {
918
+ async function handleOpenclawTaskStatus(_registry, input) {
685
919
  if (!validateInputIsObject(input)) {
686
920
  return errorResponse("Invalid input: expected an object");
687
921
  }
@@ -698,6 +932,7 @@ async function handleOpenclawTaskStatus(_client, input) {
698
932
  task_id: task.id,
699
933
  type: task.type,
700
934
  status: task.status,
935
+ instance: task.instanceId,
701
936
  created_at: task.createdAt.toISOString()
702
937
  };
703
938
  if (task.startedAt) {
@@ -721,7 +956,7 @@ var VALID_TASK_STATUSES = [
721
956
  "failed",
722
957
  "cancelled"
723
958
  ];
724
- async function handleOpenclawTaskList(_client, input) {
959
+ async function handleOpenclawTaskList(_registry, input) {
725
960
  if (!validateInputIsObject(input)) {
726
961
  return errorResponse("Invalid input: expected an object");
727
962
  }
@@ -740,12 +975,21 @@ async function handleOpenclawTaskList(_client, input) {
740
975
  }
741
976
  session_id = sidResult.value;
742
977
  }
743
- 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 });
744
987
  const stats = taskManager.stats();
745
988
  const taskList = tasks.map((t) => ({
746
989
  task_id: t.id,
747
990
  type: t.type,
748
991
  status: t.status,
992
+ instance: t.instanceId,
749
993
  priority: t.priority,
750
994
  created_at: t.createdAt.toISOString(),
751
995
  has_result: t.status === "completed" && !!t.result
@@ -761,7 +1005,7 @@ async function handleOpenclawTaskList(_client, input) {
761
1005
  )
762
1006
  );
763
1007
  }
764
- async function handleOpenclawTaskCancel(_client, input) {
1008
+ async function handleOpenclawTaskCancel(_registry, input) {
765
1009
  if (!validateInputIsObject(input)) {
766
1010
  return errorResponse("Invalid input: expected an object");
767
1011
  }
@@ -816,14 +1060,15 @@ function createMcpServer(deps2) {
816
1060
  return server;
817
1061
  }
818
1062
  function registerTools(server, deps2) {
819
- const { client: client2 } = deps2;
1063
+ const { registry: registry2 } = deps2;
820
1064
  const toolHandlers = /* @__PURE__ */ new Map([
821
- ["openclaw_chat", (input) => handleOpenclawChat(client2, input)],
822
- ["openclaw_status", (input) => handleOpenclawStatus(client2, input)],
823
- ["openclaw_chat_async", (input) => handleOpenclawChatAsync(client2, input)],
824
- ["openclaw_task_status", (input) => handleOpenclawTaskStatus(client2, input)],
825
- ["openclaw_task_list", (input) => handleOpenclawTaskList(client2, input)],
826
- ["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)]
827
1072
  ]);
828
1073
  const allTools = [
829
1074
  openclawChatTool,
@@ -831,7 +1076,8 @@ function registerTools(server, deps2) {
831
1076
  openclawChatAsyncTool,
832
1077
  openclawTaskStatusTool,
833
1078
  openclawTaskListTool,
834
- openclawTaskCancelTool
1079
+ openclawTaskCancelTool,
1080
+ openclawInstancesTool
835
1081
  ];
836
1082
  server.setRequestHandler(ListToolsRequestSchema, async () => {
837
1083
  return { tools: allTools };
@@ -941,9 +1187,9 @@ var OpenClawAuthProvider = class {
941
1187
  /**
942
1188
  * Auto-approve: generate auth code and redirect immediately.
943
1189
  */
944
- async authorize(client2, params, res) {
1190
+ async authorize(client, params, res) {
945
1191
  const code = randomUUID();
946
- this.codes.set(code, { client: client2, params, createdAt: Date.now() });
1192
+ this.codes.set(code, { client, params, createdAt: Date.now() });
947
1193
  const searchParams = new URLSearchParams({ code });
948
1194
  if (params.state !== void 0) {
949
1195
  searchParams.set("state", params.state);
@@ -960,13 +1206,13 @@ var OpenClawAuthProvider = class {
960
1206
  }
961
1207
  return codeData.params.codeChallenge;
962
1208
  }
963
- async exchangeAuthorizationCode(client2, authorizationCode, _codeVerifier, _redirectUri, resource) {
1209
+ async exchangeAuthorizationCode(client, authorizationCode, _codeVerifier, _redirectUri, resource) {
964
1210
  const codeData = this.codes.get(authorizationCode);
965
1211
  if (!codeData || Date.now() - codeData.createdAt > AUTH_CODE_TTL_MS) {
966
1212
  if (codeData) this.codes.delete(authorizationCode);
967
1213
  throw new InvalidRequestError("Invalid authorization code");
968
1214
  }
969
- if (codeData.client.client_id !== client2.client_id) {
1215
+ if (codeData.client.client_id !== client.client_id) {
970
1216
  throw new InvalidRequestError("Authorization code was not issued to this client");
971
1217
  }
972
1218
  this.codes.delete(authorizationCode);
@@ -975,13 +1221,13 @@ var OpenClawAuthProvider = class {
975
1221
  const scopes = codeData.params.scopes || [];
976
1222
  this.tokens.set(accessToken, {
977
1223
  token: accessToken,
978
- clientId: client2.client_id,
1224
+ clientId: client.client_id,
979
1225
  scopes,
980
1226
  expiresAt: Date.now() + TOKEN_TTL_MS,
981
1227
  resource: resource || codeData.params.resource
982
1228
  });
983
1229
  this.refreshTokens.set(refreshToken, {
984
- clientId: client2.client_id,
1230
+ clientId: client.client_id,
985
1231
  scopes,
986
1232
  expiresAt: Date.now() + REFRESH_TOKEN_TTL_MS,
987
1233
  resource: resource || codeData.params.resource
@@ -994,13 +1240,13 @@ var OpenClawAuthProvider = class {
994
1240
  scope: scopes.join(" ")
995
1241
  };
996
1242
  }
997
- async exchangeRefreshToken(client2, refreshToken, scopes, resource) {
1243
+ async exchangeRefreshToken(client, refreshToken, scopes, resource) {
998
1244
  const data = this.refreshTokens.get(refreshToken);
999
1245
  if (!data || data.expiresAt < Date.now()) {
1000
1246
  if (data) this.refreshTokens.delete(refreshToken);
1001
1247
  throw new InvalidRequestError("Invalid refresh token");
1002
1248
  }
1003
- if (data.clientId !== client2.client_id) {
1249
+ if (data.clientId !== client.client_id) {
1004
1250
  throw new InvalidRequestError("Refresh token was not issued to this client");
1005
1251
  }
1006
1252
  this.refreshTokens.delete(refreshToken);
@@ -1009,13 +1255,13 @@ var OpenClawAuthProvider = class {
1009
1255
  const tokenScopes = scopes || data.scopes;
1010
1256
  this.tokens.set(accessToken, {
1011
1257
  token: accessToken,
1012
- clientId: client2.client_id,
1258
+ clientId: client.client_id,
1013
1259
  scopes: tokenScopes,
1014
1260
  expiresAt: Date.now() + TOKEN_TTL_MS,
1015
1261
  resource: resource || data.resource
1016
1262
  });
1017
1263
  this.refreshTokens.set(newRefreshToken, {
1018
- clientId: client2.client_id,
1264
+ clientId: client.client_id,
1019
1265
  scopes: tokenScopes,
1020
1266
  expiresAt: Date.now() + REFRESH_TOKEN_TTL_MS,
1021
1267
  resource: resource || data.resource
@@ -1284,18 +1530,20 @@ async function createSSEServer(config, deps2) {
1284
1530
 
1285
1531
  // src/index.ts
1286
1532
  var args = parseArguments(SERVER_VERSION);
1287
- var client = new OpenClawClient(args.openclawUrl, args.gatewayToken, args.timeout);
1533
+ var registry = new InstanceRegistry(args.instances);
1288
1534
  var deps = {
1289
- client,
1535
+ registry,
1290
1536
  serverName: SERVER_NAME,
1291
1537
  serverVersion: SERVER_VERSION
1292
1538
  };
1293
1539
  async function main() {
1294
1540
  log(`Starting ${SERVER_NAME} v${SERVER_VERSION}`);
1295
- log(`OpenClaw URL: ${args.openclawUrl}`);
1296
1541
  log(`Transport: ${args.transport}`);
1297
- log(`Gateway token: ${args.gatewayToken ? "configured" : "not set"}`);
1298
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
+ }
1299
1547
  if (args.transport === "sse") {
1300
1548
  const sseConfig = {
1301
1549
  port: args.port,
package/docs/CNAME ADDED
@@ -0,0 +1 @@
1
+ openclaw-mcp.cloud
Binary file