Merge branch 'main' into chore/bump-version-0.21.2

This commit is contained in:
Ola Hungerford 2026-04-12 13:58:16 -07:00 • committed by GitHub
commit 509f4aba8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -72,13 +72,26 @@ const is401Error = (error: unknown): boolean => {
return false; return false;
}; };
/**
* Sends a sanitized JSON error response to the client without exposing
* stack traces or internal error details. The full error is expected to
* have already been logged server-side via console.error.
*/
const sendErrorResponse = (
res: express.Response,
status: number,
message: string,
) => {
res.status(status).json({ error: message });
};
/** /**
* Prefer forwarding the upstream MCP 401 (WWW-Authenticate + body) so the browser * Prefer forwarding the upstream MCP 401 (WWW-Authenticate + body) so the browser
* matches direct-mode OAuth behavior. Falls back to JSON-encoding `error` if unknown. * matches direct-mode OAuth behavior. Falls back to a generic 401 JSON response
* when no upstream details were captured, to avoid leaking stack traces.
*/ */
const sendProxiedUnauthorized = ( const sendProxiedUnauthorized = (
res: express.Response, res: express.Response,
error: unknown,
headerHolder?: ProxyHeaderHolder, headerHolder?: ProxyHeaderHolder,
) => { ) => {
const captured = headerHolder?.lastUpstream401; const captured = headerHolder?.lastUpstream401;
@ -92,7 +105,7 @@ const sendProxiedUnauthorized = (
delete headerHolder.lastUpstream401; delete headerHolder.lastUpstream401;
return; return;
} }
res.status(401).json(error); sendErrorResponse(res, 401, "Unauthorized");
}; };
// Function to get HTTP headers. // Function to get HTTP headers.
@ -509,7 +522,7 @@ app.get(
} }
} catch (error) { } catch (error) {
console.error("Error in /mcp route:", error); console.error("Error in /mcp route:", error);
res.status(500).json(error); sendErrorResponse(res, 500, "Internal server error");
} }
}, },
); );
@ -545,7 +558,7 @@ app.post(
} }
} catch (error) { } catch (error) {
console.error("Error in /mcp route:", error); console.error("Error in /mcp route:", error);
res.status(500).json(error); sendErrorResponse(res, 500, "Internal server error");
} }
} else { } else {
console.log("New StreamableHttp connection request"); console.log("New StreamableHttp connection request");
@ -592,11 +605,11 @@ app.post(
"Received 401 Unauthorized from MCP server:", "Received 401 Unauthorized from MCP server:",
error instanceof Error ? error.message : error, error instanceof Error ? error.message : error,
); );
sendProxiedUnauthorized(res, error, streamableHeaderHolder); sendProxiedUnauthorized(res, streamableHeaderHolder);
return; return;
} }
console.error("Error in /mcp POST route:", error); console.error("Error in /mcp POST route:", error);
res.status(500).json(error); sendErrorResponse(res, 500, "Internal server error");
} }
} }
}, },
@ -627,7 +640,7 @@ app.delete(
res.status(200).end(); res.status(200).end();
} catch (error) { } catch (error) {
console.error("Error in /mcp route:", error); console.error("Error in /mcp route:", error);
res.status(500).json(error); sendErrorResponse(res, 500, "Internal server error");
} }
} }
}, },
@ -732,11 +745,11 @@ app.get(
console.error( console.error(
"Received 401 Unauthorized from MCP server. Authentication failure.", "Received 401 Unauthorized from MCP server. Authentication failure.",
); );
sendProxiedUnauthorized(res, error, undefined); sendProxiedUnauthorized(res, undefined);
return; return;
} }
console.error("Error in /stdio route:", error); console.error("Error in /stdio route:", error);
res.status(500).json(error); sendErrorResponse(res, 500, "Internal server error");
} }
}, },
); );
@ -781,20 +794,33 @@ app.get(
console.error( console.error(
"Received 401 Unauthorized from MCP server. Authentication failure.", "Received 401 Unauthorized from MCP server. Authentication failure.",
); );
sendProxiedUnauthorized(res, error, sseHeaderHolder); sendProxiedUnauthorized(res, sseHeaderHolder);
return; return;
} else if (error instanceof SseError && error.code === 404) { } else if (error instanceof SseError && error.code === 404) {
console.error( console.error(
"Received 404 not found from MCP server. Does the MCP server support SSE?", "Received 404 not found from MCP server. Does the MCP server support SSE?",
); );
res.status(404).json(error); sendErrorResponse(
res,
404,
"MCP server returned 404. Does it support SSE?",
);
return; return;
} else if (JSON.stringify(error).includes("ECONNREFUSED")) { } else if (
error instanceof Error &&
(error.message.includes("ECONNREFUSED") ||
(error.cause && String(error.cause).includes("ECONNREFUSED")))
) {
console.error("Connection refused. Is the MCP server running?"); console.error("Connection refused. Is the MCP server running?");
res.status(500).json(error); sendErrorResponse(
res,
500,
"Connection refused. Is the MCP server running?",
);
return;
} }
console.error("Error in /sse route:", error); console.error("Error in /sse route:", error);
res.status(500).json(error); sendErrorResponse(res, 500, "Internal server error");
} }
}, },
); );
@ -824,7 +850,7 @@ app.post(
await transport.handlePostMessage(req, res); await transport.handlePostMessage(req, res);
} catch (error) { } catch (error) {
console.error("Error in /message route:", error); console.error("Error in /message route:", error);
res.status(500).json(error); sendErrorResponse(res, 500, "Internal server error");
} }
}, },
); );
@ -882,9 +908,8 @@ app.post(
body: responseBody, body: responseBody,
}); });
} catch (error) { } catch (error) {
res.status(500).json({ console.error("Error in /fetch route:", error);
error: error instanceof Error ? error.message : String(error), sendErrorResponse(res, 500, "Internal server error");
});
} }
}, },
); );
@ -900,7 +925,7 @@ app.get("/config", originValidationMiddleware, authMiddleware, (req, res) => {
}); });
} catch (error) { } catch (error) {
console.error("Error in /config route:", error); console.error("Error in /config route:", error);
res.status(500).json(error); sendErrorResponse(res, 500, "Internal server error");
} }
}); });