Summary
The OAuth client unconditionally retries all 403 responses, even when the error is not insufficient_scope. This causes an unnecessary retry attempt with the same token that will fail for the same reason.
Location
src/mcp/client/auth/oauth2.py, lines 662-681
The Bug
elifresponse.status_code==403:
error=self._extract_field_from_www_auth(response, "error")
# Only performs step-up if error == "insufficient_scope"iferror=="insufficient_scope":
self._select_scopes(response)
token_response=yieldawaitself._perform_authorization()
awaitself._handle_token_response(token_response)
# BUG: Retries unconditionally, even when no new tokens were obtainedself._add_auth_header(request)
yieldrequest
Lines 679-681 execute regardless of whether step-up authorization occurred, causing a retry with the same credentials.
Expected vs Actual Behavior
| Scenario | Expected | Actual |
|---|
403 with insufficient_scope | Get new tokens → retry | ✅ Correct |
403 with different error (e.g., invalid_token) | Raise error immediately | ❌ Retries once with same token, then fails |
| 403 with no error field | Raise error immediately | ❌ Retries once with same token, then fails |
Impact
- Wasted network round-trip: Client makes doomed retry request that will fail for the same reason
- Poor error feedback: Delays error reporting by one request cycle
- Spec non-compliance: MCP Authorization Spec implies retry only for
insufficient_scope - Resource waste: Unnecessary load on server and client
Fix
Move the retry logic inside the if error == "insufficient_scope": block and raise an error otherwise:
elifresponse.status_code==403:
error=self._extract_field_from_www_auth(response, "error")
iferror=="insufficient_scope":
try:
self._select_scopes(response)
token_response=yieldawaitself._perform_authorization()
awaitself._handle_token_response(token_response)
# Retry with new tokensself._add_auth_header(request)
yieldrequestexceptException:
logger.exception("OAuth flow error")
raiseelse:
# Permanent authorization failure - cannot be resolved by retryraiseOAuthFlowError(
f"Access forbidden: {erroror'insufficient permissions'}"
)References
Authored by Claude, reviewed by @maxisbey
Summary
The OAuth client unconditionally retries all 403 responses, even when the error is not
insufficient_scope. This causes an unnecessary retry attempt with the same token that will fail for the same reason.Location
src/mcp/client/auth/oauth2.py, lines 662-681The Bug
Lines 679-681 execute regardless of whether step-up authorization occurred, causing a retry with the same credentials.
Expected vs Actual Behavior
insufficient_scopeinvalid_token)Impact
insufficient_scopeFix
Move the retry logic inside the
if error == "insufficient_scope":block and raise an error otherwise:References
insufficient_scopeas the only 403 error codeAuthored by Claude, reviewed by @maxisbey