[codex] Make MCP connection startup fallible (#27261)

## Why

Required MCP server startup was enforced in `Session::new` after
`McpConnectionManager` had already created the clients. That split let
other manager construction paths bypass the same requirement and exposed
manager internals solely so the session could validate them. Keeping
required-server readiness in the constructor gives every caller one
consistent startup contract.

## What changed

- make `McpConnectionManager::new` return `anyhow::Result<Self>` and
fail when an enabled, required server cannot initialize
- pass the startup cancellation token into the constructor so
required-server waits remain cancellable
- propagate constructor failures through resource reads, connector
discovery, and MCP status collection
- preserve the active manager and cancellation token when a refreshed
replacement fails
- keep required-startup failure collection private and cover the
constructor error contract directly

## Validation

- updated the focused connection-manager test to assert the complete
required-server startup error
- local tests not run; relying on CI
This commit is contained in:
Ahmed Ibrahim authored and GitHub committed 2026-06-10 00:17:58 -07:00
1 parent 00a25e1e0c
commit a7b6baecc6
8 files changed
+129 -107

No files matched your search

+4 -1
View File
@@ -17,6 +17,7 @@ use codex_protocol::models::PermissionProfile;
use codex_tools::DiscoverableTool;
use rmcp::model::ToolAnnotations;
use serde::Deserialize;
use tokio_util::sync::CancellationToken;
use tracing::warn;
use crate::config::Config;
@@ -285,13 +286,15 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_mcp_manager(
let (tx_event, rx_event) = unbounded();
drop(rx_event);
let (mut mcp_connection_manager, cancel_token) = McpConnectionManager::new(
let cancel_token = CancellationToken::new();
let mut mcp_connection_manager = McpConnectionManager::new(
&mcp_servers,
config.mcp_oauth_credentials_store_mode,
auth_status_entries,
&config.permissions.approval_policy,
INITIAL_SUBMIT_ID.to_owned(),
tx_event,
cancel_token.clone(),
PermissionProfile::default(),
// Connector discovery is threadless. Use an actually configured env if
// one exists, but do not reintroduce the old hidden-local fallback.