Skip to content

Build1 publisher2 min readPublished

MCP servers that answer expired sessions with 401 push OAuth clients into needless re-logins

Silent MCP disconnects in Copilot CLI, Gemini CLI, Cursor, Codex and Open WebUI trace to 401 and 404 errors handled as one failure, a dev.to post argues. Tokens need a refresh and sessions need a new initialize, so clients and gateways need two recovery paths.

The Engineer · Build desk

Illustration accompanying MCP servers that answer expired sessions with 401 push OAuth clients into needless re-logins

What happened

  • Under the MCP spec, a request carrying the id of an ended session must get HTTP 404, and the client must discard that id and send a fresh initialize.
  • A 401 belongs to OAuth, where the MCP server checks each bearer token's expiry, scope and audience and rejects an invalid one with 401.
  • One rmcp-based server answered unrecognized session IDs with "401 Unauthorized: Session not found" instead of the 404 the spec requires.
  • Google issues a refresh token only when the authorization request includes access_type=offline, and it ignores offline_access when requested as a scope.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • cost OAuth clients such as claude.ai and Claude Code read any 401 as a bad credential, so one wrong status code on the server costs users an interactive login while their token is still valid.
  • constraint Because the spec gives a session no lease or expiry timestamp, clients cannot renew one ahead of time and need an initialize path ready on every request.
  • exposure Teams on a custom auth server that never prompts clients for offline_access lose every user's session on a fixed 7-day timer, whatever their refresh code does.

A client can check responses in this order, following the post's recovery rules:

1. A 404 on a request that carries a session id means the session is gone. Drop the id, send a fresh initialize, keep the token [5][3]. 2. A 401 with a WWW-Authenticate header means the credential failed, and the token needs a refresh [3][10]. 3. A 401 without that header is, by the post's test, a stale session the server misreported, so the credentials stay [10].

Case 3 exists only because servers get the codes wrong [7]. The server fix, per the post, is to return 404 for unknown or expired sessions and 401 with WWW-Authenticate only for actual credential problems [9]. The author wrote that this is "the single highest-leverage fix in this whole post" [9].

The two paths also have to know about each other. The post calls token expiry and session expiry unrelated [3]. It also says a Streamable HTTP transport ties a session ID to the credentials that created it, so a client that refreshes its bearer token and keeps sending the old Mcp-Session-Id presents the server with a mismatch [15]. The post says most teams never see this coming, because the refresh itself succeeds [15]. I'd write the refresh path to expect a 404 on the next request and hand it straight to initialize without showing the user an error.

Refresh also needs a refresh token to exist. On one Meta-backed server, a production report found refresh_token advertised but never issued, because Meta's OAuth returns a long-lived access token with no refresh token [12]. The post's fix is to check for a refresh_token field right after the initial grant and fail loudly in development, instead of waiting for a user to hit it three hours into a production session [14].

The cross-client pattern rests on the author's reading of issue trackers. The post lists Copilot CLI, Gemini CLI, Cursor, Open WebUI, Codex and several homegrown gateways as showing the same symptom: "it worked, then it silently stopped working, and the only fix was to log in again" [1]. It says the root cause is almost never broken OAuth [2]. The one failure it works through in detail, the rmcp-based server, is described as hurting claude.ai and Claude Code, and neither is on that list [8][1]. The post does not match each named client's reports to a specific status-code or refresh mistake.

What to watch

  • Whether rmcp and other MCP server libraries change their response for an unrecognized session ID from 401 to the spec's 404.
  • Whether claude.ai and Claude Code stop discarding tokens on 401 responses that lack a WWW-Authenticate header.
  • Whether the MCP lifecycle specification adds a lease or expiry signal for sessions, so clients could renew before hitting a 404.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories