Refresh_tokens are not used

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

It looks like refresh_token isn’t used when using oauth

We have the following setup of the MCP for test:

  • MCP server built on top of Apollo MCP
  • locally deployed oauth server (proxying requests to auth0)
  • auth0 configured (for test) to give 1-minute valid access tokens
  • refresh_token is supplied with access_token

After login the connection works for 1 minute, as expected, then at some point we hit “unauthorized” and token is not auto-refreshed, nothing similar in logs

Steps to Reproduce

  • Configure oauth for 1-minute access_token + refresh_token
  • Connect to MCP and authenticate.
  • You’ll be able to use MCP for 1 minute only, although you can refresh tokens many times
  • The same happens with longer-lasting tokens, like 1 hour or 8 hours, it’s just easier to replicate and demonstrate with 1-minute tokens

Expected Behavior

When cursor understands the token is expired, it should refresh it

Operating System

Linux

Current Cursor Version (Menu → About Cursor → Copy)

Version: 2.0.34
VSCode Version: 1.99.3
Commit: 45fd70f3fe72037444ba35c9e51ce86a1977ac10
Date: 2025-10-29T06:51:29.202Z
Electron: 34.5.8
Chromium: 132.0.6834.210
Node.js: 20.19.1
V8: 13.2.152.41-electron.0
OS: Linux x64 6.14.0-33-generic
Same happens on macOS as well

For AI issues: which model did you use?

Auto

Additional Information

Log

2025-11-05 18:38:13.829 [info] Handling LogoutServer action
2025-11-05 18:38:13.829 [info] Clearing stored OAuth data
2025-11-05 18:38:13.842 [info] Successfully cleared OAuth tokens
2025-11-05 18:38:13.842 [info] Cleaning up
2025-11-05 18:38:13.850 [info] Handling ReloadClient action
2025-11-05 18:38:13.850 [info] Creating streamableHttp transport
2025-11-05 18:38:13.851 [info] Connecting to streamableHttp server
2025-11-05 18:38:13.861 [info] No stored tokens found
2025-11-05 18:38:15.557 [info] No stored client information found
2025-11-05 18:38:15.559 [info] Using redirect URL {"url":"cursor://anysphere.cursor-mcp/oauth/user-mcp-server-name/callback"}
2025-11-05 18:38:15.987 [info] Saving client information {"redirects":1,"clientIdPresent":true}
2025-11-05 18:38:15.995 [info] No stored tokens found
2025-11-05 18:38:15.996 [info] Using redirect URL {"url":"cursor://anysphere.cursor-mcp/oauth/user-mcp-server-name/callback"}
2025-11-05 18:38:15.996 [info] Using redirect URL {"url":"cursor://anysphere.cursor-mcp/oauth/user-mcp-server-name/callback"}
2025-11-05 18:38:15.996 [info] Saving PKCE code verifier {"verifierLen":43}
2025-11-05 18:38:15.999 [info] Redirect to authorization requested {"url":"<AUTH_SERVER_URL>/oauth/authorize?response_type=code&client_id=<CLIENT_ID>&code_challenge=<CODE_CHALLENGE>&code_challenge_method=S256&redirect_uri=cursor%3A%2F%2Fanysphere.cursor-mcp%2Foauth%2Fuser-mcp-server-name%2Fcallback&resource=<MCP_SERVER_URL>"}
2025-11-05 18:38:16.001 [info] Stored server URL for OAuth flow
2025-11-05 18:38:16.001 [info] OAuth provider needs auth callback during connection
2025-11-05 18:38:16.001 [error] Error connecting to streamableHttp server, falling back to SSE: Unauthorized
2025-11-05 18:38:16.001 [warning] Unauthorized error connecting to streamableHttp server, returning transport
2025-11-05 18:38:16.001 [info] Successfully connected to streamableHttp server
2025-11-05 18:38:16.002 [info] Storing streamableHttp client
2025-11-05 18:38:16.002 [info] Successfully reloaded client
2025-11-05 18:38:16.016 [info] Handling ListOfferings action, server stored: true
2025-11-05 18:38:16.017 [info] Connected to streamableHttp server, fetching offerings
2025-11-05 18:38:16.017 [info] Found 0 tools, 0 prompts, and 0 resources
2025-11-05 18:38:25.092 [info] Received OAuth callback with code
2025-11-05 18:38:26.802 [info] Using redirect URL {"url":"cursor://anysphere.cursor-mcp/oauth/user-mcp-server-name/callback"}
2025-11-05 18:38:27.384 [info] Saving tokens {"accessTokenLen":1234,"refreshPresent":true,"expiresIn":60} <<<<< got the token, refresh_token is also there
2025-11-05 18:38:27.388 [info] OAuth authorization completed
2025-11-05 18:38:27.404 [info] Handling ReloadClient action
2025-11-05 18:38:27.404 [info] Cleaning up
2025-11-05 18:38:27.404 [info] Creating streamableHttp transport
2025-11-05 18:38:27.404 [info] Connecting to streamableHttp server
2025-11-05 18:38:28.415 [info] Successfully connected to streamableHttp server
2025-11-05 18:38:28.415 [info] Storing streamableHttp client
2025-11-05 18:38:28.415 [info] Successfully reloaded client
2025-11-05 18:38:28.423 [info] Handling ListOfferings action, server stored: true
2025-11-05 18:38:28.423 [info] Connected to streamableHttp server, fetching offerings
2025-11-05 18:38:28.915 [info] Found 1 tools, 0 prompts, and 0 resources
2025-11-05 18:38:44.400 [info] Handling CallTool action for tool 'ConsultToolName'
2025-11-05 18:38:44.400 [info] Calling tool 'ConsultToolName' with toolCallId: tool_b685a09b-3f72-4530-be3a-31ef9cc2b49 <<<<< first call, ok
2025-11-05 18:38:45.374 [info] Successfully called tool 'ConsultToolName'
2025-11-05 18:39:10.309 [info] Handling CallTool action for tool 'ConsultToolName'
2025-11-05 18:39:10.309 [info] Calling tool 'ConsultToolName' with toolCallId: tool_2f0cc963-34d2-4e28-8c2d-c9e0032fd00 <<<<< second call, still ok
2025-11-05 18:39:11.271 [info] Successfully called tool 'ConsultToolName'
2025-11-05 18:39:28.811 [error] Client error for command fetch failed
2025-11-05 18:39:28.811 [error] Client error for command fetch failed
2025-11-05 18:39:55.640 [info] Handling CallTool action for tool 'ConsultToolName'
2025-11-05 18:39:55.640 [info] Calling tool 'ConsultToolName' with toolCallId: tool_130b8f7a-e428-418d-af2d-84ad055256b <<<<< third call, token already expired. However, 401 was simply passed to LLM. No attempts to refresh token
2025-11-05 18:39:56.231 [info] Successfully called tool 'ConsultToolName'
2025-11-05 18:40:31.107 [info] Handling CallTool action for tool 'ConsultToolName'
2025-11-05 18:40:31.107 [info] Calling tool 'ConsultToolName' with toolCallId: tool_6e7690c8-4620-456b-ab27-7ed3d05ceeb <<<<< fourth call, this time cursor understood the expiration, but didn't refresh the token
2025-11-05 18:40:32.984 [info] Using redirect URL {"url":"cursor://anysphere.cursor-mcp/oauth/user-mcp-server-name/callback"}
2025-11-05 18:40:32.985 [info] Using redirect URL {"url":"cursor://anysphere.cursor-mcp/oauth/user-mcp-server-name/callback"}
2025-11-05 18:40:32.985 [info] Saving PKCE code verifier {"verifierLen":43}
2025-11-05 18:40:32.989 [info] Redirect to authorization requested {"url":"<AUTH_SERVER_URL>/oauth/authorize?response_type=code&client_id=<CLIENT_ID>&code_challenge=<CODE_CHALLENGE>&code_challenge_method=S256&redirect_uri=cursor%3A%2F%2Fanysphere.cursor-mcp%2Foauth%2Fuser-mcp-server-name%2Fcallback&resource=<MCP_URL>"}
2025-11-05 18:40:32.991 [info] Stored server URL for OAuth flow
2025-11-05 18:40:32.991 [info] OAuth provider needs auth callback during connection
2025-11-05 18:40:32.991 [error] Error calling tool 'ConsultToolName': Unauthorized

Message from chat:

Calling ConsultToolName again:


[1 tool called]


**Fourth call result: authentication error**

Received an "Unauthorized" error. The authentication issue persists.

**Test results summary:**
1. First call: Success — received result
2. Second call: Success — received result
3. Third call: Failed — 401 Invalid token
4. Fourth call: Failed — Unauthorized

The tool works when authenticated; the current session appears unauthorized. Re-authenticate with the MCP server (OAuth) to restore access.

Does this stop you from using Cursor

Yes - Cursor is unusable

Hey, thanks for the report. The logs show that Cursor receives and saves the refresh token but doesn’t use it after the access token expires.

From the logs:

  • Saving tokens {“accessTokenLen":1234,"refreshPresent":true,"expiresIn":60} - refresh token received
  • After expiration, Cursor prompts for full re-authorization instead of using the refresh token

This looks like a bug in the OAuth refresh flow for MCP servers.

Temporary workaround: you’ll need to re-authenticate after the access token expires. I know this defeats the purpose of refresh tokens.