Skip to main content

codex_mcp/
server.rs

1use std::collections::HashMap;
2
3use codex_config::AppToolApproval;
4use codex_config::McpServerConfig;
5use codex_config::McpServerTransportConfig;
6
7/// The runtime launch strategy for an effective MCP server.
8#[derive(Debug, Clone)]
9pub(crate) enum McpServerLaunch {
10    Configured(Box<McpServerConfig>),
11}
12
13/// MCP server after runtime additions have been applied.
14#[derive(Debug, Clone)]
15pub struct EffectiveMcpServer {
16    launch: McpServerLaunch,
17}
18
19impl EffectiveMcpServer {
20    pub fn configured(config: McpServerConfig) -> Self {
21        Self {
22            launch: McpServerLaunch::Configured(Box::new(config)),
23        }
24    }
25
26    pub(crate) fn launch(&self) -> &McpServerLaunch {
27        &self.launch
28    }
29
30    pub fn configured_config(&self) -> Option<&McpServerConfig> {
31        match &self.launch {
32            McpServerLaunch::Configured(config) => Some(config.as_ref()),
33        }
34    }
35
36    pub fn enabled(&self) -> bool {
37        match &self.launch {
38            McpServerLaunch::Configured(config) => config.enabled,
39        }
40    }
41
42    pub fn required(&self) -> bool {
43        match &self.launch {
44            McpServerLaunch::Configured(config) => config.required,
45        }
46    }
47}
48
49/// Transport origin retained for metrics and diagnostics after server launch.
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub(crate) enum McpServerOrigin {
52    Stdio,
53    StreamableHttp(String),
54}
55
56impl McpServerOrigin {
57    pub fn as_str(&self) -> &str {
58        match self {
59            Self::Stdio => "stdio",
60            Self::StreamableHttp(origin) => origin,
61        }
62    }
63
64    fn from_transport(transport: &McpServerTransportConfig) -> Option<Self> {
65        match transport {
66            McpServerTransportConfig::StreamableHttp { url, .. } => {
67                let parsed = url::Url::parse(url).ok()?;
68                Some(Self::StreamableHttp(parsed.origin().ascii_serialization()))
69            }
70            McpServerTransportConfig::Stdio { .. } => Some(Self::Stdio),
71        }
72    }
73}
74
75/// Semantic metadata that must survive after the server is launched.
76#[derive(Debug, Clone)]
77pub(crate) struct McpServerMetadata {
78    pub environment_id: String,
79    pub pollutes_memory: bool,
80    pub origin: Option<McpServerOrigin>,
81    pub supports_parallel_tool_calls: bool,
82    pub default_tools_approval_mode: Option<AppToolApproval>,
83    pub tool_approval_modes: HashMap<String, AppToolApproval>,
84}
85
86impl McpServerMetadata {
87    pub fn tool_approval_mode(&self, tool_name: &str) -> AppToolApproval {
88        self.tool_approval_modes
89            .get(tool_name)
90            .copied()
91            .or(self.default_tools_approval_mode)
92            .unwrap_or_default()
93    }
94}
95
96impl From<&EffectiveMcpServer> for McpServerMetadata {
97    fn from(server: &EffectiveMcpServer) -> Self {
98        match server.launch() {
99            McpServerLaunch::Configured(config) => Self {
100                environment_id: config.environment_id.clone(),
101                pollutes_memory: true,
102                origin: McpServerOrigin::from_transport(&config.transport),
103                supports_parallel_tool_calls: config.supports_parallel_tool_calls,
104                default_tools_approval_mode: config.default_tools_approval_mode,
105                tool_approval_modes: config
106                    .tools
107                    .iter()
108                    .filter_map(|(name, config)| {
109                        config
110                            .approval_mode
111                            .map(|approval_mode| (name.clone(), approval_mode))
112                    })
113                    .collect(),
114            },
115        }
116    }
117}