claude.ai connector via mcp-proxy.anthropic.com rejected with mcp_unauthorized_after_token_refresh while claude.ai backend uses the same grant successfully (OAuth 2.1 refresh rotation race)
Environment
- Claude Code version: 2.1.162
- Platform: macOS 26.3.1 (darwin)
- Server type: claude.ai connector (remote MCP, OAuth 2.1 via PropelAuth authorization server)
Summary
Since ~June 1, claude.ai connectors accessed from Claude Code via mcp-proxy.anthropic.com fail to connect with:
{"type":"error","error":{"type":"authentication_error","message":"MCP server rejected freshly refreshed token","details":{"error_code":"mcp_unauthorized_after_token_refresh"}}}
Example request IDs: req_011CbiSiuDUJ12c2mYM1QnFD, req_011CbiSnn9vVyxcbyXMiomT6 (2026-06-04 ~15:19-15:20 UTC).
The same connector works from claude.ai itself. Our MCP resource-server logs show that during the very same reconnect attempt, requests from claude.ai backend infrastructure (AWS 18.203.x.x / 63.32.x.x) complete a full authenticated MCP handshake (initialize 200, notifications 202, tools/list 200), while requests from the proxy egress (160.79.106.x) are all rejected 401 — interleaved within the same seconds. So the user's grant is valid and token introspection on our side works; only the proxy path presents a token our server rejects.
Server-side observations
- Resource server: NestJS MCP server validating tokens via the authorization server's OAuth 2.1 introspection endpoint (RFC 7662). Authorization server: PropelAuth (
/oauth/2.1endpoints, refresh token rotation per OAuth 2.1). 160.79.106.x(proxy egress) traffic first appears in our logs on 2026-06-01 and has a 100% 401 rate (296/296 requests over 4 days) — it has never connected successfully.- No introspection-endpoint failures logged server-side; introspection of the proxy-presented token returns
active: false.
Suspected cause
Refresh-token rotation race between two Anthropic components sharing one connector grant: the claude.ai backend and mcp-proxy.anthropic.com appear to refresh the same grant independently. With single-use refresh tokens (OAuth 2.1), the proxy's refresh succeeds at the token endpoint (possibly within a reuse-grace window) but the resulting access token belongs to a superseded/revoked token family, so the resource server's introspection returns active: false and the request is rejected — matching the mcp_unauthorized_after_token_refresh error exactly.
If the proxy shared the claude.ai connector token store instead of refreshing independently (or coordinated refreshes), this race would not occur.
Steps to reproduce
- Add a remote MCP connector on claude.ai whose authorization server implements OAuth 2.1 refresh-token rotation (e.g. PropelAuth).
- Authenticate the connector in claude.ai — works.
- In Claude Code, use the synced claude.ai connector → connection fails with
mcp_unauthorized_after_token_refresh. /mcp→ reconnect → claude.ai-side health check succeeds (server logs show authenticated handshake from claude.ai backend IPs) while the proxy still reports unauthorized.
Expected behavior
The proxy uses/coordinates the same valid grant the claude.ai backend uses, and the connector works in Claude Code.
Actual behavior
claude.ai <connector name> is still unauthorized. Make sure the browser sign-in completed, then try again from /mcp. — repeatedly, even though browser sign-in completes and the grant is demonstrably valid for claude.ai itself.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗