@@ -216,7 +216,7 @@ function createInMemoryFetch(
216216 } ;
217217}
218218
219- describe ( 'Server Startup Readiness with Real Config ' , ( ) => {
219+ describe ( 'Server Startup Readiness - Race Condition Test ' , ( ) => {
220220 let MCPHubCtor : MCPHubConstructor ;
221221 let MCPServerEndpointCtor : MCPServerEndpointConstructor ;
222222 const baseUrl = 'http://mcp.local/mcp' ;
@@ -228,64 +228,76 @@ describe('Server Startup Readiness with Real Config', () => {
228228 ) ) ;
229229 } ) ;
230230
231- it ( 'should wait for all processes to be ready before accepting MCP connections (using examples/config.json )' , async ( ) => {
231+ it ( 'should NOT return empty tools list when connecting during server initialization (BUG TEST )' , async ( ) => {
232232 // Use the REAL config file from examples/config.json
233233 const hub = new MCPHubCtor ( configPath , { port : 3100 } ) ;
234234
235- // Initialize hub - this loads config and starts all configured servers
236- await hub . initialize ( ) ;
237-
238- // Verify that at least one server connected successfully
239- const connections = Array . from ( hub . connections . values ( ) ) ;
240- const connectedServers = connections . filter (
241- ( c ) => c . status === 'connected' ,
242- ) ;
243-
244- // At least one server from the config should be connected
245- expect ( connectedServers . length ) . toBeGreaterThan ( 0 ) ;
246-
247- // Create the MCP endpoint AFTER hub initialization
235+ // Create the MCP endpoint IMMEDIATELY before starting initialization
236+ // This simulates the race condition: endpoint is ready but servers are not
248237 const endpoint = new MCPServerEndpointCtor ( hub , { stateless : true } ) ;
249238 const fetchImpl = createInMemoryFetch ( endpoint ) ;
250239
251- // Now attempt to connect to the MCP endpoint
240+ // Start hub initialization in the background (don't await yet)
241+ const initPromise = hub . initialize ( ) ;
242+
243+ // Immediately try to connect to the MCP endpoint while servers are still initializing
244+ // This is where the bug occurs: endpoint accepts connection but servers aren't ready
252245 const transport = new StreamableHTTPClientTransport ( new URL ( baseUrl ) , {
253246 fetch : fetchImpl ,
254247 } ) ;
255248 const client = new Client ( {
256- name : 'readiness -test-client' ,
249+ name : 'race-condition -test-client' ,
257250 version : '1.0.0' ,
258251 } ) ;
259252
260253 try {
261254 await client . connect ( transport ) ;
262255
263- // Request tools list - this is the key test
264- const toolsResult = await client . request (
256+ // Request tools list IMMEDIATELY - this should expose the race condition
257+ // BUG: Without the fix, this returns empty list because servers aren't ready yet
258+ // FIX: With the fix, this should wait for servers to be ready or return all tools
259+ const toolsResultDuringInit = await client . request (
265260 {
266261 method : 'tools/list' ,
267262 params : { } ,
268263 } ,
269264 ListToolsResultSchema ,
270265 ) ;
271266
272- // The bug was: when service starts and immediately connects to /mcp,
273- // it returns empty list because processes aren't ready yet
274- // After fix: tools list should contain tools from connected servers
275- expect ( toolsResult ) . toBeDefined ( ) ;
276- expect ( toolsResult . tools ) . toBeDefined ( ) ;
277- expect ( Array . isArray ( toolsResult . tools ) ) . toBe ( true ) ;
267+ console . log (
268+ `[DURING INIT] Received ${ toolsResultDuringInit . tools . length } tools` ,
269+ ) ;
278270
279- // With real MCP servers from examples/config.json, tools.length should be > 0
280- expect ( toolsResult . tools . length ) . toBeGreaterThan ( 0 ) ;
271+ // Wait for initialization to complete
272+ await initPromise ;
273+
274+ // Now request tools list again after initialization is complete
275+ const toolsResultAfterInit = await client . request (
276+ {
277+ method : 'tools/list' ,
278+ params : { } ,
279+ } ,
280+ ListToolsResultSchema ,
281+ ) ;
281282
282283 console . log (
283- `✓ Successfully received ${ toolsResult . tools . length } tools from MCP servers` ,
284+ `[AFTER INIT] Received ${ toolsResultAfterInit . tools . length } tools` ,
285+ ) ;
286+
287+ // THE KEY ASSERTION: Tools list should NEVER be empty
288+ // This tests the bug: without the fix, toolsResultDuringInit.tools.length === 0
289+ // With the fix, it should always return tools (either by waiting or by being ready)
290+ expect ( toolsResultDuringInit . tools . length ) . toBeGreaterThan ( 0 ) ;
291+ expect ( toolsResultAfterInit . tools . length ) . toBeGreaterThan ( 0 ) ;
292+
293+ // Both requests should return the same number of tools
294+ expect ( toolsResultDuringInit . tools . length ) . toBe (
295+ toolsResultAfterInit . tools . length ,
284296 ) ;
285297 } finally {
286298 await client . close ( ) ;
287299 await endpoint . close ( ) ;
288300 await hub . disconnectAll ( ) ;
289301 }
290- } , 60000 ) ; // Increased timeout for real server connections
302+ } , 60000 ) ;
291303} ) ;
0 commit comments