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:
jif-oai
2026-05-07 10:36:32 +02:00
committed by GitHub
co-authored by Codex
parent 40e282849c
commit b2268999fe
35 changed files with 668 additions and 345 deletions
-20
View File
@@ -123,10 +123,6 @@ enum Subcommand {
/// Start Codex as an MCP server (stdio).
McpServer,
/// Internal: start a Codex-shipped MCP server (stdio).
#[clap(hide = true, name = "builtin-mcp")]
BuiltinMcp(BuiltinMcpCommand),
/// [experimental] Run the app server or related tooling.
AppServer(AppServerCommand),
@@ -179,13 +175,6 @@ enum Subcommand {
Features(FeaturesCli),
}
#[derive(Debug, Args)]
struct BuiltinMcpCommand {
name: String,
#[arg(long)]
codex_home: PathBuf,
}
#[derive(Debug, Parser)]
#[command(bin_name = "codex plugin")]
struct PluginCli {
@@ -820,15 +809,6 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
)?;
codex_mcp_server::run_main(arg0_paths.clone(), root_config_overrides).await?;
}
Some(Subcommand::BuiltinMcp(command)) => {
reject_remote_mode_for_subcommand(
root_remote.as_deref(),
root_remote_auth_token_env.as_deref(),
"builtin-mcp",
)?;
let codex_home = AbsolutePathBuf::try_from(command.codex_home)?;
codex_builtin_mcps::run_builtin_mcp_server(&command.name, &codex_home).await?;
}
Some(Subcommand::Mcp(mut mcp_cli)) => {
reject_remote_mode_for_subcommand(
root_remote.as_deref(),