@alfe.ai/mcp-bundler 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -86,11 +86,15 @@ var Connection = class Connection {
86
86
  reconnectBlockedUntilMs = 0;
87
87
  static RECONNECT_BACKOFF_BASE_MS = 500;
88
88
  static RECONNECT_BACKOFF_MAX_MS = 3e4;
89
+ onUnexpectedClose;
90
+ onStderrLine;
89
91
  constructor(params) {
90
92
  this.name = params.name;
91
93
  this.config = params.config;
92
94
  this.deps = params.deps;
93
95
  this.logger = params.logger;
96
+ this.onUnexpectedClose = params.onUnexpectedClose;
97
+ this.onStderrLine = params.onStderrLine;
94
98
  }
95
99
  /** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */
96
100
  snapshotTools() {
@@ -123,7 +127,10 @@ var Connection = class Connection {
123
127
  env: sanitizeStdioEnv(this.config.env)
124
128
  } : this.config;
125
129
  this.logger?.debug(`[mcp-bundler] connecting server "${this.name}"`);
126
- const client = await this.deps.connect(safeConfig);
130
+ const client = await this.deps.connect(safeConfig, {
131
+ serverName: this.name,
132
+ onStderrLine: this.onStderrLine
133
+ });
127
134
  try {
128
135
  const advertised = await client.listTools();
129
136
  this.client = client;
@@ -161,6 +168,9 @@ var Connection = class Connection {
161
168
  this.logger?.warn(`[mcp-bundler] server "${this.name}" connection closed unexpectedly; will re-spawn on next use`);
162
169
  this.client = void 0;
163
170
  this.tools = [];
171
+ try {
172
+ this.onUnexpectedClose?.();
173
+ } catch {}
164
174
  }
165
175
  /**
166
176
  * Re-discover tools. Used on reconnect or `tools/list_changed` notification.
@@ -228,7 +238,7 @@ var Connection = class Connection {
228
238
  * Kept in a separate function so tests can substitute a mock without
229
239
  * pulling the SDK into the test bundle.
230
240
  */
231
- async function defaultConnect(server) {
241
+ async function defaultConnect(server, ctx) {
232
242
  const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
233
243
  const client = new Client({
234
244
  name: "alfe-mcp-bundler",
@@ -237,12 +247,27 @@ async function defaultConnect(server) {
237
247
  if ("command" in server) {
238
248
  const stdio = server;
239
249
  const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
250
+ const onStderrLine = ctx?.onStderrLine;
240
251
  const transport = new StdioClientTransport({
241
252
  command: stdio.command,
242
253
  args: stdio.args ?? [],
243
254
  env: { ...sanitizeStdioEnv(stdio.env) },
244
- cwd: stdio.cwd
255
+ cwd: stdio.cwd,
256
+ ...onStderrLine ? { stderr: "pipe" } : {}
245
257
  });
258
+ if (onStderrLine) {
259
+ let carry = "";
260
+ transport.stderr?.on("data", (chunk) => {
261
+ const parts = (carry + chunk.toString()).split("\n");
262
+ carry = parts.pop() ?? "";
263
+ for (const line of parts) {
264
+ if (line.trim() === "") continue;
265
+ try {
266
+ onStderrLine(line);
267
+ } catch {}
268
+ }
269
+ });
270
+ }
246
271
  await client.connect(transport);
247
272
  } else {
248
273
  const remote = server;
@@ -292,6 +317,11 @@ async function defaultConnect(server) {
292
317
  //#region src/bundler.ts
293
318
  const DEFAULT_IDLE_TTL_MS = 600 * 1e3;
294
319
  const DEFAULT_IDLE_SWEEP_INTERVAL_MS = 60 * 1e3;
320
+ /** First text content of an error result, for host error reporting. */
321
+ function extractErrorText(result) {
322
+ for (const item of result.content) if (item.type === "text" && typeof item.text === "string") return item.text.slice(0, 500);
323
+ return "(no error text)";
324
+ }
295
325
  /**
296
326
  * Provider-agnostic MCP server bundler. Holds N MCP server connections,
297
327
  * exposes a unified namespaced tool catalog, and routes calls to the right
@@ -308,6 +338,9 @@ var McpBundler = class {
308
338
  idleSweepIntervalMs;
309
339
  idleSweepTimer;
310
340
  deps;
341
+ onToolError;
342
+ onServerCrash;
343
+ onServerStderr;
311
344
  disposed = false;
312
345
  reconcileLatch = Promise.resolve();
313
346
  constructor(opts = {}, deps) {
@@ -315,8 +348,34 @@ var McpBundler = class {
315
348
  this.idleTtlMs = opts.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
316
349
  this.idleSweepIntervalMs = opts.idleSweepIntervalMs ?? DEFAULT_IDLE_SWEEP_INTERVAL_MS;
317
350
  this.deps = deps ?? { connect: defaultConnect };
351
+ this.onToolError = opts.onToolError;
352
+ this.onServerCrash = opts.onServerCrash;
353
+ this.onServerStderr = opts.onServerStderr;
318
354
  if (this.idleTtlMs > 0) this.startIdleSweep();
319
355
  }
356
+ /** Construct a Connection with the host hooks bound to its server name. */
357
+ buildConnection(name, config) {
358
+ const crash = this.onServerCrash;
359
+ const stderr = this.onServerStderr;
360
+ return new Connection({
361
+ name,
362
+ config,
363
+ deps: this.deps,
364
+ logger: this.logger,
365
+ ...crash ? { onUnexpectedClose: () => {
366
+ crash(name);
367
+ } } : {},
368
+ ...stderr ? { onStderrLine: (line) => {
369
+ stderr(name, line);
370
+ } } : {}
371
+ });
372
+ }
373
+ /** Fire the host's tool-error hook; exceptions must never affect the call path. */
374
+ reportToolError(info) {
375
+ try {
376
+ this.onToolError?.(info);
377
+ } catch {}
378
+ }
320
379
  /**
321
380
  * Diff `desired` against current connections, spawn newcomers, dispose
322
381
  * removals, hot-restart on config change. Pull-based — call whenever the
@@ -349,24 +408,14 @@ var McpBundler = class {
349
408
  for (const [name, config] of Object.entries(desired)) {
350
409
  const existing = this.connections.get(name);
351
410
  if (!existing) {
352
- this.connections.set(name, new Connection({
353
- name,
354
- config,
355
- deps: this.deps,
356
- logger: this.logger
357
- }));
411
+ this.connections.set(name, this.buildConnection(name, config));
358
412
  added.push(name);
359
413
  continue;
360
414
  }
361
415
  const nextFingerprint = JSON.stringify(config);
362
416
  if (existing.configFingerprint() !== nextFingerprint) {
363
417
  await existing.close();
364
- this.connections.set(name, new Connection({
365
- name,
366
- config,
367
- deps: this.deps,
368
- logger: this.logger
369
- }));
418
+ this.connections.set(name, this.buildConnection(name, config));
370
419
  changed.push(name);
371
420
  } else unchanged.push(name);
372
421
  }
@@ -441,10 +490,25 @@ var McpBundler = class {
441
490
  }]
442
491
  };
443
492
  try {
444
- return await route.connection.callTool(route.original, args, signal);
493
+ const result = await route.connection.callTool(route.original, args, signal);
494
+ if (result.isError) this.reportToolError({
495
+ server: route.connection.name,
496
+ tool: route.original,
497
+ prefixed,
498
+ kind: "result-error",
499
+ message: extractErrorText(result)
500
+ });
501
+ return result;
445
502
  } catch (err) {
446
503
  const msg = err instanceof Error ? err.message : String(err);
447
504
  this.logger?.error(`[mcp-bundler] tool call failed for "${prefixed}"`, { err: msg });
505
+ this.reportToolError({
506
+ server: route.connection.name,
507
+ tool: route.original,
508
+ prefixed,
509
+ kind: "thrown",
510
+ message: msg
511
+ });
448
512
  return {
449
513
  isError: true,
450
514
  content: [{
package/dist/index.d.cts CHANGED
@@ -50,12 +50,43 @@ interface McpToolCallResult {
50
50
  content: Record<string, unknown>[];
51
51
  isError?: boolean;
52
52
  }
53
+ /** A failed MCP tool call, surfaced to the host via `BundlerOptions.onToolError`. */
54
+ interface McpToolErrorInfo {
55
+ /** Server name (key in the store). */
56
+ server: string;
57
+ /** Original tool name as the server advertises it. */
58
+ tool: string;
59
+ /** Namespaced name the model invoked. */
60
+ prefixed: string;
61
+ /**
62
+ * `thrown` — the call rejected (transport/connect failure, timeout).
63
+ * `result-error` — the server returned `isError: true` (the MCP SDK also
64
+ * converts handler throws inside the child into this shape, so this is
65
+ * where most real tool failures surface).
66
+ */
67
+ kind: 'thrown' | 'result-error';
68
+ /** Error message / first text content of the error result. */
69
+ message: string;
70
+ }
53
71
  interface BundlerOptions {
54
72
  logger?: Logger;
55
73
  /** Idle TTL for spawned children (ms). 0 disables. Default: 600_000 (10 min). */
56
74
  idleTtlMs?: number;
57
75
  /** Sweep interval for idle reaping (ms). Default: 60_000 (1 min). */
58
76
  idleSweepIntervalMs?: number;
77
+ /**
78
+ * Host hook fired when a tool call fails — thrown or `isError` result.
79
+ * Invoked best-effort (exceptions swallowed); must not block.
80
+ */
81
+ onToolError?: (info: McpToolErrorInfo) => void;
82
+ /** Host hook fired when a server's transport closes unexpectedly (child crash / network drop). */
83
+ onServerCrash?: (server: string) => void;
84
+ /**
85
+ * Host hook fired per stderr line from stdio children. Setting this switches
86
+ * the child's stderr from `inherit` to `pipe` (the hook consumes the stream,
87
+ * so the pipe can't back up).
88
+ */
89
+ onServerStderr?: (server: string, line: string) => void;
59
90
  }
60
91
  //# sourceMappingURL=types.d.ts.map
61
92
  //#endregion
@@ -63,13 +94,24 @@ interface BundlerOptions {
63
94
  /** Env keys OpenClaw rejects from stdio MCP env blocks. Filter them out before spawning. */
64
95
  declare const STDIO_ENV_DENYLIST: Set<string>;
65
96
  declare function sanitizeStdioEnv(env: Record<string, string> | undefined): Record<string, string>;
97
+ /** Per-connect context the Connection threads into the connect factory. */
98
+ interface ConnectContext {
99
+ /** Server name (store key) — for logging/attribution. */
100
+ serverName: string;
101
+ /**
102
+ * When set, stdio children are spawned with `stderr: 'pipe'` and each stderr
103
+ * line is delivered here. When absent, stderr stays `inherit` (host fd).
104
+ */
105
+ onStderrLine?: (line: string) => void;
106
+ }
66
107
  interface ConnectionDeps {
67
108
  /**
68
109
  * Factory for an MCP Client connected to the given config. Injected so tests
69
110
  * can mock without spawning real processes. In production this wraps
70
- * `@modelcontextprotocol/sdk/client`.
111
+ * `@modelcontextprotocol/sdk/client`. The context arg is optional so
112
+ * existing test mocks keep working.
71
113
  */
72
- connect: (server: McpServerConfig) => Promise<McpClientHandle>;
114
+ connect: (server: McpServerConfig, ctx?: ConnectContext) => Promise<McpClientHandle>;
73
115
  }
74
116
  /**
75
117
  * Minimal interface our connection layer needs from an MCP client. Mirrors
@@ -118,11 +160,17 @@ declare class Connection {
118
160
  private reconnectBlockedUntilMs;
119
161
  private static readonly RECONNECT_BACKOFF_BASE_MS;
120
162
  private static readonly RECONNECT_BACKOFF_MAX_MS;
163
+ private readonly onUnexpectedClose;
164
+ private readonly onStderrLine;
121
165
  constructor(params: {
122
166
  name: string;
123
167
  config: McpServerConfig;
124
168
  deps: ConnectionDeps;
125
169
  logger?: Logger;
170
+ /** Fired when the transport closes unexpectedly (crash), after internal cleanup. */
171
+ onUnexpectedClose?: () => void;
172
+ /** Threaded to the connect factory — pipes stdio child stderr when set. */
173
+ onStderrLine?: (line: string) => void;
126
174
  });
127
175
  /** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */
128
176
  snapshotTools(): McpToolDescriptor[];
@@ -166,7 +214,7 @@ declare class Connection {
166
214
  * Kept in a separate function so tests can substitute a mock without
167
215
  * pulling the SDK into the test bundle.
168
216
  */
169
- declare function defaultConnect(server: McpServerConfig): Promise<McpClientHandle>;
217
+ declare function defaultConnect(server: McpServerConfig, ctx?: ConnectContext): Promise<McpClientHandle>;
170
218
  //# sourceMappingURL=connection.d.ts.map
171
219
  //#endregion
172
220
  //#region src/bundler.d.ts
@@ -186,9 +234,16 @@ declare class McpBundler {
186
234
  private readonly idleSweepIntervalMs;
187
235
  private idleSweepTimer;
188
236
  private readonly deps;
237
+ private readonly onToolError;
238
+ private readonly onServerCrash;
239
+ private readonly onServerStderr;
189
240
  private disposed;
190
241
  private reconcileLatch;
191
242
  constructor(opts?: BundlerOptions, deps?: ConnectionDeps);
243
+ /** Construct a Connection with the host hooks bound to its server name. */
244
+ private buildConnection;
245
+ /** Fire the host's tool-error hook; exceptions must never affect the call path. */
246
+ private reportToolError;
192
247
  /**
193
248
  * Diff `desired` against current connections, spawn newcomers, dispose
194
249
  * removals, hot-restart on config change. Pull-based — call whenever the
@@ -512,5 +567,5 @@ declare function fromMcpDescriptor(descriptor: McpToolDescriptor): ValidatableTo
512
567
  //# sourceMappingURL=pattern-a-validator.d.ts.map
513
568
 
514
569
  //#endregion
515
- 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 PatternAOptions, type PatternAViolation, type ReconcileDiff, type RemoteServerConfig, STDIO_ENV_DENYLIST, type ServerOwner, type StdioServerConfig, Store, type StoreOptions, type StoreSchema, type StoredServerEntry, type ValidatableTool, assertPatternA, buildNamespacedToolName, checkPatternA, defaultConnect, defaultStorePath, disambiguateAgainst, fromMcpDescriptor, sanitizeNameSegment, sanitizeStdioEnv, toServerConfig, toStoredEntry };
570
+ export { type AddServerOptions, type BundlerOptions, type ConnectContext, Connection, type ConnectionDeps, type Logger, Manager, type ManagerOptions, McpBundler, type McpClientHandle, type McpServerConfig, type McpToolCallResult, type McpToolDescriptor, type McpToolErrorInfo, type McpTransportKind, type PatternAOptions, type PatternAViolation, type ReconcileDiff, type RemoteServerConfig, STDIO_ENV_DENYLIST, type ServerOwner, type StdioServerConfig, Store, type StoreOptions, type StoreSchema, type StoredServerEntry, type ValidatableTool, assertPatternA, buildNamespacedToolName, checkPatternA, defaultConnect, defaultStorePath, disambiguateAgainst, fromMcpDescriptor, sanitizeNameSegment, sanitizeStdioEnv, toServerConfig, toStoredEntry };
516
571
  //# 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","../src/pattern-a-validator.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","../src/pattern-a-validator.ts"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -50,12 +50,43 @@ interface McpToolCallResult {
50
50
  content: Record<string, unknown>[];
51
51
  isError?: boolean;
52
52
  }
53
+ /** A failed MCP tool call, surfaced to the host via `BundlerOptions.onToolError`. */
54
+ interface McpToolErrorInfo {
55
+ /** Server name (key in the store). */
56
+ server: string;
57
+ /** Original tool name as the server advertises it. */
58
+ tool: string;
59
+ /** Namespaced name the model invoked. */
60
+ prefixed: string;
61
+ /**
62
+ * `thrown` — the call rejected (transport/connect failure, timeout).
63
+ * `result-error` — the server returned `isError: true` (the MCP SDK also
64
+ * converts handler throws inside the child into this shape, so this is
65
+ * where most real tool failures surface).
66
+ */
67
+ kind: 'thrown' | 'result-error';
68
+ /** Error message / first text content of the error result. */
69
+ message: string;
70
+ }
53
71
  interface BundlerOptions {
54
72
  logger?: Logger;
55
73
  /** Idle TTL for spawned children (ms). 0 disables. Default: 600_000 (10 min). */
56
74
  idleTtlMs?: number;
57
75
  /** Sweep interval for idle reaping (ms). Default: 60_000 (1 min). */
58
76
  idleSweepIntervalMs?: number;
77
+ /**
78
+ * Host hook fired when a tool call fails — thrown or `isError` result.
79
+ * Invoked best-effort (exceptions swallowed); must not block.
80
+ */
81
+ onToolError?: (info: McpToolErrorInfo) => void;
82
+ /** Host hook fired when a server's transport closes unexpectedly (child crash / network drop). */
83
+ onServerCrash?: (server: string) => void;
84
+ /**
85
+ * Host hook fired per stderr line from stdio children. Setting this switches
86
+ * the child's stderr from `inherit` to `pipe` (the hook consumes the stream,
87
+ * so the pipe can't back up).
88
+ */
89
+ onServerStderr?: (server: string, line: string) => void;
59
90
  }
60
91
  //# sourceMappingURL=types.d.ts.map
61
92
  //#endregion
@@ -63,13 +94,24 @@ interface BundlerOptions {
63
94
  /** Env keys OpenClaw rejects from stdio MCP env blocks. Filter them out before spawning. */
64
95
  declare const STDIO_ENV_DENYLIST: Set<string>;
65
96
  declare function sanitizeStdioEnv(env: Record<string, string> | undefined): Record<string, string>;
97
+ /** Per-connect context the Connection threads into the connect factory. */
98
+ interface ConnectContext {
99
+ /** Server name (store key) — for logging/attribution. */
100
+ serverName: string;
101
+ /**
102
+ * When set, stdio children are spawned with `stderr: 'pipe'` and each stderr
103
+ * line is delivered here. When absent, stderr stays `inherit` (host fd).
104
+ */
105
+ onStderrLine?: (line: string) => void;
106
+ }
66
107
  interface ConnectionDeps {
67
108
  /**
68
109
  * Factory for an MCP Client connected to the given config. Injected so tests
69
110
  * can mock without spawning real processes. In production this wraps
70
- * `@modelcontextprotocol/sdk/client`.
111
+ * `@modelcontextprotocol/sdk/client`. The context arg is optional so
112
+ * existing test mocks keep working.
71
113
  */
72
- connect: (server: McpServerConfig) => Promise<McpClientHandle>;
114
+ connect: (server: McpServerConfig, ctx?: ConnectContext) => Promise<McpClientHandle>;
73
115
  }
74
116
  /**
75
117
  * Minimal interface our connection layer needs from an MCP client. Mirrors
@@ -118,11 +160,17 @@ declare class Connection {
118
160
  private reconnectBlockedUntilMs;
119
161
  private static readonly RECONNECT_BACKOFF_BASE_MS;
120
162
  private static readonly RECONNECT_BACKOFF_MAX_MS;
163
+ private readonly onUnexpectedClose;
164
+ private readonly onStderrLine;
121
165
  constructor(params: {
122
166
  name: string;
123
167
  config: McpServerConfig;
124
168
  deps: ConnectionDeps;
125
169
  logger?: Logger;
170
+ /** Fired when the transport closes unexpectedly (crash), after internal cleanup. */
171
+ onUnexpectedClose?: () => void;
172
+ /** Threaded to the connect factory — pipes stdio child stderr when set. */
173
+ onStderrLine?: (line: string) => void;
126
174
  });
127
175
  /** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */
128
176
  snapshotTools(): McpToolDescriptor[];
@@ -166,7 +214,7 @@ declare class Connection {
166
214
  * Kept in a separate function so tests can substitute a mock without
167
215
  * pulling the SDK into the test bundle.
168
216
  */
169
- declare function defaultConnect(server: McpServerConfig): Promise<McpClientHandle>;
217
+ declare function defaultConnect(server: McpServerConfig, ctx?: ConnectContext): Promise<McpClientHandle>;
170
218
  //# sourceMappingURL=connection.d.ts.map
171
219
  //#endregion
172
220
  //#region src/bundler.d.ts
@@ -186,9 +234,16 @@ declare class McpBundler {
186
234
  private readonly idleSweepIntervalMs;
187
235
  private idleSweepTimer;
188
236
  private readonly deps;
237
+ private readonly onToolError;
238
+ private readonly onServerCrash;
239
+ private readonly onServerStderr;
189
240
  private disposed;
190
241
  private reconcileLatch;
191
242
  constructor(opts?: BundlerOptions, deps?: ConnectionDeps);
243
+ /** Construct a Connection with the host hooks bound to its server name. */
244
+ private buildConnection;
245
+ /** Fire the host's tool-error hook; exceptions must never affect the call path. */
246
+ private reportToolError;
192
247
  /**
193
248
  * Diff `desired` against current connections, spawn newcomers, dispose
194
249
  * removals, hot-restart on config change. Pull-based — call whenever the
@@ -512,5 +567,5 @@ declare function fromMcpDescriptor(descriptor: McpToolDescriptor): ValidatableTo
512
567
  //# sourceMappingURL=pattern-a-validator.d.ts.map
513
568
 
514
569
  //#endregion
515
- 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 PatternAOptions, type PatternAViolation, type ReconcileDiff, type RemoteServerConfig, STDIO_ENV_DENYLIST, type ServerOwner, type StdioServerConfig, Store, type StoreOptions, type StoreSchema, type StoredServerEntry, type ValidatableTool, assertPatternA, buildNamespacedToolName, checkPatternA, defaultConnect, defaultStorePath, disambiguateAgainst, fromMcpDescriptor, sanitizeNameSegment, sanitizeStdioEnv, toServerConfig, toStoredEntry };
570
+ export { type AddServerOptions, type BundlerOptions, type ConnectContext, Connection, type ConnectionDeps, type Logger, Manager, type ManagerOptions, McpBundler, type McpClientHandle, type McpServerConfig, type McpToolCallResult, type McpToolDescriptor, type McpToolErrorInfo, type McpTransportKind, type PatternAOptions, type PatternAViolation, type ReconcileDiff, type RemoteServerConfig, STDIO_ENV_DENYLIST, type ServerOwner, type StdioServerConfig, Store, type StoreOptions, type StoreSchema, type StoredServerEntry, type ValidatableTool, assertPatternA, buildNamespacedToolName, checkPatternA, defaultConnect, defaultStorePath, disambiguateAgainst, fromMcpDescriptor, sanitizeNameSegment, sanitizeStdioEnv, toServerConfig, toStoredEntry };
516
571
  //# 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","../src/pattern-a-validator.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","../src/pattern-a-validator.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -85,11 +85,15 @@ var Connection = class Connection {
85
85
  reconnectBlockedUntilMs = 0;
86
86
  static RECONNECT_BACKOFF_BASE_MS = 500;
87
87
  static RECONNECT_BACKOFF_MAX_MS = 3e4;
88
+ onUnexpectedClose;
89
+ onStderrLine;
88
90
  constructor(params) {
89
91
  this.name = params.name;
90
92
  this.config = params.config;
91
93
  this.deps = params.deps;
92
94
  this.logger = params.logger;
95
+ this.onUnexpectedClose = params.onUnexpectedClose;
96
+ this.onStderrLine = params.onStderrLine;
93
97
  }
94
98
  /** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */
95
99
  snapshotTools() {
@@ -122,7 +126,10 @@ var Connection = class Connection {
122
126
  env: sanitizeStdioEnv(this.config.env)
123
127
  } : this.config;
124
128
  this.logger?.debug(`[mcp-bundler] connecting server "${this.name}"`);
125
- const client = await this.deps.connect(safeConfig);
129
+ const client = await this.deps.connect(safeConfig, {
130
+ serverName: this.name,
131
+ onStderrLine: this.onStderrLine
132
+ });
126
133
  try {
127
134
  const advertised = await client.listTools();
128
135
  this.client = client;
@@ -160,6 +167,9 @@ var Connection = class Connection {
160
167
  this.logger?.warn(`[mcp-bundler] server "${this.name}" connection closed unexpectedly; will re-spawn on next use`);
161
168
  this.client = void 0;
162
169
  this.tools = [];
170
+ try {
171
+ this.onUnexpectedClose?.();
172
+ } catch {}
163
173
  }
164
174
  /**
165
175
  * Re-discover tools. Used on reconnect or `tools/list_changed` notification.
@@ -227,7 +237,7 @@ var Connection = class Connection {
227
237
  * Kept in a separate function so tests can substitute a mock without
228
238
  * pulling the SDK into the test bundle.
229
239
  */
230
- async function defaultConnect(server) {
240
+ async function defaultConnect(server, ctx) {
231
241
  const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
232
242
  const client = new Client({
233
243
  name: "alfe-mcp-bundler",
@@ -236,12 +246,27 @@ async function defaultConnect(server) {
236
246
  if ("command" in server) {
237
247
  const stdio = server;
238
248
  const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
249
+ const onStderrLine = ctx?.onStderrLine;
239
250
  const transport = new StdioClientTransport({
240
251
  command: stdio.command,
241
252
  args: stdio.args ?? [],
242
253
  env: { ...sanitizeStdioEnv(stdio.env) },
243
- cwd: stdio.cwd
254
+ cwd: stdio.cwd,
255
+ ...onStderrLine ? { stderr: "pipe" } : {}
244
256
  });
257
+ if (onStderrLine) {
258
+ let carry = "";
259
+ transport.stderr?.on("data", (chunk) => {
260
+ const parts = (carry + chunk.toString()).split("\n");
261
+ carry = parts.pop() ?? "";
262
+ for (const line of parts) {
263
+ if (line.trim() === "") continue;
264
+ try {
265
+ onStderrLine(line);
266
+ } catch {}
267
+ }
268
+ });
269
+ }
245
270
  await client.connect(transport);
246
271
  } else {
247
272
  const remote = server;
@@ -291,6 +316,11 @@ async function defaultConnect(server) {
291
316
  //#region src/bundler.ts
292
317
  const DEFAULT_IDLE_TTL_MS = 600 * 1e3;
293
318
  const DEFAULT_IDLE_SWEEP_INTERVAL_MS = 60 * 1e3;
319
+ /** First text content of an error result, for host error reporting. */
320
+ function extractErrorText(result) {
321
+ for (const item of result.content) if (item.type === "text" && typeof item.text === "string") return item.text.slice(0, 500);
322
+ return "(no error text)";
323
+ }
294
324
  /**
295
325
  * Provider-agnostic MCP server bundler. Holds N MCP server connections,
296
326
  * exposes a unified namespaced tool catalog, and routes calls to the right
@@ -307,6 +337,9 @@ var McpBundler = class {
307
337
  idleSweepIntervalMs;
308
338
  idleSweepTimer;
309
339
  deps;
340
+ onToolError;
341
+ onServerCrash;
342
+ onServerStderr;
310
343
  disposed = false;
311
344
  reconcileLatch = Promise.resolve();
312
345
  constructor(opts = {}, deps) {
@@ -314,8 +347,34 @@ var McpBundler = class {
314
347
  this.idleTtlMs = opts.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
315
348
  this.idleSweepIntervalMs = opts.idleSweepIntervalMs ?? DEFAULT_IDLE_SWEEP_INTERVAL_MS;
316
349
  this.deps = deps ?? { connect: defaultConnect };
350
+ this.onToolError = opts.onToolError;
351
+ this.onServerCrash = opts.onServerCrash;
352
+ this.onServerStderr = opts.onServerStderr;
317
353
  if (this.idleTtlMs > 0) this.startIdleSweep();
318
354
  }
355
+ /** Construct a Connection with the host hooks bound to its server name. */
356
+ buildConnection(name, config) {
357
+ const crash = this.onServerCrash;
358
+ const stderr = this.onServerStderr;
359
+ return new Connection({
360
+ name,
361
+ config,
362
+ deps: this.deps,
363
+ logger: this.logger,
364
+ ...crash ? { onUnexpectedClose: () => {
365
+ crash(name);
366
+ } } : {},
367
+ ...stderr ? { onStderrLine: (line) => {
368
+ stderr(name, line);
369
+ } } : {}
370
+ });
371
+ }
372
+ /** Fire the host's tool-error hook; exceptions must never affect the call path. */
373
+ reportToolError(info) {
374
+ try {
375
+ this.onToolError?.(info);
376
+ } catch {}
377
+ }
319
378
  /**
320
379
  * Diff `desired` against current connections, spawn newcomers, dispose
321
380
  * removals, hot-restart on config change. Pull-based — call whenever the
@@ -348,24 +407,14 @@ var McpBundler = class {
348
407
  for (const [name, config] of Object.entries(desired)) {
349
408
  const existing = this.connections.get(name);
350
409
  if (!existing) {
351
- this.connections.set(name, new Connection({
352
- name,
353
- config,
354
- deps: this.deps,
355
- logger: this.logger
356
- }));
410
+ this.connections.set(name, this.buildConnection(name, config));
357
411
  added.push(name);
358
412
  continue;
359
413
  }
360
414
  const nextFingerprint = JSON.stringify(config);
361
415
  if (existing.configFingerprint() !== nextFingerprint) {
362
416
  await existing.close();
363
- this.connections.set(name, new Connection({
364
- name,
365
- config,
366
- deps: this.deps,
367
- logger: this.logger
368
- }));
417
+ this.connections.set(name, this.buildConnection(name, config));
369
418
  changed.push(name);
370
419
  } else unchanged.push(name);
371
420
  }
@@ -440,10 +489,25 @@ var McpBundler = class {
440
489
  }]
441
490
  };
442
491
  try {
443
- return await route.connection.callTool(route.original, args, signal);
492
+ const result = await route.connection.callTool(route.original, args, signal);
493
+ if (result.isError) this.reportToolError({
494
+ server: route.connection.name,
495
+ tool: route.original,
496
+ prefixed,
497
+ kind: "result-error",
498
+ message: extractErrorText(result)
499
+ });
500
+ return result;
444
501
  } catch (err) {
445
502
  const msg = err instanceof Error ? err.message : String(err);
446
503
  this.logger?.error(`[mcp-bundler] tool call failed for "${prefixed}"`, { err: msg });
504
+ this.reportToolError({
505
+ server: route.connection.name,
506
+ tool: route.original,
507
+ prefixed,
508
+ kind: "thrown",
509
+ message: msg
510
+ });
447
511
  return {
448
512
  isError: true,
449
513
  content: [{
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","../src/pattern-a-validator.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 * Register a callback fired when the underlying transport closes or errors\n * unexpectedly (e.g. the child process crashed). Lets the Connection drop\n * its dead client so the next call re-spawns. Optional so test mocks can\n * omit it.\n */\n onClose?(handler: () => void): 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 /** Set while `close()` runs so the transport's onclose isn't treated as a crash. */\n private closing = false;\n /** Consecutive failed connect attempts — drives reconnect backoff. */\n private consecutiveFailures = 0;\n /** Epoch ms before which re-connect attempts fast-fail (crash-loop guard). */\n private reconnectBlockedUntilMs = 0;\n\n private static readonly RECONNECT_BACKOFF_BASE_MS = 500;\n private static readonly RECONNECT_BACKOFF_MAX_MS = 30_000;\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 if (Date.now() < this.reconnectBlockedUntilMs) {\n throw new Error(\n `server \"${this.name}\" is in reconnect backoff after ${this.consecutiveFailures.toString()} failed attempt(s)`,\n );\n }\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.closing = false;\n // Drop the dead client on an unexpected transport close so the next\n // call re-spawns instead of calling into a corpse. Bind the callback to\n // THIS handle so a late close from an already-replaced client can't wipe\n // out a freshly respawned one.\n client.onClose?.(() => { this.handleUnexpectedClose(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.consecutiveFailures = 0;\n this.reconnectBlockedUntilMs = 0;\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 this.consecutiveFailures += 1;\n const backoff = Math.min(\n Connection.RECONNECT_BACKOFF_BASE_MS * 2 ** (this.consecutiveFailures - 1),\n Connection.RECONNECT_BACKOFF_MAX_MS,\n );\n this.reconnectBlockedUntilMs = Date.now() + backoff;\n throw err;\n }\n }\n\n /**\n * Handle an unexpected transport close (crash / network drop). Clears the\n * dead client + tools so the next `ensureConnected` re-spawns. No-op if we\n * initiated the close ourselves (idle reap / reconcile removal).\n */\n private handleUnexpectedClose(handle: McpClientHandle): void {\n // Ignore if we initiated the close, or if this callback belongs to a\n // client we've already replaced (stale late-fire after a respawn).\n if (this.closing || this.client !== handle) return;\n this.logger?.warn(`[mcp-bundler] server \"${this.name}\" connection closed unexpectedly; will re-spawn on next use`);\n this.client = undefined;\n this.tools = [];\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 // Mark this close as intentional so the transport's onclose callback\n // doesn't trip the unexpected-close re-spawn path.\n this.closing = true;\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 let closeHandler: (() => void) | undefined;\n let closed = false;\n const fireClose = () => {\n if (closed) return;\n closed = true;\n closeHandler?.();\n };\n // The high-level SDK Client proxies its transport's lifecycle callbacks.\n client.onclose = fireClose;\n client.onerror = fireClose;\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 closed = true; // suppress the onClose callback for an intentional close\n await client.close();\n },\n onClose(handler: () => void) {\n closeHandler = handler;\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","/**\n * Pattern A validator — locks the \"explicit selector arg\" contract for\n * credential-touching tools across the openclaw-* plugin family.\n *\n * Background\n * ----------\n * Pattern A says every credential-touching tool on a multi-account-capable\n * provider MUST take a required selector arg in its JSON Schema so the LLM\n * picks the account deliberately. The reference implementation lives in\n * `@alfe.ai/openclaw-google` (`google_run_command` / `google_disconnect_\n * account` both require `email`). PR 7 of channels-and-credential-driven-\n * integrations sweeps the same shape across notion / xero / myob.\n *\n * Why a validator\n * ---------------\n * The contract is easy to break by accident — a new tool gets added, the\n * selector arg gets forgotten, the LLM silently dispatches to whichever\n * account was loaded first. There's no runtime safety net once the plugin\n * is published. This validator runs in plugin build steps (or CI) and\n * fails the build if a tool that declares itself credential-touching\n * doesn't carry the selector.\n *\n * Scope of this file\n * ------------------\n * Pure functions — no I/O. Plugin packages call into these helpers from\n * their own build scripts (e.g. `pnpm build` or a dedicated lint task) and\n * pass in their `ToolDef[]` or `McpToolDescriptor[]` collection. The\n * validator does NOT know how to read manifests or files — it only checks\n * the shape of the in-memory descriptor list. Wiring is up to each plugin.\n *\n * Consumers\n * ---------\n * - `@alfe.ai/myob-mcp` — direct MCP, explicit TypeBox schemas.\n * - `@alfe.ai/notion-mcp` — proxy MCP, injects selector at runtime.\n * - `@alfe.ai/xero-mcp` — proxy MCP, injects selector at runtime.\n * - `@alfe.ai/openclaw-google` — already compliant; can opt-in for regression.\n *\n * Future\n * ------\n * When PR 7-deferred lands (atlassian / github / microsoft openclaw\n * packages), they hook into the same validator. The atlassian / github\n * cases will likely run the validator over the proxied child server's\n * `listTools()` response after schema-injection, to confirm the injection\n * actually landed on every tool.\n */\n\nimport type { McpToolDescriptor } from \"./types.js\";\n\n// ── Public types ────────────────────────────────────────────\n\n/**\n * A tool descriptor that the validator can inspect. Plugins can pass\n * either {@link McpToolDescriptor} (for proxy plugins surfacing child\n * tools) or a leaner local shape (for direct-MCP plugins). Both come\n * down to a tool name and a JSON Schema parameter object.\n */\nexport interface ValidatableTool {\n /** Tool name as the LLM sees it (post-namespacing). */\n name: string;\n /** JSON Schema for tool parameters (the `inputSchema`). */\n parameters: Record<string, unknown>;\n}\n\nexport interface PatternAOptions {\n /**\n * The required selector property name. Provider-specific (Google uses\n * `email`; Notion will use `workspaceId`; Xero will use `xeroTenantId`;\n * MYOB will use `myobBusinessId`). Plugins MAY support more than one\n * acceptable name — pass an array.\n */\n selector: string | readonly string[];\n /**\n * Tool names exempt from the selector requirement — typically the\n * `list_accounts` discovery tool and any pure-utility tools that don't\n * touch credentials. Match is exact (post-namespacing).\n */\n exempt?: readonly string[];\n}\n\nexport interface PatternAViolation {\n tool: string;\n reason:\n | \"missing-selector-property\"\n | \"selector-not-required\"\n | \"selector-property-not-string\";\n detail: string;\n}\n\n// ── Core check ───────────────────────────────────────────────\n\n/**\n * Validate that every non-exempt tool's JSON Schema declares the selector\n * property AND lists it in `required`. Returns the full set of violations\n * so the caller can report them all in one pass — failing fast on the\n * first one tends to hide cascading bugs in real plugins.\n *\n * The check is intentionally schema-shape-only — it does NOT execute the\n * tool, call the LLM, or talk to the cloud. It's a fast structural pass\n * suitable for build-time use.\n */\nexport function checkPatternA(\n tools: readonly ValidatableTool[],\n options: PatternAOptions,\n): PatternAViolation[] {\n const selectorNames = typeof options.selector === \"string\"\n ? [options.selector]\n : options.selector;\n const exempt = new Set(options.exempt ?? []);\n const violations: PatternAViolation[] = [];\n\n for (const tool of tools) {\n if (exempt.has(tool.name)) continue;\n\n const schema = tool.parameters;\n const properties = (schema as { properties?: Record<string, unknown> }).properties ?? {};\n const required = (schema as { required?: unknown[] }).required ?? [];\n\n const matched = selectorNames.find((name) => name in properties);\n\n if (!matched) {\n violations.push({\n tool: tool.name,\n reason: \"missing-selector-property\",\n detail: `expected one of [${selectorNames.join(\", \")}] in inputSchema.properties`,\n });\n continue;\n }\n\n if (!required.includes(matched)) {\n violations.push({\n tool: tool.name,\n reason: \"selector-not-required\",\n detail: `selector \"${matched}\" present in properties but missing from inputSchema.required`,\n });\n continue;\n }\n\n const propSchema = properties[matched];\n const type = (propSchema as { type?: unknown }).type;\n if (type !== \"string\") {\n violations.push({\n tool: tool.name,\n reason: \"selector-property-not-string\",\n detail: `selector \"${matched}\" must be JSON Schema type=string (found ${JSON.stringify(type)})`,\n });\n }\n }\n\n return violations;\n}\n\n/**\n * Thin wrapper that throws an Error listing every violation if any are\n * present. Convenient for build scripts that want a single guard call.\n */\nexport function assertPatternA(\n tools: readonly ValidatableTool[],\n options: PatternAOptions,\n): void {\n const violations = checkPatternA(tools, options);\n if (violations.length === 0) return;\n const lines = violations.map((v) => ` - [${v.reason}] ${v.tool}: ${v.detail}`);\n throw new Error(\n `Pattern A validation failed for ${String(violations.length)} tool(s):\\n${lines.join(\"\\n\")}`,\n );\n}\n\n/**\n * Cast an {@link McpToolDescriptor} (proxy-plugin shape) to the leaner\n * {@link ValidatableTool} the validator accepts. Useful for proxy\n * plugins that already maintain a `cachedTools: McpToolDescriptor[]`\n * collection.\n */\nexport function fromMcpDescriptor(descriptor: McpToolDescriptor): ValidatableTool {\n return { name: descriptor.prefixed, parameters: descriptor.parameters };\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","../src/pattern-a-validator.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\n/** Per-connect context the Connection threads into the connect factory. */\nexport interface ConnectContext {\n /** Server name (store key) — for logging/attribution. */\n serverName: string;\n /**\n * When set, stdio children are spawned with `stderr: 'pipe'` and each stderr\n * line is delivered here. When absent, stderr stays `inherit` (host fd).\n */\n onStderrLine?: (line: string) => void;\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`. The context arg is optional so\n * existing test mocks keep working.\n */\n connect: (server: McpServerConfig, ctx?: ConnectContext) => 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 * Register a callback fired when the underlying transport closes or errors\n * unexpectedly (e.g. the child process crashed). Lets the Connection drop\n * its dead client so the next call re-spawns. Optional so test mocks can\n * omit it.\n */\n onClose?(handler: () => void): 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 /** Set while `close()` runs so the transport's onclose isn't treated as a crash. */\n private closing = false;\n /** Consecutive failed connect attempts — drives reconnect backoff. */\n private consecutiveFailures = 0;\n /** Epoch ms before which re-connect attempts fast-fail (crash-loop guard). */\n private reconnectBlockedUntilMs = 0;\n\n private static readonly RECONNECT_BACKOFF_BASE_MS = 500;\n private static readonly RECONNECT_BACKOFF_MAX_MS = 30_000;\n\n private readonly onUnexpectedClose: (() => void) | undefined;\n private readonly onStderrLine: ((line: string) => void) | undefined;\n\n constructor(params: {\n name: string;\n config: McpServerConfig;\n deps: ConnectionDeps;\n logger?: Logger;\n /** Fired when the transport closes unexpectedly (crash), after internal cleanup. */\n onUnexpectedClose?: () => void;\n /** Threaded to the connect factory — pipes stdio child stderr when set. */\n onStderrLine?: (line: string) => void;\n }) {\n this.name = params.name;\n this.config = params.config;\n this.deps = params.deps;\n this.logger = params.logger;\n this.onUnexpectedClose = params.onUnexpectedClose;\n this.onStderrLine = params.onStderrLine;\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 if (Date.now() < this.reconnectBlockedUntilMs) {\n throw new Error(\n `server \"${this.name}\" is in reconnect backoff after ${this.consecutiveFailures.toString()} failed attempt(s)`,\n );\n }\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 serverName: this.name,\n onStderrLine: this.onStderrLine,\n });\n try {\n const advertised = await client.listTools();\n this.client = client;\n this.closing = false;\n // Drop the dead client on an unexpected transport close so the next\n // call re-spawns instead of calling into a corpse. Bind the callback to\n // THIS handle so a late close from an already-replaced client can't wipe\n // out a freshly respawned one.\n client.onClose?.(() => { this.handleUnexpectedClose(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.consecutiveFailures = 0;\n this.reconnectBlockedUntilMs = 0;\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 this.consecutiveFailures += 1;\n const backoff = Math.min(\n Connection.RECONNECT_BACKOFF_BASE_MS * 2 ** (this.consecutiveFailures - 1),\n Connection.RECONNECT_BACKOFF_MAX_MS,\n );\n this.reconnectBlockedUntilMs = Date.now() + backoff;\n throw err;\n }\n }\n\n /**\n * Handle an unexpected transport close (crash / network drop). Clears the\n * dead client + tools so the next `ensureConnected` re-spawns. No-op if we\n * initiated the close ourselves (idle reap / reconcile removal).\n */\n private handleUnexpectedClose(handle: McpClientHandle): void {\n // Ignore if we initiated the close, or if this callback belongs to a\n // client we've already replaced (stale late-fire after a respawn).\n if (this.closing || this.client !== handle) return;\n this.logger?.warn(`[mcp-bundler] server \"${this.name}\" connection closed unexpectedly; will re-spawn on next use`);\n this.client = undefined;\n this.tools = [];\n try {\n this.onUnexpectedClose?.();\n } catch {\n /* host hook must never break the connection lifecycle */\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 // Mark this close as intentional so the transport's onclose callback\n // doesn't trip the unexpected-close re-spawn path.\n this.closing = true;\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, ctx?: ConnectContext): 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 onStderrLine = ctx?.onStderrLine;\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 // Default is 'inherit' (child stderr lands on the HOST's fd 2, invisible\n // to any output monitoring). Pipe only when the host consumes it — an\n // unread pipe would back-pressure the child's stderr writes.\n ...(onStderrLine ? { stderr: 'pipe' as const } : {}),\n });\n if (onStderrLine) {\n // The SDK exposes the PassThrough immediately when stderr:'pipe', so\n // attaching before connect() captures early startup output too.\n let carry = '';\n transport.stderr?.on('data', (chunk: Buffer) => {\n const parts = (carry + chunk.toString()).split('\\n');\n carry = parts.pop() ?? '';\n for (const line of parts) {\n if (line.trim() === '') continue;\n try {\n onStderrLine(line);\n } catch {\n /* host hook must never break the transport */\n }\n }\n });\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 let closeHandler: (() => void) | undefined;\n let closed = false;\n const fireClose = () => {\n if (closed) return;\n closed = true;\n closeHandler?.();\n };\n // The high-level SDK Client proxies its transport's lifecycle callbacks.\n client.onclose = fireClose;\n client.onerror = fireClose;\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 closed = true; // suppress the onClose callback for an intentional close\n await client.close();\n },\n onClose(handler: () => void) {\n closeHandler = handler;\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 McpToolErrorInfo,\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/** First text content of an error result, for host error reporting. */\nfunction extractErrorText(result: McpToolCallResult): string {\n for (const item of result.content) {\n if (item.type === 'text' && typeof item.text === 'string') return item.text.slice(0, 500);\n }\n return '(no error text)';\n}\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 readonly onToolError: ((info: McpToolErrorInfo) => void) | undefined;\n private readonly onServerCrash: ((server: string) => void) | undefined;\n private readonly onServerStderr: ((server: string, line: string) => void) | undefined;\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 this.onToolError = opts.onToolError;\n this.onServerCrash = opts.onServerCrash;\n this.onServerStderr = opts.onServerStderr;\n if (this.idleTtlMs > 0) this.startIdleSweep();\n }\n\n /** Construct a Connection with the host hooks bound to its server name. */\n private buildConnection(name: string, config: McpServerConfig): Connection {\n const crash = this.onServerCrash;\n const stderr = this.onServerStderr;\n return new Connection({\n name,\n config,\n deps: this.deps,\n logger: this.logger,\n ...(crash ? { onUnexpectedClose: () => { crash(name); } } : {}),\n ...(stderr ? { onStderrLine: (line: string) => { stderr(name, line); } } : {}),\n });\n }\n\n /** Fire the host's tool-error hook; exceptions must never affect the call path. */\n private reportToolError(info: McpToolErrorInfo): void {\n try {\n this.onToolError?.(info);\n } catch {\n /* host hook must never break tool routing */\n }\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, this.buildConnection(name, config));\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, this.buildConnection(name, config));\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 const result = await route.connection.callTool(route.original, args, signal);\n if (result.isError) {\n this.reportToolError({\n server: route.connection.name,\n tool: route.original,\n prefixed,\n kind: 'result-error',\n message: extractErrorText(result),\n });\n }\n return result;\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 this.reportToolError({\n server: route.connection.name,\n tool: route.original,\n prefixed,\n kind: 'thrown',\n message: msg,\n });\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","/**\n * Pattern A validator — locks the \"explicit selector arg\" contract for\n * credential-touching tools across the openclaw-* plugin family.\n *\n * Background\n * ----------\n * Pattern A says every credential-touching tool on a multi-account-capable\n * provider MUST take a required selector arg in its JSON Schema so the LLM\n * picks the account deliberately. The reference implementation lives in\n * `@alfe.ai/openclaw-google` (`google_run_command` / `google_disconnect_\n * account` both require `email`). PR 7 of channels-and-credential-driven-\n * integrations sweeps the same shape across notion / xero / myob.\n *\n * Why a validator\n * ---------------\n * The contract is easy to break by accident — a new tool gets added, the\n * selector arg gets forgotten, the LLM silently dispatches to whichever\n * account was loaded first. There's no runtime safety net once the plugin\n * is published. This validator runs in plugin build steps (or CI) and\n * fails the build if a tool that declares itself credential-touching\n * doesn't carry the selector.\n *\n * Scope of this file\n * ------------------\n * Pure functions — no I/O. Plugin packages call into these helpers from\n * their own build scripts (e.g. `pnpm build` or a dedicated lint task) and\n * pass in their `ToolDef[]` or `McpToolDescriptor[]` collection. The\n * validator does NOT know how to read manifests or files — it only checks\n * the shape of the in-memory descriptor list. Wiring is up to each plugin.\n *\n * Consumers\n * ---------\n * - `@alfe.ai/myob-mcp` — direct MCP, explicit TypeBox schemas.\n * - `@alfe.ai/notion-mcp` — proxy MCP, injects selector at runtime.\n * - `@alfe.ai/xero-mcp` — proxy MCP, injects selector at runtime.\n * - `@alfe.ai/openclaw-google` — already compliant; can opt-in for regression.\n *\n * Future\n * ------\n * When PR 7-deferred lands (atlassian / github / microsoft openclaw\n * packages), they hook into the same validator. The atlassian / github\n * cases will likely run the validator over the proxied child server's\n * `listTools()` response after schema-injection, to confirm the injection\n * actually landed on every tool.\n */\n\nimport type { McpToolDescriptor } from \"./types.js\";\n\n// ── Public types ────────────────────────────────────────────\n\n/**\n * A tool descriptor that the validator can inspect. Plugins can pass\n * either {@link McpToolDescriptor} (for proxy plugins surfacing child\n * tools) or a leaner local shape (for direct-MCP plugins). Both come\n * down to a tool name and a JSON Schema parameter object.\n */\nexport interface ValidatableTool {\n /** Tool name as the LLM sees it (post-namespacing). */\n name: string;\n /** JSON Schema for tool parameters (the `inputSchema`). */\n parameters: Record<string, unknown>;\n}\n\nexport interface PatternAOptions {\n /**\n * The required selector property name. Provider-specific (Google uses\n * `email`; Notion will use `workspaceId`; Xero will use `xeroTenantId`;\n * MYOB will use `myobBusinessId`). Plugins MAY support more than one\n * acceptable name — pass an array.\n */\n selector: string | readonly string[];\n /**\n * Tool names exempt from the selector requirement — typically the\n * `list_accounts` discovery tool and any pure-utility tools that don't\n * touch credentials. Match is exact (post-namespacing).\n */\n exempt?: readonly string[];\n}\n\nexport interface PatternAViolation {\n tool: string;\n reason:\n | \"missing-selector-property\"\n | \"selector-not-required\"\n | \"selector-property-not-string\";\n detail: string;\n}\n\n// ── Core check ───────────────────────────────────────────────\n\n/**\n * Validate that every non-exempt tool's JSON Schema declares the selector\n * property AND lists it in `required`. Returns the full set of violations\n * so the caller can report them all in one pass — failing fast on the\n * first one tends to hide cascading bugs in real plugins.\n *\n * The check is intentionally schema-shape-only — it does NOT execute the\n * tool, call the LLM, or talk to the cloud. It's a fast structural pass\n * suitable for build-time use.\n */\nexport function checkPatternA(\n tools: readonly ValidatableTool[],\n options: PatternAOptions,\n): PatternAViolation[] {\n const selectorNames = typeof options.selector === \"string\"\n ? [options.selector]\n : options.selector;\n const exempt = new Set(options.exempt ?? []);\n const violations: PatternAViolation[] = [];\n\n for (const tool of tools) {\n if (exempt.has(tool.name)) continue;\n\n const schema = tool.parameters;\n const properties = (schema as { properties?: Record<string, unknown> }).properties ?? {};\n const required = (schema as { required?: unknown[] }).required ?? [];\n\n const matched = selectorNames.find((name) => name in properties);\n\n if (!matched) {\n violations.push({\n tool: tool.name,\n reason: \"missing-selector-property\",\n detail: `expected one of [${selectorNames.join(\", \")}] in inputSchema.properties`,\n });\n continue;\n }\n\n if (!required.includes(matched)) {\n violations.push({\n tool: tool.name,\n reason: \"selector-not-required\",\n detail: `selector \"${matched}\" present in properties but missing from inputSchema.required`,\n });\n continue;\n }\n\n const propSchema = properties[matched];\n const type = (propSchema as { type?: unknown }).type;\n if (type !== \"string\") {\n violations.push({\n tool: tool.name,\n reason: \"selector-property-not-string\",\n detail: `selector \"${matched}\" must be JSON Schema type=string (found ${JSON.stringify(type)})`,\n });\n }\n }\n\n return violations;\n}\n\n/**\n * Thin wrapper that throws an Error listing every violation if any are\n * present. Convenient for build scripts that want a single guard call.\n */\nexport function assertPatternA(\n tools: readonly ValidatableTool[],\n options: PatternAOptions,\n): void {\n const violations = checkPatternA(tools, options);\n if (violations.length === 0) return;\n const lines = violations.map((v) => ` - [${v.reason}] ${v.tool}: ${v.detail}`);\n throw new Error(\n `Pattern A validation failed for ${String(violations.length)} tool(s):\\n${lines.join(\"\\n\")}`,\n );\n}\n\n/**\n * Cast an {@link McpToolDescriptor} (proxy-plugin shape) to the leaner\n * {@link ValidatableTool} the validator accepts. Useful for proxy\n * plugins that already maintain a `cachedTools: McpToolDescriptor[]`\n * collection.\n */\nexport function fromMcpDescriptor(descriptor: McpToolDescriptor): ValidatableTool {\n return { name: descriptor.prefixed, parameters: descriptor.parameters };\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.2.2",
3
+ "version": "0.3.0",
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",