Recover MCP servers after OAuth reauthentication (#37337)

## Why

An OAuth-backed Streamable HTTP MCP server can fail startup when its stored credentials are rejected. If a client then completes OAuth sign-in and replaces those credentials, the failed server needs to become available without restarting the Codex session.

## What changed

- Track the credential store and OAuth tokens used for each MCP connection, and detect replacements after authentication failures.
- Refresh affected MCP servers before the next turn and include their recovered tools in the model request.
- Classify HTTP authentication challenges and rejected refresh tokens as requiring reauthentication, with sign-in guidance appropriate to local and client-managed environments.

## Testing

- Cover recovery from expired credentials through an externally written replacement token.
- Cover authentication-error classification, startup messaging, and `Auto` store migration from file credentials to the keyring.

GitOrigin-RevId: 9b090089d4e7a44ffc182f4e25da20a18e97b70f
This commit is contained in:
jif authored and copyberry committed 2026-08-07 00:17:46 +00:00
1 parent 7257826ab2
commit b3ffe3d001
14 files changed
+408 -57

No files matched your search

+22 -1
View File
@@ -302,11 +302,32 @@ impl Session {
{
self.mark_mcp_runtime_dirty();
}
let recovered_oauth_servers = self
.services
.mcp_runtime
.updated_oauth_credentials_after_auth_failure()
.await;
if !recovered_oauth_servers.is_empty()
&& let Ok(_refresh) = self.mcp_refresh.acquire().await
&& self
.services
.mcp_runtime
.has_authentication_failed_servers(&recovered_oauth_servers)
.await
{
self.mark_mcp_runtime_dirty();
}
self.refresh_mcp_if_dirty().await;
let required_servers = required_servers
.iter()
.chain(&recovered_oauth_servers)
.cloned()
.collect::<Vec<_>>();
if let Some(binding) = self
.services
.mcp_runtime
.current_binding_with_required_servers(required_servers)
.current_binding_with_required_servers(&required_servers)
.await
{
return binding;