[BUG] MCP client shows "token expired" for a 403 insufficient_scope response

Status Fixed / completed
Reported on v2.1.258
Maintainer reply None cached
Activity 1 comment · opened Sep 6, 2026 · closed Oct 12, 2026

What happens

An MCP server returns 403 with WWW-Authenticate: Bearer error="insufficient_scope" when a token lacks the scope for a tool. Claude Code shows the user:

MCP server "..." requires re-authorization (token expired)

The token was not expired. It had ~55 minutes of its 1-hour lifetime remaining. The real problem was a missing scope on the grant.

Why it matters

The message sends you to debug the wrong thing. We spent a full session investigating token lifetimes and refresh handling before reading server logs and finding a 403, not a 401.

Server response

Read tools need events:read, write tools need events:write. The grant only carried events:read.

POST /api/mcp        (tools/call: events_list)   -> 200
POST /api/mcp        (tools/call: events_get)    -> 200
POST /api/mcp        (tools/call: events_delete) -> 403

The 403 carries:

HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
                  scope="events:write",
                  resource_metadata="https://example.com/.well-known/oauth-protected-resource/api/mcp"

{"jsonrpc":"2.0","id":null,
 "error":{"code":-32600,"message":"Insufficient scope: events:write required"}}

What we tested

Server-side logs for the whole session confirm:

  1. No 401 was ever returned. Only two 403s. The client reported an expiry for a response that never claimed one.
  2. No refresh was attempted. Zero POST /oauth/token with grant_type=refresh_token. The only token call was the authorization_code exchange after manual re-auth.
  3. The token was valid. Access-token TTL 3600s, token issued ~5 minutes before the failure.
  4. Re-auth "fixed" it by widening scopes, not by refreshing. The second /authorize requested scope=events:read events:write; the first had not. The same delete then succeeded.

Expected

Distinguish the two WWW-Authenticate error codes:

  • error="invalid_token" (401) -> "token expired", re-authorize
  • error="insufficient_scope" (403) -> say the scope is missing, and name it

Ideally, perform step-up authorization using the scope from the challenge. At minimum, don't call it an expiry.

Environment

  • Claude Code 2.1.258
  • MCP spec 2026-07-28, OAuth 2.1 AS, TypeScript SDK v2
  • Server returns the challenge per spec §authorization

Related

Previously reported and closed without a fix: #19066, #28258, #44652. #28258 got "resolved in the next release" (2026-03); #44652 reported the same behavior six weeks later on 2.1.92, and a commenter confirmed it on 2.1.121. All three are locked, so this is a fresh report. Still reproducing on 2.1.258.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗