@alfe.ai/mcp-bundler 0.3.1 → 0.4.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 +145 -23
- package/dist/index.d.cts +85 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +85 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +145 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -90,6 +90,9 @@ var Connection = class Connection {
|
|
|
90
90
|
static RECONNECT_BACKOFF_MAX_MS = 3e4;
|
|
91
91
|
onUnexpectedClose;
|
|
92
92
|
onStderrLine;
|
|
93
|
+
connectTimeoutMs;
|
|
94
|
+
/** Whether any connect has ever been attempted — drives the eager retry sweep. */
|
|
95
|
+
connectAttempted = false;
|
|
93
96
|
constructor(params) {
|
|
94
97
|
this.name = params.name;
|
|
95
98
|
this.config = params.config;
|
|
@@ -97,6 +100,7 @@ var Connection = class Connection {
|
|
|
97
100
|
this.logger = params.logger;
|
|
98
101
|
this.onUnexpectedClose = params.onUnexpectedClose;
|
|
99
102
|
this.onStderrLine = params.onStderrLine;
|
|
103
|
+
this.connectTimeoutMs = params.connectTimeoutMs ?? 0;
|
|
100
104
|
}
|
|
101
105
|
/** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */
|
|
102
106
|
snapshotTools() {
|
|
@@ -118,6 +122,15 @@ var Connection = class Connection {
|
|
|
118
122
|
lastErrorMessage() {
|
|
119
123
|
return this.lastError;
|
|
120
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Whether a connect was ever attempted (success or failure). A reconciled
|
|
127
|
+
* connection that was never warmed has neither a client nor a `lastError` —
|
|
128
|
+
* this flag lets an eager host's retry sweep find it without also
|
|
129
|
+
* resurrecting cleanly-closed (idle-reaped) connections.
|
|
130
|
+
*/
|
|
131
|
+
hasConnectAttempted() {
|
|
132
|
+
return this.connectAttempted;
|
|
133
|
+
}
|
|
121
134
|
/** Idle timestamp for reaping. */
|
|
122
135
|
idleSinceMs() {
|
|
123
136
|
return Date.now() - this.lastUsedAt;
|
|
@@ -136,23 +149,33 @@ var Connection = class Connection {
|
|
|
136
149
|
return this.connectInFlight;
|
|
137
150
|
}
|
|
138
151
|
async connectAndDiscover() {
|
|
152
|
+
this.connectAttempted = true;
|
|
139
153
|
const safeConfig = "command" in this.config ? {
|
|
140
154
|
...this.config,
|
|
141
155
|
env: sanitizeStdioEnv(this.config.env)
|
|
142
156
|
} : this.config;
|
|
143
157
|
this.logger?.debug(`[mcp-bundler] connecting server "${this.name}"`);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
client = await this.deps.connect(safeConfig, {
|
|
158
|
+
const attempt = (async () => {
|
|
159
|
+
const c = await this.deps.connect(safeConfig, {
|
|
147
160
|
serverName: this.name,
|
|
148
161
|
onStderrLine: this.onStderrLine
|
|
149
162
|
});
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
163
|
+
try {
|
|
164
|
+
return {
|
|
165
|
+
client: c,
|
|
166
|
+
advertised: await c.listTools()
|
|
167
|
+
};
|
|
168
|
+
} catch (err) {
|
|
169
|
+
await c.close().catch(() => void 0);
|
|
170
|
+
throw err;
|
|
171
|
+
}
|
|
172
|
+
})();
|
|
173
|
+
try {
|
|
174
|
+
const { client, advertised } = await this.raceConnectTimeout(attempt);
|
|
175
|
+
this.client = client;
|
|
153
176
|
this.closing = false;
|
|
154
|
-
|
|
155
|
-
this.handleUnexpectedClose(
|
|
177
|
+
client.onClose?.(() => {
|
|
178
|
+
this.handleUnexpectedClose(client);
|
|
156
179
|
});
|
|
157
180
|
this.tools = advertised.map((t) => ({
|
|
158
181
|
prefixed: buildNamespacedToolName(this.name, t.name),
|
|
@@ -168,7 +191,7 @@ var Connection = class Connection {
|
|
|
168
191
|
this.reconnectBlockedUntilMs = 0;
|
|
169
192
|
this.logger?.info(`[mcp-bundler] server "${this.name}" connected, ${this.tools.length.toString()} tool(s)`);
|
|
170
193
|
} catch (err) {
|
|
171
|
-
|
|
194
|
+
attempt.then(({ client }) => client.close()).catch(() => void 0);
|
|
172
195
|
this.consecutiveFailures += 1;
|
|
173
196
|
this.lastError = err instanceof Error ? err.message : String(err);
|
|
174
197
|
const backoff = Math.min(Connection.RECONNECT_BACKOFF_BASE_MS * 2 ** (this.consecutiveFailures - 1), Connection.RECONNECT_BACKOFF_MAX_MS);
|
|
@@ -176,6 +199,23 @@ var Connection = class Connection {
|
|
|
176
199
|
throw err;
|
|
177
200
|
}
|
|
178
201
|
}
|
|
202
|
+
/** Race the attempt against `connectTimeoutMs`; 0 disables the bound. */
|
|
203
|
+
async raceConnectTimeout(attempt) {
|
|
204
|
+
const ms = this.connectTimeoutMs;
|
|
205
|
+
if (ms <= 0) return attempt;
|
|
206
|
+
let timer;
|
|
207
|
+
const timeout = new Promise((_, reject) => {
|
|
208
|
+
timer = setTimeout(() => {
|
|
209
|
+
reject(/* @__PURE__ */ new Error(`server "${this.name}" connect timed out after ${ms.toString()}ms — spawned but did not complete the MCP handshake/discovery`));
|
|
210
|
+
}, ms);
|
|
211
|
+
if (typeof timer === "object" && "unref" in timer) timer.unref();
|
|
212
|
+
});
|
|
213
|
+
try {
|
|
214
|
+
return await Promise.race([attempt, timeout]);
|
|
215
|
+
} finally {
|
|
216
|
+
clearTimeout(timer);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
179
219
|
/**
|
|
180
220
|
* Handle an unexpected transport close (crash / network drop). Clears the
|
|
181
221
|
* dead client + tools so the next `ensureConnected` re-spawns. No-op if we
|
|
@@ -335,6 +375,8 @@ async function defaultConnect(server, ctx) {
|
|
|
335
375
|
//#region src/bundler.ts
|
|
336
376
|
const DEFAULT_IDLE_TTL_MS = 600 * 1e3;
|
|
337
377
|
const DEFAULT_IDLE_SWEEP_INTERVAL_MS = 60 * 1e3;
|
|
378
|
+
const DEFAULT_RETRY_SWEEP_INTERVAL_MS = 60 * 1e3;
|
|
379
|
+
const DEFAULT_CONNECT_TIMEOUT_MS = 120 * 1e3;
|
|
338
380
|
/** First text content of an error result, for host error reporting. */
|
|
339
381
|
function extractErrorText(result) {
|
|
340
382
|
for (const item of result.content) if (item.type === "text" && typeof item.text === "string") return item.text.slice(0, 500);
|
|
@@ -355,6 +397,11 @@ var McpBundler = class {
|
|
|
355
397
|
idleTtlMs;
|
|
356
398
|
idleSweepIntervalMs;
|
|
357
399
|
idleSweepTimer;
|
|
400
|
+
retrySweepIntervalMs;
|
|
401
|
+
retrySweepTimer;
|
|
402
|
+
retrySweepInFlight = false;
|
|
403
|
+
connectTimeoutMs;
|
|
404
|
+
retryNeverConnected;
|
|
358
405
|
deps;
|
|
359
406
|
onToolError;
|
|
360
407
|
onServerCrash;
|
|
@@ -365,11 +412,15 @@ var McpBundler = class {
|
|
|
365
412
|
this.logger = opts.logger;
|
|
366
413
|
this.idleTtlMs = opts.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
|
|
367
414
|
this.idleSweepIntervalMs = opts.idleSweepIntervalMs ?? DEFAULT_IDLE_SWEEP_INTERVAL_MS;
|
|
415
|
+
this.retrySweepIntervalMs = opts.retrySweepIntervalMs ?? DEFAULT_RETRY_SWEEP_INTERVAL_MS;
|
|
416
|
+
this.connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
417
|
+
this.retryNeverConnected = opts.retryNeverConnected ?? false;
|
|
368
418
|
this.deps = deps ?? { connect: defaultConnect };
|
|
369
419
|
this.onToolError = opts.onToolError;
|
|
370
420
|
this.onServerCrash = opts.onServerCrash;
|
|
371
421
|
this.onServerStderr = opts.onServerStderr;
|
|
372
422
|
if (this.idleTtlMs > 0) this.startIdleSweep();
|
|
423
|
+
if (this.retrySweepIntervalMs > 0) this.startRetrySweep();
|
|
373
424
|
}
|
|
374
425
|
/** Construct a Connection with the host hooks bound to its server name. */
|
|
375
426
|
buildConnection(name, config) {
|
|
@@ -380,6 +431,7 @@ var McpBundler = class {
|
|
|
380
431
|
config,
|
|
381
432
|
deps: this.deps,
|
|
382
433
|
logger: this.logger,
|
|
434
|
+
connectTimeoutMs: config.connectionTimeoutMs ?? this.connectTimeoutMs,
|
|
383
435
|
...crash ? { onUnexpectedClose: () => {
|
|
384
436
|
crash(name);
|
|
385
437
|
} } : {},
|
|
@@ -486,6 +538,40 @@ var McpBundler = class {
|
|
|
486
538
|
}
|
|
487
539
|
}));
|
|
488
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* Re-attempt every server that is NOT connected but HAS a recorded connect
|
|
543
|
+
* failure (`connected: false && lastError`). This is the self-heal path: a
|
|
544
|
+
* server that failed its first warm — because its backing credential /
|
|
545
|
+
* account / network wasn't resolvable yet — gets reconnected once that
|
|
546
|
+
* dependency appears, WITHOUT needing an operator to restart the daemon.
|
|
547
|
+
*
|
|
548
|
+
* Convergence: this only calls `ensureConnected()`, which honours each
|
|
549
|
+
* Connection's own exponential reconnect backoff (`reconnectBlockedUntilMs`,
|
|
550
|
+
* 500ms → 30s cap). A server that keeps `exit(1)`-ing fast-fails while in
|
|
551
|
+
* backoff, so repeated sweeps are cheap and never become a tight crash-loop
|
|
552
|
+
* — the backoff widens with each failure. Servers that connect on their
|
|
553
|
+
* first warm are left alone, as are cleanly-closed (idle-reaped) ones.
|
|
554
|
+
*
|
|
555
|
+
* Lazily-added servers that were NEVER attempted (no client, no
|
|
556
|
+
* `lastError`) are also left alone by default — but with
|
|
557
|
+
* `retryNeverConnected` (the daemon's eager mode) the sweep picks them up,
|
|
558
|
+
* so a server added after the host's warmup pass self-heals within one
|
|
559
|
+
* sweep instead of stranding its tools until a restart.
|
|
560
|
+
*
|
|
561
|
+
* Returns the statuses of the servers it attempted (empty if none needed a
|
|
562
|
+
* retry). Never throws — per-server failures are reflected in the status.
|
|
563
|
+
*/
|
|
564
|
+
async retryFailed() {
|
|
565
|
+
if (this.disposed) return [];
|
|
566
|
+
const targets = Array.from(this.connections.values()).filter((conn) => !conn.isConnected() && (conn.lastErrorMessage() !== void 0 || this.retryNeverConnected && !conn.hasConnectAttempted()));
|
|
567
|
+
if (targets.length === 0) return [];
|
|
568
|
+
return (await Promise.allSettled(targets.map(async (conn) => {
|
|
569
|
+
try {
|
|
570
|
+
await conn.ensureConnected();
|
|
571
|
+
} catch {}
|
|
572
|
+
return this.statusOf(conn);
|
|
573
|
+
}))).flatMap((r) => r.status === "fulfilled" ? [r.value] : []);
|
|
574
|
+
}
|
|
489
575
|
/** Build the live status descriptor for one connection. */
|
|
490
576
|
statusOf(conn) {
|
|
491
577
|
const status = {
|
|
@@ -615,6 +701,10 @@ var McpBundler = class {
|
|
|
615
701
|
clearInterval(this.idleSweepTimer);
|
|
616
702
|
this.idleSweepTimer = void 0;
|
|
617
703
|
}
|
|
704
|
+
if (this.retrySweepTimer) {
|
|
705
|
+
clearInterval(this.retrySweepTimer);
|
|
706
|
+
this.retrySweepTimer = void 0;
|
|
707
|
+
}
|
|
618
708
|
await Promise.allSettled(Array.from(this.connections.values()).map((c) => c.close()));
|
|
619
709
|
this.connections.clear();
|
|
620
710
|
}
|
|
@@ -626,6 +716,28 @@ var McpBundler = class {
|
|
|
626
716
|
}, this.idleSweepIntervalMs);
|
|
627
717
|
if (typeof this.idleSweepTimer === "object" && "unref" in this.idleSweepTimer) this.idleSweepTimer.unref();
|
|
628
718
|
}
|
|
719
|
+
startRetrySweep() {
|
|
720
|
+
this.retrySweepTimer = setInterval(() => {
|
|
721
|
+
if (this.retrySweepInFlight) return;
|
|
722
|
+
this.retrySweepInFlight = true;
|
|
723
|
+
this.retryFailed().then((attempted) => {
|
|
724
|
+
if (attempted.length === 0) return;
|
|
725
|
+
const healed = attempted.filter((s) => s.connected);
|
|
726
|
+
const stillDown = attempted.filter((s) => !s.connected);
|
|
727
|
+
this.logger?.debug("[mcp-bundler] retry sweep", {
|
|
728
|
+
attempted: attempted.length,
|
|
729
|
+
healed: healed.length,
|
|
730
|
+
stillDown: stillDown.length
|
|
731
|
+
});
|
|
732
|
+
for (const s of healed) this.logger?.info(`[mcp-bundler] server "${s.name}" self-healed on retry sweep`, { toolCount: s.toolCount });
|
|
733
|
+
}).catch((err) => {
|
|
734
|
+
this.logger?.warn("[mcp-bundler] retry sweep error", { err: err instanceof Error ? err.message : String(err) });
|
|
735
|
+
}).finally(() => {
|
|
736
|
+
this.retrySweepInFlight = false;
|
|
737
|
+
});
|
|
738
|
+
}, this.retrySweepIntervalMs);
|
|
739
|
+
if (typeof this.retrySweepTimer === "object" && "unref" in this.retrySweepTimer) this.retrySweepTimer.unref();
|
|
740
|
+
}
|
|
629
741
|
async sweepIdle() {
|
|
630
742
|
if (this.idleTtlMs <= 0) return;
|
|
631
743
|
const targets = [];
|
|
@@ -930,8 +1042,11 @@ var Manager = class {
|
|
|
930
1042
|
/**
|
|
931
1043
|
* Register or overwrite a server entry. Mutation lands in the store
|
|
932
1044
|
* synchronously; if a bundler has been attached via `loadIntoBundler`,
|
|
933
|
-
* it
|
|
934
|
-
* thrown — the store is the source of truth, the bundler is
|
|
1045
|
+
* it is re-reconciled BEFORE `onChange` listeners fire (errors logged,
|
|
1046
|
+
* never thrown — the store is the source of truth, the bundler is
|
|
1047
|
+
* derived). The ordering is load-bearing: the daemon's `onChange`
|
|
1048
|
+
* handler warms the bundler's current connections, so the just-added
|
|
1049
|
+
* server must already have its Connection object or it is never warmed.
|
|
935
1050
|
*/
|
|
936
1051
|
async addServer(config, opts) {
|
|
937
1052
|
if (!opts.id) throw new Error("Manager.addServer: id is required");
|
|
@@ -952,9 +1067,8 @@ var Manager = class {
|
|
|
952
1067
|
}
|
|
953
1068
|
};
|
|
954
1069
|
});
|
|
955
|
-
this.
|
|
1070
|
+
await this.reconcileBundlerLogged();
|
|
956
1071
|
this.fireChange();
|
|
957
|
-
return Promise.resolve();
|
|
958
1072
|
}
|
|
959
1073
|
/**
|
|
960
1074
|
* Remove a single server entry. No-op if the id isn't in the store.
|
|
@@ -962,17 +1076,17 @@ var Manager = class {
|
|
|
962
1076
|
* when supplied — the CLI uses this to guard `alfe mcp remove` from
|
|
963
1077
|
* accidentally clobbering integration- or cli-owned entries.
|
|
964
1078
|
*/
|
|
965
|
-
removeServer(id, opts = {}) {
|
|
1079
|
+
async removeServer(id, opts = {}) {
|
|
966
1080
|
const existing = lookupEntry(this.store.read().servers, id);
|
|
967
|
-
if (!existing) return
|
|
968
|
-
if (opts.expectedOwner && existing.owner !== opts.expectedOwner)
|
|
1081
|
+
if (!existing) return false;
|
|
1082
|
+
if (opts.expectedOwner && existing.owner !== opts.expectedOwner) throw new Error(`Manager.removeServer: server "${id}" is owned by "${existing.owner}", not "${opts.expectedOwner}"`);
|
|
969
1083
|
this.store.update((cur) => ({
|
|
970
1084
|
...cur,
|
|
971
1085
|
servers: Object.fromEntries(Object.entries(cur.servers).filter(([k]) => k !== id))
|
|
972
1086
|
}));
|
|
973
|
-
this.
|
|
1087
|
+
await this.reconcileBundlerLogged();
|
|
974
1088
|
this.fireChange();
|
|
975
|
-
return
|
|
1089
|
+
return true;
|
|
976
1090
|
}
|
|
977
1091
|
/** Drop every entry whose owner matches — used by integration uninstall. */
|
|
978
1092
|
async removeServersByOwner(owner) {
|
|
@@ -988,10 +1102,10 @@ var Manager = class {
|
|
|
988
1102
|
};
|
|
989
1103
|
});
|
|
990
1104
|
if (removed.length > 0) {
|
|
991
|
-
this.
|
|
1105
|
+
await this.reconcileBundlerLogged();
|
|
992
1106
|
this.fireChange();
|
|
993
1107
|
}
|
|
994
|
-
return
|
|
1108
|
+
return removed;
|
|
995
1109
|
}
|
|
996
1110
|
/** Read-only snapshot for `alfe mcp list` and similar UIs. */
|
|
997
1111
|
listServers() {
|
|
@@ -1061,11 +1175,19 @@ var Manager = class {
|
|
|
1061
1175
|
this.bundler = void 0;
|
|
1062
1176
|
return Promise.resolve();
|
|
1063
1177
|
}
|
|
1064
|
-
|
|
1178
|
+
/**
|
|
1179
|
+
* Awaited by every mutator so `onChange` listeners observe a bundler
|
|
1180
|
+
* that already contains the mutation. Reconcile failures are logged,
|
|
1181
|
+
* never thrown — a mutation must not fail because the derived bundler
|
|
1182
|
+
* hiccuped.
|
|
1183
|
+
*/
|
|
1184
|
+
async reconcileBundlerLogged() {
|
|
1065
1185
|
if (!this.bundler) return;
|
|
1066
|
-
|
|
1186
|
+
try {
|
|
1187
|
+
await this.reconcileBundler();
|
|
1188
|
+
} catch (err) {
|
|
1067
1189
|
this.logger?.warn("[mcp-bundler/manager] bundler reconcile failed", { err: errMsg(err) });
|
|
1068
|
-
}
|
|
1190
|
+
}
|
|
1069
1191
|
}
|
|
1070
1192
|
async reconcileBundler() {
|
|
1071
1193
|
if (!this.bundler) return;
|
package/dist/index.d.cts
CHANGED
|
@@ -9,6 +9,8 @@ interface StdioServerConfig {
|
|
|
9
9
|
args?: string[];
|
|
10
10
|
env?: Record<string, string>;
|
|
11
11
|
cwd?: string;
|
|
12
|
+
/** Per-server connect+discovery bound (ms) — overrides `BundlerOptions.connectTimeoutMs`. */
|
|
13
|
+
connectionTimeoutMs?: number;
|
|
12
14
|
}
|
|
13
15
|
interface RemoteServerConfig {
|
|
14
16
|
url: string;
|
|
@@ -92,6 +94,33 @@ interface BundlerOptions {
|
|
|
92
94
|
idleTtlMs?: number;
|
|
93
95
|
/** Sweep interval for idle reaping (ms). Default: 60_000 (1 min). */
|
|
94
96
|
idleSweepIntervalMs?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Sweep interval for re-attempting servers that failed to connect
|
|
99
|
+
* (`connected: false && lastError`), so a server whose backing
|
|
100
|
+
* credential/account/network appears AFTER the first warm self-heals without
|
|
101
|
+
* an operator restart (ms). 0 disables. Default: 60_000 (1 min). The
|
|
102
|
+
* fine-grained backoff still lives on each Connection — this is the coarse
|
|
103
|
+
* outer bound that keeps re-checking.
|
|
104
|
+
*/
|
|
105
|
+
retrySweepIntervalMs?: number;
|
|
106
|
+
/**
|
|
107
|
+
* Upper bound (ms) on one connect + tool-discovery attempt. A hung child —
|
|
108
|
+
* or a cold `npx` download that never completes the MCP handshake — throws
|
|
109
|
+
* past this bound, which records `lastError` and arms reconnect backoff so
|
|
110
|
+
* the retry sweep can re-attempt. Without a bound such a server has no
|
|
111
|
+
* error, no diagnostics, and no self-heal path. Per-server
|
|
112
|
+
* `connectionTimeoutMs` overrides. 0 disables. Default: 120_000 (generous
|
|
113
|
+
* enough for a cold `npx` package download).
|
|
114
|
+
*/
|
|
115
|
+
connectTimeoutMs?: number;
|
|
116
|
+
/**
|
|
117
|
+
* Also have the retry sweep attempt servers that have NEVER had a connect
|
|
118
|
+
* attempt (e.g. added after the host's warmup pass already ran). Off by
|
|
119
|
+
* default to preserve pure-lazy embedding semantics; the daemon — which
|
|
120
|
+
* eagerly warms everything — enables it so a missed warm self-heals within
|
|
121
|
+
* one sweep instead of stranding the server's tools until a restart.
|
|
122
|
+
*/
|
|
123
|
+
retryNeverConnected?: boolean;
|
|
95
124
|
/**
|
|
96
125
|
* Host hook fired when a tool call fails — thrown or `isError` result.
|
|
97
126
|
* Invoked best-effort (exceptions swallowed); must not block.
|
|
@@ -182,6 +211,9 @@ declare class Connection {
|
|
|
182
211
|
private static readonly RECONNECT_BACKOFF_MAX_MS;
|
|
183
212
|
private readonly onUnexpectedClose;
|
|
184
213
|
private readonly onStderrLine;
|
|
214
|
+
private readonly connectTimeoutMs;
|
|
215
|
+
/** Whether any connect has ever been attempted — drives the eager retry sweep. */
|
|
216
|
+
private connectAttempted;
|
|
185
217
|
constructor(params: {
|
|
186
218
|
name: string;
|
|
187
219
|
config: McpServerConfig;
|
|
@@ -191,6 +223,8 @@ declare class Connection {
|
|
|
191
223
|
onUnexpectedClose?: () => void;
|
|
192
224
|
/** Threaded to the connect factory — pipes stdio child stderr when set. */
|
|
193
225
|
onStderrLine?: (line: string) => void;
|
|
226
|
+
/** Bound (ms) on one connect + discovery attempt. 0/undefined disables. */
|
|
227
|
+
connectTimeoutMs?: number;
|
|
194
228
|
});
|
|
195
229
|
/** Returns the most recent known tool list. May be empty if the server hasn't connected yet. */
|
|
196
230
|
snapshotTools(): McpToolDescriptor[];
|
|
@@ -202,6 +236,13 @@ declare class Connection {
|
|
|
202
236
|
failureCount(): number;
|
|
203
237
|
/** Message from the most recent failed connect attempt, if any. */
|
|
204
238
|
lastErrorMessage(): string | undefined;
|
|
239
|
+
/**
|
|
240
|
+
* Whether a connect was ever attempted (success or failure). A reconciled
|
|
241
|
+
* connection that was never warmed has neither a client nor a `lastError` —
|
|
242
|
+
* this flag lets an eager host's retry sweep find it without also
|
|
243
|
+
* resurrecting cleanly-closed (idle-reaped) connections.
|
|
244
|
+
*/
|
|
245
|
+
hasConnectAttempted(): boolean;
|
|
205
246
|
/** Idle timestamp for reaping. */
|
|
206
247
|
idleSinceMs(): number;
|
|
207
248
|
/**
|
|
@@ -210,6 +251,8 @@ declare class Connection {
|
|
|
210
251
|
*/
|
|
211
252
|
ensureConnected(): Promise<void>;
|
|
212
253
|
private connectAndDiscover;
|
|
254
|
+
/** Race the attempt against `connectTimeoutMs`; 0 disables the bound. */
|
|
255
|
+
private raceConnectTimeout;
|
|
213
256
|
/**
|
|
214
257
|
* Handle an unexpected transport close (crash / network drop). Clears the
|
|
215
258
|
* dead client + tools so the next `ensureConnected` re-spawns. No-op if we
|
|
@@ -259,6 +302,11 @@ declare class McpBundler {
|
|
|
259
302
|
private readonly idleTtlMs;
|
|
260
303
|
private readonly idleSweepIntervalMs;
|
|
261
304
|
private idleSweepTimer;
|
|
305
|
+
private readonly retrySweepIntervalMs;
|
|
306
|
+
private retrySweepTimer;
|
|
307
|
+
private retrySweepInFlight;
|
|
308
|
+
private readonly connectTimeoutMs;
|
|
309
|
+
private readonly retryNeverConnected;
|
|
262
310
|
private readonly deps;
|
|
263
311
|
private readonly onToolError;
|
|
264
312
|
private readonly onServerCrash;
|
|
@@ -296,6 +344,30 @@ declare class McpBundler {
|
|
|
296
344
|
* swallowed per-server (logged), so one bad server doesn't fail the batch.
|
|
297
345
|
*/
|
|
298
346
|
warmup(): Promise<void>;
|
|
347
|
+
/**
|
|
348
|
+
* Re-attempt every server that is NOT connected but HAS a recorded connect
|
|
349
|
+
* failure (`connected: false && lastError`). This is the self-heal path: a
|
|
350
|
+
* server that failed its first warm — because its backing credential /
|
|
351
|
+
* account / network wasn't resolvable yet — gets reconnected once that
|
|
352
|
+
* dependency appears, WITHOUT needing an operator to restart the daemon.
|
|
353
|
+
*
|
|
354
|
+
* Convergence: this only calls `ensureConnected()`, which honours each
|
|
355
|
+
* Connection's own exponential reconnect backoff (`reconnectBlockedUntilMs`,
|
|
356
|
+
* 500ms → 30s cap). A server that keeps `exit(1)`-ing fast-fails while in
|
|
357
|
+
* backoff, so repeated sweeps are cheap and never become a tight crash-loop
|
|
358
|
+
* — the backoff widens with each failure. Servers that connect on their
|
|
359
|
+
* first warm are left alone, as are cleanly-closed (idle-reaped) ones.
|
|
360
|
+
*
|
|
361
|
+
* Lazily-added servers that were NEVER attempted (no client, no
|
|
362
|
+
* `lastError`) are also left alone by default — but with
|
|
363
|
+
* `retryNeverConnected` (the daemon's eager mode) the sweep picks them up,
|
|
364
|
+
* so a server added after the host's warmup pass self-heals within one
|
|
365
|
+
* sweep instead of stranding its tools until a restart.
|
|
366
|
+
*
|
|
367
|
+
* Returns the statuses of the servers it attempted (empty if none needed a
|
|
368
|
+
* retry). Never throws — per-server failures are reflected in the status.
|
|
369
|
+
*/
|
|
370
|
+
retryFailed(): Promise<McpServerStatus[]>;
|
|
299
371
|
/** Build the live status descriptor for one connection. */
|
|
300
372
|
private statusOf;
|
|
301
373
|
/** Live status for every known server. Synchronous snapshot, no I/O. */
|
|
@@ -331,6 +403,7 @@ declare class McpBundler {
|
|
|
331
403
|
*/
|
|
332
404
|
dispose(): Promise<void>;
|
|
333
405
|
private startIdleSweep;
|
|
406
|
+
private startRetrySweep;
|
|
334
407
|
private sweepIdle;
|
|
335
408
|
}
|
|
336
409
|
//# sourceMappingURL=bundler.d.ts.map
|
|
@@ -512,8 +585,11 @@ declare class Manager {
|
|
|
512
585
|
/**
|
|
513
586
|
* Register or overwrite a server entry. Mutation lands in the store
|
|
514
587
|
* synchronously; if a bundler has been attached via `loadIntoBundler`,
|
|
515
|
-
* it
|
|
516
|
-
* thrown — the store is the source of truth, the bundler is
|
|
588
|
+
* it is re-reconciled BEFORE `onChange` listeners fire (errors logged,
|
|
589
|
+
* never thrown — the store is the source of truth, the bundler is
|
|
590
|
+
* derived). The ordering is load-bearing: the daemon's `onChange`
|
|
591
|
+
* handler warms the bundler's current connections, so the just-added
|
|
592
|
+
* server must already have its Connection object or it is never warmed.
|
|
517
593
|
*/
|
|
518
594
|
addServer(config: McpServerConfig, opts: AddServerOptions): Promise<void>;
|
|
519
595
|
/**
|
|
@@ -564,7 +640,13 @@ declare class Manager {
|
|
|
564
640
|
* shared instance survives multi-manager environments (rare).
|
|
565
641
|
*/
|
|
566
642
|
dispose(): Promise<void>;
|
|
567
|
-
|
|
643
|
+
/**
|
|
644
|
+
* Awaited by every mutator so `onChange` listeners observe a bundler
|
|
645
|
+
* that already contains the mutation. Reconcile failures are logged,
|
|
646
|
+
* never thrown — a mutation must not fail because the derived bundler
|
|
647
|
+
* hiccuped.
|
|
648
|
+
*/
|
|
649
|
+
private reconcileBundlerLogged;
|
|
568
650
|
private reconcileBundler;
|
|
569
651
|
private fireChange;
|
|
570
652
|
}
|
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;
|
|
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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"}
|