use std::collections::HashMap; use std::collections::HashSet; use codex_mcp::CODEX_APPS_MCP_SERVER_NAME; use codex_mcp::ToolInfo as McpToolInfo; use codex_mcp::filter_non_codex_apps_mcp_tools_only; use codex_tools::ToolsConfig; use crate::config::Config; use crate::connectors; pub(crate) const DIRECT_MCP_TOOL_EXPOSURE_THRESHOLD: usize = 100; pub(crate) struct McpToolExposure { pub(crate) direct_tools: HashMap, pub(crate) deferred_tools: Option>, } pub(crate) fn build_mcp_tool_exposure( all_mcp_tools: &HashMap, connectors: Option<&[connectors::AppInfo]>, explicitly_enabled_connectors: &[connectors::AppInfo], config: &Config, tools_config: &ToolsConfig, ) -> McpToolExposure { let mut deferred_tools = filter_non_codex_apps_mcp_tools_only(all_mcp_tools); if let Some(connectors) = connectors { deferred_tools.extend(filter_codex_apps_mcp_tools( all_mcp_tools, connectors, config, )); } if !tools_config.search_tool || deferred_tools.len() < DIRECT_MCP_TOOL_EXPOSURE_THRESHOLD { return McpToolExposure { direct_tools: deferred_tools, deferred_tools: None, }; } let direct_tools = filter_codex_apps_mcp_tools(all_mcp_tools, explicitly_enabled_connectors, config); McpToolExposure { direct_tools, deferred_tools: Some(deferred_tools), } } fn filter_codex_apps_mcp_tools( mcp_tools: &HashMap, connectors: &[connectors::AppInfo], config: &Config, ) -> HashMap { let allowed: HashSet<&str> = connectors .iter() .map(|connector| connector.id.as_str()) .collect(); mcp_tools .iter() .filter(|(_, tool)| { if tool.server_name != CODEX_APPS_MCP_SERVER_NAME { return false; } let Some(connector_id) = tool.connector_id.as_deref() else { return false; }; allowed.contains(connector_id) && connectors::codex_app_tool_is_enabled(config, tool) }) .map(|(name, tool)| (name.clone(), tool.clone())) .collect() }