@alfe.ai/mcp-bundler 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -40,6 +40,24 @@ interface ReconcileDiff {
40
40
  changed: string[];
41
41
  unchanged: string[];
42
42
  }
43
+ /**
44
+ * Live connection status for one MCP server in the bundler. Surfaced so a
45
+ * host can report whether a server actually connected and how many tools it
46
+ * advertises — e.g. the daemon confirming an `alfe mcp add` before replying
47
+ * to the agent, rather than optimistically claiming tools will appear.
48
+ */
49
+ interface McpServerStatus {
50
+ /** Server name (store key). */
51
+ name: string;
52
+ /** Whether a child process / remote connection is currently established. */
53
+ connected: boolean;
54
+ /** Tools currently advertised (0 until the server connects + discovers). */
55
+ toolCount: number;
56
+ /** Consecutive failed connect attempts; 0 when healthy. */
57
+ consecutiveFailures: number;
58
+ /** Message from the most recent failed connect attempt, if any. */
59
+ lastError?: string;
60
+ }
43
61
  interface Logger {
44
62
  debug: (msg: string, meta?: Record<string, unknown>) => void;
45
63
  info: (msg: string, meta?: Record<string, unknown>) => void;
@@ -50,12 +68,43 @@ interface McpToolCallResult {
50
68
  content: Record<string, unknown>[];
51
69
  isError?: boolean;
52
70
  }
71
+ /** A failed MCP tool call, surfaced to the host via `BundlerOptions.onToolError`. */
72
+ interface McpToolErrorInfo {
73
+ /** Server name (key in the store). */
74
+ server: string;
75
+ /** Original tool name as the server advertises it. */
76
+ tool: string;
77
+ /** Namespaced name the model invoked. */
78
+ prefixed: string;
79
+ /**
80
+ * `thrown` — the call rejected (transport/connect failure, timeout).
81
+ * `result-error` — the server returned `isError: true` (the MCP SDK also
82
+ * converts handler throws inside the child into this shape, so this is
83
+ * where most real tool failures surface).
84
+ */
85
+ kind: 'thrown' | 'result-error';
86
+ /** Error message / first text content of the error result. */
87
+ message: string;
88
+ }
53
89
  interface BundlerOptions {
54
90
  logger?: Logger;
55
91
  /** Idle TTL for spawned children (ms). 0 disables. Default: 600_000 (10 min). */
56
92
  idleTtlMs?: number;
57
93
  /** Sweep interval for idle reaping (ms). Default: 60_000 (1 min). */
58
94
  idleSweepIntervalMs?: number;
95
+ /**
96
+ * Host hook fired when a tool call fails — thrown or `isError` result.
97
+ * Invoked best-effort (exceptions swallowed); must not block.
98
+ */
99
+ onToolError?: (info: McpToolErrorInfo) => void;
100
+ /** Host hook fired when a server's transport closes unexpectedly (child crash / network drop). */
101
+ onServerCrash?: (server: string) => void;
102
+ /**
103
+ * Host hook fired per stderr line from stdio children. Setting this switches
104
+ * the child's stderr from `inherit` to `pipe` (the hook consumes the stream,
105
+ * so the pipe can't back up).
106
+ */
107
+ onServerStderr?: (server: string, line: string) => void;
59
108
  }
60
109
  //# sourceMappingURL=types.d.ts.map
61
110
  //#endregion
@@ -63,13 +112,24 @@ interface BundlerOptions {
63
112
  /** Env keys OpenClaw rejects from stdio MCP env blocks. Filter them out before spawning. */
64
113
  declare const STDIO_ENV_DENYLIST: Set<string>;
65
114
  declare function sanitizeStdioEnv(env: Record<string, string> | undefined): Record<string, string>;
115
+ /** Per-connect context the Connection threads into the connect factory. */
116
+ interface ConnectContext {
117
+ /** Server name (store key) — for logging/attribution. */
118
+ serverName: string;
119
+ /**
120
+ * When set, stdio children are spawned with `stderr: 'pipe'` and each stderr
121
+ * line is delivered here. When absent, stderr stays `inherit` (host fd).
122
+ */
123
+ onStderrLine?: (line: string) => void;
124
+ }
66
125
  interface ConnectionDeps {
67
126
  /**
68
127
  * Factory for an MCP Client connected to the given config. Injected so tests
69
128
  * can mock without spawning real processes. In production this wraps
70
- * `@modelcontextprotocol/sdk/client`.
129
+ * `@modelcontextprotocol/sdk/client`. The context arg is optional so
130
+ * existing test mocks keep working.
71
131
  */
72
- connect: (server: McpServerConfig) => Promise<McpClientHandle>;
132
+ connect: (server: McpServerConfig, ctx?: ConnectContext) => Promise<McpClientHandle>;
73
133
  }
74
134
  /**
75
135
  * Minimal interface our connection layer needs from an MCP client. Mirrors
@@ -114,20 +174,34 @@ declare class Connection {
114
174
  private closing;
115
175
  /** Consecutive failed connect attempts — drives reconnect backoff. */
116
176
  private consecutiveFailures;
177
+ /** Message from the most recent failed connect attempt; cleared on success. */
178
+ private lastError;
117
179
  /** Epoch ms before which re-connect attempts fast-fail (crash-loop guard). */
118
180
  private reconnectBlockedUntilMs;
119
181
  private static readonly RECONNECT_BACKOFF_BASE_MS;
120
182
  private static readonly RECONNECT_BACKOFF_MAX_MS;
183
+ private readonly onUnexpectedClose;
184
+ private readonly onStderrLine;
121
185
  constructor(params: {
122
186
  name: string;
123
187
  config: McpServerConfig;
124
188
  deps: ConnectionDeps;
125
189
  logger?: Logger;
190
+ /** Fired when the transport closes unexpectedly (crash), after internal cleanup. */
191
+ onUnexpectedClose?: () => void;
192
+ /** Threaded to the connect factory — pipes stdio child stderr when set. */
193
+ onStderrLine?: (line: string) => void;
126
194
  });
127
195
  /** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */
128
196
  snapshotTools(): McpToolDescriptor[];
129
197
  /** Whether an MCP child process / remote connection has been established. */
130
198
  isConnected(): boolean;
199
+ /** Tools currently advertised; 0 until the server connects + discovers. */
200
+ toolCount(): number;
201
+ /** Consecutive failed connect attempts; 0 when healthy. */
202
+ failureCount(): number;
203
+ /** Message from the most recent failed connect attempt, if any. */
204
+ lastErrorMessage(): string | undefined;
131
205
  /** Idle timestamp for reaping. */
132
206
  idleSinceMs(): number;
133
207
  /**
@@ -166,7 +240,7 @@ declare class Connection {
166
240
  * Kept in a separate function so tests can substitute a mock without
167
241
  * pulling the SDK into the test bundle.
168
242
  */
169
- declare function defaultConnect(server: McpServerConfig): Promise<McpClientHandle>;
243
+ declare function defaultConnect(server: McpServerConfig, ctx?: ConnectContext): Promise<McpClientHandle>;
170
244
  //# sourceMappingURL=connection.d.ts.map
171
245
  //#endregion
172
246
  //#region src/bundler.d.ts
@@ -186,9 +260,16 @@ declare class McpBundler {
186
260
  private readonly idleSweepIntervalMs;
187
261
  private idleSweepTimer;
188
262
  private readonly deps;
263
+ private readonly onToolError;
264
+ private readonly onServerCrash;
265
+ private readonly onServerStderr;
189
266
  private disposed;
190
267
  private reconcileLatch;
191
268
  constructor(opts?: BundlerOptions, deps?: ConnectionDeps);
269
+ /** Construct a Connection with the host hooks bound to its server name. */
270
+ private buildConnection;
271
+ /** Fire the host's tool-error hook; exceptions must never affect the call path. */
272
+ private reportToolError;
192
273
  /**
193
274
  * Diff `desired` against current connections, spawn newcomers, dispose
194
275
  * removals, hot-restart on config change. Pull-based — call whenever the
@@ -215,6 +296,24 @@ declare class McpBundler {
215
296
  * swallowed per-server (logged), so one bad server doesn't fail the batch.
216
297
  */
217
298
  warmup(): Promise<void>;
299
+ /** Build the live status descriptor for one connection. */
300
+ private statusOf;
301
+ /** Live status for every known server. Synchronous snapshot, no I/O. */
302
+ statuses(): McpServerStatus[];
303
+ /**
304
+ * Eagerly connect ONE server and return its resulting status. Unlike
305
+ * `warmup()` — which fans out over all servers and swallows failures with
306
+ * no return value — this surfaces the outcome so a caller (e.g. the daemon
307
+ * confirming an `alfe mcp add`) can report "connected, N tools" or the exact
308
+ * connect error back to the agent.
309
+ *
310
+ * Never throws: a connect failure (or a warm timeout) is reflected in the
311
+ * returned status (`connected: false`, `lastError` set). Returns `undefined`
312
+ * only when the named server isn't present in the bundler.
313
+ */
314
+ warmServer(name: string, timeoutMs?: number): Promise<McpServerStatus | undefined>;
315
+ /** Race a promise against a timeout, clearing the timer either way. */
316
+ private raceTimeout;
218
317
  /**
219
318
  * Invoke a tool by its namespaced name. Routes to the originating server.
220
319
  * Errors are returned as `{ isError: true, content: [...] }` so a failing
@@ -433,6 +532,24 @@ declare class Manager {
433
532
  id: string;
434
533
  entry: StoredServerEntry;
435
534
  }[];
535
+ /**
536
+ * Live connection status per server from the attached bundler. Empty when
537
+ * no bundler is attached (e.g. a CLI-only manager). Lets callers show
538
+ * whether each registered server actually connected and how many tools it
539
+ * advertises, instead of only the stored config.
540
+ */
541
+ serverStatuses(): McpServerStatus[];
542
+ /**
543
+ * Reconcile the bundler against the current store (so a just-added entry
544
+ * has a connection object) then eagerly connect ONE server and return its
545
+ * status. Used by the daemon to CONFIRM an `alfe mcp add` actually connected
546
+ * before replying to the agent — turning the fire-and-forget warm into a
547
+ * result the caller can report ("connected, N tools" or the real error).
548
+ *
549
+ * Returns `null` when no bundler is attached or the id isn't present; never
550
+ * throws (a connect failure is carried in the returned status).
551
+ */
552
+ warmServer(id: string, timeoutMs?: number): Promise<McpServerStatus | null>;
436
553
  /**
437
554
  * Push the current store contents into a bundler instance (which owns
438
555
  * connections / tools). Wires up a store watcher so external mutations
@@ -512,5 +629,5 @@ declare function fromMcpDescriptor(descriptor: McpToolDescriptor): ValidatableTo
512
629
  //# sourceMappingURL=pattern-a-validator.d.ts.map
513
630
 
514
631
  //#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 };
632
+ export { type AddServerOptions, type BundlerOptions, type ConnectContext, Connection, type ConnectionDeps, type Logger, Manager, type ManagerOptions, McpBundler, type McpClientHandle, type McpServerConfig, type McpServerStatus, 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
633
  //# 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
@@ -81,15 +81,21 @@ var Connection = class Connection {
81
81
  closing = false;
82
82
  /** Consecutive failed connect attempts — drives reconnect backoff. */
83
83
  consecutiveFailures = 0;
84
+ /** Message from the most recent failed connect attempt; cleared on success. */
85
+ lastError;
84
86
  /** Epoch ms before which re-connect attempts fast-fail (crash-loop guard). */
85
87
  reconnectBlockedUntilMs = 0;
86
88
  static RECONNECT_BACKOFF_BASE_MS = 500;
87
89
  static RECONNECT_BACKOFF_MAX_MS = 3e4;
90
+ onUnexpectedClose;
91
+ onStderrLine;
88
92
  constructor(params) {
89
93
  this.name = params.name;
90
94
  this.config = params.config;
91
95
  this.deps = params.deps;
92
96
  this.logger = params.logger;
97
+ this.onUnexpectedClose = params.onUnexpectedClose;
98
+ this.onStderrLine = params.onStderrLine;
93
99
  }
94
100
  /** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */
95
101
  snapshotTools() {
@@ -99,6 +105,18 @@ var Connection = class Connection {
99
105
  isConnected() {
100
106
  return this.client !== void 0;
101
107
  }
108
+ /** Tools currently advertised; 0 until the server connects + discovers. */
109
+ toolCount() {
110
+ return this.tools.length;
111
+ }
112
+ /** Consecutive failed connect attempts; 0 when healthy. */
113
+ failureCount() {
114
+ return this.consecutiveFailures;
115
+ }
116
+ /** Message from the most recent failed connect attempt, if any. */
117
+ lastErrorMessage() {
118
+ return this.lastError;
119
+ }
102
120
  /** Idle timestamp for reaping. */
103
121
  idleSinceMs() {
104
122
  return Date.now() - this.lastUsedAt;
@@ -122,13 +140,18 @@ var Connection = class Connection {
122
140
  env: sanitizeStdioEnv(this.config.env)
123
141
  } : this.config;
124
142
  this.logger?.debug(`[mcp-bundler] connecting server "${this.name}"`);
125
- const client = await this.deps.connect(safeConfig);
143
+ let client;
126
144
  try {
145
+ client = await this.deps.connect(safeConfig, {
146
+ serverName: this.name,
147
+ onStderrLine: this.onStderrLine
148
+ });
127
149
  const advertised = await client.listTools();
128
- this.client = client;
150
+ const connected = client;
151
+ this.client = connected;
129
152
  this.closing = false;
130
- client.onClose?.(() => {
131
- this.handleUnexpectedClose(client);
153
+ connected.onClose?.(() => {
154
+ this.handleUnexpectedClose(connected);
132
155
  });
133
156
  this.tools = advertised.map((t) => ({
134
157
  prefixed: buildNamespacedToolName(this.name, t.name),
@@ -140,11 +163,13 @@ var Connection = class Connection {
140
163
  }));
141
164
  this.lastUsedAt = Date.now();
142
165
  this.consecutiveFailures = 0;
166
+ this.lastError = void 0;
143
167
  this.reconnectBlockedUntilMs = 0;
144
168
  this.logger?.info(`[mcp-bundler] server "${this.name}" connected, ${this.tools.length.toString()} tool(s)`);
145
169
  } catch (err) {
146
- await client.close().catch(() => void 0);
170
+ if (client) await client.close().catch(() => void 0);
147
171
  this.consecutiveFailures += 1;
172
+ this.lastError = err instanceof Error ? err.message : String(err);
148
173
  const backoff = Math.min(Connection.RECONNECT_BACKOFF_BASE_MS * 2 ** (this.consecutiveFailures - 1), Connection.RECONNECT_BACKOFF_MAX_MS);
149
174
  this.reconnectBlockedUntilMs = Date.now() + backoff;
150
175
  throw err;
@@ -160,6 +185,9 @@ var Connection = class Connection {
160
185
  this.logger?.warn(`[mcp-bundler] server "${this.name}" connection closed unexpectedly; will re-spawn on next use`);
161
186
  this.client = void 0;
162
187
  this.tools = [];
188
+ try {
189
+ this.onUnexpectedClose?.();
190
+ } catch {}
163
191
  }
164
192
  /**
165
193
  * Re-discover tools. Used on reconnect or `tools/list_changed` notification.
@@ -227,7 +255,7 @@ var Connection = class Connection {
227
255
  * Kept in a separate function so tests can substitute a mock without
228
256
  * pulling the SDK into the test bundle.
229
257
  */
230
- async function defaultConnect(server) {
258
+ async function defaultConnect(server, ctx) {
231
259
  const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
232
260
  const client = new Client({
233
261
  name: "alfe-mcp-bundler",
@@ -236,12 +264,27 @@ async function defaultConnect(server) {
236
264
  if ("command" in server) {
237
265
  const stdio = server;
238
266
  const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
267
+ const onStderrLine = ctx?.onStderrLine;
239
268
  const transport = new StdioClientTransport({
240
269
  command: stdio.command,
241
270
  args: stdio.args ?? [],
242
271
  env: { ...sanitizeStdioEnv(stdio.env) },
243
- cwd: stdio.cwd
272
+ cwd: stdio.cwd,
273
+ ...onStderrLine ? { stderr: "pipe" } : {}
244
274
  });
275
+ if (onStderrLine) {
276
+ let carry = "";
277
+ transport.stderr?.on("data", (chunk) => {
278
+ const parts = (carry + chunk.toString()).split("\n");
279
+ carry = parts.pop() ?? "";
280
+ for (const line of parts) {
281
+ if (line.trim() === "") continue;
282
+ try {
283
+ onStderrLine(line);
284
+ } catch {}
285
+ }
286
+ });
287
+ }
245
288
  await client.connect(transport);
246
289
  } else {
247
290
  const remote = server;
@@ -291,6 +334,11 @@ async function defaultConnect(server) {
291
334
  //#region src/bundler.ts
292
335
  const DEFAULT_IDLE_TTL_MS = 600 * 1e3;
293
336
  const DEFAULT_IDLE_SWEEP_INTERVAL_MS = 60 * 1e3;
337
+ /** First text content of an error result, for host error reporting. */
338
+ function extractErrorText(result) {
339
+ for (const item of result.content) if (item.type === "text" && typeof item.text === "string") return item.text.slice(0, 500);
340
+ return "(no error text)";
341
+ }
294
342
  /**
295
343
  * Provider-agnostic MCP server bundler. Holds N MCP server connections,
296
344
  * exposes a unified namespaced tool catalog, and routes calls to the right
@@ -307,6 +355,9 @@ var McpBundler = class {
307
355
  idleSweepIntervalMs;
308
356
  idleSweepTimer;
309
357
  deps;
358
+ onToolError;
359
+ onServerCrash;
360
+ onServerStderr;
310
361
  disposed = false;
311
362
  reconcileLatch = Promise.resolve();
312
363
  constructor(opts = {}, deps) {
@@ -314,8 +365,34 @@ var McpBundler = class {
314
365
  this.idleTtlMs = opts.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
315
366
  this.idleSweepIntervalMs = opts.idleSweepIntervalMs ?? DEFAULT_IDLE_SWEEP_INTERVAL_MS;
316
367
  this.deps = deps ?? { connect: defaultConnect };
368
+ this.onToolError = opts.onToolError;
369
+ this.onServerCrash = opts.onServerCrash;
370
+ this.onServerStderr = opts.onServerStderr;
317
371
  if (this.idleTtlMs > 0) this.startIdleSweep();
318
372
  }
373
+ /** Construct a Connection with the host hooks bound to its server name. */
374
+ buildConnection(name, config) {
375
+ const crash = this.onServerCrash;
376
+ const stderr = this.onServerStderr;
377
+ return new Connection({
378
+ name,
379
+ config,
380
+ deps: this.deps,
381
+ logger: this.logger,
382
+ ...crash ? { onUnexpectedClose: () => {
383
+ crash(name);
384
+ } } : {},
385
+ ...stderr ? { onStderrLine: (line) => {
386
+ stderr(name, line);
387
+ } } : {}
388
+ });
389
+ }
390
+ /** Fire the host's tool-error hook; exceptions must never affect the call path. */
391
+ reportToolError(info) {
392
+ try {
393
+ this.onToolError?.(info);
394
+ } catch {}
395
+ }
319
396
  /**
320
397
  * Diff `desired` against current connections, spawn newcomers, dispose
321
398
  * removals, hot-restart on config change. Pull-based — call whenever the
@@ -348,24 +425,14 @@ var McpBundler = class {
348
425
  for (const [name, config] of Object.entries(desired)) {
349
426
  const existing = this.connections.get(name);
350
427
  if (!existing) {
351
- this.connections.set(name, new Connection({
352
- name,
353
- config,
354
- deps: this.deps,
355
- logger: this.logger
356
- }));
428
+ this.connections.set(name, this.buildConnection(name, config));
357
429
  added.push(name);
358
430
  continue;
359
431
  }
360
432
  const nextFingerprint = JSON.stringify(config);
361
433
  if (existing.configFingerprint() !== nextFingerprint) {
362
434
  await existing.close();
363
- this.connections.set(name, new Connection({
364
- name,
365
- config,
366
- deps: this.deps,
367
- logger: this.logger
368
- }));
435
+ this.connections.set(name, this.buildConnection(name, config));
369
436
  changed.push(name);
370
437
  } else unchanged.push(name);
371
438
  }
@@ -418,6 +485,64 @@ var McpBundler = class {
418
485
  }
419
486
  }));
420
487
  }
488
+ /** Build the live status descriptor for one connection. */
489
+ statusOf(conn) {
490
+ const status = {
491
+ name: conn.name,
492
+ connected: conn.isConnected(),
493
+ toolCount: conn.toolCount(),
494
+ consecutiveFailures: conn.failureCount()
495
+ };
496
+ const err = conn.lastErrorMessage();
497
+ if (err !== void 0) status.lastError = err;
498
+ return status;
499
+ }
500
+ /** Live status for every known server. Synchronous snapshot, no I/O. */
501
+ statuses() {
502
+ return Array.from(this.connections.values()).map((conn) => this.statusOf(conn));
503
+ }
504
+ /**
505
+ * Eagerly connect ONE server and return its resulting status. Unlike
506
+ * `warmup()` — which fans out over all servers and swallows failures with
507
+ * no return value — this surfaces the outcome so a caller (e.g. the daemon
508
+ * confirming an `alfe mcp add`) can report "connected, N tools" or the exact
509
+ * connect error back to the agent.
510
+ *
511
+ * Never throws: a connect failure (or a warm timeout) is reflected in the
512
+ * returned status (`connected: false`, `lastError` set). Returns `undefined`
513
+ * only when the named server isn't present in the bundler.
514
+ */
515
+ async warmServer(name, timeoutMs) {
516
+ if (this.disposed) return void 0;
517
+ const conn = this.connections.get(name);
518
+ if (!conn) return void 0;
519
+ const connect = conn.ensureConnected();
520
+ connect.catch(() => void 0);
521
+ try {
522
+ if (timeoutMs !== void 0 && timeoutMs > 0) await this.raceTimeout(connect, timeoutMs, name);
523
+ else await connect;
524
+ } catch (err) {
525
+ const status = this.statusOf(conn);
526
+ status.lastError ??= err instanceof Error ? err.message : String(err);
527
+ return status;
528
+ }
529
+ return this.statusOf(conn);
530
+ }
531
+ /** Race a promise against a timeout, clearing the timer either way. */
532
+ async raceTimeout(p, timeoutMs, name) {
533
+ let timer;
534
+ const timeout = new Promise((_, reject) => {
535
+ timer = setTimeout(() => {
536
+ reject(/* @__PURE__ */ new Error(`server "${name}" warm timed out after ${timeoutMs.toString()}ms`));
537
+ }, timeoutMs);
538
+ if (typeof timer === "object" && "unref" in timer) timer.unref();
539
+ });
540
+ try {
541
+ await Promise.race([p, timeout]);
542
+ } finally {
543
+ clearTimeout(timer);
544
+ }
545
+ }
421
546
  /**
422
547
  * Invoke a tool by its namespaced name. Routes to the originating server.
423
548
  * Errors are returned as `{ isError: true, content: [...] }` so a failing
@@ -440,10 +565,25 @@ var McpBundler = class {
440
565
  }]
441
566
  };
442
567
  try {
443
- return await route.connection.callTool(route.original, args, signal);
568
+ const result = await route.connection.callTool(route.original, args, signal);
569
+ if (result.isError) this.reportToolError({
570
+ server: route.connection.name,
571
+ tool: route.original,
572
+ prefixed,
573
+ kind: "result-error",
574
+ message: extractErrorText(result)
575
+ });
576
+ return result;
444
577
  } catch (err) {
445
578
  const msg = err instanceof Error ? err.message : String(err);
446
579
  this.logger?.error(`[mcp-bundler] tool call failed for "${prefixed}"`, { err: msg });
580
+ this.reportToolError({
581
+ server: route.connection.name,
582
+ tool: route.original,
583
+ prefixed,
584
+ kind: "thrown",
585
+ message: msg
586
+ });
447
587
  return {
448
588
  isError: true,
449
589
  content: [{
@@ -861,6 +1001,30 @@ var Manager = class {
861
1001
  }));
862
1002
  }
863
1003
  /**
1004
+ * Live connection status per server from the attached bundler. Empty when
1005
+ * no bundler is attached (e.g. a CLI-only manager). Lets callers show
1006
+ * whether each registered server actually connected and how many tools it
1007
+ * advertises, instead of only the stored config.
1008
+ */
1009
+ serverStatuses() {
1010
+ return this.bundler ? this.bundler.statuses() : [];
1011
+ }
1012
+ /**
1013
+ * Reconcile the bundler against the current store (so a just-added entry
1014
+ * has a connection object) then eagerly connect ONE server and return its
1015
+ * status. Used by the daemon to CONFIRM an `alfe mcp add` actually connected
1016
+ * before replying to the agent — turning the fire-and-forget warm into a
1017
+ * result the caller can report ("connected, N tools" or the real error).
1018
+ *
1019
+ * Returns `null` when no bundler is attached or the id isn't present; never
1020
+ * throws (a connect failure is carried in the returned status).
1021
+ */
1022
+ async warmServer(id, timeoutMs) {
1023
+ if (!this.bundler) return null;
1024
+ await this.reconcileBundler();
1025
+ return await this.bundler.warmServer(id, timeoutMs) ?? null;
1026
+ }
1027
+ /**
864
1028
  * Push the current store contents into a bundler instance (which owns
865
1029
  * connections / tools). Wires up a store watcher so external mutations
866
1030
  * (e.g. another shell running `alfe mcp add`) re-reconcile.