fix: detect 401 errors from StreamableHTTP transport for OAuth flow
The server-side error handling only checked for SseError instances when detecting 401 responses from MCP servers. StreamableHTTPClientTransport throws a different error type (generic Error with "HTTP 401" message) when there's no authProvider configured. This caused the proxy to return 500 instead of 401 to the client, preventing the OAuth discovery and redirect flow from triggering. Added is401Error() helper that checks for: - SseError with code 401 - StreamableHTTPError with code 401 - Generic Error with "HTTP 401" or "(401)" in message Fixes #960 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2b79e36464
commit
d1fcbd2c93
1 changed files with 24 additions and 5 deletions
|
|
@ -17,7 +17,10 @@ import {
|
|||
StdioClientTransport,
|
||||
getDefaultEnvironment,
|
||||
} from "@modelcontextprotocol/sdk/client/stdio.js";
|
||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
||||
import {
|
||||
StreamableHTTPClientTransport,
|
||||
StreamableHTTPError,
|
||||
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
||||
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
||||
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
||||
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
||||
|
|
@ -44,6 +47,22 @@ const { values } = parseArgs({
|
|||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper function to detect 401 Unauthorized errors from various transport types.
|
||||
* StreamableHTTPClientTransport throws a generic Error with "HTTP 401" in the message
|
||||
* when there's no authProvider configured, while SSEClientTransport throws SseError.
|
||||
*/
|
||||
const is401Error = (error: unknown): boolean => {
|
||||
if (error instanceof SseError && error.code === 401) return true;
|
||||
if (error instanceof StreamableHTTPError && error.code === 401) return true;
|
||||
if (
|
||||
error instanceof Error &&
|
||||
(error.message.includes("HTTP 401") || error.message.includes("(401)"))
|
||||
)
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
// Function to get HTTP headers.
|
||||
const getHttpHeaders = (req: express.Request): Record<string, string> => {
|
||||
const headers: Record<string, string> = {};
|
||||
|
|
@ -508,10 +527,10 @@ app.post(
|
|||
req.body,
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof SseError && error.code === 401) {
|
||||
if (is401Error(error)) {
|
||||
console.error(
|
||||
"Received 401 Unauthorized from MCP server:",
|
||||
error.message,
|
||||
error instanceof Error ? error.message : error,
|
||||
);
|
||||
res.status(401).json(error);
|
||||
return;
|
||||
|
|
@ -649,7 +668,7 @@ app.get(
|
|||
transportToServer: serverTransport,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof SseError && error.code === 401) {
|
||||
if (is401Error(error)) {
|
||||
console.error(
|
||||
"Received 401 Unauthorized from MCP server. Authentication failure.",
|
||||
);
|
||||
|
|
@ -695,7 +714,7 @@ app.get(
|
|||
transportToServer: serverTransport,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof SseError && error.code === 401) {
|
||||
if (is401Error(error)) {
|
||||
console.error(
|
||||
"Received 401 Unauthorized from MCP server. Authentication failure.",
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue