Summary
When both the access token and refresh token are expired simultaneously
(e.g. after 30 days of inactivity, or when both are deleted server-side),
mcp-remote enters an infinite re-authentication loop. The user successfully
completes OAuth login and the authorization code is delivered to the
localhost:8033 callback server — but mcp-remote never calls POST /token
to exchange it. No new tokens are issued. The loop continues indefinitely
without restarting Claude Desktop.
Reproduction Steps
- Connect Claude Desktop to a remote MCP server using mcp-remote (OAuth flow)
- Authenticate successfully — confirm tools are working
- Delete both the access token and refresh token from the server's token
store (simulating natural expiry after 30 days of inactivity)
- Do NOT restart Claude Desktop
- Ask Claude to run any tool
- The OAuth login screen opens — fill in credentials and complete login
- The browser shows the success page / redirects to localhost:8033
Result: mcp-remote loops back to the OAuth screen. Repeating step 6 any
number of times does not fix it.
Expected: After completing login, mcp-remote exchanges the authorization
code for new tokens and subsequent tool calls succeed.
Server-Side Evidence
With full logging enabled on the OAuth server, after handleSuCallback
redirects to localhost:8033/oauth/callback?code=...:
challengeForAuthorizationCode() is never called
exchangeAuthorizationCode() is never called
POST /token never arrives at the server
- mcp-remote immediately retries with the old expired token
This confirms the authorization code is received at localhost:8033
(the redirect succeeds) but mcp-remote never processes it into a token
exchange. The promise waiting for the auth code does not resolve.
Server Log Pattern
[OAuth] verifyAccessToken() — token NOT FOUND: d205778f...
[OAuth] exchangeRefreshToken() — refresh token NOT FOUND
[OAuth] authorize() — created sessionId: 3beeb7e4...
[OAuth] handleAuthorizeStart() — session lookup: FOUND
[OAuth] handleSuCallback() — SU token exchange SUCCESS
[OAuth] handleSuCallback() — MCP auth code generated, redirecting to: http://localhost:8033/oauth/ca...
← no challengeForAuthorizationCode, no exchangeAuthorizationCode, no POST /token
[OAuth] verifyAccessToken() — token NOT FOUND: d205778f... ← same old token
[OAuth] exchangeRefreshToken() — refresh token NOT FOUND
[OAuth] authorize() — created sessionId: 446c62d9... ← loop
Workaround
Restarting Claude Desktop fully (Cmd+Q → reopen) resets mcp-remote's
internal state. The next OAuth flow completes correctly including the
token exchange.
Environment
- mcp-remote: 0.1.38
- Claude Desktop, macOS
- Triggered by: both access token + refresh token expired simultaneously
Related Issues
Summary
When both the access token and refresh token are expired simultaneously
(e.g. after 30 days of inactivity, or when both are deleted server-side),
mcp-remote enters an infinite re-authentication loop. The user successfully
completes OAuth login and the authorization code is delivered to the
localhost:8033 callback server — but mcp-remote never calls
POST /tokento exchange it. No new tokens are issued. The loop continues indefinitely
without restarting Claude Desktop.
Reproduction Steps
store (simulating natural expiry after 30 days of inactivity)
Result: mcp-remote loops back to the OAuth screen. Repeating step 6 any
number of times does not fix it.
Expected: After completing login, mcp-remote exchanges the authorization
code for new tokens and subsequent tool calls succeed.
Server-Side Evidence
With full logging enabled on the OAuth server, after
handleSuCallbackredirects to
localhost:8033/oauth/callback?code=...:challengeForAuthorizationCode()is never calledexchangeAuthorizationCode()is never calledPOST /tokennever arrives at the serverThis confirms the authorization code is received at
localhost:8033(the redirect succeeds) but mcp-remote never processes it into a token
exchange. The promise waiting for the auth code does not resolve.
Server Log Pattern
[OAuth] verifyAccessToken() — token NOT FOUND: d205778f...
[OAuth] exchangeRefreshToken() — refresh token NOT FOUND
[OAuth] authorize() — created sessionId: 3beeb7e4...
[OAuth] handleAuthorizeStart() — session lookup: FOUND
[OAuth] handleSuCallback() — SU token exchange SUCCESS
[OAuth] handleSuCallback() — MCP auth code generated, redirecting to: http://localhost:8033/oauth/ca...
← no challengeForAuthorizationCode, no exchangeAuthorizationCode, no POST /token
[OAuth] verifyAccessToken() — token NOT FOUND: d205778f... ← same old token
[OAuth] exchangeRefreshToken() — refresh token NOT FOUND
[OAuth] authorize() — created sessionId: 446c62d9... ← loop
Workaround
Restarting Claude Desktop fully (Cmd+Q → reopen) resets mcp-remote's
internal state. The next OAuth flow completes correctly including the
token exchange.
Environment
Related Issues