Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions codex-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"agent-graph-store",
"agent-identity",
"backend-client",
"builtin-mcps",
Comment thread
jif-oai marked this conversation as resolved.
"bwrap",
"ansi-escape",
"async-utils",
Expand Down Expand Up @@ -138,6 +139,7 @@ codex-apply-patch = { path = "apply-patch" }
codex-arg0 = { path = "arg0" }
codex-async-utils = { path = "async-utils" }
codex-backend-client = { path = "backend-client" }
codex-builtin-mcps = { path = "builtin-mcps" }
codex-chatgpt = { path = "chatgpt" }
codex-cli = { path = "cli" }
codex-client = { path = "codex-client" }
Expand Down
6 changes: 6 additions & 0 deletions codex-rs/builtin-mcps/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
load("//:defs.bzl", "codex_rust_crate")

codex_rust_crate(
name = "builtin-mcps",
crate_name = "codex_builtin_mcps",
)
21 changes: 21 additions & 0 deletions codex-rs/builtin-mcps/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
edition.workspace = true
license.workspace = true
name = "codex-builtin-mcps"
version.workspace = true

[lib]
name = "codex_builtin_mcps"
path = "src/lib.rs"

[lints]
workspace = true

[dependencies]
anyhow = { workspace = true }
codex-config = { workspace = true }
codex-memories-mcp = { workspace = true }
codex-utils-absolute-path = { workspace = true }

[dev-dependencies]
pretty_assertions = { workspace = true }
132 changes: 132 additions & 0 deletions codex-rs/builtin-mcps/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
//! Built-in MCP servers shipped with Codex.
//!
//! Built-ins use the same stdio MCP path as user-configured servers, but are
//! declared here so product-owned MCPs do not need to live in `codex-core`.

use codex_config::McpServerConfig;
use codex_config::McpServerTransportConfig;
use codex_utils_absolute_path::AbsolutePathBuf;
use std::collections::HashMap;
use std::path::Path;

pub const MEMORIES_MCP_SERVER_NAME: &str = "memories";
const BUILTIN_MCP_SUBCOMMAND: &str = "builtin-mcp";

#[derive(Debug, Clone, Copy)]
pub struct BuiltinMcpServerOptions<'a> {
pub codex_self_exe: Option<&'a Path>,
pub codex_home: &'a Path,
pub memories_enabled: bool,
}

pub fn configured_builtin_mcp_servers(
options: BuiltinMcpServerOptions<'_>,
) -> HashMap<String, McpServerConfig> {
let Some(codex_self_exe) = options.codex_self_exe else {
return HashMap::new();
};

let mut servers = HashMap::new();
if options.memories_enabled {
servers.insert(
MEMORIES_MCP_SERVER_NAME.to_string(),
builtin_stdio_server_config(
codex_self_exe,
options.codex_home,
MEMORIES_MCP_SERVER_NAME,
),
);
}
servers
}

pub async fn run_builtin_mcp_server(
name: &str,
codex_home: &AbsolutePathBuf,
) -> anyhow::Result<()> {
match name {
MEMORIES_MCP_SERVER_NAME => codex_memories_mcp::run_stdio_server(codex_home).await,
_ => anyhow::bail!("unknown built-in MCP server: {name}"),
}
}

fn builtin_stdio_server_config(
codex_self_exe: &Path,
codex_home: &Path,
name: &str,
) -> McpServerConfig {
McpServerConfig {
transport: McpServerTransportConfig::Stdio {
command: codex_self_exe.to_string_lossy().into_owned(),
args: vec![
BUILTIN_MCP_SUBCOMMAND.to_string(),
name.to_string(),
"--codex-home".to_string(),
codex_home.to_string_lossy().into_owned(),
],
env: None,
env_vars: Vec::new(),
cwd: None,
},
experimental_environment: None,
enabled: true,
required: false,
supports_parallel_tool_calls: true,
disabled_reason: None,
startup_timeout_sec: None,
tool_timeout_sec: None,
default_tools_approval_mode: None,
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
tools: HashMap::new(),
}
}

#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;

#[test]
fn configured_builtin_mcp_servers_adds_memories_when_enabled() {
let codex_home = AbsolutePathBuf::try_from("/tmp/codex-home").expect("absolute codex home");
let servers = configured_builtin_mcp_servers(BuiltinMcpServerOptions {
codex_self_exe: Some(Path::new("/tmp/codex")),
codex_home: codex_home.as_path(),
memories_enabled: true,
});

let server = servers
.get(MEMORIES_MCP_SERVER_NAME)
.expect("memories server should exist");
assert_eq!(
server.transport,
McpServerTransportConfig::Stdio {
command: "/tmp/codex".to_string(),
args: vec![
"builtin-mcp".to_string(),
"memories".to_string(),
"--codex-home".to_string(),
"/tmp/codex-home".to_string(),
],
env: None,
env_vars: Vec::new(),
cwd: None,
}
);
}

#[test]
fn configured_builtin_mcp_servers_requires_reexec_path() {
let codex_home = AbsolutePathBuf::try_from("/tmp/codex-home").expect("absolute codex home");
let servers = configured_builtin_mcp_servers(BuiltinMcpServerOptions {
codex_self_exe: None,
codex_home: codex_home.as_path(),
memories_enabled: true,
});

assert!(servers.is_empty());
}
}
1 change: 1 addition & 0 deletions codex-rs/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ codex-app-server = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-app-server-test-client = { workspace = true }
codex-arg0 = { workspace = true }
codex-builtin-mcps = { workspace = true }
codex-chatgpt = { workspace = true }
codex-cloud-tasks = { path = "../cloud-tasks" }
codex-utils-cli = { workspace = true }
Expand Down
20 changes: 20 additions & 0 deletions codex-rs/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ 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),

Expand Down Expand Up @@ -175,6 +179,13 @@ 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 {
Expand Down Expand Up @@ -809,6 +820,15 @@ 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(),
Expand Down
1 change: 1 addition & 0 deletions codex-rs/codex-mcp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ anyhow = { workspace = true }
async-channel = { workspace = true }
codex-async-utils = { workspace = true }
codex-api = { workspace = true }
codex-builtin-mcps = { workspace = true }
codex-config = { workspace = true }
codex-exec-server = { workspace = true }
codex-login = { workspace = true }
Expand Down
3 changes: 3 additions & 0 deletions codex-rs/codex-mcp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub use auth_elicitation::build_auth_elicitation_plan;
pub use auth_elicitation::connector_auth_failure_from_tool_result;
pub use codex_apps::CodexAppsToolsCacheKey;
pub use codex_apps::codex_apps_tools_cache_key;
pub use codex_builtin_mcps::BuiltinMcpServerOptions;
pub use codex_builtin_mcps::MEMORIES_MCP_SERVER_NAME;
pub use codex_builtin_mcps::configured_builtin_mcp_servers;

pub use mcp::configured_mcp_servers;
pub use mcp::effective_mcp_servers;
Expand Down
6 changes: 5 additions & 1 deletion codex-rs/codex-mcp/src/mcp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ pub struct McpConfig {
/// ChatGPT auth is checked separately at runtime before the built-in apps
/// MCP server is added.
pub apps_enabled: bool,
/// User-configured and plugin-provided MCP servers keyed by server name.
/// Configured MCP servers keyed by server name.
///
/// This includes product-owned built-ins, user-configured servers, and
/// plugin-provided servers. Runtime-only additions belong in
/// [`effective_mcp_servers`].
pub configured_mcp_servers: HashMap<String, McpServerConfig>,
/// Plugin metadata used to attribute MCP tools/connectors to plugin display names.
pub plugin_capability_summaries: Vec<PluginCapabilitySummary>,
Expand Down
6 changes: 6 additions & 0 deletions codex-rs/core/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,9 @@
"browser_use_external": {
"type": "boolean"
},
"builtin_mcp": {
"type": "boolean"
},
"child_agents_md": {
"type": "boolean"
},
Expand Down Expand Up @@ -3931,6 +3934,9 @@
"browser_use_external": {
"type": "boolean"
},
"builtin_mcp": {
"type": "boolean"
},
"child_agents_md": {
"type": "boolean"
},
Expand Down
Loading
Loading