fix: return JSON stubs for OAuth discovery probes (Re-authenticate error)#38
Conversation
…ror)
When an MCP client clicks "Re-authenticate" in the Claude Code UI, the
client unconditionally probes OAuth discovery endpoints before proceeding:
POST /register
GET /.well-known/oauth-authorization-server
GET /.well-known/oauth-protected-resource
GET /.well-known/openid-configuration
This server does NOT implement MCP-client OAuth — auth is delegated to the
transport layer (Cloudflare tunnel for the public endpoint, no auth for
localhost). But Express's default 404 handler returned an HTML body
("<!DOCTYPE html>..."), which the MCP client's JSON parser choked on with
"SyntaxError: Unrecognized token '<'". The Re-authenticate UI would show
a garbled HTTP 404 error even though the underlying connection was fine.
Add four handlers in HttpTransport.setupRoutes that return well-formed
JSON for each probed path:
- POST /register → 501 registration_not_supported
- GET /.well-known/* → 404 not_found with mcp_auth=tunnel-delegated
The MCP client sees valid JSON, treats "no OAuth advertised" as "no OAuth
required", and completes connection without OAuth. Normal /mcp tool calls
are unaffected — they don't hit these paths.
Verified against the live service on localhost:8180 after hot-reload:
all four paths now return proper JSON bodies with 404/501 status and
no HTML.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
User does not have a PR Review subscription. Go to Team management and add this email to the PR Review subscription. |
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 24 minutes and 44 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces OAuth discovery stubs to the HttpTransport in both JavaScript and TypeScript implementations. These stubs handle requests to well-known OAuth and OpenID configuration paths by returning structured JSON 404 or 501 errors. This change prevents MCP clients from crashing when they encounter default HTML error pages during authentication probes. I have no feedback to provide as there were no review comments to evaluate.
PR #38 review WARNING — the OAuth stub responses leaked the server's auth architecture (`mcp_auth: "tunnel-delegated"` + a detailed description mentioning "Cloudflare tunnel") in the response body. On localhost that's fine — the audience is already trusted and the debug info helps local development. But kms.yaker.org is a public endpoint behind Cloudflare, and an attacker who bypasses the tunnel (direct IP probe, SSRF, or during a CF outage) now gets a machine-readable signal that auth is entirely delegated to the transport layer. More useful to the attacker than a generic 404. Fix: detect localhost requests via req.hostname (localhost, 127.0.0.1, ::1) and only include the detailed error_description + mcp_auth field on those. Public-facing responses get a generic "OAuth is not available" / "Dynamic client registration is not supported" body. Also: renamed the `error` field from `not_found` to `oauth_not_supported` on the /.well-known/* paths. The reviewer noted that `not_found` isn't a standard OAuth error code per RFC 6749 §5.2 — `oauth_not_supported` more accurately describes the condition and signals to future clients that the server is intentionally not advertising OAuth metadata. Reviewer also flagged a concern about a pre-existing oauth.enabled config branch that could collide with these stubs. Verified via grep: there is NO oauth.enabled conditional in HttpTransport.ts today. The concern came from the pre-existing dead test file (src/__tests__/transport/HttpTransport.test.ts, excluded via testPathIgnorePatterns) which references methods that were removed in PR #22. Non-issue for the current codebase — added a comment documenting this so future readers don't re-raise. Verified against live service: $ curl http://localhost:8180/.well-known/oauth-authorization-server → {"error":"oauth_not_supported","error_description":"This MCP server does not implement OAuth...","mcp_auth":"tunnel-delegated"} $ curl -H "Host: kms.yaker.org" http://localhost:8180/.well-known/oauth-authorization-server → {"error":"oauth_not_supported","error_description":"OAuth is not available on this endpoint."} Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eanly (#83) PR #38 (51cbdb8) added OAuth discovery stubs to fix HTML-404 parse errors, but chose 501 for POST /register. Claude Code's MCP SDK treats 501 as a fatal "Dynamic client registration is not supported" error and surfaces: SDK auth failed: Dynamic client registration is not supported by this MCP server. Auth is tunnel-delegated — connect without OAuth. The user's ~/.claude.json has the server as { type: "http", url: ... } with no auth, so the SDK should fall through to unauthenticated when discovery returns "no auth." A 404 reads as "no such endpoint, fall through"; a 501 reads as "DCR explicitly rejected — fatal." The .well-known/* stubs already return 404 via a shared oauthNotSupported handler. Route /register through the same handler — single source of truth for "no OAuth, anywhere," and the SDK probe completes cleanly. Info-disclosure hardening preserved: mcp_auth: "tunnel-delegated" debug field only ships on localhost requests; the public Cloudflare-tunneled response stays generic. Tests: src/__tests__/HttpTransport.oauth_stubs.test.ts covers all four endpoints, both the 404-shape contract and the localhost-only debug field hardening (9 cases). Full suite: 404/404 passing. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Fixes the long-standing "Re-authenticate" error in Claude Code's MCP UI when reconnecting to `personal-kms` at `localhost:8180`:
```
... \`\`\`Error: SDK auth failed: HTTP 404: Invalid OAuth error response: SyntaxError:
JSON Parse error: Unrecognized token '<'. Raw body:
Root cause
When a user clicks Re-authenticate in Claude Code's MCP UI, the client unconditionally probes OAuth discovery endpoints before proceeding — regardless of whether the server actually advertises OAuth:
This KMS server does not implement MCP-client OAuth — auth is delegated to the transport layer (Cloudflare tunnel for `kms.yaker.org`, no auth for `localhost:8180`). Before this fix, none of those four paths had any handler, so they fell through to Express's default 404, which returns:
```html
<title>Error</title> \`\`\`The MCP client tries to parse that HTML as JSON and crashes with `SyntaxError: Unrecognized token '<'`. The reconnection still works (because it hits `/mcp` not `/register`), but the UI shows a garbled error and the user-experience is broken.
Fix
Four stub handlers in `HttpTransport.setupRoutes` that return well-formed JSON:
The MCP client sees valid JSON → treats "no OAuth advertised" as "no OAuth required" → completes the connection without OAuth. Normal `/mcp` tool calls are unaffected.
Verification
Verified against the live service after `node --watch` hot-reloaded the rebuilt `dist/`:
```bash
$ curl -s -X POST http://localhost:8180/register -H 'Content-Type: application/json' -d '{}'
{"error":"registration_not_supported","error_description":"Dynamic client registration is not supported by this MCP server. Auth is tunnel-delegated — connect without OAuth.","mcp_auth":"tunnel-delegated"}
$ curl -s http://localhost:8180/.well-known/oauth-authorization-server
{"error":"not_found","error_description":"This MCP server does not implement OAuth...","mcp_auth":"tunnel-delegated"}
$ curl -s http://localhost:8180/.well-known/oauth-protected-resource
{"error":"not_found","error_description":"...","mcp_auth":"tunnel-delegated"}
$ curl -s http://localhost:8180/.well-known/openid-configuration
{"error":"not_found","error_description":"...","mcp_auth":"tunnel-delegated"}
```
All four return proper JSON bodies with 404/501 status and no HTML.
Test plan
Scope
🤖 Generated with Claude Code