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)

Resolved 💬 2 comments Opened Jun 4, 2026 by utvara Closed Jun 8, 2026

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.1 endpoints, 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

  1. Add a remote MCP connector on claude.ai whose authorization server implements OAuth 2.1 refresh-token rotation (e.g. PropelAuth).
  2. Authenticate the connector in claude.ai — works.
  3. In Claude Code, use the synced claude.ai connector → connection fails with mcp_unauthorized_after_token_refresh.
  4. /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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗