Skip to content

fix(mcp): gate every spelling of the OAuth endpoint - #3187

Open
L4XB wants to merge 1 commit into
upstash:masterfrom
L4XB:fix/mcp-oauth-route-spelling
Open

L4XB wants to merge 1 commit into
upstash:masterfrom
L4XB:fix/mcp-oauth-route-spelling

Conversation

@L4XB

@L4XB L4XB commented Sep 11, 2026

Copy link
Copy Markdown

Summary

requiresAuthentication in packages/mcp/src/index.ts decides whether a request hits the protected OAuth endpoint with an exact string compare against /mcp/oauth. Express 5 routes case-insensitively and with an optional trailing slash, but req.baseUrl and req.path keep the request's spelling. So /mcp/oauth/, /MCP/OAUTH or /Mcp/OAuth/ reach the same mcpRouter.all("/oauth") handler while the gate says "not the OAuth endpoint", and those requests are served like the anonymous /mcp route instead of getting the 401 challenge.

The telemetry layer already treats both spellings as the OAuth route (mcpRouteFromUrl strips trailing slashes and lowercases, see the test "labels both protected Express route spellings as OAuth"), so the two layers disagreed. This PR applies the same normalization in the gate:

const isOAuthEndpoint =
  `${req.baseUrl}${req.path}`.replace(/\/+$/, "").toLowerCase() === "/mcp/oauth";

No routing changes, no behaviour change for /mcp or for the exact /mcp/oauth spelling.

Tests

New integration test "protects every spelling Express routes to the OAuth endpoint" posts an initialize request without credentials to /mcp/oauth/, /MCP/OAUTH and /Mcp/OAuth/ against the built server and expects 401 for each. It fails on master (/mcp/oauth/: expected 200 to be 401) and passes with the fix.

pnpm --filter @upstash/context7-mcp build && pnpm --filter @upstash/context7-mcp test
Test Files 11 passed, Tests 127 passed
lint / typecheck clean

Changeset: @upstash/context7-mcp patch.

requiresAuthentication compared the raw request path with "/mcp/oauth".
Express matches routes case-insensitively and with an optional trailing
slash, so "/mcp/oauth/" and "/MCP/OAUTH" reached the OAuth handler while
the gate treated them like the anonymous /mcp route. Compare the
canonical form instead, the same normalization mcpRouteFromUrl already
uses for telemetry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant