rmcp OAuth tokens never refresh based on expires_at

Resolved 💬 3 comments Opened Nov 13, 2025 by LaelLuo Closed Nov 18, 2025

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 ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗