Repository navigation
Add CORS to /mcp so browser-based MCP connectors can probe it - #29
Merged
Merged
Conversation
Browser-based MCP clients (Grok/ChatGPT connector UIs) call /mcp via fetch(), but /mcp had no CORS: the OPTIONS preflight hit the bearer gate and returned 401, and the 401 responses carried no Access-Control-Allow- Origin and did not expose WWW-Authenticate, so the browser couldn't read the auth-discovery hint. Add a CORS layer as the OUTERMOST layer on the /mcp router so the preflight is answered (2xx) before the token gate, and the 401's WWW-Authenticate is exposed cross-origin. Authorization is listed explicitly in allow-headers (the `*` wildcard does not cover it per the Fetch spec); WWW-Authenticate and mcp-session-id are exposed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014T9N8USDfNK5yzznPGg7Ym
There was a problem hiding this comment.
Pull request overview
This PR adds a dedicated CORS layer to the /mcp router so browser-based MCP connector UIs can successfully complete CORS preflights and read authentication-discovery headers (e.g., WWW-Authenticate) from cross-origin responses.
Changes:
- Adds a
/mcp-scopedCorsLayerconfigured forGET, POST, OPTIONS. - Explicitly allows
Authorizationand MCP-related request headers and exposesWWW-Authenticateandmcp-session-idin responses. - Applies the CORS layer outside the
/mcpbearer-token gate so preflights don’t get blocked by authentication.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Browser-based MCP connector UIs (Grok, ChatGPT) call the MCP endpoint via
fetch()from their own origin, but/mcphad no CORS — so the browser blocked it at the first probe:OPTIONS /mcp(the CORS preflight, sent without a token) hit the bearer gate and returned 401, so the preflight failed and the real request never happened.POST /mcp→ 401 carried noAccess-Control-Allow-Originand did not exposeWWW-Authenticate, so a browser couldn't read the response or the auth-discovery hint it needs to find the authorization server./oauth/*and/.well-known/*already had a CORS layer;/mcp(theprotected_mcprouter) never got one. This is a likely cause of the "Connection failed" seen when adding the server as a custom connector.Fix
Add a CORS layer to the
/mcprouter as the outermost layer, so:OPTIONSpreflight is answered (2xx + CORS headers) before the bearer gate runs;401'sWWW-Authenticateis exposed cross-origin (plusmcp-session-id);Authorizationis listed explicitly inAccess-Control-Allow-Headers— the*wildcard does not coverAuthorizationper the Fetch spec;GET, POST, OPTIONS; allowed headers includecontent-type,accept,mcp-protocol-version,mcp-session-id,last-event-id./oauth/*and/.well-known/*CORS is unchanged.Testing
cargo buildclean (one pre-existing macro warning).Origin: https://grok.com:OPTIONS /mcp→ 200 withaccess-control-allow-origin: *,allow-methods: GET,POST,OPTIONS,allow-headers: authorization,content-type,accept,mcp-protocol-version,mcp-session-id,last-event-id(was 401).POST /mcp(no token) → 401 now carryingaccess-control-allow-origin: *andaccess-control-expose-headers: www-authenticate,mcp-session-id./.well-known/*CORS still present (no regression).Notes
mainafter Restore authorization-code flow via II finish_url redirect (so Claude.ai can connect) #28's squash-merge; this is a fresh PR.II_URL(beta.id.ai) isn't yet honoring thefinish_urlwe return in the key-request response — our side sends it correctly (verified against the live deployment). That needs the #4086 finish_url-capable II build deployed; it's not addressed here.🤖 Generated with Claude Code
https://claude.ai/code/session_014T9N8USDfNK5yzznPGg7Ym
Generated by Claude Code