[BUG] Step-up authorization not working for scope elevation in MCP TypeScript SDK
Preflight Checklist
- [x] 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?
File: packages/client/src/client/auth.ts in modelcontextprotocol/typescript-sdk
The MCP client fails to obtain elevated scopes during step-up authorization (403 insufficient_scope) because the authInternal() function refreshes tokens without requesting the new scopes.
What Actually Happens (if refresh_token exists)
- ✅ Client makes request with token
- ✅ Server returns
403 insufficient_scopewithscope="products:write" - ✅ Client calls
auth(authProvider, { scope: "products:write", ... }) - ❌ Auth function refreshes token WITHOUT passing new scopes
- ❌ Gets same token back with original scopes (missing
products:write) - ❌ Retry fails with same 403
- ✅ Infinite loop protection aborts: "Server returned 403 after trying upscoping"
The Bug
Location
File: packages/client/src/client/auth.ts
Function: authInternal() at lines 560-573
Problem
- Missing
scopeparameter: Line 563-570 doesn't pass thescopeparameter torefreshAuthorization(), so it refreshes with the same scopes as the original token - No fallback to user consent: If refresh succeeds but doesn't grant new scopes, the function never falls through to start a new OAuth flow (lines 584-595)
What Actually Happens (if refresh_token is not there)
- ✅ Client makes request with token
- ✅ Server returns
403 insufficient_scopewithscope="products:write" - ✅ Client calls
auth(authProvider, { scope: "products:write", ... }) - ✅ Client invokes
pkceflow and sends redirect url - ❌ Auth Client provider disabled redirect_url, and doesn't show to user to consent for step-up authz
- ❌ Retry fails with 401 (token expired)
What Should Happen?
- Client makes request with token
- Server returns
403 Forbiddenwith:
``http``
WWW-Authenticate: Bearer error="insufficient_scope",
scope="products:write"
- Client detects insufficient scope and calls
auth()with elevated scopes - Auth function should either:
- Refresh token WITH new scopes, OR
- Start new OAuth PKCE flow for user consent
- Auth client provider enables redirect and shows to user for consent
- Client gets new token with elevated permissions
- Request succeeds
Error Messages/Logs
below are logs (by having 403 from mcp server)
### refresh token as null, to trigger auth flow
2026-02-24T17:06:42.643Z [DEBUG] MCP server "dev-mcp-server-1": Found client info
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Returning tokens
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Token length: 90
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Has refresh token: false
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Expires in: 2835s
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Saving code verifier
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Authorization URL: https://masked-server/mcp/oauth2/authorize?response_typ
e=code&client_id=oacli_U2TpyjRx44Ascs&code_challenge=%5BREDACTED%5D&code_challenge_method=S256&redirect_uri=http%3A%2F%2Flocalhost%3A3118%2
Fcallback&state=%5BREDACTED%5D&scope=product:write&resource=https%3A%2F%2Fmasked_resource%2F
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Scopes in URL: product:write
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Captured scopes from authorization URL: product:write
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Redirection handling is disabled, skipping redirect
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": HTTP connection dropped after 764s uptime
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Connection error: Unauthorized
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Tool 'create_product' failed after 0s: Unauthorized
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Tool call returned 401 Unauthorized - token may have expired
2026-02-24T17:06:42.644Z [DEBUG] mcp__dev-mcp-server-1__create_product tool error (773ms): MCP server "dev-mcp-server-1" requires re-authorization (tok
en expired)
Steps to Reproduce
Have an mcp tool in mcp server to return
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
scope="products:write",
resource_metadata="https://your-mcp-server.example.com/.well-known/oauth-protected-resource"
Content-Type: application/json
{
"error": "insufficient_scope",
"error_description": "Additional permissions required"
}
Required Header Components
- Status Code: 403 Forbidden (NOT 401)
- WWW-Authenticate Header with:
- error="insufficient_scope" (REQUIRED - triggers step-up flow)
- scope="products:write" (REQUIRED - tells client what scopes to request)
- resource_metadata="..." (OPTIONAL - helps client discover auth server)
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.39
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
MCP Spec Compliance
According to the MCP Authorization Spec (https://modelcontextprotocol.io/specification/draft/basic/authorization#step-up-authorization-flow):
When a client makes a request with an access token with insufficient scope during runtime operations, the server SHOULD respond with: -HTTP 403 Forbiddenstatus code -WWW-Authenticateheader witherror="insufficient_scope"-scopeparameter specifying the minimum scopes needed
Clients SHOULD respond to these errors by requesting a new access token with an increased set of scopes (from user) via a step-up authorization flow.
Current implementation violates this by not actually requesting elevated scopes during the step-up flow.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗