From b5a736e2b82fd35b22535ef52f732b9331f982d9 Mon Sep 17 00:00:00 2001 From: MumuTW Date: Fri, 6 Mar 2026 10:59:04 +0000 Subject: [PATCH] fix: prevent SSE transport crash on concurrent STDIO connections Move webAppTransports map registration to after SSEServerTransport.start() completes in both /stdio and /sse endpoints. Previously, the transport was registered before being fully connected, allowing the /message endpoint to access it prematurely and causing "Not connected" errors when multiple STDIO connection requests arrived in quick succession. Also add .catch() handlers to stderr forwarding send() and close() calls to prevent unhandled rejections when the SSE connection drops while the server process is still running. Fixes #1014 Co-Authored-By: Claude Opus 4.6 --- server/src/index.ts | 54 +++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/server/src/index.ts b/server/src/index.ts index 4d1fffa29..e7c03c832 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -596,7 +596,6 @@ app.get( const endpoint = `${prefix}/message`; const webAppTransport = new SSEServerTransport(endpoint, res); - webAppTransports.set(webAppTransport.sessionId, webAppTransport); console.log("Created client transport"); serverTransports.set(webAppTransport.sessionId, serverTransport); @@ -604,23 +603,27 @@ app.get( await webAppTransport.start(); + webAppTransports.set(webAppTransport.sessionId, webAppTransport); + (serverTransport as StdioClientTransport).stderr!.on("data", (chunk) => { if (chunk.toString().includes("MODULE_NOT_FOUND")) { // Server command not found, remove transports const message = "Command not found, transports removed"; - webAppTransport.send({ - jsonrpc: "2.0", - method: "notifications/message", - params: { - level: "emergency", - logger: "proxy", - data: { - message, + webAppTransport + .send({ + jsonrpc: "2.0", + method: "notifications/message", + params: { + level: "emergency", + logger: "proxy", + data: { + message, + }, }, - }, - }); - webAppTransport.close(); - serverTransport.close(); + }) + .catch(() => {}); + webAppTransport.close().catch(() => {}); + serverTransport.close().catch(() => {}); webAppTransports.delete(webAppTransport.sessionId); serverTransports.delete(webAppTransport.sessionId); sessionHeaderHolders.delete(webAppTransport.sessionId); @@ -658,17 +661,19 @@ app.get( } else { level = "info"; } - webAppTransport.send({ - jsonrpc: "2.0", - method: "notifications/message", - params: { - level, - logger: "stdio", - data: { - message, + webAppTransport + .send({ + jsonrpc: "2.0", + method: "notifications/message", + params: { + level, + logger: "stdio", + data: { + message, + }, }, - }, - }); + }) + .catch(() => {}); } }); @@ -707,7 +712,6 @@ app.get( const endpoint = `${prefix}/message`; const webAppTransport = new SSEServerTransport(endpoint, res); - webAppTransports.set(webAppTransport.sessionId, webAppTransport); console.log("Created client transport"); serverTransports.set(webAppTransport.sessionId, serverTransport!); // eslint-disable-line @typescript-eslint/no-non-null-assertion @@ -718,6 +722,8 @@ app.get( await webAppTransport.start(); + webAppTransports.set(webAppTransport.sessionId, webAppTransport); + mcpProxy({ transportToClient: webAppTransport, transportToServer: serverTransport,