anthropics / anthropics/claude-code

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

Abierto
#92,518 0 comentarios 0 reacciones 0 asignados Ver en GitHub
area:mcp bug has repro
Lenguaje dominante
Python
Estrellas
145k
Forks
23.1k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

### 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
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](https://modelcontextprotocol.io/specification/2026-07-28/basic/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**.

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Línea de trabajo

No source file or test is named. Start by locating the MCP client's handling of HTTP 401/403 responses and WWW-Authenticate challenges, then compare invalid_token with insufficient_scope using the provided response. Done means a 403 insufficient_scope response reports the missing scope instead of token expiry, while 401 invalid_token retains the expiry behavior.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
typescript
Área
api, authentication
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Activo
Claridad
Bastante claro
Aptitud para principiantes
52/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.