[Bug] MCP OAuth refresh tokens stored but never used - causing widespread auth failures
Summary
Claude Code stores MCP OAuth refresh tokens but never uses them to refresh expired access tokens. This causes authentication failures requiring manual re-authentication, sometimes multiple times per day.
Evidence
Refresh tokens ARE stored
// From macOS Keychain - Claude Code-credentials
"supabase|...": {
"accessToken": "sbp_oauth_...",
"refreshToken": "UVemFCAEqbCNEpPqkG", // ← Stored but never used
"expiresAt": 1769635674007
}
MCP servers DO support refresh
Both Linear and Sentry OAuth metadata explicitly support refresh tokens:
// https://mcp.linear.app/.well-known/oauth-authorization-server
// https://mcp.sentry.dev/.well-known/oauth-authorization-server
"grant_types_supported": ["authorization_code", "refresh_token"]
Tokens expire with no refresh attempt
After ~24 hours (or less), tokens expire and users see:
- "Invalid grant: refresh token is invalid"
- MCP servers showing empty
accessToken: ""andexpiresAt: 0 - Silent failures where MCP appears connected but returns "Unauthorized"
Impact
- Atlassian Rovo MCP: "basically unusable after ~5 minutes" (see #5706)
- Microsoft OAuth users: 60-90 minute token TTL = constant re-auth
- Autonomous workflows: Fail mid-execution when tokens expire
- Developer productivity: Multiple manual re-authentications per day
MCP Spec Compliance
The MCP Authorization spec states:
"Authorization servers SHOULD issue short-lived access tokens to reduce the impact of leaked tokens."
This implies clients MUST implement refresh token handling. Claude Code does not.
Related Issues (20+)
This is the root cause of many fragmented issues:
| Issue | Problem |
|-------|---------|
| #5706 | Original feature request (7+ months, no response) |
| #16957 | Exit-time refresh tokens not persisted |
| #19456 | Keychain permission errors on token update |
| #12447 | OAuth expiration disrupts autonomous workflows |
| #19481 | /mcp reconnect doesn't refresh expired tokens |
| #10784 | OAuth expiration causes retry storm (3.75M wasted tokens) |
| #18225 | Repeated 401 errors during active sessions |
| #10250 | OAuth succeeds but MCP reconnection fails |
Expected Behavior
- When access token expires, Claude Code should use stored refresh token
- On successful refresh, persist new access + refresh tokens
- On refresh failure, prompt user to re-authenticate (not fail silently)
Environment
- Claude Code: 2.1.x (affects multiple versions)
- Platform: macOS (also reported on Linux)
- MCP servers affected: Linear, Sentry, Supabase, Atlassian, custom OAuth servers
---
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗