rmcp OAuth tokens never refresh based on expires_at

Resolved 💬 3 comments Opened Nov 13, 2025 by LaelLuo Closed Nov 18, 2025
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

When Codex uses the rmcp streamable HTTP transport against OAuth-enabled MCP servers (e.g. Notion), the cached OAuthTokenResponse keeps the original expires_in duration forever. AuthorizationManager::get_access_token() only refreshes when that duration reaches zero, so tokens that are still considered valid locally never trigger a refresh even after the server rotates them. Eventually every request—including the initialization handshake—fails with Auth required, forcing the user to run codex mcp login ... again.

Expected

The client should derive the remaining lifetime from the stored expires_at (or an equivalent timestamp) and refresh via the refresh_token before the server-side expiry, so long-lived SSE connections continue seamlessly.

Actual

Because expires_in stays at the original value, rmcp never refreshes; once the server-side token rotation happens, all subsequent requests fail immediately with Auth required.

View original on GitHub ↗

3 Comments

github-actions[bot] contributor · 9 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #6570

Powered by Codex Action

gpeal contributor · 9 months ago

What version of the CLI are you on? 0.56 had an oauth token refresh fix from https://github.com/openai/codex/pull/6234

LaelLuo contributor · 9 months ago

In fact, I have been using a version that I maintain based on the main branch. This bug occurs when the mcp accessToken expires and codex is launched; it prompts for a re-login instead of automatically refreshing the accessToken. Moreover, if the accessToken expires during the operation of codex, the accessToken is also not automatically refreshed. I checked the automatic refresh logic of rmcp, and it only triggers when expires_in is 0. However, expires_in is not updated in reality, so the logic for automatically refreshing the accessToken will never be triggered.