[BUG] MCP 403 insufficient_scope step-up authorization is dropped and misreported as "token expired"

Open 💬 3 comments Opened Jun 16, 2026 by jasonliang228

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:

  1. Surface the generated authorization URL directly so the user can grant the missing scope.
  2. 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

  1. Configure an OAuth-protected remote MCP server over Streamable HTTP where different tools require different scopes.
  2. In Claude Code, authorize the server with a valid token that holds only the base/read scope (not the elevated one).
  3. Call a tool that requires an additional management/write scope.
  4. The server returns a step-up challenge:
HTTP/2 403

WWW-Authenticate: Bearer
  error="insufficient_scope",
  scope="<missing_scope>",
  resource_metadata="..."
  1. Expected: Claude Code surfaces an authorization URL for <missing_scope> so the user can grant it, then retries the call.
  2. 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.

View original on GitHub ↗

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