[BUG] MCP 403 insufficient_scope step-up authorization is dropped and misreported as "token expired"
Preflight Checklist
- [ ] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When an OAuth-protected MCP server returns a valid 403 insufficient_scope step-up challenge during a tools/call, Claude Code does not surface the step-up authorization URL to the user (per the MCP step-up authorization flow: https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#step-up-authorization-flow).
Instead it reports:
MCP server "<name>" requires re-authorization (token expired)
This is incorrect — the access token is valid and unexpired; it only lacks the additional required scope.
The MCP transport log shows the client parses the insufficient_scope challenge, extracts the missing scope, and builds the correct authorization URL — then drops it with the log:
Redirection handling is disabled, skipping redirect
The agent layer receives only the misleading "token expired" string, with no structured 403 / scope information.
(The /mcp interactive flow can surface the URL afterward with the missing scope, so the OAuth machinery works; it just isn't wired into the tools/call path.)
What Should Happen?
Claude Code should treat 403 insufficient_scope as an OAuth step-up challenge:
- Surface the generated authorization URL directly so the user can grant the missing scope.
- Report it as "additional scope required," not "token expired," when the token is valid. This error message is now misleading to customers.
Error Messages/Logs
### 1. Server response (captured via direct `curl --http2`, bypassing the client)
HTTP/2 403
www-authenticate: Bearer error="insufficient_scope",
scope="<missing_scope>",
resource_metadata="https://<host>/.well-known/oauth-protected-resource/<server>",
error_description="Additional permissions required for this operation"
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "The access token does not have the required scopes for this operation.",
"data": {
"http_status_code": 403,
"title": "Insufficient Scope"
}
}
}
### 2. Claude Code MCP transport log (same call)
Marked step-up pending: <missing_scope>
Has refresh token: false
Expires in: ~5,000,000s
# token still valid (~60 days)
Authorization URL:
https://<auth-server>/oauth/authorize?...&scope=<missing_scope>&...
Persisted step-up scope: <missing_scope>
Redirection handling is disabled, skipping redirect
# URL dropped here
Tool call returned 401 Unauthorized - token may have expired
# mislabel (original failure was a 403 insufficient_scope challenge)
### 3. User-facing error
MCP server "<name>" requires re-authorization (token expired)
Steps to Reproduce
- Configure an OAuth-protected remote MCP server over Streamable HTTP where different tools require different scopes.
- In Claude Code, authorize the server with a valid token that holds only the base/read scope (not the elevated one).
- Call a tool that requires an additional management/write scope.
- The server returns a step-up challenge:
HTTP/2 403
WWW-Authenticate: Bearer
error="insufficient_scope",
scope="<missing_scope>",
resource_metadata="..."
- Expected: Claude Code surfaces an authorization URL for
<missing_scope>so the user can grant it, then retries the call. - Actual: Claude Code reports:
MCP server "<name>" requires re-authorization (token expired)
The authorization URL is built but never surfaced (Redirection handling is disabled, skipping redirect), and no interactive authorization flow is triggered. User has to go to /mcp and go through the oauth flow again.
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
Approximately <= 2.1.152
Claude Code Version
2.1.177
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
The same token succeeds for lower-privilege calls immediately before and after the failure, confirming this is a scope challenge rather than an authentication failure.
Claude Code logs show:
Has refresh token: false
Expires in: ~5,000,000s
Marked step-up pending: <missing_scope>
Authorization URL: ...
Redirection handling is disabled, skipping redirect
The client successfully parses the challenge and constructs the authorization URL, but never surfaces it and instead reports "token expired".
Related issues:
- #44652 — 403 insufficient_scope does not trigger re-authorization flow
- #28258 — step-up authorization not working for scope elevation
This report adds transport-log evidence showing where the step-up challenge is lost.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗