@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.cjs +184 -20
- package/dist/index.d.cts +121 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +121 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +184 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -82,15 +82,21 @@ var Connection = class Connection {
|
|
|
82
82
|
closing = false;
|
|
83
83
|
/** Consecutive failed connect attempts — drives reconnect backoff. */
|
|
84
84
|
consecutiveFailures = 0;
|
|
85
|
+
/** Message from the most recent failed connect attempt; cleared on success. */
|
|
86
|
+
lastError;
|
|
85
87
|
/** Epoch ms before which re-connect attempts fast-fail (crash-loop guard). */
|
|
86
88
|
reconnectBlockedUntilMs = 0;
|
|
87
89
|
static RECONNECT_BACKOFF_BASE_MS = 500;
|
|
88
90
|
static RECONNECT_BACKOFF_MAX_MS = 3e4;
|
|
91
|
+
onUnexpectedClose;
|
|
92
|
+
onStderrLine;
|
|
89
93
|
constructor(params) {
|
|
90
94
|
this.name = params.name;
|
|
91
95
|
this.config = params.config;
|
|
92
96
|
this.deps = params.deps;
|
|
93
97
|
this.logger = params.logger;
|
|
98
|
+
this.onUnexpectedClose = params.onUnexpectedClose;
|
|
99
|
+
this.onStderrLine = params.onStderrLine;
|
|
94
100
|
}
|
|
95
101
|
/** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */
|
|
96
102
|
snapshotTools() {
|
|
@@ -100,6 +106,18 @@ var Connection = class Connection {
|
|
|
100
106
|
isConnected() {
|
|
101
107
|
return this.client !== void 0;
|
|
102
108
|
}
|
|
109
|
+
/** Tools currently advertised; 0 until the server connects + discovers. */
|
|
110
|
+
toolCount() {
|
|
111
|
+
return this.tools.length;
|
|
112
|
+
}
|
|
113
|
+
/** Consecutive failed connect attempts; 0 when healthy. */
|
|
114
|
+
failureCount() {
|
|
115
|
+
return this.consecutiveFailures;
|
|
116
|
+
}
|
|
117
|
+
/** Message from the most recent failed connect attempt, if any. */
|
|
118
|
+
lastErrorMessage() {
|
|
119
|
+
return this.lastError;
|
|
120
|
+
}
|
|
103
121
|
/** Idle timestamp for reaping. */
|
|
104
122
|
idleSinceMs() {
|
|
105
123
|
return Date.now() - this.lastUsedAt;
|
|
@@ -123,13 +141,18 @@ var Connection = class Connection {
|
|
|
123
141
|
env: sanitizeStdioEnv(this.config.env)
|
|
124
142
|
} : this.config;
|
|
125
143
|
this.logger?.debug(`[mcp-bundler] connecting server "${this.name}"`);
|
|
126
|
-
|
|
144
|
+
let client;
|
|
127
145
|
try {
|
|
146
|
+
client = await this.deps.connect(safeConfig, {
|
|
147
|
+
serverName: this.name,
|
|
148
|
+
onStderrLine: this.onStderrLine
|
|
149
|
+
});
|
|
128
150
|
const advertised = await client.listTools();
|
|
129
|
-
|
|
151
|
+
const connected = client;
|
|
152
|
+
this.client = connected;
|
|
130
153
|
this.closing = false;
|
|
131
|
-
|
|
132
|
-
this.handleUnexpectedClose(
|
|
154
|
+
connected.onClose?.(() => {
|
|
155
|
+
this.handleUnexpectedClose(connected);
|
|
133
156
|
});
|
|
134
157
|
this.tools = advertised.map((t) => ({
|
|
135
158
|
prefixed: buildNamespacedToolName(this.name, t.name),
|
|
@@ -141,11 +164,13 @@ var Connection = class Connection {
|
|
|
141
164
|
}));
|
|
142
165
|
this.lastUsedAt = Date.now();
|
|
143
166
|
this.consecutiveFailures = 0;
|
|
167
|
+
this.lastError = void 0;
|
|
144
168
|
this.reconnectBlockedUntilMs = 0;
|
|
145
169
|
this.logger?.info(`[mcp-bundler] server "${this.name}" connected, ${this.tools.length.toString()} tool(s)`);
|
|
146
170
|
} catch (err) {
|
|
147
|
-
await client.close().catch(() => void 0);
|
|
171
|
+
if (client) await client.close().catch(() => void 0);
|
|
148
172
|
this.consecutiveFailures += 1;
|
|
173
|
+
this.lastError = err instanceof Error ? err.message : String(err);
|
|
149
174
|
const backoff = Math.min(Connection.RECONNECT_BACKOFF_BASE_MS * 2 ** (this.consecutiveFailures - 1), Connection.RECONNECT_BACKOFF_MAX_MS);
|
|
150
175
|
this.reconnectBlockedUntilMs = Date.now() + backoff;
|
|
151
176
|
throw err;
|
|
@@ -161,6 +186,9 @@ var Connection = class Connection {
|
|
|
161
186
|
this.logger?.warn(`[mcp-bundler] server "${this.name}" connection closed unexpectedly; will re-spawn on next use`);
|
|
162
187
|
this.client = void 0;
|
|
163
188
|
this.tools = [];
|
|
189
|
+
try {
|
|
190
|
+
this.onUnexpectedClose?.();
|
|
191
|
+
} catch {}
|
|
164
192
|
}
|
|
165
193
|
/**
|
|
166
194
|
* Re-discover tools. Used on reconnect or `tools/list_changed` notification.
|
|
@@ -228,7 +256,7 @@ var Connection = class Connection {
|
|
|
228
256
|
* Kept in a separate function so tests can substitute a mock without
|
|
229
257
|
* pulling the SDK into the test bundle.
|
|
230
258
|
*/
|
|
231
|
-
async function defaultConnect(server) {
|
|
259
|
+
async function defaultConnect(server, ctx) {
|
|
232
260
|
const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
|
|
233
261
|
const client = new Client({
|
|
234
262
|
name: "alfe-mcp-bundler",
|
|
@@ -237,12 +265,27 @@ async function defaultConnect(server) {
|
|
|
237
265
|
if ("command" in server) {
|
|
238
266
|
const stdio = server;
|
|
239
267
|
const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
|
|
268
|
+
const onStderrLine = ctx?.onStderrLine;
|
|
240
269
|
const transport = new StdioClientTransport({
|
|
241
270
|
command: stdio.command,
|
|
242
271
|
args: stdio.args ?? [],
|
|
243
272
|
env: { ...sanitizeStdioEnv(stdio.env) },
|
|
244
|
-
cwd: stdio.cwd
|
|
273
|
+
cwd: stdio.cwd,
|
|
274
|
+
...onStderrLine ? { stderr: "pipe" } : {}
|
|
245
275
|
});
|
|
276
|
+
if (onStderrLine) {
|
|
277
|
+
let carry = "";
|
|
278
|
+
transport.stderr?.on("data", (chunk) => {
|
|
279
|
+
const parts = (carry + chunk.toString()).split("\n");
|
|
280
|
+
carry = parts.pop() ?? "";
|
|
281
|
+
for (const line of parts) {
|
|
282
|
+
if (line.trim() === "") continue;
|
|
283
|
+
try {
|
|
284
|
+
onStderrLine(line);
|
|
285
|
+
} catch {}
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
246
289
|
await client.connect(transport);
|
|
247
290
|
} else {
|
|
248
291
|
const remote = server;
|
|
@@ -292,6 +335,11 @@ async function defaultConnect(server) {
|
|
|
292
335
|
//#region src/bundler.ts
|
|
293
336
|
const DEFAULT_IDLE_TTL_MS = 600 * 1e3;
|
|
294
337
|
const DEFAULT_IDLE_SWEEP_INTERVAL_MS = 60 * 1e3;
|
|
338
|
+
/** First text content of an error result, for host error reporting. */
|
|
339
|
+
function extractErrorText(result) {
|
|
340
|
+
for (const item of result.content) if (item.type === "text" && typeof item.text === "string") return item.text.slice(0, 500);
|
|
341
|
+
return "(no error text)";
|
|
342
|
+
}
|
|
295
343
|
/**
|
|
296
344
|
* Provider-agnostic MCP server bundler. Holds N MCP server connections,
|
|
297
345
|
* exposes a unified namespaced tool catalog, and routes calls to the right
|
|
@@ -308,6 +356,9 @@ var McpBundler = class {
|
|
|
308
356
|
idleSweepIntervalMs;
|
|
309
357
|
idleSweepTimer;
|
|
310
358
|
deps;
|
|
359
|
+
onToolError;
|
|
360
|
+
onServerCrash;
|
|
361
|
+
onServerStderr;
|
|
311
362
|
disposed = false;
|
|
312
363
|
reconcileLatch = Promise.resolve();
|
|
313
364
|
constructor(opts = {}, deps) {
|
|
@@ -315,8 +366,34 @@ var McpBundler = class {
|
|
|
315
366
|
this.idleTtlMs = opts.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
|
|
316
367
|
this.idleSweepIntervalMs = opts.idleSweepIntervalMs ?? DEFAULT_IDLE_SWEEP_INTERVAL_MS;
|
|
317
368
|
this.deps = deps ?? { connect: defaultConnect };
|
|
369
|
+
this.onToolError = opts.onToolError;
|
|
370
|
+
this.onServerCrash = opts.onServerCrash;
|
|
371
|
+
this.onServerStderr = opts.onServerStderr;
|
|
318
372
|
if (this.idleTtlMs > 0) this.startIdleSweep();
|
|
319
373
|
}
|
|
374
|
+
/** Construct a Connection with the host hooks bound to its server name. */
|
|
375
|
+
buildConnection(name, config) {
|
|
376
|
+
const crash = this.onServerCrash;
|
|
377
|
+
const stderr = this.onServerStderr;
|
|
378
|
+
return new Connection({
|
|
379
|
+
name,
|
|
380
|
+
config,
|
|
381
|
+
deps: this.deps,
|
|
382
|
+
logger: this.logger,
|
|
383
|
+
...crash ? { onUnexpectedClose: () => {
|
|
384
|
+
crash(name);
|
|
385
|
+
} } : {},
|
|
386
|
+
...stderr ? { onStderrLine: (line) => {
|
|
387
|
+
stderr(name, line);
|
|
388
|
+
} } : {}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
/** Fire the host's tool-error hook; exceptions must never affect the call path. */
|
|
392
|
+
reportToolError(info) {
|
|
393
|
+
try {
|
|
394
|
+
this.onToolError?.(info);
|
|
395
|
+
} catch {}
|
|
396
|
+
}
|
|
320
397
|
/**
|
|
321
398
|
* Diff `desired` against current connections, spawn newcomers, dispose
|
|
322
399
|
* removals, hot-restart on config change. Pull-based — call whenever the
|
|
@@ -349,24 +426,14 @@ var McpBundler = class {
|
|
|
349
426
|
for (const [name, config] of Object.entries(desired)) {
|
|
350
427
|
const existing = this.connections.get(name);
|
|
351
428
|
if (!existing) {
|
|
352
|
-
this.connections.set(name,
|
|
353
|
-
name,
|
|
354
|
-
config,
|
|
355
|
-
deps: this.deps,
|
|
356
|
-
logger: this.logger
|
|
357
|
-
}));
|
|
429
|
+
this.connections.set(name, this.buildConnection(name, config));
|
|
358
430
|
added.push(name);
|
|
359
431
|
continue;
|
|
360
432
|
}
|
|
361
433
|
const nextFingerprint = JSON.stringify(config);
|
|
362
434
|
if (existing.configFingerprint() !== nextFingerprint) {
|
|
363
435
|
await existing.close();
|
|
364
|
-
this.connections.set(name,
|
|
365
|
-
name,
|
|
366
|
-
config,
|
|
367
|
-
deps: this.deps,
|
|
368
|
-
logger: this.logger
|
|
369
|
-
}));
|
|
436
|
+
this.connections.set(name, this.buildConnection(name, config));
|
|
370
437
|
changed.push(name);
|
|
371
438
|
} else unchanged.push(name);
|
|
372
439
|
}
|
|
@@ -419,6 +486,64 @@ var McpBundler = class {
|
|
|
419
486
|
}
|
|
420
487
|
}));
|
|
421
488
|
}
|
|
489
|
+
/** Build the live status descriptor for one connection. */
|
|
490
|
+
statusOf(conn) {
|
|
491
|
+
const status = {
|
|
492
|
+
name: conn.name,
|
|
493
|
+
connected: conn.isConnected(),
|
|
494
|
+
toolCount: conn.toolCount(),
|
|
495
|
+
consecutiveFailures: conn.failureCount()
|
|
496
|
+
};
|
|
497
|
+
const err = conn.lastErrorMessage();
|
|
498
|
+
if (err !== void 0) status.lastError = err;
|
|
499
|
+
return status;
|
|
500
|
+
}
|
|
501
|
+
/** Live status for every known server. Synchronous snapshot, no I/O. */
|
|
502
|
+
statuses() {
|
|
503
|
+
return Array.from(this.connections.values()).map((conn) => this.statusOf(conn));
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Eagerly connect ONE server and return its resulting status. Unlike
|
|
507
|
+
* `warmup()` — which fans out over all servers and swallows failures with
|
|
508
|
+
* no return value — this surfaces the outcome so a caller (e.g. the daemon
|
|
509
|
+
* confirming an `alfe mcp add`) can report "connected, N tools" or the exact
|
|
510
|
+
* connect error back to the agent.
|
|
511
|
+
*
|
|
512
|
+
* Never throws: a connect failure (or a warm timeout) is reflected in the
|
|
513
|
+
* returned status (`connected: false`, `lastError` set). Returns `undefined`
|
|
514
|
+
* only when the named server isn't present in the bundler.
|
|
515
|
+
*/
|
|
516
|
+
async warmServer(name, timeoutMs) {
|
|
517
|
+
if (this.disposed) return void 0;
|
|
518
|
+
const conn = this.connections.get(name);
|
|
519
|
+
if (!conn) return void 0;
|
|
520
|
+
const connect = conn.ensureConnected();
|
|
521
|
+
connect.catch(() => void 0);
|
|
522
|
+
try {
|
|
523
|
+
if (timeoutMs !== void 0 && timeoutMs > 0) await this.raceTimeout(connect, timeoutMs, name);
|
|
524
|
+
else await connect;
|
|
525
|
+
} catch (err) {
|
|
526
|
+
const status = this.statusOf(conn);
|
|
527
|
+
status.lastError ??= err instanceof Error ? err.message : String(err);
|
|
528
|
+
return status;
|
|
529
|
+
}
|
|
530
|
+
return this.statusOf(conn);
|
|
531
|
+
}
|
|
532
|
+
/** Race a promise against a timeout, clearing the timer either way. */
|
|
533
|
+
async raceTimeout(p, timeoutMs, name) {
|
|
534
|
+
let timer;
|
|
535
|
+
const timeout = new Promise((_, reject) => {
|
|
536
|
+
timer = setTimeout(() => {
|
|
537
|
+
reject(/* @__PURE__ */ new Error(`server "${name}" warm timed out after ${timeoutMs.toString()}ms`));
|
|
538
|
+
}, timeoutMs);
|
|
539
|
+
if (typeof timer === "object" && "unref" in timer) timer.unref();
|
|
540
|
+
});
|
|
541
|
+
try {
|
|
542
|
+
await Promise.race([p, timeout]);
|
|
543
|
+
} finally {
|
|
544
|
+
clearTimeout(timer);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
422
547
|
/**
|
|
423
548
|
* Invoke a tool by its namespaced name. Routes to the originating server.
|
|
424
549
|
* Errors are returned as `{ isError: true, content: [...] }` so a failing
|
|
@@ -441,10 +566,25 @@ var McpBundler = class {
|
|
|
441
566
|
}]
|
|
442
567
|
};
|
|
443
568
|
try {
|
|
444
|
-
|
|
569
|
+
const result = await route.connection.callTool(route.original, args, signal);
|
|
570
|
+
if (result.isError) this.reportToolError({
|
|
571
|
+
server: route.connection.name,
|
|
572
|
+
tool: route.original,
|
|
573
|
+
prefixed,
|
|
574
|
+
kind: "result-error",
|
|
575
|
+
message: extractErrorText(result)
|
|
576
|
+
});
|
|
577
|
+
return result;
|
|
445
578
|
} catch (err) {
|
|
446
579
|
const msg = err instanceof Error ? err.message : String(err);
|
|
447
580
|
this.logger?.error(`[mcp-bundler] tool call failed for "${prefixed}"`, { err: msg });
|
|
581
|
+
this.reportToolError({
|
|
582
|
+
server: route.connection.name,
|
|
583
|
+
tool: route.original,
|
|
584
|
+
prefixed,
|
|
585
|
+
kind: "thrown",
|
|
586
|
+
message: msg
|
|
587
|
+
});
|
|
448
588
|
return {
|
|
449
589
|
isError: true,
|
|
450
590
|
content: [{
|
|
@@ -862,6 +1002,30 @@ var Manager = class {
|
|
|
862
1002
|
}));
|
|
863
1003
|
}
|
|
864
1004
|
/**
|
|
1005
|
+
* Live connection status per server from the attached bundler. Empty when
|
|
1006
|
+
* no bundler is attached (e.g. a CLI-only manager). Lets callers show
|
|
1007
|
+
* whether each registered server actually connected and how many tools it
|
|
1008
|
+
* advertises, instead of only the stored config.
|
|
1009
|
+
*/
|
|
1010
|
+
serverStatuses() {
|
|
1011
|
+
return this.bundler ? this.bundler.statuses() : [];
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Reconcile the bundler against the current store (so a just-added entry
|
|
1015
|
+
* has a connection object) then eagerly connect ONE server and return its
|
|
1016
|
+
* status. Used by the daemon to CONFIRM an `alfe mcp add` actually connected
|
|
1017
|
+
* before replying to the agent — turning the fire-and-forget warm into a
|
|
1018
|
+
* result the caller can report ("connected, N tools" or the real error).
|
|
1019
|
+
*
|
|
1020
|
+
* Returns `null` when no bundler is attached or the id isn't present; never
|
|
1021
|
+
* throws (a connect failure is carried in the returned status).
|
|
1022
|
+
*/
|
|
1023
|
+
async warmServer(id, timeoutMs) {
|
|
1024
|
+
if (!this.bundler) return null;
|
|
1025
|
+
await this.reconcileBundler();
|
|
1026
|
+
return await this.bundler.warmServer(id, timeoutMs) ?? null;
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
865
1029
|
* Push the current store contents into a bundler instance (which owns
|
|
866
1030
|
* connections / tools). Wires up a store watcher so external mutations
|
|
867
1031
|
* (e.g. another shell running `alfe mcp add`) re-reconcile.
|
package/dist/index.d.cts
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.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -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":";;AAIA;;;AAAkD,KAAtC,eAAA,GAAkB,iBAAoB,GAAA,kBAAA;AAAkB,UAEnD,iBAAA,CAFmD;EAEnD,OAAA,EAAA,MAAA;EAOA,IAAA,CAAA,EAAA,MAAA,EAAA;EAOL,GAAA,CAAA,EAXJ,MAWI,CAAA,MAAgB,EAAA,MAAA,CAAA;EAKX,GAAA,CAAA,EAAA,MAAA;AAejB;
|
|
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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"}
|