From 2bec6f662bb9ce9e68e4d0c5a3b53da8f4030540 Mon Sep 17 00:00:00 2001 From: Luca Moretti Date: Fri, 20 Feb 2026 14:03:07 +0000 Subject: [PATCH] fix: omit x-custom-auth-headers in direct connections to prevent CORS failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The x-custom-auth-headers header is a proxy implementation detail used by the inspector server to forward custom headers to the MCP server. When connecting directly (not through the proxy), this header is sent directly to the MCP server, which doesn't expect it and doesn't include it in Access-Control-Allow-Headers — causing the browser to block the request. Only include x-custom-auth-headers when connection type is not 'direct'. Fixes #1100 --- .../hooks/__tests__/useConnection.test.tsx | 28 +++++++++++++++++++ client/src/lib/hooks/useConnection.ts | 4 ++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/client/src/lib/hooks/__tests__/useConnection.test.tsx b/client/src/lib/hooks/__tests__/useConnection.test.tsx index 4907a085b..4a3546547 100644 --- a/client/src/lib/hooks/__tests__/useConnection.test.tsx +++ b/client/src/lib/hooks/__tests__/useConnection.test.tsx @@ -1283,6 +1283,34 @@ describe("useConnection", () => { ); }); + test("omits x-custom-auth-headers in direct connections to avoid CORS issues", async () => { + const customHeaders: CustomHeaders = [ + { name: "X-Tenant-ID", value: "acme-inc", enabled: true }, + { name: "X-Environment", value: "staging", enabled: true }, + ]; + + const propsWithDirectConnection = { + ...defaultProps, + customHeaders, + connectionType: "direct" as const, + }; + + const { result } = renderHook(() => + useConnection(propsWithDirectConnection), + ); + + await act(async () => { + await result.current.connect(); + }); + + // In direct connection mode, x-custom-auth-headers should NOT be sent + // because it is a proxy implementation detail that breaks CORS (#1100) + const headers = mockSSETransport.options?.requestInit?.headers; + expect(headers).toHaveProperty("X-Tenant-ID", "acme-inc"); + expect(headers).toHaveProperty("X-Environment", "staging"); + expect(headers).not.toHaveProperty("x-custom-auth-headers"); + }); + test("uses OAuth token when no custom headers or legacy auth provided", async () => { const propsWithoutAuth = { ...defaultProps, diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index e14d1037f..85482c980 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -559,7 +559,9 @@ export function useConnection({ }); // Add custom header names as a special request header for server processing - if (customHeaderNames.length > 0) { + // Only add when using proxy connection — this is a proxy implementation detail + // that would break CORS on direct connections (see #1100) + if (customHeaderNames.length > 0 && connectionType !== "direct") { headers["x-custom-auth-headers"] = JSON.stringify(customHeaderNames); }