mirror of
https://github.com/openai/codex.git
synced 2026-11-03 16:23:51 +00:00
feat: make built-in MCPs first-class runtime servers (#21356)
## DISCLAIMER This is experimental and no production service must rely on this ## Why Built-in MCPs are product-owned runtime capabilities, but they were previously flattened into the same config-backed stdio path as user-configured servers. That made them depend on a hidden `codex builtin-mcp` re-exec path, exposed them through config-oriented CLI flows, and erased distinctions the runtime needs to preserve—most notably whether an MCP call should count as external context for memory-mode pollution. ## What changed - Model product-owned built-ins separately from config-backed MCP servers via `BuiltinMcpServer` and `EffectiveMcpServer`. - Launch built-ins in process through a reusable async transport instead of the hidden `builtin-mcp` stdio subcommand. - Keep config-oriented CLI operations such as `codex mcp list/get/login/logout` scoped to configured servers, while merging built-ins only into the effective runtime server set. - Retain server metadata after launch so parallel-tool support and context classification come from the live server set; built-in `memories` is now classified as local Codex state rather than external context. ## Test plan - `cargo test -p codex-mcp` - `cargo test -p codex-core --test suite builtin_memories_mcp_call_does_not_mark_thread_memory_mode_polluted_when_configured` --------- Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
1 parent
40e282849c
commit
b2268999fe
35 files changed
+668
-345
No files matched your search
@@ -3360,7 +3360,6 @@ async fn to_mcp_config_empty_mcp_requirements_preserve_builtin_mcps() -> anyhow:
|
||||
}))
|
||||
.build()
|
||||
.await?;
|
||||
config.codex_self_exe = Some(PathBuf::from("/tmp/codex"));
|
||||
let _ = config.features.enable(Feature::BuiltInMcp);
|
||||
let _ = config.features.enable(Feature::MemoryTool);
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
@@ -3368,11 +3367,8 @@ async fn to_mcp_config_empty_mcp_requirements_preserve_builtin_mcps() -> anyhow:
|
||||
let mcp_config = config.to_mcp_config(&plugins_manager).await;
|
||||
|
||||
assert_eq!(
|
||||
mcp_config
|
||||
.configured_mcp_servers
|
||||
.get(codex_mcp::MEMORIES_MCP_SERVER_NAME)
|
||||
.map(|server| (server.enabled, server.disabled_reason.clone())),
|
||||
Some((true, None))
|
||||
mcp_config.builtin_mcp_servers,
|
||||
vec![codex_mcp::BuiltinMcpServer::Memories]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -3399,7 +3395,6 @@ async fn to_mcp_config_nonempty_mcp_requirements_preserve_builtin_mcps() -> anyh
|
||||
}))
|
||||
.build()
|
||||
.await?;
|
||||
config.codex_self_exe = Some(PathBuf::from("/tmp/codex"));
|
||||
let _ = config.features.enable(Feature::BuiltInMcp);
|
||||
let _ = config.features.enable(Feature::MemoryTool);
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
@@ -3407,11 +3402,8 @@ async fn to_mcp_config_nonempty_mcp_requirements_preserve_builtin_mcps() -> anyh
|
||||
let mcp_config = config.to_mcp_config(&plugins_manager).await;
|
||||
|
||||
assert_eq!(
|
||||
mcp_config
|
||||
.configured_mcp_servers
|
||||
.get(codex_mcp::MEMORIES_MCP_SERVER_NAME)
|
||||
.map(|server| (server.enabled, server.disabled_reason.clone())),
|
||||
Some((true, None))
|
||||
mcp_config.builtin_mcp_servers,
|
||||
vec![codex_mcp::BuiltinMcpServer::Memories]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -4244,10 +4236,7 @@ async fn to_mcp_config_includes_enabled_builtin_mcps() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides {
|
||||
codex_self_exe: Some(PathBuf::from("/tmp/codex")),
|
||||
..ConfigOverrides::default()
|
||||
},
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.await?;
|
||||
@@ -4258,22 +4247,13 @@ async fn to_mcp_config_includes_enabled_builtin_mcps() -> std::io::Result<()> {
|
||||
let mcp_config = config.to_mcp_config(&plugins_manager).await;
|
||||
|
||||
assert_eq!(
|
||||
mcp_config
|
||||
mcp_config.builtin_mcp_servers,
|
||||
vec![codex_mcp::BuiltinMcpServer::Memories]
|
||||
);
|
||||
assert!(
|
||||
!mcp_config
|
||||
.configured_mcp_servers
|
||||
.get(codex_mcp::MEMORIES_MCP_SERVER_NAME)
|
||||
.map(|server| &server.transport),
|
||||
Some(&McpServerTransportConfig::Stdio {
|
||||
command: "/tmp/codex".to_string(),
|
||||
args: vec![
|
||||
"builtin-mcp".to_string(),
|
||||
"memories".to_string(),
|
||||
"--codex-home".to_string(),
|
||||
codex_home.path().display().to_string(),
|
||||
],
|
||||
env: None,
|
||||
env_vars: Vec::new(),
|
||||
cwd: None,
|
||||
})
|
||||
.contains_key(codex_mcp::MEMORIES_MCP_SERVER_NAME)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -4284,10 +4264,7 @@ async fn to_mcp_config_omits_builtin_mcps_when_feature_is_disabled() -> std::io:
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides {
|
||||
codex_self_exe: Some(PathBuf::from("/tmp/codex")),
|
||||
..ConfigOverrides::default()
|
||||
},
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.await?;
|
||||
@@ -4296,11 +4273,7 @@ async fn to_mcp_config_omits_builtin_mcps_when_feature_is_disabled() -> std::io:
|
||||
|
||||
let mcp_config = config.to_mcp_config(&plugins_manager).await;
|
||||
|
||||
assert!(
|
||||
!mcp_config
|
||||
.configured_mcp_servers
|
||||
.contains_key(codex_mcp::MEMORIES_MCP_SERVER_NAME)
|
||||
);
|
||||
assert!(mcp_config.builtin_mcp_servers.is_empty());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -4316,10 +4289,7 @@ async fn to_mcp_config_reserves_enabled_builtin_mcp_names() -> std::io::Result<(
|
||||
)]),
|
||||
..ConfigToml::default()
|
||||
},
|
||||
ConfigOverrides {
|
||||
codex_self_exe: Some(PathBuf::from("/tmp/codex")),
|
||||
..ConfigOverrides::default()
|
||||
},
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.await?;
|
||||
@@ -4329,13 +4299,15 @@ async fn to_mcp_config_reserves_enabled_builtin_mcp_names() -> std::io::Result<(
|
||||
|
||||
let mcp_config = config.to_mcp_config(&plugins_manager).await;
|
||||
|
||||
assert!(matches!(
|
||||
mcp_config
|
||||
assert_eq!(
|
||||
mcp_config.builtin_mcp_servers,
|
||||
vec![codex_mcp::BuiltinMcpServer::Memories]
|
||||
);
|
||||
assert!(
|
||||
!mcp_config
|
||||
.configured_mcp_servers
|
||||
.get(codex_mcp::MEMORIES_MCP_SERVER_NAME)
|
||||
.map(|server| &server.transport),
|
||||
Some(McpServerTransportConfig::Stdio { .. })
|
||||
));
|
||||
.contains_key(codex_mcp::MEMORIES_MCP_SERVER_NAME)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ use codex_git_utils::resolve_root_git_project_for_trust;
|
||||
use codex_login::AuthManagerConfig;
|
||||
use codex_mcp::BuiltinMcpServerOptions;
|
||||
use codex_mcp::McpConfig;
|
||||
use codex_mcp::configured_builtin_mcp_servers;
|
||||
use codex_mcp::enabled_builtin_mcp_servers;
|
||||
use codex_memories_read::memory_root;
|
||||
use codex_model_provider_info::LEGACY_OLLAMA_CHAT_PROVIDER_ID;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
@@ -1091,9 +1091,7 @@ impl Config {
|
||||
) -> McpConfig {
|
||||
let plugins_input = self.plugins_config_input();
|
||||
let loaded_plugins = plugins_manager.plugins_for_config(&plugins_input).await;
|
||||
let builtin_mcp_servers = configured_builtin_mcp_servers(BuiltinMcpServerOptions {
|
||||
codex_self_exe: self.codex_self_exe.as_deref(),
|
||||
codex_home: self.codex_home.as_path(),
|
||||
let builtin_mcp_servers = enabled_builtin_mcp_servers(BuiltinMcpServerOptions {
|
||||
memories_enabled: self.features.enabled(Feature::BuiltInMcp)
|
||||
&& self.features.enabled(Feature::MemoryTool)
|
||||
&& self.memories.use_memories,
|
||||
@@ -1122,7 +1120,9 @@ impl Config {
|
||||
// allowlists.
|
||||
filter_mcp_servers_by_requirements(&mut configured_mcp_servers, Some(mcp_requirements));
|
||||
}
|
||||
configured_mcp_servers.extend(builtin_mcp_servers);
|
||||
for builtin_server in &builtin_mcp_servers {
|
||||
configured_mcp_servers.remove(builtin_server.name());
|
||||
}
|
||||
|
||||
McpConfig {
|
||||
chatgpt_base_url: self.chatgpt_base_url.clone(),
|
||||
@@ -1139,6 +1139,7 @@ impl Config {
|
||||
use_legacy_landlock: self.features.use_legacy_landlock(),
|
||||
apps_enabled: self.features.enabled(Feature::Apps),
|
||||
configured_mcp_servers,
|
||||
builtin_mcp_servers,
|
||||
plugin_capability_summaries: loaded_plugins.capability_summaries().to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user