MCP Streamable HTTP: OAuth token refresh keeps old Mcp-Session-Id

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

After a silent MCP OAuth token refresh, Cursor keeps using the existing Streamable HTTP Mcp-Session-Id with the new bearer token.

Servers that bind the MCP session to the access token (e.g. Prepr at https://mcp.prepr.io) then reject requests with:

Session token does not match bearer token (JSON-RPC -32003)

Cursor marks the connection as failed but leaves auth valid (conn=failed, auth=valid), so it does not trigger needsAuth or a new initialize. The MCP server stays broken until manual reconnect / full re-login / app restart.

Steps to Reproduce

  1. Add a remote OAuth MCP server that binds Mcp-Session-Id to the bearer token (example: Prepr at https://mcp.prepr.io, URL-only config in mcp.json).
  2. Authenticate successfully and use MCP tools for a while.
  3. Wait until Cursor performs a silent OAuth access-token refresh (near token expiry / refresh flow).
  4. Call any MCP tool again.
  5. Observe failure: Session token does not match bearer token.
  6. Check MCP logs: refresh succeeds, then requests fail; FSM becomes conn=failed, auth=valid (no needsAuth).
  7. Note that only toggling the MCP server off/on, full re-login, or restarting Cursor recovers it.

Expected Behavior

After a successful MCP OAuth token refresh (or when the server returns a session/bearer mismatch), Cursor should:

  1. Drop the current Streamable HTTP session
  2. Call initialize again with the new bearer token
  3. Use the new Mcp-Session-Id for subsequent requests

It should not keep a stale session while auth remains marked valid, and it should recover automatically without requiring manual reconnect.

Operating System

MacOS

Version Information

Version: 3.21.16
VS Code Extension API: 1.128.0
Commit: 8ae78e8eee1e63479c7e0504b664bc0a80c68000
Date: 2026-09-19T06:43:22.276Z
Layout: Agent Window
Build Type: Stable
Release Track: Default
Electron: 42.10.0
Chromium: 148.0.7778.280
Node.js: 24.18.1
V8: 14.8.178.38-electron.0
xterm.js: 6.1.0-beta.291
OS: Darwin arm64 27.0.0

For AI issues: which model did you use?

N/A – MCP client / OAuth transport bug (not model-specific)

Additional Information

MCP config is URL-only OAuth (no static bearer in mcp.json):

{
“mcpServers”: {
“Prepr”: {
“url”: “https://mcp.prepr.io”
}
}
}

Workaround that recovers temporarily:

  • Toggle the Prepr MCP server off/on in Settings → MCP
  • Or complete full interactive OAuth again
  • Or restart Cursor

Manual verification: refreshing the OAuth access token and calling initialize again with the new bearer produces a new Mcp-Session-Id and restores tool calls. Cursor’s silent refresh does the token part but skips session re-initialization.

Suggested fix: after OAuth token refresh (or on -32003 session/bearer mismatch), invalidate the Streamable HTTP session and re-run initialize.

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hey @Tom_Swinkels, thanks for the detailed report.

Quickest way to unblock: toggle the Prepr server off and back on in Cursor Settings → MCP (or reload the window) to force a fresh session. After a silent token refresh, Cursor keeps the old session instead of re-initializing - that’s not intended behavior and nothing you misconfigured. We’ve let the team know and it’s an issue we’re tracking.

Two quick things that’d help: the HTTP status on that first failing POST (400 vs 403), and - if you control the Prepr side - note that a server which no longer accepts a session ID should return HTTP 404, which Cursor already re-initializes on automatically, so that sidesteps this in the meantime.

I’ll follow up here when there’s an update.