@alfe.ai/mcp-bundler 0.1.0 → 0.1.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/dist/index.cjs CHANGED
@@ -2,8 +2,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let node_fs = require("node:fs");
3
3
  let node_path = require("node:path");
4
4
  let node_os = require("node:os");
5
- let node_child_process = require("node:child_process");
6
- let node_util = require("node:util");
7
5
  //#region src/tool-naming.ts
8
6
  /**
9
7
  * Tool name sanitization and collision handling.
@@ -717,60 +715,40 @@ function errMsg$1(err) {
717
715
  }
718
716
  //#endregion
719
717
  //#region src/manager.ts
720
- const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
721
- const defaultOpenclawExecutor = {
722
- async setBatch(batch) {
723
- if (batch.length === 0) return;
724
- await execFileAsync("openclaw", [
725
- "config",
726
- "set",
727
- "--batch-json",
728
- JSON.stringify(batch)
729
- ], { timeout: 1e4 });
730
- },
731
- async unset(path) {
732
- await execFileAsync("openclaw", [
733
- "config",
734
- "unset",
735
- path
736
- ], { timeout: 1e4 });
737
- }
738
- };
739
- const DEFAULT_MIRROR_DEBOUNCE_MS = 250;
740
718
  /**
741
- * Bundler manager — owns the alfe store, mirrors it into openclaw.json,
742
- * and surfaces a small CRUD API the CLI and integration applier both
743
- * call into.
719
+ * Bundler manager — owns the `~/.alfe/mcp/servers.json` store and surfaces a
720
+ * small CRUD API the CLI and integration applier both call into.
721
+ *
722
+ * Single source of truth: every consumer (daemon-hosted bundler, CLI `alfe mcp
723
+ * list`, integration uninstall) reads from this store. Openclaw.json is no
724
+ * longer kept in sync — the daemon hosts the bundler children and the
725
+ * openclaw plugin reaches them via IPC, so the openclaw.json mirror became
726
+ * dead weight and an active source of duplicate spawning on claude-cli /
727
+ * codex-cli backends.
744
728
  *
745
- * The store is the Alfe-owned source of truth; openclaw.json is a
746
- * derived mirror so the runtime keeps consuming its existing format.
729
+ * Call `loadIntoBundler(bundler)` once at daemon startup to wire the store
730
+ * into a live `McpBundler` subsequent store mutations (including those
731
+ * landed by other processes via the file watcher) re-reconcile automatically.
747
732
  */
748
733
  var Manager = class {
749
734
  store;
750
- executor;
751
735
  logger;
752
- mirrorDebounceMs;
753
- mirrorTimer;
754
- mirrorPromise = Promise.resolve();
755
- mirrorPending;
756
736
  bundler;
757
737
  changeListeners = /* @__PURE__ */ new Set();
758
738
  storeUnsubscribe;
759
739
  constructor(opts = {}) {
760
740
  this.store = opts.store ?? new Store({ logger: opts.logger });
761
- this.executor = opts.executor ?? defaultOpenclawExecutor;
762
741
  this.logger = opts.logger;
763
- this.mirrorDebounceMs = opts.mirrorDebounceMs ?? DEFAULT_MIRROR_DEBOUNCE_MS;
764
742
  }
765
743
  /** Direct accessor — handy for tests and the CLI's `alfe mcp list`. */
766
744
  getStore() {
767
745
  return this.store;
768
746
  }
769
747
  /**
770
- * Register or overwrite a server entry. Resolves as soon as the store
771
- * mutation is committed to disk the openclaw.json mirror runs async
772
- * in the background and is debounced so back-to-back calls coalesce
773
- * into one runtime restart. Call `flush()` to await the mirror.
748
+ * Register or overwrite a server entry. Mutation lands in the store
749
+ * synchronously; if a bundler has been attached via `loadIntoBundler`,
750
+ * it gets re-reconciled in the background (errors logged, never
751
+ * thrown the store is the source of truth, the bundler is derived).
774
752
  */
775
753
  async addServer(config, opts) {
776
754
  if (!opts.id) throw new Error("Manager.addServer: id is required");
@@ -791,7 +769,7 @@ var Manager = class {
791
769
  }
792
770
  };
793
771
  });
794
- this.scheduleMirror();
772
+ this.scheduleBundlerReconcile();
795
773
  this.fireChange();
796
774
  return Promise.resolve();
797
775
  }
@@ -800,9 +778,6 @@ var Manager = class {
800
778
  * Refuses to remove an entry whose owner doesn't match `expectedOwner`
801
779
  * when supplied — the CLI uses this to guard `alfe mcp remove` from
802
780
  * accidentally clobbering integration- or cli-owned entries.
803
- *
804
- * Resolves as soon as the store mutation is committed. Mirror runs
805
- * async; call `flush()` to await it.
806
781
  */
807
782
  removeServer(id, opts = {}) {
808
783
  const existing = lookupEntry(this.store.read().servers, id);
@@ -812,7 +787,7 @@ var Manager = class {
812
787
  ...cur,
813
788
  servers: Object.fromEntries(Object.entries(cur.servers).filter(([k]) => k !== id))
814
789
  }));
815
- this.scheduleMirror();
790
+ this.scheduleBundlerReconcile();
816
791
  this.fireChange();
817
792
  return Promise.resolve(true);
818
793
  }
@@ -830,7 +805,7 @@ var Manager = class {
830
805
  };
831
806
  });
832
807
  if (removed.length > 0) {
833
- this.scheduleMirror();
808
+ this.scheduleBundlerReconcile();
834
809
  this.fireChange();
835
810
  }
836
811
  return Promise.resolve(removed);
@@ -865,16 +840,11 @@ var Manager = class {
865
840
  };
866
841
  }
867
842
  /**
868
- * Cancel any pending mirror-write, flush the in-flight one, and stop
869
- * watching the store. Safe to call multiple times.
843
+ * Detach from the bundler and stop watching the store. Safe to call
844
+ * multiple times. Does not dispose the underlying `Store` so the
845
+ * shared instance survives multi-manager environments (rare).
870
846
  */
871
847
  async dispose() {
872
- if (this.mirrorTimer) {
873
- clearTimeout(this.mirrorTimer);
874
- this.mirrorTimer = void 0;
875
- await this.runMirror();
876
- }
877
- await this.mirrorPromise.catch(() => void 0);
878
848
  if (this.storeUnsubscribe) {
879
849
  this.storeUnsubscribe();
880
850
  this.storeUnsubscribe = void 0;
@@ -882,80 +852,13 @@ var Manager = class {
882
852
  this.store.dispose();
883
853
  this.changeListeners.clear();
884
854
  this.bundler = void 0;
855
+ return Promise.resolve();
885
856
  }
886
- /**
887
- * Force the debounced mirror to run now and wait for it to finish.
888
- * Surfaces the executor error if the mirror failed — callers wrap in
889
- * try/catch (or .rejects in tests) if they need to handle it.
890
- */
891
- async flush() {
892
- if (this.mirrorTimer) {
893
- clearTimeout(this.mirrorTimer);
894
- this.mirrorTimer = void 0;
895
- await this.runMirror();
896
- }
897
- await this.mirrorPromise;
898
- }
899
- scheduleMirror() {
900
- if (this.mirrorPending) return;
901
- this.mirrorPromise = new Promise((resolve, reject) => {
902
- this.mirrorPending = {
903
- resolve,
904
- reject
905
- };
906
- });
907
- this.mirrorTimer = setTimeout(() => {
908
- this.mirrorTimer = void 0;
909
- this.runMirror();
910
- }, this.mirrorDebounceMs);
911
- this.mirrorTimer.unref();
912
- }
913
- async runMirror() {
914
- const pending = this.mirrorPending;
915
- this.mirrorPending = void 0;
916
- try {
917
- await this.applyMirror();
918
- pending?.resolve();
919
- } catch (err) {
920
- pending?.reject(err);
921
- this.logger?.error("[mcp-bundler/manager] mirror write failed", { err: errMsg(err) });
922
- }
923
- }
924
- /**
925
- * Compute the diff between this manager's owned set and what the store
926
- * declares now, then apply the openclaw config delta. Foreign keys
927
- * (entries in openclaw.json#mcp.servers.* not in our store) are
928
- * preserved — we only touch the names we previously claimed.
929
- */
930
- async applyMirror() {
931
- let previousOwned = [];
932
- let desiredOwned = [];
933
- let batch = [];
934
- this.store.update((cur) => {
935
- previousOwned = cur._ownedOpenclawKeys.slice();
936
- const entries = Object.entries(cur.servers);
937
- desiredOwned = entries.map(([id]) => id).sort();
938
- batch = entries.flatMap(([id, entry]) => renderEntryToBatch(id, entry));
939
- return {
940
- ...cur,
941
- _ownedOpenclawKeys: desiredOwned
942
- };
857
+ scheduleBundlerReconcile() {
858
+ if (!this.bundler) return;
859
+ this.reconcileBundler().catch((err) => {
860
+ this.logger?.warn("[mcp-bundler/manager] bundler reconcile failed", { err: errMsg(err) });
943
861
  });
944
- await this.executor.setBatch(batch);
945
- const toUnset = previousOwned.filter((k) => !desiredOwned.includes(k));
946
- for (const id of toUnset) try {
947
- await this.executor.unset(`mcp.servers.${id}`);
948
- } catch (err) {
949
- this.logger?.warn("[mcp-bundler/manager] mirror unset failed (continuing)", {
950
- err: errMsg(err),
951
- key: `mcp.servers.${id}`
952
- });
953
- }
954
- if (this.bundler) try {
955
- await this.reconcileBundler();
956
- } catch (err) {
957
- this.logger?.warn("[mcp-bundler/manager] post-mirror reconcile failed", { err: errMsg(err) });
958
- }
959
862
  }
960
863
  async reconcileBundler() {
961
864
  if (!this.bundler) return;
@@ -972,53 +875,6 @@ var Manager = class {
972
875
  }
973
876
  }
974
877
  };
975
- /**
976
- * Render one store entry as the set of dotted-path batch entries that
977
- * `openclaw config set --batch-json` expects.
978
- *
979
- * Mirrors the format the integrations applier already emits today so the
980
- * runtime sees the same shape regardless of which writer produced it.
981
- */
982
- function renderEntryToBatch(id, entry) {
983
- const prefix = `mcp.servers.${id}`;
984
- const out = [];
985
- if (entry.transport === "stdio") {
986
- out.push({
987
- path: `${prefix}.command`,
988
- value: entry.command
989
- });
990
- if (entry.args && entry.args.length > 0) out.push({
991
- path: `${prefix}.args`,
992
- value: entry.args
993
- });
994
- if (entry.env) for (const [k, v] of Object.entries(entry.env)) out.push({
995
- path: `${prefix}.env.${k}`,
996
- value: v
997
- });
998
- if (entry.cwd) out.push({
999
- path: `${prefix}.cwd`,
1000
- value: entry.cwd
1001
- });
1002
- return out;
1003
- }
1004
- out.push({
1005
- path: `${prefix}.url`,
1006
- value: entry.url
1007
- });
1008
- out.push({
1009
- path: `${prefix}.transport`,
1010
- value: entry.transport
1011
- });
1012
- if (entry.headers) for (const [k, v] of Object.entries(entry.headers)) out.push({
1013
- path: `${prefix}.headers.${k}`,
1014
- value: v
1015
- });
1016
- if (entry.connectionTimeoutMs !== void 0) out.push({
1017
- path: `${prefix}.connectionTimeoutMs`,
1018
- value: entry.connectionTimeoutMs
1019
- });
1020
- return out;
1021
- }
1022
878
  function errMsg(err) {
1023
879
  return err instanceof Error ? err.message : String(err);
1024
880
  }
@@ -1043,7 +899,6 @@ exports.STDIO_ENV_DENYLIST = STDIO_ENV_DENYLIST;
1043
899
  exports.Store = Store;
1044
900
  exports.buildNamespacedToolName = buildNamespacedToolName;
1045
901
  exports.defaultConnect = defaultConnect;
1046
- exports.defaultOpenclawExecutor = defaultOpenclawExecutor;
1047
902
  exports.defaultStorePath = defaultStorePath;
1048
903
  exports.disambiguateAgainst = disambiguateAgainst;
1049
904
  exports.sanitizeNameSegment = sanitizeNameSegment;
package/dist/index.d.cts CHANGED
@@ -350,36 +350,13 @@ declare function toStoredEntry(config: McpServerConfig, meta: {
350
350
  }): StoredServerEntry;
351
351
  //#endregion
352
352
  //#region src/manager.d.ts
353
- /**
354
- * Shells out to `openclaw config set --batch-json` / `openclaw config unset`
355
- * to keep `openclaw.json#mcp.servers.*` in sync with the bundler store.
356
- * Default executor reuses the same machinery the integrations applier has
357
- * used for ~6 months; tests inject a fake.
358
- */
359
- interface OpenclawExecutor {
360
- setBatch: (batch: {
361
- path: string;
362
- value: unknown;
363
- }[]) => Promise<void>;
364
- unset: (path: string) => Promise<void>;
365
- }
366
- declare const defaultOpenclawExecutor: OpenclawExecutor;
367
353
  interface ManagerOptions {
368
354
  /** Pre-constructed store. If omitted, one is built with default options. */
369
355
  store?: Store;
370
- /** Override for the openclaw mirror executor (tests inject a fake). */
371
- executor?: OpenclawExecutor;
372
356
  logger?: Logger;
373
- /**
374
- * Debounce window for the mirror-write. Mutations landing inside this
375
- * window coalesce into a single openclaw config update, avoiding the
376
- * auto-restart race where two back-to-back `addServer` calls hit an
377
- * openclaw that's mid-shutdown from the first write's watcher.
378
- */
379
- mirrorDebounceMs?: number;
380
357
  }
381
358
  interface AddServerOptions {
382
- /** Required — flat-namespace key under `mcp.servers.*`. */
359
+ /** Required — flat-namespace key under the bundler store. */
383
360
  id: string;
384
361
  /** Marks ownership for bulk removal. Defaults to `manual`. */
385
362
  owner?: ServerOwner;
@@ -389,21 +366,23 @@ interface AddServerOptions {
389
366
  transport?: McpTransportKind;
390
367
  }
391
368
  /**
392
- * Bundler manager — owns the alfe store, mirrors it into openclaw.json,
393
- * and surfaces a small CRUD API the CLI and integration applier both
394
- * call into.
369
+ * Bundler manager — owns the `~/.alfe/mcp/servers.json` store and surfaces a
370
+ * small CRUD API the CLI and integration applier both call into.
371
+ *
372
+ * Single source of truth: every consumer (daemon-hosted bundler, CLI `alfe mcp
373
+ * list`, integration uninstall) reads from this store. Openclaw.json is no
374
+ * longer kept in sync — the daemon hosts the bundler children and the
375
+ * openclaw plugin reaches them via IPC, so the openclaw.json mirror became
376
+ * dead weight and an active source of duplicate spawning on claude-cli /
377
+ * codex-cli backends.
395
378
  *
396
- * The store is the Alfe-owned source of truth; openclaw.json is a
397
- * derived mirror so the runtime keeps consuming its existing format.
379
+ * Call `loadIntoBundler(bundler)` once at daemon startup to wire the store
380
+ * into a live `McpBundler` subsequent store mutations (including those
381
+ * landed by other processes via the file watcher) re-reconcile automatically.
398
382
  */
399
383
  declare class Manager {
400
384
  private readonly store;
401
- private readonly executor;
402
385
  private readonly logger?;
403
- private readonly mirrorDebounceMs;
404
- private mirrorTimer?;
405
- private mirrorPromise;
406
- private mirrorPending?;
407
386
  private bundler?;
408
387
  private changeListeners;
409
388
  private storeUnsubscribe?;
@@ -411,10 +390,10 @@ declare class Manager {
411
390
  /** Direct accessor — handy for tests and the CLI's `alfe mcp list`. */
412
391
  getStore(): Store;
413
392
  /**
414
- * Register or overwrite a server entry. Resolves as soon as the store
415
- * mutation is committed to disk the openclaw.json mirror runs async
416
- * in the background and is debounced so back-to-back calls coalesce
417
- * into one runtime restart. Call `flush()` to await the mirror.
393
+ * Register or overwrite a server entry. Mutation lands in the store
394
+ * synchronously; if a bundler has been attached via `loadIntoBundler`,
395
+ * it gets re-reconciled in the background (errors logged, never
396
+ * thrown the store is the source of truth, the bundler is derived).
418
397
  */
419
398
  addServer(config: McpServerConfig, opts: AddServerOptions): Promise<void>;
420
399
  /**
@@ -422,9 +401,6 @@ declare class Manager {
422
401
  * Refuses to remove an entry whose owner doesn't match `expectedOwner`
423
402
  * when supplied — the CLI uses this to guard `alfe mcp remove` from
424
403
  * accidentally clobbering integration- or cli-owned entries.
425
- *
426
- * Resolves as soon as the store mutation is committed. Mirror runs
427
- * async; call `flush()` to await it.
428
404
  */
429
405
  removeServer(id: string, opts?: {
430
406
  expectedOwner?: ServerOwner;
@@ -445,30 +421,17 @@ declare class Manager {
445
421
  /** Subscribe to store mutations. Returns an unsubscribe fn. */
446
422
  onChange(cb: () => void): () => void;
447
423
  /**
448
- * Cancel any pending mirror-write, flush the in-flight one, and stop
449
- * watching the store. Safe to call multiple times.
424
+ * Detach from the bundler and stop watching the store. Safe to call
425
+ * multiple times. Does not dispose the underlying `Store` so the
426
+ * shared instance survives multi-manager environments (rare).
450
427
  */
451
428
  dispose(): Promise<void>;
452
- /**
453
- * Force the debounced mirror to run now and wait for it to finish.
454
- * Surfaces the executor error if the mirror failed — callers wrap in
455
- * try/catch (or .rejects in tests) if they need to handle it.
456
- */
457
- flush(): Promise<void>;
458
- private scheduleMirror;
459
- private runMirror;
460
- /**
461
- * Compute the diff between this manager's owned set and what the store
462
- * declares now, then apply the openclaw config delta. Foreign keys
463
- * (entries in openclaw.json#mcp.servers.* not in our store) are
464
- * preserved — we only touch the names we previously claimed.
465
- */
466
- private applyMirror;
429
+ private scheduleBundlerReconcile;
467
430
  private reconcileBundler;
468
431
  private fireChange;
469
432
  }
470
433
  //# sourceMappingURL=manager.d.ts.map
471
434
 
472
435
  //#endregion
473
- export { type AddServerOptions, type BundlerOptions, Connection, type ConnectionDeps, type Logger, Manager, type ManagerOptions, McpBundler, type McpClientHandle, type McpServerConfig, type McpToolCallResult, type McpToolDescriptor, type McpTransportKind, type OpenclawExecutor, type ReconcileDiff, type RemoteServerConfig, STDIO_ENV_DENYLIST, type ServerOwner, type StdioServerConfig, Store, type StoreOptions, type StoreSchema, type StoredServerEntry, buildNamespacedToolName, defaultConnect, defaultOpenclawExecutor, defaultStorePath, disambiguateAgainst, sanitizeNameSegment, sanitizeStdioEnv, toServerConfig, toStoredEntry };
436
+ export { type AddServerOptions, type BundlerOptions, Connection, type ConnectionDeps, type Logger, Manager, type ManagerOptions, McpBundler, type McpClientHandle, type McpServerConfig, type McpToolCallResult, type McpToolDescriptor, type McpTransportKind, type ReconcileDiff, type RemoteServerConfig, STDIO_ENV_DENYLIST, type ServerOwner, type StdioServerConfig, Store, type StoreOptions, type StoreSchema, type StoredServerEntry, buildNamespacedToolName, defaultConnect, defaultStorePath, disambiguateAgainst, sanitizeNameSegment, sanitizeStdioEnv, toServerConfig, toStoredEntry };
474
437
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/connection.ts","../src/bundler.ts","../src/tool-naming.ts","../src/store.ts","../src/manager.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/connection.ts","../src/bundler.ts","../src/tool-naming.ts","../src/store.ts","../src/manager.ts"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -350,36 +350,13 @@ declare function toStoredEntry(config: McpServerConfig, meta: {
350
350
  }): StoredServerEntry;
351
351
  //#endregion
352
352
  //#region src/manager.d.ts
353
- /**
354
- * Shells out to `openclaw config set --batch-json` / `openclaw config unset`
355
- * to keep `openclaw.json#mcp.servers.*` in sync with the bundler store.
356
- * Default executor reuses the same machinery the integrations applier has
357
- * used for ~6 months; tests inject a fake.
358
- */
359
- interface OpenclawExecutor {
360
- setBatch: (batch: {
361
- path: string;
362
- value: unknown;
363
- }[]) => Promise<void>;
364
- unset: (path: string) => Promise<void>;
365
- }
366
- declare const defaultOpenclawExecutor: OpenclawExecutor;
367
353
  interface ManagerOptions {
368
354
  /** Pre-constructed store. If omitted, one is built with default options. */
369
355
  store?: Store;
370
- /** Override for the openclaw mirror executor (tests inject a fake). */
371
- executor?: OpenclawExecutor;
372
356
  logger?: Logger;
373
- /**
374
- * Debounce window for the mirror-write. Mutations landing inside this
375
- * window coalesce into a single openclaw config update, avoiding the
376
- * auto-restart race where two back-to-back `addServer` calls hit an
377
- * openclaw that's mid-shutdown from the first write's watcher.
378
- */
379
- mirrorDebounceMs?: number;
380
357
  }
381
358
  interface AddServerOptions {
382
- /** Required — flat-namespace key under `mcp.servers.*`. */
359
+ /** Required — flat-namespace key under the bundler store. */
383
360
  id: string;
384
361
  /** Marks ownership for bulk removal. Defaults to `manual`. */
385
362
  owner?: ServerOwner;
@@ -389,21 +366,23 @@ interface AddServerOptions {
389
366
  transport?: McpTransportKind;
390
367
  }
391
368
  /**
392
- * Bundler manager — owns the alfe store, mirrors it into openclaw.json,
393
- * and surfaces a small CRUD API the CLI and integration applier both
394
- * call into.
369
+ * Bundler manager — owns the `~/.alfe/mcp/servers.json` store and surfaces a
370
+ * small CRUD API the CLI and integration applier both call into.
371
+ *
372
+ * Single source of truth: every consumer (daemon-hosted bundler, CLI `alfe mcp
373
+ * list`, integration uninstall) reads from this store. Openclaw.json is no
374
+ * longer kept in sync — the daemon hosts the bundler children and the
375
+ * openclaw plugin reaches them via IPC, so the openclaw.json mirror became
376
+ * dead weight and an active source of duplicate spawning on claude-cli /
377
+ * codex-cli backends.
395
378
  *
396
- * The store is the Alfe-owned source of truth; openclaw.json is a
397
- * derived mirror so the runtime keeps consuming its existing format.
379
+ * Call `loadIntoBundler(bundler)` once at daemon startup to wire the store
380
+ * into a live `McpBundler` subsequent store mutations (including those
381
+ * landed by other processes via the file watcher) re-reconcile automatically.
398
382
  */
399
383
  declare class Manager {
400
384
  private readonly store;
401
- private readonly executor;
402
385
  private readonly logger?;
403
- private readonly mirrorDebounceMs;
404
- private mirrorTimer?;
405
- private mirrorPromise;
406
- private mirrorPending?;
407
386
  private bundler?;
408
387
  private changeListeners;
409
388
  private storeUnsubscribe?;
@@ -411,10 +390,10 @@ declare class Manager {
411
390
  /** Direct accessor — handy for tests and the CLI's `alfe mcp list`. */
412
391
  getStore(): Store;
413
392
  /**
414
- * Register or overwrite a server entry. Resolves as soon as the store
415
- * mutation is committed to disk the openclaw.json mirror runs async
416
- * in the background and is debounced so back-to-back calls coalesce
417
- * into one runtime restart. Call `flush()` to await the mirror.
393
+ * Register or overwrite a server entry. Mutation lands in the store
394
+ * synchronously; if a bundler has been attached via `loadIntoBundler`,
395
+ * it gets re-reconciled in the background (errors logged, never
396
+ * thrown the store is the source of truth, the bundler is derived).
418
397
  */
419
398
  addServer(config: McpServerConfig, opts: AddServerOptions): Promise<void>;
420
399
  /**
@@ -422,9 +401,6 @@ declare class Manager {
422
401
  * Refuses to remove an entry whose owner doesn't match `expectedOwner`
423
402
  * when supplied — the CLI uses this to guard `alfe mcp remove` from
424
403
  * accidentally clobbering integration- or cli-owned entries.
425
- *
426
- * Resolves as soon as the store mutation is committed. Mirror runs
427
- * async; call `flush()` to await it.
428
404
  */
429
405
  removeServer(id: string, opts?: {
430
406
  expectedOwner?: ServerOwner;
@@ -445,30 +421,17 @@ declare class Manager {
445
421
  /** Subscribe to store mutations. Returns an unsubscribe fn. */
446
422
  onChange(cb: () => void): () => void;
447
423
  /**
448
- * Cancel any pending mirror-write, flush the in-flight one, and stop
449
- * watching the store. Safe to call multiple times.
424
+ * Detach from the bundler and stop watching the store. Safe to call
425
+ * multiple times. Does not dispose the underlying `Store` so the
426
+ * shared instance survives multi-manager environments (rare).
450
427
  */
451
428
  dispose(): Promise<void>;
452
- /**
453
- * Force the debounced mirror to run now and wait for it to finish.
454
- * Surfaces the executor error if the mirror failed — callers wrap in
455
- * try/catch (or .rejects in tests) if they need to handle it.
456
- */
457
- flush(): Promise<void>;
458
- private scheduleMirror;
459
- private runMirror;
460
- /**
461
- * Compute the diff between this manager's owned set and what the store
462
- * declares now, then apply the openclaw config delta. Foreign keys
463
- * (entries in openclaw.json#mcp.servers.* not in our store) are
464
- * preserved — we only touch the names we previously claimed.
465
- */
466
- private applyMirror;
429
+ private scheduleBundlerReconcile;
467
430
  private reconcileBundler;
468
431
  private fireChange;
469
432
  }
470
433
  //# sourceMappingURL=manager.d.ts.map
471
434
 
472
435
  //#endregion
473
- export { type AddServerOptions, type BundlerOptions, Connection, type ConnectionDeps, type Logger, Manager, type ManagerOptions, McpBundler, type McpClientHandle, type McpServerConfig, type McpToolCallResult, type McpToolDescriptor, type McpTransportKind, type OpenclawExecutor, type ReconcileDiff, type RemoteServerConfig, STDIO_ENV_DENYLIST, type ServerOwner, type StdioServerConfig, Store, type StoreOptions, type StoreSchema, type StoredServerEntry, buildNamespacedToolName, defaultConnect, defaultOpenclawExecutor, defaultStorePath, disambiguateAgainst, sanitizeNameSegment, sanitizeStdioEnv, toServerConfig, toStoredEntry };
436
+ export { type AddServerOptions, type BundlerOptions, Connection, type ConnectionDeps, type Logger, Manager, type ManagerOptions, McpBundler, type McpClientHandle, type McpServerConfig, type McpToolCallResult, type McpToolDescriptor, type McpTransportKind, type ReconcileDiff, type RemoteServerConfig, STDIO_ENV_DENYLIST, type ServerOwner, type StdioServerConfig, Store, type StoreOptions, type StoreSchema, type StoredServerEntry, buildNamespacedToolName, defaultConnect, defaultStorePath, disambiguateAgainst, sanitizeNameSegment, sanitizeStdioEnv, toServerConfig, toStoredEntry };
474
437
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/connection.ts","../src/bundler.ts","../src/tool-naming.ts","../src/store.ts","../src/manager.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/connection.ts","../src/bundler.ts","../src/tool-naming.ts","../src/store.ts","../src/manager.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,8 +1,6 @@
1
1
  import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, watch, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { homedir } from "node:os";
4
- import { execFile } from "node:child_process";
5
- import { promisify } from "node:util";
6
4
  //#region src/tool-naming.ts
7
5
  /**
8
6
  * Tool name sanitization and collision handling.
@@ -716,60 +714,40 @@ function errMsg$1(err) {
716
714
  }
717
715
  //#endregion
718
716
  //#region src/manager.ts
719
- const execFileAsync = promisify(execFile);
720
- const defaultOpenclawExecutor = {
721
- async setBatch(batch) {
722
- if (batch.length === 0) return;
723
- await execFileAsync("openclaw", [
724
- "config",
725
- "set",
726
- "--batch-json",
727
- JSON.stringify(batch)
728
- ], { timeout: 1e4 });
729
- },
730
- async unset(path) {
731
- await execFileAsync("openclaw", [
732
- "config",
733
- "unset",
734
- path
735
- ], { timeout: 1e4 });
736
- }
737
- };
738
- const DEFAULT_MIRROR_DEBOUNCE_MS = 250;
739
717
  /**
740
- * Bundler manager — owns the alfe store, mirrors it into openclaw.json,
741
- * and surfaces a small CRUD API the CLI and integration applier both
742
- * call into.
718
+ * Bundler manager — owns the `~/.alfe/mcp/servers.json` store and surfaces a
719
+ * small CRUD API the CLI and integration applier both call into.
720
+ *
721
+ * Single source of truth: every consumer (daemon-hosted bundler, CLI `alfe mcp
722
+ * list`, integration uninstall) reads from this store. Openclaw.json is no
723
+ * longer kept in sync — the daemon hosts the bundler children and the
724
+ * openclaw plugin reaches them via IPC, so the openclaw.json mirror became
725
+ * dead weight and an active source of duplicate spawning on claude-cli /
726
+ * codex-cli backends.
743
727
  *
744
- * The store is the Alfe-owned source of truth; openclaw.json is a
745
- * derived mirror so the runtime keeps consuming its existing format.
728
+ * Call `loadIntoBundler(bundler)` once at daemon startup to wire the store
729
+ * into a live `McpBundler` subsequent store mutations (including those
730
+ * landed by other processes via the file watcher) re-reconcile automatically.
746
731
  */
747
732
  var Manager = class {
748
733
  store;
749
- executor;
750
734
  logger;
751
- mirrorDebounceMs;
752
- mirrorTimer;
753
- mirrorPromise = Promise.resolve();
754
- mirrorPending;
755
735
  bundler;
756
736
  changeListeners = /* @__PURE__ */ new Set();
757
737
  storeUnsubscribe;
758
738
  constructor(opts = {}) {
759
739
  this.store = opts.store ?? new Store({ logger: opts.logger });
760
- this.executor = opts.executor ?? defaultOpenclawExecutor;
761
740
  this.logger = opts.logger;
762
- this.mirrorDebounceMs = opts.mirrorDebounceMs ?? DEFAULT_MIRROR_DEBOUNCE_MS;
763
741
  }
764
742
  /** Direct accessor — handy for tests and the CLI's `alfe mcp list`. */
765
743
  getStore() {
766
744
  return this.store;
767
745
  }
768
746
  /**
769
- * Register or overwrite a server entry. Resolves as soon as the store
770
- * mutation is committed to disk the openclaw.json mirror runs async
771
- * in the background and is debounced so back-to-back calls coalesce
772
- * into one runtime restart. Call `flush()` to await the mirror.
747
+ * Register or overwrite a server entry. Mutation lands in the store
748
+ * synchronously; if a bundler has been attached via `loadIntoBundler`,
749
+ * it gets re-reconciled in the background (errors logged, never
750
+ * thrown the store is the source of truth, the bundler is derived).
773
751
  */
774
752
  async addServer(config, opts) {
775
753
  if (!opts.id) throw new Error("Manager.addServer: id is required");
@@ -790,7 +768,7 @@ var Manager = class {
790
768
  }
791
769
  };
792
770
  });
793
- this.scheduleMirror();
771
+ this.scheduleBundlerReconcile();
794
772
  this.fireChange();
795
773
  return Promise.resolve();
796
774
  }
@@ -799,9 +777,6 @@ var Manager = class {
799
777
  * Refuses to remove an entry whose owner doesn't match `expectedOwner`
800
778
  * when supplied — the CLI uses this to guard `alfe mcp remove` from
801
779
  * accidentally clobbering integration- or cli-owned entries.
802
- *
803
- * Resolves as soon as the store mutation is committed. Mirror runs
804
- * async; call `flush()` to await it.
805
780
  */
806
781
  removeServer(id, opts = {}) {
807
782
  const existing = lookupEntry(this.store.read().servers, id);
@@ -811,7 +786,7 @@ var Manager = class {
811
786
  ...cur,
812
787
  servers: Object.fromEntries(Object.entries(cur.servers).filter(([k]) => k !== id))
813
788
  }));
814
- this.scheduleMirror();
789
+ this.scheduleBundlerReconcile();
815
790
  this.fireChange();
816
791
  return Promise.resolve(true);
817
792
  }
@@ -829,7 +804,7 @@ var Manager = class {
829
804
  };
830
805
  });
831
806
  if (removed.length > 0) {
832
- this.scheduleMirror();
807
+ this.scheduleBundlerReconcile();
833
808
  this.fireChange();
834
809
  }
835
810
  return Promise.resolve(removed);
@@ -864,16 +839,11 @@ var Manager = class {
864
839
  };
865
840
  }
866
841
  /**
867
- * Cancel any pending mirror-write, flush the in-flight one, and stop
868
- * watching the store. Safe to call multiple times.
842
+ * Detach from the bundler and stop watching the store. Safe to call
843
+ * multiple times. Does not dispose the underlying `Store` so the
844
+ * shared instance survives multi-manager environments (rare).
869
845
  */
870
846
  async dispose() {
871
- if (this.mirrorTimer) {
872
- clearTimeout(this.mirrorTimer);
873
- this.mirrorTimer = void 0;
874
- await this.runMirror();
875
- }
876
- await this.mirrorPromise.catch(() => void 0);
877
847
  if (this.storeUnsubscribe) {
878
848
  this.storeUnsubscribe();
879
849
  this.storeUnsubscribe = void 0;
@@ -881,80 +851,13 @@ var Manager = class {
881
851
  this.store.dispose();
882
852
  this.changeListeners.clear();
883
853
  this.bundler = void 0;
854
+ return Promise.resolve();
884
855
  }
885
- /**
886
- * Force the debounced mirror to run now and wait for it to finish.
887
- * Surfaces the executor error if the mirror failed — callers wrap in
888
- * try/catch (or .rejects in tests) if they need to handle it.
889
- */
890
- async flush() {
891
- if (this.mirrorTimer) {
892
- clearTimeout(this.mirrorTimer);
893
- this.mirrorTimer = void 0;
894
- await this.runMirror();
895
- }
896
- await this.mirrorPromise;
897
- }
898
- scheduleMirror() {
899
- if (this.mirrorPending) return;
900
- this.mirrorPromise = new Promise((resolve, reject) => {
901
- this.mirrorPending = {
902
- resolve,
903
- reject
904
- };
905
- });
906
- this.mirrorTimer = setTimeout(() => {
907
- this.mirrorTimer = void 0;
908
- this.runMirror();
909
- }, this.mirrorDebounceMs);
910
- this.mirrorTimer.unref();
911
- }
912
- async runMirror() {
913
- const pending = this.mirrorPending;
914
- this.mirrorPending = void 0;
915
- try {
916
- await this.applyMirror();
917
- pending?.resolve();
918
- } catch (err) {
919
- pending?.reject(err);
920
- this.logger?.error("[mcp-bundler/manager] mirror write failed", { err: errMsg(err) });
921
- }
922
- }
923
- /**
924
- * Compute the diff between this manager's owned set and what the store
925
- * declares now, then apply the openclaw config delta. Foreign keys
926
- * (entries in openclaw.json#mcp.servers.* not in our store) are
927
- * preserved — we only touch the names we previously claimed.
928
- */
929
- async applyMirror() {
930
- let previousOwned = [];
931
- let desiredOwned = [];
932
- let batch = [];
933
- this.store.update((cur) => {
934
- previousOwned = cur._ownedOpenclawKeys.slice();
935
- const entries = Object.entries(cur.servers);
936
- desiredOwned = entries.map(([id]) => id).sort();
937
- batch = entries.flatMap(([id, entry]) => renderEntryToBatch(id, entry));
938
- return {
939
- ...cur,
940
- _ownedOpenclawKeys: desiredOwned
941
- };
856
+ scheduleBundlerReconcile() {
857
+ if (!this.bundler) return;
858
+ this.reconcileBundler().catch((err) => {
859
+ this.logger?.warn("[mcp-bundler/manager] bundler reconcile failed", { err: errMsg(err) });
942
860
  });
943
- await this.executor.setBatch(batch);
944
- const toUnset = previousOwned.filter((k) => !desiredOwned.includes(k));
945
- for (const id of toUnset) try {
946
- await this.executor.unset(`mcp.servers.${id}`);
947
- } catch (err) {
948
- this.logger?.warn("[mcp-bundler/manager] mirror unset failed (continuing)", {
949
- err: errMsg(err),
950
- key: `mcp.servers.${id}`
951
- });
952
- }
953
- if (this.bundler) try {
954
- await this.reconcileBundler();
955
- } catch (err) {
956
- this.logger?.warn("[mcp-bundler/manager] post-mirror reconcile failed", { err: errMsg(err) });
957
- }
958
861
  }
959
862
  async reconcileBundler() {
960
863
  if (!this.bundler) return;
@@ -971,53 +874,6 @@ var Manager = class {
971
874
  }
972
875
  }
973
876
  };
974
- /**
975
- * Render one store entry as the set of dotted-path batch entries that
976
- * `openclaw config set --batch-json` expects.
977
- *
978
- * Mirrors the format the integrations applier already emits today so the
979
- * runtime sees the same shape regardless of which writer produced it.
980
- */
981
- function renderEntryToBatch(id, entry) {
982
- const prefix = `mcp.servers.${id}`;
983
- const out = [];
984
- if (entry.transport === "stdio") {
985
- out.push({
986
- path: `${prefix}.command`,
987
- value: entry.command
988
- });
989
- if (entry.args && entry.args.length > 0) out.push({
990
- path: `${prefix}.args`,
991
- value: entry.args
992
- });
993
- if (entry.env) for (const [k, v] of Object.entries(entry.env)) out.push({
994
- path: `${prefix}.env.${k}`,
995
- value: v
996
- });
997
- if (entry.cwd) out.push({
998
- path: `${prefix}.cwd`,
999
- value: entry.cwd
1000
- });
1001
- return out;
1002
- }
1003
- out.push({
1004
- path: `${prefix}.url`,
1005
- value: entry.url
1006
- });
1007
- out.push({
1008
- path: `${prefix}.transport`,
1009
- value: entry.transport
1010
- });
1011
- if (entry.headers) for (const [k, v] of Object.entries(entry.headers)) out.push({
1012
- path: `${prefix}.headers.${k}`,
1013
- value: v
1014
- });
1015
- if (entry.connectionTimeoutMs !== void 0) out.push({
1016
- path: `${prefix}.connectionTimeoutMs`,
1017
- value: entry.connectionTimeoutMs
1018
- });
1019
- return out;
1020
- }
1021
877
  function errMsg(err) {
1022
878
  return err instanceof Error ? err.message : String(err);
1023
879
  }
@@ -1035,6 +891,6 @@ function lookupAddedAt(servers, id) {
1035
891
  return entry ? entry.addedAt : void 0;
1036
892
  }
1037
893
  //#endregion
1038
- export { Connection, Manager, McpBundler, STDIO_ENV_DENYLIST, Store, buildNamespacedToolName, defaultConnect, defaultOpenclawExecutor, defaultStorePath, disambiguateAgainst, sanitizeNameSegment, sanitizeStdioEnv, toServerConfig, toStoredEntry };
894
+ export { Connection, Manager, McpBundler, STDIO_ENV_DENYLIST, Store, buildNamespacedToolName, defaultConnect, defaultStorePath, disambiguateAgainst, sanitizeNameSegment, sanitizeStdioEnv, toServerConfig, toStoredEntry };
1039
895
 
1040
896
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["errMsg"],"sources":["../src/tool-naming.ts","../src/connection.ts","../src/bundler.ts","../src/store.ts","../src/manager.ts"],"sourcesContent":["/**\n * Tool name sanitization and collision handling.\n *\n * OpenClaw constraint: tool names must match `[A-Za-z0-9_-]` and be ≤64 chars.\n * Pattern mirrored from `openclaw/src/agents/pi-bundle-mcp-names.ts`.\n *\n * Strategy: prefix every tool with its server name (`{server}__{tool}`),\n * sanitize disallowed chars to `_`, truncate, then suffix-disambiguate\n * (`-2`, `-3`, ...) on collision.\n */\n\nconst DISALLOWED = /[^A-Za-z0-9_-]/g;\nconst MAX_LEN = 64;\nconst SEPARATOR = '__';\n\nexport function sanitizeNameSegment(value: string): string {\n return value.replace(DISALLOWED, '_');\n}\n\nexport function buildNamespacedToolName(server: string, tool: string): string {\n const base = `${sanitizeNameSegment(server)}${SEPARATOR}${sanitizeNameSegment(tool)}`;\n if (base.length <= MAX_LEN) return base;\n // Truncate from the tool side first to keep the server prefix intact.\n const reservedForServer = sanitizeNameSegment(server).length + SEPARATOR.length;\n const toolBudget = Math.max(1, MAX_LEN - reservedForServer);\n return `${sanitizeNameSegment(server)}${SEPARATOR}${sanitizeNameSegment(tool).slice(0, toolBudget)}`;\n}\n\n/**\n * Disambiguate a candidate name against an existing set by appending `-2`, `-3`, etc.\n * Mutates nothing; returns the chosen name. Caller is responsible for inserting it\n * into the set.\n */\nexport function disambiguateAgainst(candidate: string, taken: ReadonlySet<string>): string {\n if (!taken.has(candidate)) return candidate;\n for (let i = 2; i < 1000; i += 1) {\n const suffix = `-${i.toString()}`;\n const room = MAX_LEN - suffix.length;\n const trimmed = candidate.length > room ? candidate.slice(0, room) : candidate;\n const next = `${trimmed}${suffix}`;\n if (!taken.has(next)) return next;\n }\n // Pathological: 998 collisions. Fall back to a deterministic-ish hash.\n return `${candidate.slice(0, MAX_LEN - 6)}-x${(taken.size % 1000).toString().padStart(3, '0')}`;\n}\n","import type { Logger, McpServerConfig, McpToolDescriptor, McpToolCallResult, StdioServerConfig } from './types.js';\nimport { buildNamespacedToolName } from './tool-naming.js';\n\n/** Env keys OpenClaw rejects from stdio MCP env blocks. Filter them out before spawning. */\nexport const STDIO_ENV_DENYLIST = new Set([\n 'NODE_OPTIONS',\n 'PYTHONSTARTUP',\n 'PYTHONPATH',\n 'PERL5OPT',\n 'RUBYOPT',\n 'SHELLOPTS',\n 'PS4',\n]);\n\nexport function sanitizeStdioEnv(env: Record<string, string> | undefined): Record<string, string> {\n if (!env) return {};\n const safe: Record<string, string> = {};\n for (const [k, v] of Object.entries(env)) {\n if (STDIO_ENV_DENYLIST.has(k)) continue;\n safe[k] = v;\n }\n return safe;\n}\n\nexport interface ConnectionDeps {\n /**\n * Factory for an MCP Client connected to the given config. Injected so tests\n * can mock without spawning real processes. In production this wraps\n * `@modelcontextprotocol/sdk/client`.\n */\n connect: (server: McpServerConfig) => Promise<McpClientHandle>;\n}\n\n/**\n * Minimal interface our connection layer needs from an MCP client. Mirrors\n * the @modelcontextprotocol/sdk Client surface but kept narrow so we can\n * mock cleanly in tests.\n */\nexport interface McpClientHandle {\n listTools(): Promise<{ name: string; description?: string; inputSchema: Record<string, unknown> }[]>;\n callTool(name: string, args: unknown, opts?: { signal?: AbortSignal }): Promise<McpToolCallResult>;\n close(): Promise<void>;\n}\n\n/**\n * One Connection per MCP server. Owns lifecycle (lazy-spawn, close, refresh-lock).\n * Refresh-lock pattern adapted from AIWerk `index.ts:219-250` — prevents\n * reconnect + `notifications/tools/list_changed` race.\n */\nexport class Connection {\n readonly name: string;\n readonly config: McpServerConfig;\n private readonly deps: ConnectionDeps;\n private readonly logger: Logger | undefined;\n\n private client: McpClientHandle | undefined;\n private tools: McpToolDescriptor[] = [];\n private connectInFlight: Promise<void> | undefined;\n private refreshInFlight = false;\n private refreshQueued = false;\n private lastUsedAt = Date.now();\n\n constructor(params: { name: string; config: McpServerConfig; deps: ConnectionDeps; logger?: Logger }) {\n this.name = params.name;\n this.config = params.config;\n this.deps = params.deps;\n this.logger = params.logger;\n }\n\n /** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */\n snapshotTools(): McpToolDescriptor[] {\n return this.tools;\n }\n\n /** Whether an MCP child process / remote connection has been established. */\n isConnected(): boolean {\n return this.client !== undefined;\n }\n\n /** Idle timestamp for reaping. */\n idleSinceMs(): number {\n return Date.now() - this.lastUsedAt;\n }\n\n /**\n * Lazy connect + tool discovery. Safe to call concurrently; in-flight\n * connects coalesce.\n */\n async ensureConnected(): Promise<void> {\n if (this.client) return;\n if (this.connectInFlight) return this.connectInFlight;\n this.connectInFlight = this.connectAndDiscover().finally(() => {\n this.connectInFlight = undefined;\n });\n return this.connectInFlight;\n }\n\n private async connectAndDiscover(): Promise<void> {\n const safeConfig = 'command' in this.config\n ? ({ ...this.config, env: sanitizeStdioEnv(this.config.env) } satisfies StdioServerConfig)\n : this.config;\n this.logger?.debug(`[mcp-bundler] connecting server \"${this.name}\"`);\n const client = await this.deps.connect(safeConfig);\n try {\n const advertised = await client.listTools();\n this.client = client;\n this.tools = advertised.map((t) => ({\n prefixed: buildNamespacedToolName(this.name, t.name),\n server: this.name,\n original: t.name,\n label: (t.description ?? t.name).slice(0, 80),\n description: t.description ?? '',\n parameters: t.inputSchema,\n }));\n this.lastUsedAt = Date.now();\n this.logger?.info(`[mcp-bundler] server \"${this.name}\" connected, ${this.tools.length.toString()} tool(s)`);\n } catch (err) {\n await client.close().catch(() => undefined);\n throw err;\n }\n }\n\n /**\n * Re-discover tools. Used on reconnect or `tools/list_changed` notification.\n * Refresh-lock collapses concurrent refreshes; if one is in flight, the next\n * is queued (max 1 queued, since N>1 queued provides no extra freshness).\n */\n async refresh(): Promise<void> {\n if (!this.client) return this.ensureConnected();\n if (this.refreshInFlight) {\n this.refreshQueued = true;\n return;\n }\n this.refreshInFlight = true;\n try {\n const advertised = await this.client.listTools();\n this.tools = advertised.map((t) => ({\n prefixed: buildNamespacedToolName(this.name, t.name),\n server: this.name,\n original: t.name,\n label: (t.description ?? t.name).slice(0, 80),\n description: t.description ?? '',\n parameters: t.inputSchema,\n }));\n this.logger?.debug(`[mcp-bundler] server \"${this.name}\" refreshed, ${this.tools.length.toString()} tool(s)`);\n } finally {\n this.refreshInFlight = false;\n if (this.refreshQueued) {\n this.refreshQueued = false;\n // Trigger one more refresh; do not await so caller isn't blocked on cascading refreshes.\n void this.refresh().catch((err: unknown) => {\n this.logger?.warn(`[mcp-bundler] queued refresh for \"${this.name}\" failed`, { err: err instanceof Error ? err.message : String(err) });\n });\n }\n }\n }\n\n async callTool(originalName: string, args: unknown, signal?: AbortSignal): Promise<McpToolCallResult> {\n await this.ensureConnected();\n if (!this.client) throw new Error(`server \"${this.name}\" failed to connect`);\n this.lastUsedAt = Date.now();\n return this.client.callTool(originalName, args, signal ? { signal } : undefined);\n }\n\n /**\n * Close the underlying transport. Idempotent. If a connect is in flight\n * (warmup racing with reconcile-removal), wait for it to settle and then\n * close the client it produced — otherwise the child process is orphaned.\n */\n async close(): Promise<void> {\n if (this.connectInFlight) {\n await this.connectInFlight.catch(() => undefined);\n }\n const c = this.client;\n this.client = undefined;\n this.tools = [];\n if (c) await c.close().catch((err: unknown) => {\n this.logger?.warn(`[mcp-bundler] close error for \"${this.name}\"`, { err: err instanceof Error ? err.message : String(err) });\n });\n }\n\n /**\n * Stable hash of the config for diff detection in `reconcile`.\n * Two configs with the same hash are equivalent (no restart needed).\n */\n configFingerprint(): string {\n return JSON.stringify(this.config);\n }\n}\n\n/**\n * Build the production `connect` factory using the official MCP SDK.\n * Kept in a separate function so tests can substitute a mock without\n * pulling the SDK into the test bundle.\n */\nexport async function defaultConnect(server: McpServerConfig): Promise<McpClientHandle> {\n const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');\n const client = new Client({ name: 'alfe-mcp-bundler', version: '0.0.0' }, {});\n\n if ('command' in server) {\n const stdio = server;\n const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');\n const transport = new StdioClientTransport({\n command: stdio.command,\n args: stdio.args ?? [],\n env: { ...sanitizeStdioEnv(stdio.env) } as Record<string, string>,\n cwd: stdio.cwd,\n });\n await client.connect(transport);\n } else {\n const remote = server;\n if (remote.transport === 'streamable-http') {\n const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js');\n const transport = new StreamableHTTPClientTransport(new URL(remote.url), {\n requestInit: { headers: remote.headers ?? {} },\n });\n await client.connect(transport);\n } else {\n // SSE is deprecated in newer MCP SDK in favor of streamable-http, but\n // some servers still only support SSE — keep transport for back-compat.\n /* eslint-disable @typescript-eslint/no-deprecated */\n const { SSEClientTransport } = await import('@modelcontextprotocol/sdk/client/sse.js');\n const transport = new SSEClientTransport(new URL(remote.url), {\n requestInit: { headers: remote.headers ?? {} },\n });\n /* eslint-enable @typescript-eslint/no-deprecated */\n await client.connect(transport);\n }\n }\n\n return {\n async listTools() {\n const result = await client.listTools();\n return result.tools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as Record<string, unknown>,\n }));\n },\n async callTool(name, args, opts) {\n return (await client.callTool({ name, arguments: args as Record<string, unknown> | undefined }, undefined, opts)) as McpToolCallResult;\n },\n async close() {\n await client.close();\n },\n };\n}\n","import { Connection, defaultConnect, type ConnectionDeps } from './connection.js';\nimport { disambiguateAgainst } from './tool-naming.js';\nimport type {\n BundlerOptions,\n Logger,\n McpServerConfig,\n McpToolCallResult,\n McpToolDescriptor,\n ReconcileDiff,\n} from './types.js';\n\nconst DEFAULT_IDLE_TTL_MS = 10 * 60 * 1000;\nconst DEFAULT_IDLE_SWEEP_INTERVAL_MS = 60 * 1000;\n\n/**\n * Provider-agnostic MCP server bundler. Holds N MCP server connections,\n * exposes a unified namespaced tool catalog, and routes calls to the right\n * server.\n *\n * Designed to be embedded in any host (OpenClaw plugin, AI proxy, Lambda).\n * Public surface is intentionally synchronous where the host needs sync\n * (snapshot, listTools), async only where I/O is unavoidable.\n */\nexport class McpBundler {\n private readonly logger: Logger | undefined;\n private readonly connections = new Map<string, Connection>();\n private readonly idleTtlMs: number;\n private readonly idleSweepIntervalMs: number;\n private idleSweepTimer: ReturnType<typeof setInterval> | undefined;\n private readonly deps: ConnectionDeps;\n private disposed = false;\n // Serialize reconcile() so concurrent callers (multiple plugin tool factory\n // ticks within the same ms) don't interleave and orphan Connections, leaking\n // child processes. Acquired via a chain-of-promises latch.\n private reconcileLatch: Promise<unknown> = Promise.resolve();\n\n constructor(opts: BundlerOptions = {}, deps?: ConnectionDeps) {\n this.logger = opts.logger;\n this.idleTtlMs = opts.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;\n this.idleSweepIntervalMs = opts.idleSweepIntervalMs ?? DEFAULT_IDLE_SWEEP_INTERVAL_MS;\n this.deps = deps ?? { connect: defaultConnect };\n if (this.idleTtlMs > 0) this.startIdleSweep();\n }\n\n /**\n * Diff `desired` against current connections, spawn newcomers, dispose\n * removals, hot-restart on config change. Pull-based — call whenever the\n * host's config snapshot may have changed. Cheap if no diff.\n *\n * Lazy: newly-added servers are NOT eagerly connected; they connect on the\n * first `callTool()` (or first `listTools()` after `forceDiscover()`).\n * This avoids paying spawn cost for servers the agent never uses.\n */\n async reconcile(desired: Record<string, McpServerConfig>): Promise<ReconcileDiff> {\n if (this.disposed) throw new Error('McpBundler: disposed');\n // Serialize reconciles. Caller awaits its slot; in-flight reconciles run\n // in declaration order. Errors don't poison the latch — `.catch` swallows\n // for chaining, the actual error rejects the awaited slot.\n const slot = this.reconcileLatch.then(async () => this.doReconcile(desired));\n this.reconcileLatch = slot.catch(() => undefined);\n return slot;\n }\n\n private async doReconcile(desired: Record<string, McpServerConfig>): Promise<ReconcileDiff> {\n if (this.disposed) throw new Error('McpBundler: disposed');\n const desiredNames = new Set(Object.keys(desired));\n const currentNames = new Set(this.connections.keys());\n\n const added: string[] = [];\n const removed: string[] = [];\n const changed: string[] = [];\n const unchanged: string[] = [];\n\n // Removals: dispose connections no longer in desired set.\n for (const name of currentNames) {\n if (!desiredNames.has(name)) {\n const conn = this.connections.get(name);\n this.connections.delete(name);\n if (conn) await conn.close();\n removed.push(name);\n }\n }\n\n // Additions and changes.\n for (const [name, config] of Object.entries(desired)) {\n const existing = this.connections.get(name);\n if (!existing) {\n this.connections.set(name, new Connection({ name, config, deps: this.deps, logger: this.logger }));\n added.push(name);\n continue;\n }\n const nextFingerprint = JSON.stringify(config);\n if (existing.configFingerprint() !== nextFingerprint) {\n // Config changed — close old, replace with fresh (lazy reconnect).\n await existing.close();\n this.connections.set(name, new Connection({ name, config, deps: this.deps, logger: this.logger }));\n changed.push(name);\n } else {\n unchanged.push(name);\n }\n }\n\n if (added.length || removed.length || changed.length) {\n this.logger?.info('[mcp-bundler] reconciled', {\n added: added.length,\n removed: removed.length,\n changed: changed.length,\n unchanged: unchanged.length,\n });\n }\n return { added, removed, changed, unchanged };\n }\n\n /**\n * Synchronous snapshot of all currently-known tools across connected servers.\n * Servers that have not connected yet contribute nothing. Intended for use\n * inside OpenClaw's plugin tool factory which must be sync.\n *\n * Tool names are namespaced and disambiguated (suffix `-2`, `-3` on collision)\n * so cross-server name clashes never produce duplicate registrations.\n */\n listTools(): McpToolDescriptor[] {\n const seen = new Set<string>();\n const out: McpToolDescriptor[] = [];\n for (const conn of this.connections.values()) {\n for (const tool of conn.snapshotTools()) {\n const finalName = disambiguateAgainst(tool.prefixed, seen);\n seen.add(finalName);\n out.push(finalName === tool.prefixed ? tool : { ...tool, prefixed: finalName });\n }\n }\n return out;\n }\n\n /**\n * Eagerly connect to every configured server and discover tools. Used by\n * hosts that want a hot list rather than the lazy default. Errors are\n * swallowed per-server (logged), so one bad server doesn't fail the batch.\n */\n async warmup(): Promise<void> {\n if (this.disposed) return;\n await Promise.allSettled(\n Array.from(this.connections.values()).map(async (conn) => {\n try {\n await conn.ensureConnected();\n } catch (err) {\n this.logger?.warn(`[mcp-bundler] warmup failed for \"${conn.name}\"`, {\n err: err instanceof Error ? err.message : String(err),\n });\n }\n }),\n );\n }\n\n /**\n * Invoke a tool by its namespaced name. Routes to the originating server.\n * Errors are returned as `{ isError: true, content: [...] }` so a failing\n * tool doesn't crash the host.\n */\n async callTool(prefixed: string, args: unknown, signal?: AbortSignal): Promise<McpToolCallResult> {\n if (this.disposed) {\n return { isError: true, content: [{ type: 'text', text: 'mcp-bundler disposed' }] };\n }\n const route = this.routeToolName(prefixed);\n if (!route) {\n return {\n isError: true,\n content: [{ type: 'text', text: `unknown tool: ${prefixed}` }],\n };\n }\n try {\n return await route.connection.callTool(route.original, args, signal);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n this.logger?.error(`[mcp-bundler] tool call failed for \"${prefixed}\"`, { err: msg });\n return {\n isError: true,\n content: [{ type: 'text', text: `tool ${prefixed} failed: ${msg}` }],\n };\n }\n }\n\n /**\n * Resolve a namespaced tool name back to its server connection and original\n * tool name. Returns undefined if the tool is not currently advertised.\n */\n private routeToolName(prefixed: string): { connection: Connection; original: string } | undefined {\n for (const conn of this.connections.values()) {\n for (const tool of conn.snapshotTools()) {\n if (tool.prefixed === prefixed) return { connection: conn, original: tool.original };\n }\n }\n return undefined;\n }\n\n /**\n * Tear down all connections and stop background tasks. Idempotent.\n * Call from `registerRuntimeLifecycle({ cleanup })` in the host plugin.\n */\n async dispose(): Promise<void> {\n if (this.disposed) return;\n this.disposed = true;\n if (this.idleSweepTimer) {\n clearInterval(this.idleSweepTimer);\n this.idleSweepTimer = undefined;\n }\n await Promise.allSettled(Array.from(this.connections.values()).map((c) => c.close()));\n this.connections.clear();\n }\n\n private startIdleSweep(): void {\n this.idleSweepTimer = setInterval(() => {\n void this.sweepIdle().catch((err: unknown) => {\n this.logger?.warn('[mcp-bundler] idle sweep error', {\n err: err instanceof Error ? err.message : String(err),\n });\n });\n }, this.idleSweepIntervalMs);\n // Don't keep the host process alive just for the sweep.\n if (typeof this.idleSweepTimer === 'object' && 'unref' in this.idleSweepTimer) {\n (this.idleSweepTimer as { unref: () => void }).unref();\n }\n }\n\n private async sweepIdle(): Promise<void> {\n if (this.idleTtlMs <= 0) return;\n const targets: Connection[] = [];\n for (const conn of this.connections.values()) {\n if (conn.isConnected() && conn.idleSinceMs() > this.idleTtlMs) {\n targets.push(conn);\n }\n }\n if (targets.length === 0) return;\n this.logger?.debug(`[mcp-bundler] reaping ${targets.length.toString()} idle server(s)`);\n await Promise.allSettled(targets.map((c) => c.close()));\n }\n}\n","import {\n closeSync,\n existsSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n statSync,\n unlinkSync,\n watch,\n writeFileSync,\n type FSWatcher,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { Logger, McpServerConfig, StdioServerConfig, RemoteServerConfig, McpTransportKind } from './types.js';\n\n/**\n * Where a server entry came from. Used by `removeServersByOwner` so an\n * integration uninstall can drop only its own entries without touching\n * `cli`-owned (e.g. `alfe-platform`) or `manual`-owned (user-added) ones.\n */\nexport type ServerOwner = 'cli' | `integration:${string}` | 'manual';\n\ninterface StoredServerCommon {\n /** Where the entry came from — controls bulk-removal semantics. */\n owner: ServerOwner;\n /** ISO timestamp of first registration; preserved across updates. */\n addedAt: string;\n /** Optional semver of the providing package (e.g. `@alfe.ai/mcp-server` for `alfe-platform`). Used for drift detection on CLI upgrade. */\n version?: string;\n}\n\nexport type StoredServerEntry =\n | (StoredServerCommon & { transport: 'stdio' } & StdioServerConfig)\n | (StoredServerCommon & { transport: 'sse' | 'streamable-http' } & RemoteServerConfig);\n\nexport interface StoreSchema {\n servers: Record<string, StoredServerEntry>;\n config: {\n sessionIdleTtlMs?: number;\n };\n /**\n * Server names this manager has written into `openclaw.json#mcp.servers.*`.\n * Used to compute the mirror-write diff without re-reading openclaw.json\n * (which would be a second source of truth). Foreign keys not listed here\n * are preserved across mirror writes.\n */\n _ownedOpenclawKeys: string[];\n}\n\nconst DEFAULT_STORE_DIR = join(homedir(), '.alfe', 'mcp');\nconst DEFAULT_STORE_PATH = join(DEFAULT_STORE_DIR, 'servers.json');\n\n/** Inter-process lock tunings — exported as constants so tests can override. */\nconst LOCK_WAIT_MS = 5_000;\nconst LOCK_RETRY_INTERVAL_MS = 25;\nconst LOCK_STALE_MS = 10_000;\n\nexport interface StoreOptions {\n /** Absolute path to the store file. Defaults to `~/.alfe/mcp/servers.json`. */\n path?: string;\n logger?: Logger;\n}\n\n/**\n * On-disk source of truth for the bundler's configured servers.\n *\n * Mutations go through `update()` (read-modify-write with atomic\n * temp+rename) so a concurrent writer (e.g. two `alfe mcp add` invocations\n * racing) can't lose data — the second writer reads the first's state.\n *\n * Schema is owner-tagged so `removeServersByOwner` can implement\n * integration uninstall without touching CLI-owned or manual entries.\n */\nexport class Store {\n private readonly storePath: string;\n private readonly logger?: Logger;\n private watcher?: FSWatcher;\n private watcherListeners = new Set<() => void>();\n private rewatchTimer?: NodeJS.Timeout;\n\n constructor(opts: StoreOptions = {}) {\n this.storePath = opts.path ?? DEFAULT_STORE_PATH;\n this.logger = opts.logger;\n }\n\n get path(): string {\n return this.storePath;\n }\n\n read(): StoreSchema {\n if (!existsSync(this.storePath)) return cloneEmpty();\n try {\n const raw = readFileSync(this.storePath, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n return normalize(parsed);\n } catch (err) {\n this.logger?.warn('[mcp-bundler/store] failed to read store; returning empty', {\n err: errMsg(err),\n path: this.storePath,\n });\n return cloneEmpty();\n }\n }\n\n /**\n * Read-modify-write with atomic temp+rename, guarded by an\n * inter-process lock file. Caller passes a pure function that\n * produces the next state; this serialises the mutation to disk in\n * one rename, which is atomic on POSIX and on Windows when the\n * target path is on the same volume.\n *\n * The lock guards the read-then-rename window so two processes\n * (e.g. two `alfe mcp add` shells, or the CLI racing the daemon)\n * can't drop each other's writes. The lock file is at\n * `<storePath>.lock`; stale locks (older than `LOCK_STALE_MS`) are\n * stolen so a crashed writer doesn't wedge the store.\n *\n * Pure-function shape (instead of a `read()` then `write(next)`\n * pair) intentionally — it keeps the read-modify-write contract\n * local to each caller so two updates back-to-back never see each\n * other's partial state.\n */\n update(fn: (cur: StoreSchema) => StoreSchema): StoreSchema {\n mkdirSync(dirname(this.storePath), { recursive: true });\n const release = this.acquireLock();\n try {\n const cur = this.read();\n const next = fn(cur);\n const tempPath = `${this.storePath}.${String(process.pid)}.${String(Date.now())}.tmp`;\n writeFileSync(tempPath, JSON.stringify(next, null, 2), { encoding: 'utf8', mode: 0o600 });\n try {\n renameSync(tempPath, this.storePath);\n } catch (err) {\n try {\n unlinkSync(tempPath);\n } catch {\n // Best-effort cleanup; the temp file's name has the pid + timestamp\n // so an orphan won't collide with future writers.\n }\n throw err;\n }\n return next;\n } finally {\n release();\n }\n }\n\n /**\n * Acquire an inter-process file lock by atomically creating a\n * sentinel via `openSync(lockPath, 'wx')`. Spins with bounded\n * backoff up to `LOCK_WAIT_MS`. If the lock file is older than\n * `LOCK_STALE_MS` it's assumed orphaned (writer crashed mid-update)\n * and stolen — the write window is sub-second in practice, so\n * holding the lock for >5s means something went wrong.\n *\n * Returns the release function. Single-process callers are\n * unaffected — re-entering the same process spins briefly while\n * the prior call's `finally` runs.\n */\n private acquireLock(): () => void {\n const lockPath = `${this.storePath}.lock`;\n const deadline = Date.now() + LOCK_WAIT_MS;\n let fd = -1;\n for (;;) {\n try {\n fd = openSync(lockPath, 'wx', 0o600);\n break;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== 'EEXIST') throw err;\n if (this.lockIsStale(lockPath)) {\n try {\n unlinkSync(lockPath);\n } catch {\n // Another process may have just released it — fall through and retry.\n }\n continue;\n }\n if (Date.now() >= deadline) {\n throw new Error(\n `Store.update: timed out waiting for ${lockPath} (held by another writer or stale lock)`,\n );\n }\n // Synchronous spin — the lock window is sub-second under\n // normal load; busy-waiting briefly is simpler than wiring\n // async/await through every `update()` caller.\n const sleepUntil = Date.now() + LOCK_RETRY_INTERVAL_MS;\n while (Date.now() < sleepUntil) { /* spin */ }\n }\n }\n const held = fd;\n return () => {\n try {\n closeSync(held);\n } catch {\n // ignore — the unlink is what releases the lock for the next writer.\n }\n try {\n unlinkSync(lockPath);\n } catch {\n // ignore — already unlinked or stolen by a stale-lock breaker.\n }\n };\n }\n\n private lockIsStale(lockPath: string): boolean {\n try {\n const st = statSync(lockPath);\n return Date.now() - st.mtimeMs > LOCK_STALE_MS;\n } catch {\n return false;\n }\n }\n\n /**\n * Watch the store file for external changes (e.g. another `alfe mcp add`\n * shelling out from a separate process). Returns an unsubscribe fn.\n *\n * Coalesces bursts via a 50 ms debounce — editors and atomic-rename\n * writers commonly fire multiple events per logical save.\n */\n watch(cb: () => void): () => void {\n this.watcherListeners.add(cb);\n this.ensureWatcher();\n return () => {\n this.watcherListeners.delete(cb);\n if (this.watcherListeners.size === 0) this.disposeWatcher();\n };\n }\n\n dispose(): void {\n this.watcherListeners.clear();\n this.disposeWatcher();\n }\n\n private ensureWatcher(): void {\n if (this.watcher) return;\n mkdirSync(dirname(this.storePath), { recursive: true });\n // Some platforms / atomic renames make a per-file watch flaky after\n // a rename; watching the parent directory and filtering by basename\n // is more robust.\n const dir = dirname(this.storePath);\n const basename = this.storePath.slice(dir.length + 1);\n let pending: NodeJS.Timeout | undefined;\n const fire = (): void => {\n pending = undefined;\n for (const cb of this.watcherListeners) {\n try {\n cb();\n } catch (err) {\n this.logger?.warn('[mcp-bundler/store] watcher listener threw', { err: errMsg(err) });\n }\n }\n };\n try {\n this.watcher = watch(dir, (_event, fn) => {\n if (fn !== basename) return;\n if (pending) clearTimeout(pending);\n pending = setTimeout(fire, 50);\n });\n this.watcher.on('error', (err) => {\n this.logger?.warn('[mcp-bundler/store] watcher error; retrying in 1s', { err: errMsg(err) });\n this.disposeWatcher();\n if (!this.rewatchTimer && this.watcherListeners.size > 0) {\n this.rewatchTimer = setTimeout(() => {\n this.rewatchTimer = undefined;\n this.ensureWatcher();\n }, 1000);\n this.rewatchTimer.unref();\n }\n });\n } catch (err) {\n this.logger?.warn('[mcp-bundler/store] failed to start watcher', { err: errMsg(err) });\n }\n }\n\n private disposeWatcher(): void {\n if (this.watcher) {\n try {\n this.watcher.close();\n } catch {\n // close throws on already-disposed watchers; ignore.\n }\n this.watcher = undefined;\n }\n if (this.rewatchTimer) {\n clearTimeout(this.rewatchTimer);\n this.rewatchTimer = undefined;\n }\n }\n}\n\nexport function defaultStorePath(): string {\n return DEFAULT_STORE_PATH;\n}\n\n/** Pull the runtime config (transport + transport-specific fields) out of a stored entry. */\nexport function toServerConfig(entry: StoredServerEntry): McpServerConfig {\n if (entry.transport === 'stdio') {\n const { command, args, env, cwd } = entry;\n const cfg: StdioServerConfig = { command };\n if (args) cfg.args = args;\n if (env) cfg.env = env;\n if (cwd) cfg.cwd = cwd;\n return cfg;\n }\n const { url, transport, headers, connectionTimeoutMs } = entry;\n const cfg: RemoteServerConfig = { url, transport };\n if (headers) cfg.headers = headers;\n if (connectionTimeoutMs !== undefined) cfg.connectionTimeoutMs = connectionTimeoutMs;\n return cfg;\n}\n\n/** Build a stored entry from a runtime config + ownership metadata. */\nexport function toStoredEntry(\n config: McpServerConfig,\n meta: { owner: ServerOwner; transport?: McpTransportKind; version?: string; addedAt?: string },\n): StoredServerEntry {\n const addedAt = meta.addedAt ?? new Date().toISOString();\n if ('command' in config) {\n return {\n transport: 'stdio',\n owner: meta.owner,\n addedAt,\n ...(meta.version !== undefined ? { version: meta.version } : {}),\n ...config,\n };\n }\n const transport = meta.transport ?? config.transport ?? 'sse';\n if (transport === 'stdio') {\n throw new Error('toStoredEntry: transport=stdio specified but config is remote-shaped');\n }\n return {\n transport,\n owner: meta.owner,\n addedAt,\n ...(meta.version !== undefined ? { version: meta.version } : {}),\n ...config,\n };\n}\n\nfunction cloneEmpty(): StoreSchema {\n return { servers: {}, config: {}, _ownedOpenclawKeys: [] };\n}\n\nfunction normalize(raw: unknown): StoreSchema {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return cloneEmpty();\n const r = raw as Partial<StoreSchema>;\n return {\n servers: r.servers && typeof r.servers === 'object' ? r.servers : {},\n config: r.config && typeof r.config === 'object' ? r.config : {},\n _ownedOpenclawKeys: Array.isArray(r._ownedOpenclawKeys) ? r._ownedOpenclawKeys.slice() : [],\n };\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport type { McpBundler } from './bundler.js';\nimport { Store, toServerConfig, toStoredEntry, type ServerOwner, type StoredServerEntry } from './store.js';\nimport type { Logger, McpServerConfig, McpTransportKind } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Shells out to `openclaw config set --batch-json` / `openclaw config unset`\n * to keep `openclaw.json#mcp.servers.*` in sync with the bundler store.\n * Default executor reuses the same machinery the integrations applier has\n * used for ~6 months; tests inject a fake.\n */\nexport interface OpenclawExecutor {\n setBatch: (batch: { path: string; value: unknown }[]) => Promise<void>;\n unset: (path: string) => Promise<void>;\n}\n\nexport const defaultOpenclawExecutor: OpenclawExecutor = {\n async setBatch(batch) {\n if (batch.length === 0) return;\n await execFileAsync('openclaw', ['config', 'set', '--batch-json', JSON.stringify(batch)], {\n timeout: 10_000,\n });\n },\n async unset(path) {\n await execFileAsync('openclaw', ['config', 'unset', path], { timeout: 10_000 });\n },\n};\n\nexport interface ManagerOptions {\n /** Pre-constructed store. If omitted, one is built with default options. */\n store?: Store;\n /** Override for the openclaw mirror executor (tests inject a fake). */\n executor?: OpenclawExecutor;\n logger?: Logger;\n /**\n * Debounce window for the mirror-write. Mutations landing inside this\n * window coalesce into a single openclaw config update, avoiding the\n * auto-restart race where two back-to-back `addServer` calls hit an\n * openclaw that's mid-shutdown from the first write's watcher.\n */\n mirrorDebounceMs?: number;\n}\n\nexport interface AddServerOptions {\n /** Required — flat-namespace key under `mcp.servers.*`. */\n id: string;\n /** Marks ownership for bulk removal. Defaults to `manual`. */\n owner?: ServerOwner;\n /** Semver of the providing package; used for CLI version-drift detection. */\n version?: string;\n /** Explicit transport hint for remote configs. Defaults to inferring from `config`. */\n transport?: McpTransportKind;\n}\n\nconst DEFAULT_MIRROR_DEBOUNCE_MS = 250;\n\n/**\n * Bundler manager — owns the alfe store, mirrors it into openclaw.json,\n * and surfaces a small CRUD API the CLI and integration applier both\n * call into.\n *\n * The store is the Alfe-owned source of truth; openclaw.json is a\n * derived mirror so the runtime keeps consuming its existing format.\n */\nexport class Manager {\n private readonly store: Store;\n private readonly executor: OpenclawExecutor;\n private readonly logger?: Logger;\n private readonly mirrorDebounceMs: number;\n private mirrorTimer?: NodeJS.Timeout;\n private mirrorPromise: Promise<void> = Promise.resolve();\n private mirrorPending?: { resolve: () => void; reject: (err: unknown) => void };\n private bundler?: McpBundler;\n private changeListeners = new Set<() => void>();\n private storeUnsubscribe?: () => void;\n\n constructor(opts: ManagerOptions = {}) {\n this.store = opts.store ?? new Store({ logger: opts.logger });\n this.executor = opts.executor ?? defaultOpenclawExecutor;\n this.logger = opts.logger;\n this.mirrorDebounceMs = opts.mirrorDebounceMs ?? DEFAULT_MIRROR_DEBOUNCE_MS;\n }\n\n /** Direct accessor — handy for tests and the CLI's `alfe mcp list`. */\n getStore(): Store {\n return this.store;\n }\n\n /**\n * Register or overwrite a server entry. Resolves as soon as the store\n * mutation is committed to disk — the openclaw.json mirror runs async\n * in the background and is debounced so back-to-back calls coalesce\n * into one runtime restart. Call `flush()` to await the mirror.\n */\n async addServer(config: McpServerConfig, opts: AddServerOptions): Promise<void> {\n if (!opts.id) throw new Error('Manager.addServer: id is required');\n const owner = opts.owner ?? 'manual';\n this.store.update((cur) => {\n const previousAddedAt = lookupAddedAt(cur.servers, opts.id);\n const entry = toStoredEntry(config, {\n owner,\n transport: opts.transport,\n version: opts.version,\n addedAt: previousAddedAt,\n });\n return {\n ...cur,\n servers: { ...cur.servers, [opts.id]: entry },\n };\n });\n this.scheduleMirror();\n this.fireChange();\n return Promise.resolve();\n }\n\n /**\n * Remove a single server entry. No-op if the id isn't in the store.\n * Refuses to remove an entry whose owner doesn't match `expectedOwner`\n * when supplied — the CLI uses this to guard `alfe mcp remove` from\n * accidentally clobbering integration- or cli-owned entries.\n *\n * Resolves as soon as the store mutation is committed. Mirror runs\n * async; call `flush()` to await it.\n */\n removeServer(id: string, opts: { expectedOwner?: ServerOwner } = {}): Promise<boolean> {\n const current = this.store.read();\n const existing = lookupEntry(current.servers, id);\n if (!existing) return Promise.resolve(false);\n if (opts.expectedOwner && existing.owner !== opts.expectedOwner) {\n return Promise.reject(\n new Error(\n `Manager.removeServer: server \"${id}\" is owned by \"${existing.owner}\", not \"${opts.expectedOwner}\"`,\n ),\n );\n }\n this.store.update((cur) => ({\n ...cur,\n servers: Object.fromEntries(Object.entries(cur.servers).filter(([k]) => k !== id)),\n }));\n this.scheduleMirror();\n this.fireChange();\n return Promise.resolve(true);\n }\n\n /** Drop every entry whose owner matches — used by integration uninstall. */\n async removeServersByOwner(owner: ServerOwner): Promise<string[]> {\n const removed: string[] = [];\n this.store.update((cur) => {\n const next: Record<string, StoredServerEntry> = {};\n for (const [id, entry] of Object.entries(cur.servers)) {\n if (entry.owner === owner) {\n removed.push(id);\n } else {\n next[id] = entry;\n }\n }\n if (removed.length === 0) return cur;\n return { ...cur, servers: next };\n });\n if (removed.length > 0) {\n this.scheduleMirror();\n this.fireChange();\n }\n return Promise.resolve(removed);\n }\n\n /** Read-only snapshot for `alfe mcp list` and similar UIs. */\n listServers(): { id: string; entry: StoredServerEntry }[] {\n const snap = this.store.read();\n return Object.entries(snap.servers).map(([id, entry]) => ({ id, entry }));\n }\n\n /**\n * Push the current store contents into a bundler instance (which owns\n * connections / tools). Wires up a store watcher so external mutations\n * (e.g. another shell running `alfe mcp add`) re-reconcile.\n */\n async loadIntoBundler(bundler: McpBundler): Promise<void> {\n this.bundler = bundler;\n await this.reconcileBundler();\n this.storeUnsubscribe ??= this.store.watch(() => {\n void this.reconcileBundler().catch((err: unknown) => {\n this.logger?.warn('[mcp-bundler/manager] watcher reconcile failed', { err: errMsg(err) });\n });\n });\n }\n\n /** Subscribe to store mutations. Returns an unsubscribe fn. */\n onChange(cb: () => void): () => void {\n this.changeListeners.add(cb);\n return () => {\n this.changeListeners.delete(cb);\n };\n }\n\n /**\n * Cancel any pending mirror-write, flush the in-flight one, and stop\n * watching the store. Safe to call multiple times.\n */\n async dispose(): Promise<void> {\n if (this.mirrorTimer) {\n clearTimeout(this.mirrorTimer);\n this.mirrorTimer = undefined;\n // If a debounced write was queued, flush it before disposing so\n // openclaw.json doesn't end up stale from a dropped tail update.\n await this.runMirror();\n }\n await this.mirrorPromise.catch(() => undefined);\n if (this.storeUnsubscribe) {\n this.storeUnsubscribe();\n this.storeUnsubscribe = undefined;\n }\n this.store.dispose();\n this.changeListeners.clear();\n this.bundler = undefined;\n }\n\n /**\n * Force the debounced mirror to run now and wait for it to finish.\n * Surfaces the executor error if the mirror failed — callers wrap in\n * try/catch (or .rejects in tests) if they need to handle it.\n */\n async flush(): Promise<void> {\n if (this.mirrorTimer) {\n clearTimeout(this.mirrorTimer);\n this.mirrorTimer = undefined;\n await this.runMirror();\n }\n await this.mirrorPromise;\n }\n\n private scheduleMirror(): void {\n if (this.mirrorPending) {\n // A debounced write is already queued; the queued write reads the\n // latest store state when it fires, so subsequent mutations\n // coalesce for free.\n return;\n }\n this.mirrorPromise = new Promise<void>((resolve, reject) => {\n this.mirrorPending = { resolve, reject };\n });\n this.mirrorTimer = setTimeout(() => {\n this.mirrorTimer = undefined;\n void this.runMirror();\n }, this.mirrorDebounceMs);\n this.mirrorTimer.unref();\n }\n\n private async runMirror(): Promise<void> {\n const pending = this.mirrorPending;\n this.mirrorPending = undefined;\n try {\n await this.applyMirror();\n pending?.resolve();\n } catch (err) {\n pending?.reject(err);\n this.logger?.error('[mcp-bundler/manager] mirror write failed', { err: errMsg(err) });\n }\n }\n\n /**\n * Compute the diff between this manager's owned set and what the store\n * declares now, then apply the openclaw config delta. Foreign keys\n * (entries in openclaw.json#mcp.servers.* not in our store) are\n * preserved — we only touch the names we previously claimed.\n */\n private async applyMirror(): Promise<void> {\n let previousOwned: string[] = [];\n let desiredOwned: string[] = [];\n let batch: { path: string; value: unknown }[] = [];\n this.store.update((cur) => {\n previousOwned = cur._ownedOpenclawKeys.slice();\n const entries = Object.entries(cur.servers);\n desiredOwned = entries.map(([id]) => id).sort();\n batch = entries.flatMap(([id, entry]) => renderEntryToBatch(id, entry));\n return { ...cur, _ownedOpenclawKeys: desiredOwned };\n });\n await this.executor.setBatch(batch);\n const toUnset = previousOwned.filter((k) => !desiredOwned.includes(k));\n for (const id of toUnset) {\n try {\n await this.executor.unset(`mcp.servers.${id}`);\n } catch (err) {\n this.logger?.warn('[mcp-bundler/manager] mirror unset failed (continuing)', {\n err: errMsg(err),\n key: `mcp.servers.${id}`,\n });\n }\n }\n if (this.bundler) {\n try {\n await this.reconcileBundler();\n } catch (err) {\n this.logger?.warn('[mcp-bundler/manager] post-mirror reconcile failed', { err: errMsg(err) });\n }\n }\n }\n\n private async reconcileBundler(): Promise<void> {\n if (!this.bundler) return;\n const snap = this.store.read();\n const servers: Record<string, McpServerConfig> = {};\n for (const [id, entry] of Object.entries(snap.servers)) {\n servers[id] = toServerConfig(entry);\n }\n await this.bundler.reconcile(servers);\n }\n\n private fireChange(): void {\n for (const cb of this.changeListeners) {\n try {\n cb();\n } catch (err) {\n this.logger?.warn('[mcp-bundler/manager] onChange listener threw', { err: errMsg(err) });\n }\n }\n }\n}\n\n/**\n * Render one store entry as the set of dotted-path batch entries that\n * `openclaw config set --batch-json` expects.\n *\n * Mirrors the format the integrations applier already emits today so the\n * runtime sees the same shape regardless of which writer produced it.\n */\nfunction renderEntryToBatch(id: string, entry: StoredServerEntry): { path: string; value: unknown }[] {\n const prefix = `mcp.servers.${id}`;\n const out: { path: string; value: unknown }[] = [];\n if (entry.transport === 'stdio') {\n out.push({ path: `${prefix}.command`, value: entry.command });\n if (entry.args && entry.args.length > 0) out.push({ path: `${prefix}.args`, value: entry.args });\n if (entry.env) {\n for (const [k, v] of Object.entries(entry.env)) {\n out.push({ path: `${prefix}.env.${k}`, value: v });\n }\n }\n if (entry.cwd) out.push({ path: `${prefix}.cwd`, value: entry.cwd });\n return out;\n }\n out.push({ path: `${prefix}.url`, value: entry.url });\n out.push({ path: `${prefix}.transport`, value: entry.transport });\n if (entry.headers) {\n for (const [k, v] of Object.entries(entry.headers)) {\n out.push({ path: `${prefix}.headers.${k}`, value: v });\n }\n }\n if (entry.connectionTimeoutMs !== undefined) {\n out.push({ path: `${prefix}.connectionTimeoutMs`, value: entry.connectionTimeoutMs });\n }\n return out;\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Indexed access on `Record<string, T>` returns `T` (not `T | undefined`)\n * unless `noUncheckedIndexedAccess` is set in tsconfig. These helpers\n * make the optional-ness explicit so the lint rules that hate\n * always-truthy conditionals stop firing on real lookups.\n */\nfunction lookupEntry(\n servers: Record<string, StoredServerEntry>,\n id: string,\n): StoredServerEntry | undefined {\n return Object.hasOwn(servers, id) ? servers[id] : undefined;\n}\n\nfunction lookupAddedAt(servers: Record<string, StoredServerEntry>, id: string): string | undefined {\n const entry = lookupEntry(servers, id);\n return entry ? entry.addedAt : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["errMsg"],"sources":["../src/tool-naming.ts","../src/connection.ts","../src/bundler.ts","../src/store.ts","../src/manager.ts"],"sourcesContent":["/**\n * Tool name sanitization and collision handling.\n *\n * OpenClaw constraint: tool names must match `[A-Za-z0-9_-]` and be ≤64 chars.\n * Pattern mirrored from `openclaw/src/agents/pi-bundle-mcp-names.ts`.\n *\n * Strategy: prefix every tool with its server name (`{server}__{tool}`),\n * sanitize disallowed chars to `_`, truncate, then suffix-disambiguate\n * (`-2`, `-3`, ...) on collision.\n */\n\nconst DISALLOWED = /[^A-Za-z0-9_-]/g;\nconst MAX_LEN = 64;\nconst SEPARATOR = '__';\n\nexport function sanitizeNameSegment(value: string): string {\n return value.replace(DISALLOWED, '_');\n}\n\nexport function buildNamespacedToolName(server: string, tool: string): string {\n const base = `${sanitizeNameSegment(server)}${SEPARATOR}${sanitizeNameSegment(tool)}`;\n if (base.length <= MAX_LEN) return base;\n // Truncate from the tool side first to keep the server prefix intact.\n const reservedForServer = sanitizeNameSegment(server).length + SEPARATOR.length;\n const toolBudget = Math.max(1, MAX_LEN - reservedForServer);\n return `${sanitizeNameSegment(server)}${SEPARATOR}${sanitizeNameSegment(tool).slice(0, toolBudget)}`;\n}\n\n/**\n * Disambiguate a candidate name against an existing set by appending `-2`, `-3`, etc.\n * Mutates nothing; returns the chosen name. Caller is responsible for inserting it\n * into the set.\n */\nexport function disambiguateAgainst(candidate: string, taken: ReadonlySet<string>): string {\n if (!taken.has(candidate)) return candidate;\n for (let i = 2; i < 1000; i += 1) {\n const suffix = `-${i.toString()}`;\n const room = MAX_LEN - suffix.length;\n const trimmed = candidate.length > room ? candidate.slice(0, room) : candidate;\n const next = `${trimmed}${suffix}`;\n if (!taken.has(next)) return next;\n }\n // Pathological: 998 collisions. Fall back to a deterministic-ish hash.\n return `${candidate.slice(0, MAX_LEN - 6)}-x${(taken.size % 1000).toString().padStart(3, '0')}`;\n}\n","import type { Logger, McpServerConfig, McpToolDescriptor, McpToolCallResult, StdioServerConfig } from './types.js';\nimport { buildNamespacedToolName } from './tool-naming.js';\n\n/** Env keys OpenClaw rejects from stdio MCP env blocks. Filter them out before spawning. */\nexport const STDIO_ENV_DENYLIST = new Set([\n 'NODE_OPTIONS',\n 'PYTHONSTARTUP',\n 'PYTHONPATH',\n 'PERL5OPT',\n 'RUBYOPT',\n 'SHELLOPTS',\n 'PS4',\n]);\n\nexport function sanitizeStdioEnv(env: Record<string, string> | undefined): Record<string, string> {\n if (!env) return {};\n const safe: Record<string, string> = {};\n for (const [k, v] of Object.entries(env)) {\n if (STDIO_ENV_DENYLIST.has(k)) continue;\n safe[k] = v;\n }\n return safe;\n}\n\nexport interface ConnectionDeps {\n /**\n * Factory for an MCP Client connected to the given config. Injected so tests\n * can mock without spawning real processes. In production this wraps\n * `@modelcontextprotocol/sdk/client`.\n */\n connect: (server: McpServerConfig) => Promise<McpClientHandle>;\n}\n\n/**\n * Minimal interface our connection layer needs from an MCP client. Mirrors\n * the @modelcontextprotocol/sdk Client surface but kept narrow so we can\n * mock cleanly in tests.\n */\nexport interface McpClientHandle {\n listTools(): Promise<{ name: string; description?: string; inputSchema: Record<string, unknown> }[]>;\n callTool(name: string, args: unknown, opts?: { signal?: AbortSignal }): Promise<McpToolCallResult>;\n close(): Promise<void>;\n}\n\n/**\n * One Connection per MCP server. Owns lifecycle (lazy-spawn, close, refresh-lock).\n * Refresh-lock pattern adapted from AIWerk `index.ts:219-250` — prevents\n * reconnect + `notifications/tools/list_changed` race.\n */\nexport class Connection {\n readonly name: string;\n readonly config: McpServerConfig;\n private readonly deps: ConnectionDeps;\n private readonly logger: Logger | undefined;\n\n private client: McpClientHandle | undefined;\n private tools: McpToolDescriptor[] = [];\n private connectInFlight: Promise<void> | undefined;\n private refreshInFlight = false;\n private refreshQueued = false;\n private lastUsedAt = Date.now();\n\n constructor(params: { name: string; config: McpServerConfig; deps: ConnectionDeps; logger?: Logger }) {\n this.name = params.name;\n this.config = params.config;\n this.deps = params.deps;\n this.logger = params.logger;\n }\n\n /** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */\n snapshotTools(): McpToolDescriptor[] {\n return this.tools;\n }\n\n /** Whether an MCP child process / remote connection has been established. */\n isConnected(): boolean {\n return this.client !== undefined;\n }\n\n /** Idle timestamp for reaping. */\n idleSinceMs(): number {\n return Date.now() - this.lastUsedAt;\n }\n\n /**\n * Lazy connect + tool discovery. Safe to call concurrently; in-flight\n * connects coalesce.\n */\n async ensureConnected(): Promise<void> {\n if (this.client) return;\n if (this.connectInFlight) return this.connectInFlight;\n this.connectInFlight = this.connectAndDiscover().finally(() => {\n this.connectInFlight = undefined;\n });\n return this.connectInFlight;\n }\n\n private async connectAndDiscover(): Promise<void> {\n const safeConfig = 'command' in this.config\n ? ({ ...this.config, env: sanitizeStdioEnv(this.config.env) } satisfies StdioServerConfig)\n : this.config;\n this.logger?.debug(`[mcp-bundler] connecting server \"${this.name}\"`);\n const client = await this.deps.connect(safeConfig);\n try {\n const advertised = await client.listTools();\n this.client = client;\n this.tools = advertised.map((t) => ({\n prefixed: buildNamespacedToolName(this.name, t.name),\n server: this.name,\n original: t.name,\n label: (t.description ?? t.name).slice(0, 80),\n description: t.description ?? '',\n parameters: t.inputSchema,\n }));\n this.lastUsedAt = Date.now();\n this.logger?.info(`[mcp-bundler] server \"${this.name}\" connected, ${this.tools.length.toString()} tool(s)`);\n } catch (err) {\n await client.close().catch(() => undefined);\n throw err;\n }\n }\n\n /**\n * Re-discover tools. Used on reconnect or `tools/list_changed` notification.\n * Refresh-lock collapses concurrent refreshes; if one is in flight, the next\n * is queued (max 1 queued, since N>1 queued provides no extra freshness).\n */\n async refresh(): Promise<void> {\n if (!this.client) return this.ensureConnected();\n if (this.refreshInFlight) {\n this.refreshQueued = true;\n return;\n }\n this.refreshInFlight = true;\n try {\n const advertised = await this.client.listTools();\n this.tools = advertised.map((t) => ({\n prefixed: buildNamespacedToolName(this.name, t.name),\n server: this.name,\n original: t.name,\n label: (t.description ?? t.name).slice(0, 80),\n description: t.description ?? '',\n parameters: t.inputSchema,\n }));\n this.logger?.debug(`[mcp-bundler] server \"${this.name}\" refreshed, ${this.tools.length.toString()} tool(s)`);\n } finally {\n this.refreshInFlight = false;\n if (this.refreshQueued) {\n this.refreshQueued = false;\n // Trigger one more refresh; do not await so caller isn't blocked on cascading refreshes.\n void this.refresh().catch((err: unknown) => {\n this.logger?.warn(`[mcp-bundler] queued refresh for \"${this.name}\" failed`, { err: err instanceof Error ? err.message : String(err) });\n });\n }\n }\n }\n\n async callTool(originalName: string, args: unknown, signal?: AbortSignal): Promise<McpToolCallResult> {\n await this.ensureConnected();\n if (!this.client) throw new Error(`server \"${this.name}\" failed to connect`);\n this.lastUsedAt = Date.now();\n return this.client.callTool(originalName, args, signal ? { signal } : undefined);\n }\n\n /**\n * Close the underlying transport. Idempotent. If a connect is in flight\n * (warmup racing with reconcile-removal), wait for it to settle and then\n * close the client it produced — otherwise the child process is orphaned.\n */\n async close(): Promise<void> {\n if (this.connectInFlight) {\n await this.connectInFlight.catch(() => undefined);\n }\n const c = this.client;\n this.client = undefined;\n this.tools = [];\n if (c) await c.close().catch((err: unknown) => {\n this.logger?.warn(`[mcp-bundler] close error for \"${this.name}\"`, { err: err instanceof Error ? err.message : String(err) });\n });\n }\n\n /**\n * Stable hash of the config for diff detection in `reconcile`.\n * Two configs with the same hash are equivalent (no restart needed).\n */\n configFingerprint(): string {\n return JSON.stringify(this.config);\n }\n}\n\n/**\n * Build the production `connect` factory using the official MCP SDK.\n * Kept in a separate function so tests can substitute a mock without\n * pulling the SDK into the test bundle.\n */\nexport async function defaultConnect(server: McpServerConfig): Promise<McpClientHandle> {\n const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');\n const client = new Client({ name: 'alfe-mcp-bundler', version: '0.0.0' }, {});\n\n if ('command' in server) {\n const stdio = server;\n const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');\n const transport = new StdioClientTransport({\n command: stdio.command,\n args: stdio.args ?? [],\n env: { ...sanitizeStdioEnv(stdio.env) } as Record<string, string>,\n cwd: stdio.cwd,\n });\n await client.connect(transport);\n } else {\n const remote = server;\n if (remote.transport === 'streamable-http') {\n const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js');\n const transport = new StreamableHTTPClientTransport(new URL(remote.url), {\n requestInit: { headers: remote.headers ?? {} },\n });\n await client.connect(transport);\n } else {\n // SSE is deprecated in newer MCP SDK in favor of streamable-http, but\n // some servers still only support SSE — keep transport for back-compat.\n /* eslint-disable @typescript-eslint/no-deprecated */\n const { SSEClientTransport } = await import('@modelcontextprotocol/sdk/client/sse.js');\n const transport = new SSEClientTransport(new URL(remote.url), {\n requestInit: { headers: remote.headers ?? {} },\n });\n /* eslint-enable @typescript-eslint/no-deprecated */\n await client.connect(transport);\n }\n }\n\n return {\n async listTools() {\n const result = await client.listTools();\n return result.tools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as Record<string, unknown>,\n }));\n },\n async callTool(name, args, opts) {\n return (await client.callTool({ name, arguments: args as Record<string, unknown> | undefined }, undefined, opts)) as McpToolCallResult;\n },\n async close() {\n await client.close();\n },\n };\n}\n","import { Connection, defaultConnect, type ConnectionDeps } from './connection.js';\nimport { disambiguateAgainst } from './tool-naming.js';\nimport type {\n BundlerOptions,\n Logger,\n McpServerConfig,\n McpToolCallResult,\n McpToolDescriptor,\n ReconcileDiff,\n} from './types.js';\n\nconst DEFAULT_IDLE_TTL_MS = 10 * 60 * 1000;\nconst DEFAULT_IDLE_SWEEP_INTERVAL_MS = 60 * 1000;\n\n/**\n * Provider-agnostic MCP server bundler. Holds N MCP server connections,\n * exposes a unified namespaced tool catalog, and routes calls to the right\n * server.\n *\n * Designed to be embedded in any host (OpenClaw plugin, AI proxy, Lambda).\n * Public surface is intentionally synchronous where the host needs sync\n * (snapshot, listTools), async only where I/O is unavoidable.\n */\nexport class McpBundler {\n private readonly logger: Logger | undefined;\n private readonly connections = new Map<string, Connection>();\n private readonly idleTtlMs: number;\n private readonly idleSweepIntervalMs: number;\n private idleSweepTimer: ReturnType<typeof setInterval> | undefined;\n private readonly deps: ConnectionDeps;\n private disposed = false;\n // Serialize reconcile() so concurrent callers (multiple plugin tool factory\n // ticks within the same ms) don't interleave and orphan Connections, leaking\n // child processes. Acquired via a chain-of-promises latch.\n private reconcileLatch: Promise<unknown> = Promise.resolve();\n\n constructor(opts: BundlerOptions = {}, deps?: ConnectionDeps) {\n this.logger = opts.logger;\n this.idleTtlMs = opts.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;\n this.idleSweepIntervalMs = opts.idleSweepIntervalMs ?? DEFAULT_IDLE_SWEEP_INTERVAL_MS;\n this.deps = deps ?? { connect: defaultConnect };\n if (this.idleTtlMs > 0) this.startIdleSweep();\n }\n\n /**\n * Diff `desired` against current connections, spawn newcomers, dispose\n * removals, hot-restart on config change. Pull-based — call whenever the\n * host's config snapshot may have changed. Cheap if no diff.\n *\n * Lazy: newly-added servers are NOT eagerly connected; they connect on the\n * first `callTool()` (or first `listTools()` after `forceDiscover()`).\n * This avoids paying spawn cost for servers the agent never uses.\n */\n async reconcile(desired: Record<string, McpServerConfig>): Promise<ReconcileDiff> {\n if (this.disposed) throw new Error('McpBundler: disposed');\n // Serialize reconciles. Caller awaits its slot; in-flight reconciles run\n // in declaration order. Errors don't poison the latch — `.catch` swallows\n // for chaining, the actual error rejects the awaited slot.\n const slot = this.reconcileLatch.then(async () => this.doReconcile(desired));\n this.reconcileLatch = slot.catch(() => undefined);\n return slot;\n }\n\n private async doReconcile(desired: Record<string, McpServerConfig>): Promise<ReconcileDiff> {\n if (this.disposed) throw new Error('McpBundler: disposed');\n const desiredNames = new Set(Object.keys(desired));\n const currentNames = new Set(this.connections.keys());\n\n const added: string[] = [];\n const removed: string[] = [];\n const changed: string[] = [];\n const unchanged: string[] = [];\n\n // Removals: dispose connections no longer in desired set.\n for (const name of currentNames) {\n if (!desiredNames.has(name)) {\n const conn = this.connections.get(name);\n this.connections.delete(name);\n if (conn) await conn.close();\n removed.push(name);\n }\n }\n\n // Additions and changes.\n for (const [name, config] of Object.entries(desired)) {\n const existing = this.connections.get(name);\n if (!existing) {\n this.connections.set(name, new Connection({ name, config, deps: this.deps, logger: this.logger }));\n added.push(name);\n continue;\n }\n const nextFingerprint = JSON.stringify(config);\n if (existing.configFingerprint() !== nextFingerprint) {\n // Config changed — close old, replace with fresh (lazy reconnect).\n await existing.close();\n this.connections.set(name, new Connection({ name, config, deps: this.deps, logger: this.logger }));\n changed.push(name);\n } else {\n unchanged.push(name);\n }\n }\n\n if (added.length || removed.length || changed.length) {\n this.logger?.info('[mcp-bundler] reconciled', {\n added: added.length,\n removed: removed.length,\n changed: changed.length,\n unchanged: unchanged.length,\n });\n }\n return { added, removed, changed, unchanged };\n }\n\n /**\n * Synchronous snapshot of all currently-known tools across connected servers.\n * Servers that have not connected yet contribute nothing. Intended for use\n * inside OpenClaw's plugin tool factory which must be sync.\n *\n * Tool names are namespaced and disambiguated (suffix `-2`, `-3` on collision)\n * so cross-server name clashes never produce duplicate registrations.\n */\n listTools(): McpToolDescriptor[] {\n const seen = new Set<string>();\n const out: McpToolDescriptor[] = [];\n for (const conn of this.connections.values()) {\n for (const tool of conn.snapshotTools()) {\n const finalName = disambiguateAgainst(tool.prefixed, seen);\n seen.add(finalName);\n out.push(finalName === tool.prefixed ? tool : { ...tool, prefixed: finalName });\n }\n }\n return out;\n }\n\n /**\n * Eagerly connect to every configured server and discover tools. Used by\n * hosts that want a hot list rather than the lazy default. Errors are\n * swallowed per-server (logged), so one bad server doesn't fail the batch.\n */\n async warmup(): Promise<void> {\n if (this.disposed) return;\n await Promise.allSettled(\n Array.from(this.connections.values()).map(async (conn) => {\n try {\n await conn.ensureConnected();\n } catch (err) {\n this.logger?.warn(`[mcp-bundler] warmup failed for \"${conn.name}\"`, {\n err: err instanceof Error ? err.message : String(err),\n });\n }\n }),\n );\n }\n\n /**\n * Invoke a tool by its namespaced name. Routes to the originating server.\n * Errors are returned as `{ isError: true, content: [...] }` so a failing\n * tool doesn't crash the host.\n */\n async callTool(prefixed: string, args: unknown, signal?: AbortSignal): Promise<McpToolCallResult> {\n if (this.disposed) {\n return { isError: true, content: [{ type: 'text', text: 'mcp-bundler disposed' }] };\n }\n const route = this.routeToolName(prefixed);\n if (!route) {\n return {\n isError: true,\n content: [{ type: 'text', text: `unknown tool: ${prefixed}` }],\n };\n }\n try {\n return await route.connection.callTool(route.original, args, signal);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n this.logger?.error(`[mcp-bundler] tool call failed for \"${prefixed}\"`, { err: msg });\n return {\n isError: true,\n content: [{ type: 'text', text: `tool ${prefixed} failed: ${msg}` }],\n };\n }\n }\n\n /**\n * Resolve a namespaced tool name back to its server connection and original\n * tool name. Returns undefined if the tool is not currently advertised.\n */\n private routeToolName(prefixed: string): { connection: Connection; original: string } | undefined {\n for (const conn of this.connections.values()) {\n for (const tool of conn.snapshotTools()) {\n if (tool.prefixed === prefixed) return { connection: conn, original: tool.original };\n }\n }\n return undefined;\n }\n\n /**\n * Tear down all connections and stop background tasks. Idempotent.\n * Call from `registerRuntimeLifecycle({ cleanup })` in the host plugin.\n */\n async dispose(): Promise<void> {\n if (this.disposed) return;\n this.disposed = true;\n if (this.idleSweepTimer) {\n clearInterval(this.idleSweepTimer);\n this.idleSweepTimer = undefined;\n }\n await Promise.allSettled(Array.from(this.connections.values()).map((c) => c.close()));\n this.connections.clear();\n }\n\n private startIdleSweep(): void {\n this.idleSweepTimer = setInterval(() => {\n void this.sweepIdle().catch((err: unknown) => {\n this.logger?.warn('[mcp-bundler] idle sweep error', {\n err: err instanceof Error ? err.message : String(err),\n });\n });\n }, this.idleSweepIntervalMs);\n // Don't keep the host process alive just for the sweep.\n if (typeof this.idleSweepTimer === 'object' && 'unref' in this.idleSweepTimer) {\n (this.idleSweepTimer as { unref: () => void }).unref();\n }\n }\n\n private async sweepIdle(): Promise<void> {\n if (this.idleTtlMs <= 0) return;\n const targets: Connection[] = [];\n for (const conn of this.connections.values()) {\n if (conn.isConnected() && conn.idleSinceMs() > this.idleTtlMs) {\n targets.push(conn);\n }\n }\n if (targets.length === 0) return;\n this.logger?.debug(`[mcp-bundler] reaping ${targets.length.toString()} idle server(s)`);\n await Promise.allSettled(targets.map((c) => c.close()));\n }\n}\n","import {\n closeSync,\n existsSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n statSync,\n unlinkSync,\n watch,\n writeFileSync,\n type FSWatcher,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { Logger, McpServerConfig, StdioServerConfig, RemoteServerConfig, McpTransportKind } from './types.js';\n\n/**\n * Where a server entry came from. Used by `removeServersByOwner` so an\n * integration uninstall can drop only its own entries without touching\n * `cli`-owned (e.g. `alfe-platform`) or `manual`-owned (user-added) ones.\n */\nexport type ServerOwner = 'cli' | `integration:${string}` | 'manual';\n\ninterface StoredServerCommon {\n /** Where the entry came from — controls bulk-removal semantics. */\n owner: ServerOwner;\n /** ISO timestamp of first registration; preserved across updates. */\n addedAt: string;\n /** Optional semver of the providing package (e.g. `@alfe.ai/mcp-server` for `alfe-platform`). Used for drift detection on CLI upgrade. */\n version?: string;\n}\n\nexport type StoredServerEntry =\n | (StoredServerCommon & { transport: 'stdio' } & StdioServerConfig)\n | (StoredServerCommon & { transport: 'sse' | 'streamable-http' } & RemoteServerConfig);\n\nexport interface StoreSchema {\n servers: Record<string, StoredServerEntry>;\n config: {\n sessionIdleTtlMs?: number;\n };\n /**\n * Server names this manager has written into `openclaw.json#mcp.servers.*`.\n * Used to compute the mirror-write diff without re-reading openclaw.json\n * (which would be a second source of truth). Foreign keys not listed here\n * are preserved across mirror writes.\n */\n _ownedOpenclawKeys: string[];\n}\n\nconst DEFAULT_STORE_DIR = join(homedir(), '.alfe', 'mcp');\nconst DEFAULT_STORE_PATH = join(DEFAULT_STORE_DIR, 'servers.json');\n\n/** Inter-process lock tunings — exported as constants so tests can override. */\nconst LOCK_WAIT_MS = 5_000;\nconst LOCK_RETRY_INTERVAL_MS = 25;\nconst LOCK_STALE_MS = 10_000;\n\nexport interface StoreOptions {\n /** Absolute path to the store file. Defaults to `~/.alfe/mcp/servers.json`. */\n path?: string;\n logger?: Logger;\n}\n\n/**\n * On-disk source of truth for the bundler's configured servers.\n *\n * Mutations go through `update()` (read-modify-write with atomic\n * temp+rename) so a concurrent writer (e.g. two `alfe mcp add` invocations\n * racing) can't lose data — the second writer reads the first's state.\n *\n * Schema is owner-tagged so `removeServersByOwner` can implement\n * integration uninstall without touching CLI-owned or manual entries.\n */\nexport class Store {\n private readonly storePath: string;\n private readonly logger?: Logger;\n private watcher?: FSWatcher;\n private watcherListeners = new Set<() => void>();\n private rewatchTimer?: NodeJS.Timeout;\n\n constructor(opts: StoreOptions = {}) {\n this.storePath = opts.path ?? DEFAULT_STORE_PATH;\n this.logger = opts.logger;\n }\n\n get path(): string {\n return this.storePath;\n }\n\n read(): StoreSchema {\n if (!existsSync(this.storePath)) return cloneEmpty();\n try {\n const raw = readFileSync(this.storePath, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n return normalize(parsed);\n } catch (err) {\n this.logger?.warn('[mcp-bundler/store] failed to read store; returning empty', {\n err: errMsg(err),\n path: this.storePath,\n });\n return cloneEmpty();\n }\n }\n\n /**\n * Read-modify-write with atomic temp+rename, guarded by an\n * inter-process lock file. Caller passes a pure function that\n * produces the next state; this serialises the mutation to disk in\n * one rename, which is atomic on POSIX and on Windows when the\n * target path is on the same volume.\n *\n * The lock guards the read-then-rename window so two processes\n * (e.g. two `alfe mcp add` shells, or the CLI racing the daemon)\n * can't drop each other's writes. The lock file is at\n * `<storePath>.lock`; stale locks (older than `LOCK_STALE_MS`) are\n * stolen so a crashed writer doesn't wedge the store.\n *\n * Pure-function shape (instead of a `read()` then `write(next)`\n * pair) intentionally — it keeps the read-modify-write contract\n * local to each caller so two updates back-to-back never see each\n * other's partial state.\n */\n update(fn: (cur: StoreSchema) => StoreSchema): StoreSchema {\n mkdirSync(dirname(this.storePath), { recursive: true });\n const release = this.acquireLock();\n try {\n const cur = this.read();\n const next = fn(cur);\n const tempPath = `${this.storePath}.${String(process.pid)}.${String(Date.now())}.tmp`;\n writeFileSync(tempPath, JSON.stringify(next, null, 2), { encoding: 'utf8', mode: 0o600 });\n try {\n renameSync(tempPath, this.storePath);\n } catch (err) {\n try {\n unlinkSync(tempPath);\n } catch {\n // Best-effort cleanup; the temp file's name has the pid + timestamp\n // so an orphan won't collide with future writers.\n }\n throw err;\n }\n return next;\n } finally {\n release();\n }\n }\n\n /**\n * Acquire an inter-process file lock by atomically creating a\n * sentinel via `openSync(lockPath, 'wx')`. Spins with bounded\n * backoff up to `LOCK_WAIT_MS`. If the lock file is older than\n * `LOCK_STALE_MS` it's assumed orphaned (writer crashed mid-update)\n * and stolen — the write window is sub-second in practice, so\n * holding the lock for >5s means something went wrong.\n *\n * Returns the release function. Single-process callers are\n * unaffected — re-entering the same process spins briefly while\n * the prior call's `finally` runs.\n */\n private acquireLock(): () => void {\n const lockPath = `${this.storePath}.lock`;\n const deadline = Date.now() + LOCK_WAIT_MS;\n let fd = -1;\n for (;;) {\n try {\n fd = openSync(lockPath, 'wx', 0o600);\n break;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== 'EEXIST') throw err;\n if (this.lockIsStale(lockPath)) {\n try {\n unlinkSync(lockPath);\n } catch {\n // Another process may have just released it — fall through and retry.\n }\n continue;\n }\n if (Date.now() >= deadline) {\n throw new Error(\n `Store.update: timed out waiting for ${lockPath} (held by another writer or stale lock)`,\n );\n }\n // Synchronous spin — the lock window is sub-second under\n // normal load; busy-waiting briefly is simpler than wiring\n // async/await through every `update()` caller.\n const sleepUntil = Date.now() + LOCK_RETRY_INTERVAL_MS;\n while (Date.now() < sleepUntil) { /* spin */ }\n }\n }\n const held = fd;\n return () => {\n try {\n closeSync(held);\n } catch {\n // ignore — the unlink is what releases the lock for the next writer.\n }\n try {\n unlinkSync(lockPath);\n } catch {\n // ignore — already unlinked or stolen by a stale-lock breaker.\n }\n };\n }\n\n private lockIsStale(lockPath: string): boolean {\n try {\n const st = statSync(lockPath);\n return Date.now() - st.mtimeMs > LOCK_STALE_MS;\n } catch {\n return false;\n }\n }\n\n /**\n * Watch the store file for external changes (e.g. another `alfe mcp add`\n * shelling out from a separate process). Returns an unsubscribe fn.\n *\n * Coalesces bursts via a 50 ms debounce — editors and atomic-rename\n * writers commonly fire multiple events per logical save.\n */\n watch(cb: () => void): () => void {\n this.watcherListeners.add(cb);\n this.ensureWatcher();\n return () => {\n this.watcherListeners.delete(cb);\n if (this.watcherListeners.size === 0) this.disposeWatcher();\n };\n }\n\n dispose(): void {\n this.watcherListeners.clear();\n this.disposeWatcher();\n }\n\n private ensureWatcher(): void {\n if (this.watcher) return;\n mkdirSync(dirname(this.storePath), { recursive: true });\n // Some platforms / atomic renames make a per-file watch flaky after\n // a rename; watching the parent directory and filtering by basename\n // is more robust.\n const dir = dirname(this.storePath);\n const basename = this.storePath.slice(dir.length + 1);\n let pending: NodeJS.Timeout | undefined;\n const fire = (): void => {\n pending = undefined;\n for (const cb of this.watcherListeners) {\n try {\n cb();\n } catch (err) {\n this.logger?.warn('[mcp-bundler/store] watcher listener threw', { err: errMsg(err) });\n }\n }\n };\n try {\n this.watcher = watch(dir, (_event, fn) => {\n if (fn !== basename) return;\n if (pending) clearTimeout(pending);\n pending = setTimeout(fire, 50);\n });\n this.watcher.on('error', (err) => {\n this.logger?.warn('[mcp-bundler/store] watcher error; retrying in 1s', { err: errMsg(err) });\n this.disposeWatcher();\n if (!this.rewatchTimer && this.watcherListeners.size > 0) {\n this.rewatchTimer = setTimeout(() => {\n this.rewatchTimer = undefined;\n this.ensureWatcher();\n }, 1000);\n this.rewatchTimer.unref();\n }\n });\n } catch (err) {\n this.logger?.warn('[mcp-bundler/store] failed to start watcher', { err: errMsg(err) });\n }\n }\n\n private disposeWatcher(): void {\n if (this.watcher) {\n try {\n this.watcher.close();\n } catch {\n // close throws on already-disposed watchers; ignore.\n }\n this.watcher = undefined;\n }\n if (this.rewatchTimer) {\n clearTimeout(this.rewatchTimer);\n this.rewatchTimer = undefined;\n }\n }\n}\n\nexport function defaultStorePath(): string {\n return DEFAULT_STORE_PATH;\n}\n\n/** Pull the runtime config (transport + transport-specific fields) out of a stored entry. */\nexport function toServerConfig(entry: StoredServerEntry): McpServerConfig {\n if (entry.transport === 'stdio') {\n const { command, args, env, cwd } = entry;\n const cfg: StdioServerConfig = { command };\n if (args) cfg.args = args;\n if (env) cfg.env = env;\n if (cwd) cfg.cwd = cwd;\n return cfg;\n }\n const { url, transport, headers, connectionTimeoutMs } = entry;\n const cfg: RemoteServerConfig = { url, transport };\n if (headers) cfg.headers = headers;\n if (connectionTimeoutMs !== undefined) cfg.connectionTimeoutMs = connectionTimeoutMs;\n return cfg;\n}\n\n/** Build a stored entry from a runtime config + ownership metadata. */\nexport function toStoredEntry(\n config: McpServerConfig,\n meta: { owner: ServerOwner; transport?: McpTransportKind; version?: string; addedAt?: string },\n): StoredServerEntry {\n const addedAt = meta.addedAt ?? new Date().toISOString();\n if ('command' in config) {\n return {\n transport: 'stdio',\n owner: meta.owner,\n addedAt,\n ...(meta.version !== undefined ? { version: meta.version } : {}),\n ...config,\n };\n }\n const transport = meta.transport ?? config.transport ?? 'sse';\n if (transport === 'stdio') {\n throw new Error('toStoredEntry: transport=stdio specified but config is remote-shaped');\n }\n return {\n transport,\n owner: meta.owner,\n addedAt,\n ...(meta.version !== undefined ? { version: meta.version } : {}),\n ...config,\n };\n}\n\nfunction cloneEmpty(): StoreSchema {\n return { servers: {}, config: {}, _ownedOpenclawKeys: [] };\n}\n\nfunction normalize(raw: unknown): StoreSchema {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return cloneEmpty();\n const r = raw as Partial<StoreSchema>;\n return {\n servers: r.servers && typeof r.servers === 'object' ? r.servers : {},\n config: r.config && typeof r.config === 'object' ? r.config : {},\n _ownedOpenclawKeys: Array.isArray(r._ownedOpenclawKeys) ? r._ownedOpenclawKeys.slice() : [],\n };\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n","import type { McpBundler } from './bundler.js';\nimport { Store, toServerConfig, toStoredEntry, type ServerOwner, type StoredServerEntry } from './store.js';\nimport type { Logger, McpServerConfig, McpTransportKind } from './types.js';\n\nexport interface ManagerOptions {\n /** Pre-constructed store. If omitted, one is built with default options. */\n store?: Store;\n logger?: Logger;\n}\n\nexport interface AddServerOptions {\n /** Required — flat-namespace key under the bundler store. */\n id: string;\n /** Marks ownership for bulk removal. Defaults to `manual`. */\n owner?: ServerOwner;\n /** Semver of the providing package; used for CLI version-drift detection. */\n version?: string;\n /** Explicit transport hint for remote configs. Defaults to inferring from `config`. */\n transport?: McpTransportKind;\n}\n\n/**\n * Bundler manager — owns the `~/.alfe/mcp/servers.json` store and surfaces a\n * small CRUD API the CLI and integration applier both call into.\n *\n * Single source of truth: every consumer (daemon-hosted bundler, CLI `alfe mcp\n * list`, integration uninstall) reads from this store. Openclaw.json is no\n * longer kept in sync — the daemon hosts the bundler children and the\n * openclaw plugin reaches them via IPC, so the openclaw.json mirror became\n * dead weight and an active source of duplicate spawning on claude-cli /\n * codex-cli backends.\n *\n * Call `loadIntoBundler(bundler)` once at daemon startup to wire the store\n * into a live `McpBundler` — subsequent store mutations (including those\n * landed by other processes via the file watcher) re-reconcile automatically.\n */\nexport class Manager {\n private readonly store: Store;\n private readonly logger?: Logger;\n private bundler?: McpBundler;\n private changeListeners = new Set<() => void>();\n private storeUnsubscribe?: () => void;\n\n constructor(opts: ManagerOptions = {}) {\n this.store = opts.store ?? new Store({ logger: opts.logger });\n this.logger = opts.logger;\n }\n\n /** Direct accessor — handy for tests and the CLI's `alfe mcp list`. */\n getStore(): Store {\n return this.store;\n }\n\n /**\n * Register or overwrite a server entry. Mutation lands in the store\n * synchronously; if a bundler has been attached via `loadIntoBundler`,\n * it gets re-reconciled in the background (errors logged, never\n * thrown — the store is the source of truth, the bundler is derived).\n */\n async addServer(config: McpServerConfig, opts: AddServerOptions): Promise<void> {\n if (!opts.id) throw new Error('Manager.addServer: id is required');\n const owner = opts.owner ?? 'manual';\n this.store.update((cur) => {\n const previousAddedAt = lookupAddedAt(cur.servers, opts.id);\n const entry = toStoredEntry(config, {\n owner,\n transport: opts.transport,\n version: opts.version,\n addedAt: previousAddedAt,\n });\n return {\n ...cur,\n servers: { ...cur.servers, [opts.id]: entry },\n };\n });\n this.scheduleBundlerReconcile();\n this.fireChange();\n return Promise.resolve();\n }\n\n /**\n * Remove a single server entry. No-op if the id isn't in the store.\n * Refuses to remove an entry whose owner doesn't match `expectedOwner`\n * when supplied — the CLI uses this to guard `alfe mcp remove` from\n * accidentally clobbering integration- or cli-owned entries.\n */\n removeServer(id: string, opts: { expectedOwner?: ServerOwner } = {}): Promise<boolean> {\n const current = this.store.read();\n const existing = lookupEntry(current.servers, id);\n if (!existing) return Promise.resolve(false);\n if (opts.expectedOwner && existing.owner !== opts.expectedOwner) {\n return Promise.reject(\n new Error(\n `Manager.removeServer: server \"${id}\" is owned by \"${existing.owner}\", not \"${opts.expectedOwner}\"`,\n ),\n );\n }\n this.store.update((cur) => ({\n ...cur,\n servers: Object.fromEntries(Object.entries(cur.servers).filter(([k]) => k !== id)),\n }));\n this.scheduleBundlerReconcile();\n this.fireChange();\n return Promise.resolve(true);\n }\n\n /** Drop every entry whose owner matches — used by integration uninstall. */\n async removeServersByOwner(owner: ServerOwner): Promise<string[]> {\n const removed: string[] = [];\n this.store.update((cur) => {\n const next: Record<string, StoredServerEntry> = {};\n for (const [id, entry] of Object.entries(cur.servers)) {\n if (entry.owner === owner) {\n removed.push(id);\n } else {\n next[id] = entry;\n }\n }\n if (removed.length === 0) return cur;\n return { ...cur, servers: next };\n });\n if (removed.length > 0) {\n this.scheduleBundlerReconcile();\n this.fireChange();\n }\n return Promise.resolve(removed);\n }\n\n /** Read-only snapshot for `alfe mcp list` and similar UIs. */\n listServers(): { id: string; entry: StoredServerEntry }[] {\n const snap = this.store.read();\n return Object.entries(snap.servers).map(([id, entry]) => ({ id, entry }));\n }\n\n /**\n * Push the current store contents into a bundler instance (which owns\n * connections / tools). Wires up a store watcher so external mutations\n * (e.g. another shell running `alfe mcp add`) re-reconcile.\n */\n async loadIntoBundler(bundler: McpBundler): Promise<void> {\n this.bundler = bundler;\n await this.reconcileBundler();\n this.storeUnsubscribe ??= this.store.watch(() => {\n void this.reconcileBundler().catch((err: unknown) => {\n this.logger?.warn('[mcp-bundler/manager] watcher reconcile failed', { err: errMsg(err) });\n });\n });\n }\n\n /** Subscribe to store mutations. Returns an unsubscribe fn. */\n onChange(cb: () => void): () => void {\n this.changeListeners.add(cb);\n return () => {\n this.changeListeners.delete(cb);\n };\n }\n\n /**\n * Detach from the bundler and stop watching the store. Safe to call\n * multiple times. Does not dispose the underlying `Store` so the\n * shared instance survives multi-manager environments (rare).\n */\n async dispose(): Promise<void> {\n if (this.storeUnsubscribe) {\n this.storeUnsubscribe();\n this.storeUnsubscribe = undefined;\n }\n this.store.dispose();\n this.changeListeners.clear();\n this.bundler = undefined;\n return Promise.resolve();\n }\n\n private scheduleBundlerReconcile(): void {\n if (!this.bundler) return;\n void this.reconcileBundler().catch((err: unknown) => {\n this.logger?.warn('[mcp-bundler/manager] bundler reconcile failed', { err: errMsg(err) });\n });\n }\n\n private async reconcileBundler(): Promise<void> {\n if (!this.bundler) return;\n const snap = this.store.read();\n const servers: Record<string, McpServerConfig> = {};\n for (const [id, entry] of Object.entries(snap.servers)) {\n servers[id] = toServerConfig(entry);\n }\n await this.bundler.reconcile(servers);\n }\n\n private fireChange(): void {\n for (const cb of this.changeListeners) {\n try {\n cb();\n } catch (err) {\n this.logger?.warn('[mcp-bundler/manager] onChange listener threw', { err: errMsg(err) });\n }\n }\n }\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Indexed access on `Record<string, T>` returns `T` (not `T | undefined`)\n * unless `noUncheckedIndexedAccess` is set in tsconfig. These helpers\n * make the optional-ness explicit so the lint rules that hate\n * always-truthy conditionals stop firing on real lookups.\n */\nfunction lookupEntry(\n servers: Record<string, StoredServerEntry>,\n id: string,\n): StoredServerEntry | undefined {\n return Object.hasOwn(servers, id) ? servers[id] : undefined;\n}\n\nfunction lookupAddedAt(servers: Record<string, StoredServerEntry>, id: string): string | undefined {\n const entry = lookupEntry(servers, id);\n return entry ? entry.addedAt : undefined;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/mcp-bundler",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Provider-agnostic MCP server bundler — connects to N MCP servers (stdio/SSE/streamable-http), aggregates their tools with namespacing, exposes a unified callable surface",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",