codex_mcp/connection_manager/
required.rs1use 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 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}