What version of Codex CLI is running?
codex-cli 0.134.0
What subscription do you have?
ChatGPT Enterprise
Which model were you using?
No response
What platform is your computer?
macOS Darwin 25.5.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
No response
Codex doctor report
What issue are you seeing?
Logging into an MCP server that uses Google as its OAuth identity provider succeeds initially, but the stored credentials contain only an access token, with no refresh_token. After the ~1h access token expires, MCP tool calls fail with auth errors and the only recovery is to run codex mcp login <server> again and walk through the full browser flow.
Inspecting the stored credentials confirms the symptom:
{
"server_name": "<name>",
"access_token": "<JWT, ~1h lifetime>",
"expires_at": <approx now + 3600s>,
"scope": "openid email profile"
// no `refresh_token` field
}
The credential blob lives in the macOS Keychain under service Codex MCP Credentials, or in $CODEX_HOME/.credentials.json when keyring writes fall back to file.
What steps can reproduce the bug?
-
Configure any MCP server that uses Google as its OAuth provider, e.g.:
[mcp_servers.example]
url = "https://example.com/mcp"
-
Run codex mcp login example and complete the Google sign-in in the browser.
-
Inspect the stored credentials (Keychain entry for Codex MCP Credentials with account example|<hash>, or $CODEX_HOME/.credentials.json). No refresh_token field is present.
-
Wait until the access token expires, or make an MCP tool call shortly after the 1h mark. The session fails to refresh and requires manual relogin.
What is the expected behavior?
After codex mcp login against a Google-backed MCP server, the stored credentials should contain a refresh_token so the session can survive past access-token expiry without forcing the user back through the browser flow. This is how Claude Code's MCP client behaves against the same servers.
Additional information
Root cause (from reading codex-rs/rmcp-client/src/perform_oauth_login.rs): Google OAuth issues a refresh token only when the authorization request includes access_type=offline (typically with prompt=consent). This is a Google-specific query parameter, not part of OAuth scope negotiation. Google does not honor offline_access as a scope, so requesting it via the existing scopes config has no effect. Codex's authorize-URL builder appends only one parameter on top of what rmcp generates (resource=), so access_type=offline is never sent and Google therefore omits the refresh token.
A draft change on a fork (https://github.com/forketyfork/codex/pull/1) explores one possible mitigation for reference (adding extra_authorize_params to the MCP auth configuration).
What version of Codex CLI is running?
codex-cli 0.134.0
What subscription do you have?
ChatGPT Enterprise
Which model were you using?
No response
What platform is your computer?
macOS Darwin 25.5.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
No response
Codex doctor report
What issue are you seeing?
Logging into an MCP server that uses Google as its OAuth identity provider succeeds initially, but the stored credentials contain only an access token, with no
refresh_token. After the ~1h access token expires, MCP tool calls fail with auth errors and the only recovery is to runcodex mcp login <server>again and walk through the full browser flow.Inspecting the stored credentials confirms the symptom:
{ "server_name": "<name>", "access_token": "<JWT, ~1h lifetime>", "expires_at": <approx now + 3600s>, "scope": "openid email profile" // no `refresh_token` field }The credential blob lives in the macOS Keychain under service
Codex MCP Credentials, or in$CODEX_HOME/.credentials.jsonwhen keyring writes fall back to file.What steps can reproduce the bug?
Configure any MCP server that uses Google as its OAuth provider, e.g.:
Run
codex mcp login exampleand complete the Google sign-in in the browser.Inspect the stored credentials (Keychain entry for
Codex MCP Credentialswith accountexample|<hash>, or$CODEX_HOME/.credentials.json). Norefresh_tokenfield is present.Wait until the access token expires, or make an MCP tool call shortly after the 1h mark. The session fails to refresh and requires manual relogin.
What is the expected behavior?
After
codex mcp loginagainst a Google-backed MCP server, the stored credentials should contain arefresh_tokenso the session can survive past access-token expiry without forcing the user back through the browser flow. This is how Claude Code's MCP client behaves against the same servers.Additional information
Root cause (from reading
codex-rs/rmcp-client/src/perform_oauth_login.rs): Google OAuth issues a refresh token only when the authorization request includesaccess_type=offline(typically withprompt=consent). This is a Google-specific query parameter, not part of OAuth scope negotiation. Google does not honoroffline_accessas a scope, so requesting it via the existingscopesconfig has no effect. Codex's authorize-URL builder appends only one parameter on top of whatrmcpgenerates (resource=), soaccess_type=offlineis never sent and Google therefore omits the refresh token.A draft change on a fork (https://github.com/forketyfork/codex/pull/1) explores one possible mitigation for reference (adding
extra_authorize_paramsto the MCP auth configuration).