Skip to content

Commit f1eb488

Browse files
authored
Merge pull request #380 from wolfdancer/main
fix: handle the client disconnect so that the server does not crash.
2 parents 7b1299c + efa4959 commit f1eb488

File tree

1 file changed

+54
-17
lines changed

1 file changed

+54
-17
lines changed

server/src/index.ts

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ let backingServerTransport: Transport | undefined;
141141

142142
app.get("/mcp", async (req, res) => {
143143
const sessionId = req.headers["mcp-session-id"] as string;
144-
console.log(`Received GET message for sessionId ${sessionId}`);
144+
console.log(`GET /mcp for sessionId ${sessionId}`);
145145
try {
146146
const transport = webAppTransports.get(
147147
sessionId,
@@ -160,7 +160,7 @@ app.get("/mcp", async (req, res) => {
160160

161161
app.post("/mcp", async (req, res) => {
162162
const sessionId = req.headers["mcp-session-id"] as string | undefined;
163-
console.log(`Received POST message for sessionId ${sessionId}`);
163+
console.log(`POST /mcp for sessionId ${sessionId}`);
164164
if (!sessionId) {
165165
try {
166166
console.log("New streamable-http connection");
@@ -228,7 +228,7 @@ app.post("/mcp", async (req, res) => {
228228

229229
app.get("/stdio", async (req, res) => {
230230
try {
231-
console.log("New connection");
231+
console.log("GET /stdio");
232232

233233
try {
234234
await backingServerTransport?.close();
@@ -254,18 +254,53 @@ app.get("/stdio", async (req, res) => {
254254
console.log("Created web app transport");
255255

256256
await webAppTransport.start();
257-
(backingServerTransport as StdioClientTransport).stderr!.on(
258-
"data",
259-
(chunk) => {
260-
webAppTransport.send({
261-
jsonrpc: "2.0",
262-
method: "notifications/stderr",
263-
params: {
264-
content: chunk.toString(),
265-
},
266-
});
267-
},
268-
);
257+
258+
// Handle client disconnection
259+
res.on("close", () => {
260+
console.log(
261+
`Client disconnected from session ${webAppTransport.sessionId}`,
262+
);
263+
// Clean up the transport map
264+
webAppTransports.delete(webAppTransport.sessionId);
265+
});
266+
267+
// Create a stderr handler that checks connection state
268+
const stderrHandler = (chunk: Buffer) => {
269+
// Only send if the transport exists in our map (meaning it's still active)
270+
if (webAppTransports.has(webAppTransport.sessionId)) {
271+
webAppTransport
272+
.send({
273+
jsonrpc: "2.0",
274+
method: "notifications/stderr",
275+
params: {
276+
content: chunk.toString(),
277+
},
278+
})
279+
.catch((error: any) => {
280+
console.error(
281+
`Error sending stderr data to client: ${error.message}`,
282+
);
283+
// If we hit an error sending, clean up the transport
284+
webAppTransports.delete(webAppTransport.sessionId);
285+
});
286+
}
287+
};
288+
289+
if ((backingServerTransport as StdioClientTransport).stderr) {
290+
(backingServerTransport as StdioClientTransport).stderr!.on(
291+
"data",
292+
stderrHandler,
293+
);
294+
295+
// Store the handler reference so we can remove it when client disconnects
296+
res.on("close", () => {
297+
if ((backingServerTransport as StdioClientTransport).stderr) {
298+
(
299+
backingServerTransport as StdioClientTransport
300+
).stderr!.removeListener("data", stderrHandler);
301+
}
302+
});
303+
}
269304

270305
mcpProxy({
271306
transportToClient: webAppTransport,
@@ -282,7 +317,7 @@ app.get("/stdio", async (req, res) => {
282317
app.get("/sse", async (req, res) => {
283318
try {
284319
console.log(
285-
"New SSE connection. NOTE: The sse transport is deprecated and has been replaced by streamable-http",
320+
"GET /sse (NOTE: The sse transport is deprecated and has been replaced by streamable-http)",
286321
);
287322

288323
try {
@@ -324,7 +359,7 @@ app.get("/sse", async (req, res) => {
324359
app.post("/message", async (req, res) => {
325360
try {
326361
const sessionId = req.query.sessionId;
327-
console.log(`Received message for sessionId ${sessionId}`);
362+
console.log(`POST /message for sessionId ${sessionId}`);
328363

329364
const transport = webAppTransports.get(
330365
sessionId as string,
@@ -341,13 +376,15 @@ app.post("/message", async (req, res) => {
341376
});
342377

343378
app.get("/health", (req, res) => {
379+
console.log("GET /health");
344380
res.json({
345381
status: "ok",
346382
});
347383
});
348384

349385
app.get("/config", (req, res) => {
350386
try {
387+
console.log("GET /config");
351388
res.json({
352389
defaultEnvironment,
353390
defaultCommand: values.env,

0 commit comments

Comments
 (0)