From 69577544bc762c54b5d210b0299a065159ecb3b5 Mon Sep 17 00:00:00 2001 From: Ilja Herdt Date: Fri, 22 May 2026 16:30:13 -0700 Subject: [PATCH] Route OAuth callback token exchange through proxy fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /oauth/callback handler called `auth()` without a fetchFn, so the SDK fell back to global fetch for the metadata + token-endpoint requests. Auth servers that emit incomplete CORS headers (e.g. `/oauth2/token` missing Access-Control-Allow-Origin on the response) caused the token exchange to fail with `TypeError: Failed to fetch`, even when initial registration succeeded via the proxy. Wire config + connectionType into OAuthCallback and build a createProxyFetch when connectionType === "proxy", matching the existing pattern in useConnection and AuthDebugger. Reproducible against the WorkOS shop MCP at https://shop.workos.com/mcp (signin.shop.workos.com auth server). With this fix the full OAuth dance — metadata discovery, registration, authorize redirect, callback, token exchange — completes end to end. --- client/src/App.tsx | 6 +++++- client/src/components/OAuthCallback.tsx | 23 ++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index 59d15ba06..06251a85b 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1285,7 +1285,11 @@ const App = () => { ); return ( Loading...}> - + ); } diff --git a/client/src/components/OAuthCallback.tsx b/client/src/components/OAuthCallback.tsx index ccfd6d928..b104ebbeb 100644 --- a/client/src/components/OAuthCallback.tsx +++ b/client/src/components/OAuthCallback.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useMemo, useRef } from "react"; import { InspectorOAuthClientProvider } from "../lib/auth"; import { SESSION_KEYS } from "../lib/constants"; import { auth } from "@modelcontextprotocol/sdk/client/auth.js"; @@ -7,15 +7,31 @@ import { generateOAuthErrorDescription, parseOAuthCallbackParams, } from "@/utils/oauthUtils.ts"; +import { createProxyFetch } from "@/lib/proxyFetch"; +import { InspectorConfig } from "@/lib/configurationTypes"; interface OAuthCallbackProps { onConnect: (serverUrl: string) => void; + config: InspectorConfig; + connectionType: "direct" | "proxy"; } -const OAuthCallback = ({ onConnect }: OAuthCallbackProps) => { +const OAuthCallback = ({ + onConnect, + config, + connectionType, +}: OAuthCallbackProps) => { const { toast } = useToast(); const hasProcessedRef = useRef(false); + const fetchFn = useMemo( + () => + connectionType === "proxy" && config + ? createProxyFetch(config) + : undefined, + [connectionType, config], + ); + useEffect(() => { const handleCallback = async () => { // Skip if we've already processed this callback @@ -49,6 +65,7 @@ const OAuthCallback = ({ onConnect }: OAuthCallbackProps) => { result = await auth(serverAuthProvider, { serverUrl, authorizationCode: params.code, + ...(fetchFn && { fetchFn }), }); } catch (error) { console.error("OAuth callback error:", error); @@ -73,7 +90,7 @@ const OAuthCallback = ({ onConnect }: OAuthCallbackProps) => { handleCallback().finally(() => { window.history.replaceState({}, document.title, "/"); }); - }, [toast, onConnect]); + }, [toast, onConnect, fetchFn]); return (