Skip to main content

codex_mcp/connection_manager/
required.rs

1use anyhow::Result;
2use anyhow::anyhow;
3use codex_protocol::protocol::McpStartupFailure;
4use tracing::Instrument;
5use tracing::info_span;
6
7use super::McpConnectionManager;
8use crate::rmcp_client::StartupOutcomeError;
9
10impl McpConnectionManager {
11    /// Waits for every required server and reports their startup failures together.
12    ///
13    /// Callers must make the manager reachable to request handlers before awaiting this method,
14    /// because server initialization may require client elicitation.
15    pub async fn validate_required_servers(&self) -> Result<()> {
16        let failures = async {
17            let mut failures = Vec::new();
18            for server_name in &self.required_servers {
19                let Some(async_managed_client) = self.clients.get(server_name).cloned() else {
20                    failures.push(McpStartupFailure {
21                        server: server_name.clone(),
22                        error: format!("required MCP server `{server_name}` was not initialized"),
23                    });
24                    continue;
25                };
26
27                match async_managed_client.client().await {
28                    Ok(_) => {}
29                    Err(error) => failures.push(McpStartupFailure {
30                        server: server_name.clone(),
31                        error: startup_outcome_error_message(error),
32                    }),
33                }
34            }
35            failures
36        }
37        .instrument(info_span!(
38            "session_init.required_mcp_wait",
39            otel.name = "session_init.required_mcp_wait",
40            session_init.required_mcp_server_count = self.required_servers.len(),
41        ))
42        .await;
43        if failures.is_empty() {
44            return Ok(());
45        }
46
47        let details = failures
48            .iter()
49            .map(|failure| format!("{}: {}", failure.server, failure.error))
50            .collect::<Vec<_>>()
51            .join("; ");
52        Err(anyhow!(
53            "required MCP servers failed to initialize: {details}"
54        ))
55    }
56}
57
58pub(super) fn startup_outcome_error_message(error: StartupOutcomeError) -> String {
59    match error {
60        StartupOutcomeError::Cancelled => "MCP startup cancelled".to_string(),
61        StartupOutcomeError::Failed { error, .. } => error,
62    }
63}