Skip to content

Commit d3a8e83

Browse files
raymondginger2018-sudoRaymo
authored andcommitted
test(mcp): cover bounded parallel startup of MCP servers
Assert that initialize() runs connects concurrently (peak in flight > 1) while staying inside the batch bound, and that a disconnect raised during startup starts no further batch. Both use a stubbed connectServer so the test exercises scheduling only and does not spawn child processes. Reverting the batch loop back to serial makes the first test fail with "expected overlapping connections, saw peak=1".
1 parent 91f26a0 commit d3a8e83

1 file changed

Lines changed: 65 additions & 0 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { test } from "node:test";
2+
import assert from "node:assert/strict";
3+
import { McpManager } from "../mcp/mcp-manager";
4+
5+
// CONCURRENCY in initialize() mirrors the batch size asserted below.
6+
const BATCH_SIZE = 5;
7+
8+
function stubConnect(manager: McpManager, onStart?: (name: string) => void) {
9+
const started: string[] = [];
10+
let inFlight = 0;
11+
let peak = 0;
12+
13+
// Probe the scheduler instead of spawning real MCP processes: recording the
14+
// in-flight count proves batching without depending on wall-clock timing.
15+
(manager as any).connectServer = async (name: string) => {
16+
started.push(name);
17+
inFlight += 1;
18+
peak = Math.max(peak, inFlight);
19+
onStart?.(name);
20+
await new Promise((resolve) => setTimeout(resolve, 20));
21+
inFlight -= 1;
22+
};
23+
24+
return {
25+
started,
26+
peak: () => peak,
27+
};
28+
}
29+
30+
const servers = Object.fromEntries(
31+
Array.from({ length: 12 }, (_, index) => [`server-${index}`, { command: "node", args: ["-e", ""] }])
32+
);
33+
34+
test("initialize connects MCP servers in bounded parallel batches", async () => {
35+
const manager = new McpManager();
36+
const probe = stubConnect(manager);
37+
38+
await manager.initialize(servers);
39+
40+
assert.equal(probe.started.length, Object.keys(servers).length);
41+
assert.ok(probe.peak() > 1, `expected overlapping connections, saw peak=${probe.peak()}`);
42+
assert.ok(
43+
probe.peak() <= BATCH_SIZE,
44+
`expected at most ${BATCH_SIZE} concurrent connections, saw peak=${probe.peak()}`
45+
);
46+
});
47+
48+
test("initialize starts no further batch once disposed", async () => {
49+
const manager = new McpManager();
50+
let firstBatch = 0;
51+
const probe = stubConnect(manager, () => {
52+
firstBatch += 1;
53+
if (firstBatch === BATCH_SIZE) {
54+
manager.disconnect();
55+
}
56+
});
57+
58+
await manager.initialize(servers);
59+
60+
assert.ok(probe.started.length > 0, "expected the first batch to start");
61+
assert.ok(
62+
probe.started.length <= BATCH_SIZE,
63+
`expected no batch after disconnect, saw ${probe.started.length} servers started`
64+
);
65+
});

0 commit comments

Comments
 (0)