MCP CLI Clients Are Shipping Without Refresh-Token Support
The MCP OAuth specification mandates OAuth 2.1 with PKCE. But the clients everyone actually uses don't implement the refresh-token flow, forcing server teams into a security tradeoff nobody should have to make.
By Brian Cardinale
The specification says one thing, the ecosystem does another
The MCP authorization specification, finalized in November 2025, requires OAuth 2.1 with PKCE for remote MCP server authentication. The security model is sound: short-lived access tokens (5 to 60 minutes), long-lived refresh tokens (30 to 90 days, rotated on every use), and PKCE to protect the authorization code exchange.
Short-lived access tokens limit the impact of a stolen token. If someone intercepts a 15-minute access token, they have 15 minutes of access. If they intercept an 8-hour token, they have a full workday. The refresh-token flow is what makes short-lived access tokens viable without destroying the user experience. The client silently refreshes in the background, the user never re-authenticates, and revocation stays effective.
That's the theory. In practice, as of April 2026, not a single MCP client fully implements it.
The numbers
We recently compiled a support matrix tracking refresh-token support across 14 MCP clients:
- Full support: 0
- Partial or broken: 7
- Not implemented: 6
- Unknown: 1
Zero full support. The seven "partial" clients have various issues:
- refresh only at connection setup (not mid-session)
- DCR requests that omit
refresh_tokenfromgrant_types - token storage bugs that cause repeated OAuth popups
- delegating to
mcp-remotedue to lack of native implementation.
The six "not implemented" clients include some of the most widely used tools in the ecosystem:
- Claude Code
- Claude Desktop
- Cursor
- LibreChat
- Amazon Q CLI.
These clients store refresh tokens but never use them for silent renewal. When the access token expires, the user gets an error and has to manually reconnect.
Why this is a security problem
Server teams who want their MCP integrations to actually work with real clients have two choices:
Issue short-lived access tokens (5 to 60 minutes) and force users to re-authenticate constantly. This is the secure option, but it is a miserable user experience that will most likely make the whole experience feel broken. Nobody wants to be asked the same question every 5 minutes! Don't believe me? Ask any parent with a young child.
Issue long-lived access tokens (8 hours, 24 hours, or longer) to avoid constant re-authentication requests. This is the pragmatic option that most teams are choosing. But it means a stolen token grants access for hours or days instead of minutes.
Neither option is good. The first creates a terrible user experience. The second significantly impairs security. Both exist because the clients aren't doing their part.
Long-lived access tokens also undermine revocation. If you revoke a refresh token, you prevent future access tokens from being issued. But if the current access token lives for 8 hours and your resource servers validate JWTs locally (no introspection), that revoked user still has 8 hours of access. Short-lived tokens are the only reliable revocation mechanism for locally-validated JWTs.
We wrote a full MCP OAuth Security Best Practices Guide covering this and 16 other security considerations for teams implementing MCP OAuth.
The Claude Code root cause
The most well-documented failure case is Claude Code. Community contributors traced the root cause in GitHub Issue #28262:
When Claude Code's _doRefresh method is called, it needs OAuth metadata to locate the token_endpoint. It checks an in-memory cache (empty on new sessions), then falls back to RFC 8414 well-known URL discovery. For servers with non-root paths like https://mcp.example.com/v1/mcp, it constructs the wrong discovery URL and gets a 404.
The fix is straightforward: fall back to the cached discoveryState already persisted in ~/.claude/.credentials.json from the initial auth flow. Community members documented the fix. It was never merged. The issue was closed by a bot as not_planned.
The mcp-remote workaround
The de facto solution right now is mcp-remote, an OAuth bridge that wraps stdio-only clients in an OAuth-capable proxy. It handles refresh-token rotation, invalid_grant recovery, and atomic token storage. Continue.dev delegates to it. Individual users of Claude Code and Cursor route through it as a stopgap.
It works. But a community-maintained bridge shouldn't be the permanent answer to a gap in first-party client implementations. Every additional proxy layer is another thing that can break when new updates get pushed.
What server teams should do now
Until the ecosystem catches up, here's what to do in the meantime:
- Accept longer access token TTLs (1 to 8 hours) as a temporary, documented concession. This is NOT the security posture you want long term. Document it as a temporary measure pending ecosystem fixes. Have a plan to reduce TTLs when clients improve.
- Set refresh token TTLs to 30 to 90 days with rotation. When clients do fully implement the refresh flow, long-lived refresh tokens with rotation provide the intended UX without long-lived access tokens.
- Advertise
offline_accessin your authorization server'sscopes_supported. Per SEP-2207, this signals to compliant clients that they should request refresh tokens. - Implement
/oauth/revoke(RFC 7009). This is the escape valve when a long-lived access token needs to be invalidated before it expires naturally. - Document
mcp-remoteas the recommended workaround for users of clients that don't support refresh. Don't leave them to discover it on their own.
The full details, including the client-by-client breakdown and SDK status, are in our MCP Client OAuth Refresh-Token Support Matrix.
The ask
If you maintain an MCP client, fully implement the refresh-token flow. The MCP TypeScript SDK merged SEP-2207 support on March 30, 2026. The client-side plumbing is there. The Python SDK PR is open. The specification is clear.
Every week that passes without refresh-token support is another week that the most bleeding-edge technology sector is sporting a serious security regression by choice. We want widespread adoption! We don't need an already-solved problem increasing the chances of reducing consumers' trust.
The math doesn't work. Ship the refresh token flow!

