anthropics / anthropics/claude-code

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

オープン
#92,518 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
area:mcp bug has repro
主要言語
Python
スター
145k
フォーク
23.1k
PR マージ指標
PR 指標を取得中

説明

### 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**.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
typescript
領域
api, authentication
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
52/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。