Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 38 additions & 16 deletions server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,26 @@ const is401Error = (error: unknown): boolean => {
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
* 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 = (
res: express.Response,
error: unknown,
headerHolder?: ProxyHeaderHolder,
) => {
const captured = headerHolder?.lastUpstream401;
Expand All @@ -92,7 +105,7 @@ const sendProxiedUnauthorized = (
delete headerHolder.lastUpstream401;
return;
}
res.status(401).json(error);
sendErrorResponse(res, 401, "Unauthorized");
};

// Function to get HTTP headers.
Expand Down Expand Up @@ -509,7 +522,7 @@ app.get(
}
} catch (error) {
console.error("Error in /mcp route:", error);
res.status(500).json(error);
sendErrorResponse(res, 500, "Internal server error");
}
},
);
Expand Down Expand Up @@ -545,7 +558,7 @@ app.post(
}
} catch (error) {
console.error("Error in /mcp route:", error);
res.status(500).json(error);
sendErrorResponse(res, 500, "Internal server error");
}
} else {
console.log("New StreamableHttp connection request");
Expand Down Expand Up @@ -592,11 +605,11 @@ app.post(
"Received 401 Unauthorized from MCP server:",
error instanceof Error ? error.message : error,
);
sendProxiedUnauthorized(res, error, streamableHeaderHolder);
sendProxiedUnauthorized(res, streamableHeaderHolder);
return;
}
console.error("Error in /mcp POST route:", error);
res.status(500).json(error);
sendErrorResponse(res, 500, "Internal server error");
}
}
},
Expand Down Expand Up @@ -627,7 +640,7 @@ app.delete(
res.status(200).end();
} catch (error) {
console.error("Error in /mcp route:", error);
res.status(500).json(error);
sendErrorResponse(res, 500, "Internal server error");
}
}
},
Expand Down Expand Up @@ -732,11 +745,11 @@ app.get(
console.error(
"Received 401 Unauthorized from MCP server. Authentication failure.",
);
sendProxiedUnauthorized(res, error, undefined);
sendProxiedUnauthorized(res, undefined);
return;
}
console.error("Error in /stdio route:", error);
res.status(500).json(error);
sendErrorResponse(res, 500, "Internal server error");
}
},
);
Expand Down Expand Up @@ -781,20 +794,29 @@ app.get(
console.error(
"Received 401 Unauthorized from MCP server. Authentication failure.",
);
sendProxiedUnauthorized(res, error, sseHeaderHolder);
sendProxiedUnauthorized(res, sseHeaderHolder);
return;
} else if (error instanceof SseError && error.code === 404) {
console.error(
"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;
} else if (JSON.stringify(error).includes("ECONNREFUSED")) {
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);
res.status(500).json(error);
sendErrorResponse(res, 500, "Internal server error");
}
},
);
Expand Down Expand Up @@ -824,7 +846,7 @@ app.post(
await transport.handlePostMessage(req, res);
} catch (error) {
console.error("Error in /message route:", error);
res.status(500).json(error);
sendErrorResponse(res, 500, "Internal server error");
}
},
);
Expand Down Expand Up @@ -900,7 +922,7 @@ app.get("/config", originValidationMiddleware, authMiddleware, (req, res) => {
});
} catch (error) {
console.error("Error in /config route:", error);
res.status(500).json(error);
sendErrorResponse(res, 500, "Internal server error");
}
});

Expand Down
Loading