rmcp OAuth tokens never refresh based on expires_at
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.
3 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
What version of the CLI are you on? 0.56 had an oauth token refresh fix from https://github.com/openai/codex/pull/6234
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.