Merge pull request #418 from modelcontextprotocol/ihrpr/auth-sse-shttp-connect
Fix connection issue for servers with auth
This commit is contained in:
commit
eda63dafb8
4 changed files with 28 additions and 17 deletions
|
|
@ -234,7 +234,6 @@ const App = () => {
|
||||||
const onOAuthConnect = useCallback(
|
const onOAuthConnect = useCallback(
|
||||||
(serverUrl: string) => {
|
(serverUrl: string) => {
|
||||||
setSseUrl(serverUrl);
|
setSseUrl(serverUrl);
|
||||||
setTransportType("sse");
|
|
||||||
setIsAuthDebuggerVisible(false);
|
setIsAuthDebuggerVisible(false);
|
||||||
void connectMcpServer();
|
void connectMcpServer();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -249,9 +249,16 @@ export function useConnection({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const is401Error = (error: unknown): boolean => {
|
||||||
|
return (
|
||||||
|
(error instanceof SseError && error.code === 401) ||
|
||||||
|
(error instanceof Error && error.message.includes("401")) ||
|
||||||
|
(error instanceof Error && error.message.includes("Unauthorized"))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const handleAuthError = async (error: unknown) => {
|
const handleAuthError = async (error: unknown) => {
|
||||||
if (error instanceof SseError && error.code === 401) {
|
if (is401Error(error)) {
|
||||||
// Create a new auth provider with the current server URL
|
|
||||||
const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl);
|
const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl);
|
||||||
|
|
||||||
const result = await auth(serverAuthProvider, { serverUrl: sseUrl });
|
const result = await auth(serverAuthProvider, { serverUrl: sseUrl });
|
||||||
|
|
@ -330,7 +337,6 @@ export function useConnection({
|
||||||
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`);
|
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`);
|
||||||
mcpProxyServerUrl.searchParams.append("url", sseUrl);
|
mcpProxyServerUrl.searchParams.append("url", sseUrl);
|
||||||
transportOptions = {
|
transportOptions = {
|
||||||
authProvider: serverAuthProvider,
|
|
||||||
eventSourceInit: {
|
eventSourceInit: {
|
||||||
fetch: (
|
fetch: (
|
||||||
url: string | URL | globalThis.Request,
|
url: string | URL | globalThis.Request,
|
||||||
|
|
@ -347,7 +353,6 @@ export function useConnection({
|
||||||
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/mcp`);
|
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/mcp`);
|
||||||
mcpProxyServerUrl.searchParams.append("url", sseUrl);
|
mcpProxyServerUrl.searchParams.append("url", sseUrl);
|
||||||
transportOptions = {
|
transportOptions = {
|
||||||
authProvider: serverAuthProvider,
|
|
||||||
eventSourceInit: {
|
eventSourceInit: {
|
||||||
fetch: (
|
fetch: (
|
||||||
url: string | URL | globalThis.Request,
|
url: string | URL | globalThis.Request,
|
||||||
|
|
@ -425,13 +430,14 @@ export function useConnection({
|
||||||
`Failed to connect to MCP Server via the MCP Inspector Proxy: ${mcpProxyServerUrl}:`,
|
`Failed to connect to MCP Server via the MCP Inspector Proxy: ${mcpProxyServerUrl}:`,
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
|
|
||||||
const shouldRetry = await handleAuthError(error);
|
const shouldRetry = await handleAuthError(error);
|
||||||
if (shouldRetry) {
|
if (shouldRetry) {
|
||||||
return connect(undefined, retryCount + 1);
|
return connect(undefined, retryCount + 1);
|
||||||
}
|
}
|
||||||
|
if (is401Error(error)) {
|
||||||
if (error instanceof SseError && error.code === 401) {
|
|
||||||
// Don't set error state if we're about to redirect for auth
|
// Don't set error state if we're about to redirect for auth
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
||||||
9
package-lock.json
generated
9
package-lock.json
generated
|
|
@ -8407,15 +8407,6 @@
|
||||||
"node": "20 || >=22"
|
"node": "20 || >=22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
|
||||||
"version": "8.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
|
||||||
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
||||||
|
import { isJSONRPCRequest } from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
function onClientError(error: Error) {
|
function onClientError(error: Error) {
|
||||||
console.error("Error from inspector client:", error);
|
console.error("Error from inspector client:", error);
|
||||||
|
|
@ -19,7 +20,21 @@ export default function mcpProxy({
|
||||||
let transportToServerClosed = false;
|
let transportToServerClosed = false;
|
||||||
|
|
||||||
transportToClient.onmessage = (message) => {
|
transportToClient.onmessage = (message) => {
|
||||||
transportToServer.send(message).catch(onServerError);
|
transportToServer.send(message).catch((error) => {
|
||||||
|
// Send error response back to client if it was a request (has id) and connection is still open
|
||||||
|
if (isJSONRPCRequest(message) && !transportToClientClosed) {
|
||||||
|
const errorResponse = {
|
||||||
|
jsonrpc: "2.0" as const,
|
||||||
|
id: message.id,
|
||||||
|
error: {
|
||||||
|
code: -32001,
|
||||||
|
message: error.message,
|
||||||
|
data: error,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
transportToClient.send(errorResponse).catch(onClientError);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
transportToServer.onmessage = (message) => {
|
transportToServer.onmessage = (message) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue