mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
committed by
GitHub
Unverified
parent
a124ddb854
commit
32b1ae7099
Generated
-12
@@ -2162,17 +2162,6 @@ dependencies = [
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codex-builtin-mcps"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"codex-memories-mcp",
|
||||
"codex-utils-absolute-path",
|
||||
"pretty_assertions",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codex-bwrap"
|
||||
version = "0.0.0"
|
||||
@@ -3065,7 +3054,6 @@ dependencies = [
|
||||
"async-channel",
|
||||
"codex-api",
|
||||
"codex-async-utils",
|
||||
"codex-builtin-mcps",
|
||||
"codex-config",
|
||||
"codex-exec-server",
|
||||
"codex-login",
|
||||
|
||||
@@ -5,7 +5,6 @@ members = [
|
||||
"agent-graph-store",
|
||||
"agent-identity",
|
||||
"backend-client",
|
||||
"builtin-mcps",
|
||||
"bwrap",
|
||||
"ansi-escape",
|
||||
"async-utils",
|
||||
@@ -144,7 +143,6 @@ 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" }
|
||||
@@ -180,7 +178,6 @@ codex-linux-sandbox = { path = "linux-sandbox" }
|
||||
codex-lmstudio = { path = "lmstudio" }
|
||||
codex-login = { path = "login" }
|
||||
codex-message-history = { path = "message-history" }
|
||||
codex-memories-mcp = { path = "memories/mcp" }
|
||||
codex-memories-read = { path = "memories/read" }
|
||||
codex-memories-write = { path = "memories/write" }
|
||||
codex-mcp = { path = "codex-mcp" }
|
||||
|
||||
@@ -286,7 +286,7 @@ impl McpRequestProcessor {
|
||||
.mcp_servers
|
||||
.keys()
|
||||
.cloned()
|
||||
// Include built-in/plugin MCP servers that are present in the
|
||||
// Include runtime-added/plugin MCP servers that are present in the
|
||||
// effective runtime config even when they are not user-declared in
|
||||
// `config.mcp_servers`.
|
||||
.chain(effective_servers.keys().cloned())
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
load("//:defs.bzl", "codex_rust_crate")
|
||||
|
||||
codex_rust_crate(
|
||||
name = "builtin-mcps",
|
||||
crate_name = "codex_builtin_mcps",
|
||||
)
|
||||
@@ -1,22 +0,0 @@
|
||||
[package]
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
name = "codex-builtin-mcps"
|
||||
version.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "codex_builtin_mcps"
|
||||
path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
codex-memories-mcp = { workspace = true }
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
tokio = { workspace = true, features = ["io-util"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { workspace = true }
|
||||
@@ -1,101 +0,0 @@
|
||||
//! Built-in MCP servers shipped with Codex.
|
||||
//!
|
||||
//! This crate owns the catalog of product-owned MCP servers and the small
|
||||
//! amount of server-specific dispatch needed to run them. Runtime placement is
|
||||
//! chosen by `codex-mcp`; built-ins should not be flattened into user-facing
|
||||
//! MCP server config just to make them launchable.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use tokio::io::AsyncRead;
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
pub const MEMORIES_MCP_SERVER_NAME: &str = "memories";
|
||||
|
||||
/// Product-owned MCP servers that Codex can provide without user config.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum BuiltinMcpServer {
|
||||
Memories,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct BuiltinMcpServerMetadata {
|
||||
name: &'static str,
|
||||
supports_parallel_tool_calls: bool,
|
||||
pollutes_memory: bool,
|
||||
}
|
||||
|
||||
impl BuiltinMcpServer {
|
||||
const fn metadata(self) -> BuiltinMcpServerMetadata {
|
||||
match self {
|
||||
Self::Memories => BuiltinMcpServerMetadata {
|
||||
name: MEMORIES_MCP_SERVER_NAME,
|
||||
supports_parallel_tool_calls: true,
|
||||
pollutes_memory: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn name(self) -> &'static str {
|
||||
self.metadata().name
|
||||
}
|
||||
|
||||
pub const fn supports_parallel_tool_calls(self) -> bool {
|
||||
self.metadata().supports_parallel_tool_calls
|
||||
}
|
||||
|
||||
pub const fn pollutes_memory(self) -> bool {
|
||||
self.metadata().pollutes_memory
|
||||
}
|
||||
|
||||
pub async fn serve<T>(self, codex_home: &Path, transport: T) -> anyhow::Result<()>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Send + 'static,
|
||||
{
|
||||
match self {
|
||||
Self::Memories => {
|
||||
let codex_home = codex_utils_absolute_path::AbsolutePathBuf::try_from(codex_home)?;
|
||||
codex_memories_mcp::run_server(&codex_home, transport).await
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BuiltinMcpServerOptions {
|
||||
pub memories_enabled: bool,
|
||||
}
|
||||
|
||||
pub fn enabled_builtin_mcp_servers(options: BuiltinMcpServerOptions) -> Vec<BuiltinMcpServer> {
|
||||
let mut servers = Vec::new();
|
||||
if options.memories_enabled {
|
||||
servers.push(BuiltinMcpServer::Memories);
|
||||
}
|
||||
servers
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn enabled_builtin_mcp_servers_adds_memories_when_enabled() {
|
||||
assert_eq!(
|
||||
enabled_builtin_mcp_servers(BuiltinMcpServerOptions {
|
||||
memories_enabled: true,
|
||||
}),
|
||||
vec![BuiltinMcpServer::Memories]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enabled_builtin_mcp_servers_omits_memories_when_disabled() {
|
||||
assert_eq!(
|
||||
enabled_builtin_mcp_servers(BuiltinMcpServerOptions {
|
||||
memories_enabled: false,
|
||||
}),
|
||||
Vec::<BuiltinMcpServer>::new()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ 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 }
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_builtin_mcps::BuiltinMcpServer;
|
||||
use codex_rmcp_client::InProcessTransportFactory;
|
||||
use futures::FutureExt;
|
||||
use futures::future::BoxFuture;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct BuiltinMcpServerFactory {
|
||||
server: BuiltinMcpServer,
|
||||
codex_home: PathBuf,
|
||||
}
|
||||
|
||||
impl BuiltinMcpServerFactory {
|
||||
pub(crate) fn new(server: BuiltinMcpServer, codex_home: PathBuf) -> Self {
|
||||
Self { server, codex_home }
|
||||
}
|
||||
}
|
||||
|
||||
impl InProcessTransportFactory for BuiltinMcpServerFactory {
|
||||
fn open(&self) -> BoxFuture<'static, io::Result<tokio::io::DuplexStream>> {
|
||||
let server = self.server;
|
||||
let codex_home = self.codex_home.clone();
|
||||
async move {
|
||||
let (client_transport, server_transport) = tokio::io::duplex(64 * 1024);
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = server.serve(&codex_home, server_transport).await {
|
||||
tracing::warn!(
|
||||
server = server.name(),
|
||||
"built-in MCP server exited: {err:#}"
|
||||
);
|
||||
}
|
||||
});
|
||||
Ok(client_transport)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Codex Apps support for the built-in apps MCP server.
|
||||
//! Codex Apps support for the host-owned apps MCP server.
|
||||
//!
|
||||
//! This module owns the pieces that are unique to ChatGPT-hosted app
|
||||
//! connectors: cache scoping by authenticated user, disk cache reads/writes,
|
||||
|
||||
@@ -246,7 +246,6 @@ impl McpConnectionManager {
|
||||
codex_apps_tools_cache_context,
|
||||
Arc::clone(&tool_plugin_provenance),
|
||||
runtime_environment.clone(),
|
||||
codex_home.clone(),
|
||||
runtime_auth_provider,
|
||||
);
|
||||
clients.insert(server_name.clone(), async_managed_client.clone());
|
||||
|
||||
@@ -23,11 +23,6 @@ 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::BuiltinMcpServer;
|
||||
pub use codex_builtin_mcps::BuiltinMcpServerOptions;
|
||||
pub use codex_builtin_mcps::MEMORIES_MCP_SERVER_NAME;
|
||||
pub use codex_builtin_mcps::enabled_builtin_mcp_servers;
|
||||
|
||||
pub use mcp::configured_mcp_servers;
|
||||
pub use mcp::effective_mcp_servers;
|
||||
pub use mcp::effective_mcp_servers_from_configured;
|
||||
@@ -57,7 +52,6 @@ pub use mcp::qualified_mcp_tool_name_prefix;
|
||||
pub use tools::declared_openai_file_input_param_names;
|
||||
|
||||
pub(crate) mod auth_elicitation;
|
||||
pub(crate) mod builtin;
|
||||
pub(crate) mod codex_apps;
|
||||
pub(crate) mod connection_manager;
|
||||
pub(crate) mod elicitation;
|
||||
|
||||
@@ -106,7 +106,7 @@ pub struct McpPermissionPromptAutoApproveContext {
|
||||
pub struct McpConfig {
|
||||
/// Base URL for ChatGPT-hosted app MCP servers, copied from the root config.
|
||||
pub chatgpt_base_url: String,
|
||||
/// Optional path override for the built-in apps MCP server.
|
||||
/// Optional path override for the host-owned apps MCP server.
|
||||
pub apps_mcp_path_override: Option<String>,
|
||||
/// Codex home directory used for MCP OAuth state and app-tool cache files.
|
||||
pub codex_home: PathBuf,
|
||||
@@ -126,16 +126,13 @@ pub struct McpConfig {
|
||||
pub use_legacy_landlock: bool,
|
||||
/// Whether the app MCP integration is enabled by config.
|
||||
///
|
||||
/// ChatGPT auth is checked separately at runtime before the built-in apps
|
||||
/// ChatGPT auth is checked separately at runtime before the host-owned apps
|
||||
/// MCP server is added.
|
||||
pub apps_enabled: bool,
|
||||
/// Config-backed MCP servers keyed by server name.
|
||||
///
|
||||
/// Product-owned built-ins and runtime-only additions are merged later by
|
||||
/// [`effective_mcp_servers`].
|
||||
/// Runtime-only additions are merged later by [`effective_mcp_servers`].
|
||||
pub configured_mcp_servers: HashMap<String, McpServerConfig>,
|
||||
/// Product-owned built-ins enabled for this runtime config.
|
||||
pub builtin_mcp_servers: Vec<codex_builtin_mcps::BuiltinMcpServer>,
|
||||
/// Plugin metadata used to attribute MCP tools/connectors to plugin display names.
|
||||
pub plugin_capability_summaries: Vec<PluginCapabilitySummary>,
|
||||
}
|
||||
@@ -234,16 +231,10 @@ pub fn effective_mcp_servers_from_configured(
|
||||
config: &McpConfig,
|
||||
auth: Option<&CodexAuth>,
|
||||
) -> HashMap<String, EffectiveMcpServer> {
|
||||
let mut servers = configured_servers
|
||||
let servers = configured_servers
|
||||
.into_iter()
|
||||
.map(|(name, server)| (name, EffectiveMcpServer::configured(server)))
|
||||
.collect::<HashMap<_, _>>();
|
||||
for builtin_server in &config.builtin_mcp_servers {
|
||||
servers.insert(
|
||||
builtin_server.name().to_string(),
|
||||
EffectiveMcpServer::builtin(*builtin_server),
|
||||
);
|
||||
}
|
||||
with_codex_apps_mcp(servers, auth, config)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ use codex_protocol::protocol::GranularApprovalConfig;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn test_mcp_config(codex_home: PathBuf) -> McpConfig {
|
||||
McpConfig {
|
||||
@@ -29,7 +28,6 @@ fn test_mcp_config(codex_home: PathBuf) -> McpConfig {
|
||||
use_legacy_landlock: false,
|
||||
apps_enabled: false,
|
||||
configured_mcp_servers: HashMap::new(),
|
||||
builtin_mcp_servers: Vec::new(),
|
||||
plugin_capability_summaries: Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -347,53 +345,3 @@ async fn effective_mcp_servers_preserve_user_servers_and_add_codex_apps() {
|
||||
other => panic!("expected streamable http transport, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effective_mcp_servers_preserve_builtin_runtime_shape() {
|
||||
let mut config = test_mcp_config(PathBuf::from("/tmp"));
|
||||
config.builtin_mcp_servers = vec![codex_builtin_mcps::BuiltinMcpServer::Memories];
|
||||
|
||||
let effective = effective_mcp_servers(&config, /*auth*/ None);
|
||||
let memories = effective
|
||||
.get(codex_builtin_mcps::MEMORIES_MCP_SERVER_NAME)
|
||||
.expect("memories server should exist");
|
||||
|
||||
assert!(!crate::server::McpServerMetadata::from(memories).pollutes_memory);
|
||||
assert!(matches!(
|
||||
memories.launch(),
|
||||
crate::server::McpServerLaunch::Builtin(codex_builtin_mcps::BuiltinMcpServer::Memories)
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn builtin_memories_server_runs_in_process() {
|
||||
let codex_home = tempfile::tempdir().expect("tempdir");
|
||||
let mut config = test_mcp_config(codex_home.path().to_path_buf());
|
||||
config.builtin_mcp_servers = vec![codex_builtin_mcps::BuiltinMcpServer::Memories];
|
||||
|
||||
let snapshot = collect_mcp_server_status_snapshot_with_detail(
|
||||
&config,
|
||||
/*auth*/ None,
|
||||
"builtin-memories-test".to_string(),
|
||||
McpRuntimeEnvironment::new(
|
||||
Arc::new(codex_exec_server::Environment::default_for_tests()),
|
||||
codex_home.path().to_path_buf(),
|
||||
),
|
||||
McpSnapshotDetail::ToolsAndAuthOnly,
|
||||
)
|
||||
.await;
|
||||
|
||||
let tools = snapshot
|
||||
.tools_by_server
|
||||
.get(codex_builtin_mcps::MEMORIES_MCP_SERVER_NAME)
|
||||
.expect("memories tools should be listed");
|
||||
assert_eq!(
|
||||
tools
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect::<std::collections::BTreeSet<_>>(),
|
||||
["list".to_string(), "read".to_string(), "search".to_string()]
|
||||
.into_iter()
|
||||
.collect()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,14 +10,12 @@ use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::builtin::BuiltinMcpServerFactory;
|
||||
use crate::codex_apps::CachedCodexAppsToolsLoad;
|
||||
use crate::codex_apps::CodexAppsToolsCacheContext;
|
||||
use crate::codex_apps::filter_disallowed_codex_apps_tools;
|
||||
@@ -51,7 +49,6 @@ use codex_exec_server::HttpClient;
|
||||
use codex_exec_server::ReqwestHttpClient;
|
||||
use codex_protocol::protocol::Event;
|
||||
use codex_rmcp_client::ExecutorStdioServerLauncher;
|
||||
use codex_rmcp_client::InProcessTransportFactory;
|
||||
use codex_rmcp_client::LocalStdioServerLauncher;
|
||||
use codex_rmcp_client::RmcpClient;
|
||||
use codex_rmcp_client::StdioServerLauncher;
|
||||
@@ -146,7 +143,6 @@ impl AsyncManagedClient {
|
||||
codex_apps_tools_cache_context: Option<CodexAppsToolsCacheContext>,
|
||||
tool_plugin_provenance: Arc<ToolPluginProvenance>,
|
||||
runtime_environment: McpRuntimeEnvironment,
|
||||
codex_home: PathBuf,
|
||||
runtime_auth_provider: Option<SharedAuthProvider>,
|
||||
) -> Self {
|
||||
let tool_filter = server
|
||||
@@ -174,7 +170,6 @@ impl AsyncManagedClient {
|
||||
server.clone(),
|
||||
store_mode,
|
||||
runtime_environment,
|
||||
codex_home,
|
||||
runtime_auth_provider,
|
||||
)
|
||||
.await?,
|
||||
@@ -569,18 +564,10 @@ async fn make_rmcp_client(
|
||||
server: EffectiveMcpServer,
|
||||
store_mode: OAuthCredentialsStoreMode,
|
||||
runtime_environment: McpRuntimeEnvironment,
|
||||
codex_home: PathBuf,
|
||||
runtime_auth_provider: Option<SharedAuthProvider>,
|
||||
) -> Result<RmcpClient, StartupOutcomeError> {
|
||||
let config = match server.launch() {
|
||||
McpServerLaunch::Configured(config) => config.as_ref().clone(),
|
||||
McpServerLaunch::Builtin(builtin_server) => {
|
||||
let factory: Arc<dyn InProcessTransportFactory> =
|
||||
Arc::new(BuiltinMcpServerFactory::new(*builtin_server, codex_home));
|
||||
return RmcpClient::new_in_process_client(factory)
|
||||
.await
|
||||
.map_err(|err| StartupOutcomeError::from(anyhow!(err)));
|
||||
}
|
||||
};
|
||||
let McpServerConfig {
|
||||
transport,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use codex_builtin_mcps::BuiltinMcpServer;
|
||||
use codex_config::McpServerConfig;
|
||||
use codex_config::McpServerTransportConfig;
|
||||
|
||||
@@ -6,10 +5,9 @@ use codex_config::McpServerTransportConfig;
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum McpServerLaunch {
|
||||
Configured(Box<McpServerConfig>),
|
||||
Builtin(BuiltinMcpServer),
|
||||
}
|
||||
|
||||
/// MCP server after product-owned runtime additions have been applied.
|
||||
/// MCP server after runtime additions have been applied.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EffectiveMcpServer {
|
||||
launch: McpServerLaunch,
|
||||
@@ -22,12 +20,6 @@ impl EffectiveMcpServer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn builtin(server: BuiltinMcpServer) -> Self {
|
||||
Self {
|
||||
launch: McpServerLaunch::Builtin(server),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn launch(&self) -> &McpServerLaunch {
|
||||
&self.launch
|
||||
}
|
||||
@@ -35,21 +27,18 @@ impl EffectiveMcpServer {
|
||||
pub fn configured_config(&self) -> Option<&McpServerConfig> {
|
||||
match &self.launch {
|
||||
McpServerLaunch::Configured(config) => Some(config.as_ref()),
|
||||
McpServerLaunch::Builtin(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
match &self.launch {
|
||||
McpServerLaunch::Configured(config) => config.enabled,
|
||||
McpServerLaunch::Builtin(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn required(&self) -> bool {
|
||||
match &self.launch {
|
||||
McpServerLaunch::Configured(config) => config.required,
|
||||
McpServerLaunch::Builtin(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,7 +46,6 @@ impl EffectiveMcpServer {
|
||||
/// Transport origin retained for metrics and diagnostics after server launch.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum McpServerOrigin {
|
||||
InProcess,
|
||||
Stdio,
|
||||
StreamableHttp(String),
|
||||
}
|
||||
@@ -65,7 +53,6 @@ pub(crate) enum McpServerOrigin {
|
||||
impl McpServerOrigin {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::InProcess => "in_process",
|
||||
Self::Stdio => "stdio",
|
||||
Self::StreamableHttp(origin) => origin,
|
||||
}
|
||||
@@ -98,11 +85,6 @@ impl From<&EffectiveMcpServer> for McpServerMetadata {
|
||||
origin: McpServerOrigin::from_transport(&config.transport),
|
||||
supports_parallel_tool_calls: config.supports_parallel_tool_calls,
|
||||
},
|
||||
McpServerLaunch::Builtin(server) => Self {
|
||||
pollutes_memory: server.pollutes_memory(),
|
||||
origin: Some(McpServerOrigin::InProcess),
|
||||
supports_parallel_tool_calls: server.supports_parallel_tool_calls(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,9 +379,6 @@
|
||||
"browser_use_external": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"builtin_mcp": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"child_agents_md": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -3935,9 +3932,6 @@
|
||||
"browser_use_external": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"builtin_mcp": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"child_agents_md": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
@@ -3370,69 +3370,6 @@ async fn add_dir_override_extends_workspace_writable_roots() -> std::io::Result<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn to_mcp_config_empty_mcp_requirements_preserve_builtin_mcps() -> anyhow::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let requirements = codex_config::ConfigRequirementsToml {
|
||||
mcp_servers: Some(BTreeMap::new()),
|
||||
..Default::default()
|
||||
};
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.cloud_requirements(CloudRequirementsLoader::new(async move {
|
||||
Ok(Some(requirements))
|
||||
}))
|
||||
.build()
|
||||
.await?;
|
||||
let _ = config.features.enable(Feature::BuiltInMcp);
|
||||
let _ = config.features.enable(Feature::MemoryTool);
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
|
||||
let mcp_config = config.to_mcp_config(&plugins_manager).await;
|
||||
|
||||
assert_eq!(
|
||||
mcp_config.builtin_mcp_servers,
|
||||
vec![codex_mcp::BuiltinMcpServer::Memories]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn to_mcp_config_nonempty_mcp_requirements_preserve_builtin_mcps() -> anyhow::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let requirements = codex_config::ConfigRequirementsToml {
|
||||
mcp_servers: Some(BTreeMap::from([(
|
||||
"docs".to_string(),
|
||||
McpServerRequirement {
|
||||
identity: McpServerIdentity::Command {
|
||||
command: "docs-mcp".to_string(),
|
||||
},
|
||||
},
|
||||
)])),
|
||||
..Default::default()
|
||||
};
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.cloud_requirements(CloudRequirementsLoader::new(async move {
|
||||
Ok(Some(requirements))
|
||||
}))
|
||||
.build()
|
||||
.await?;
|
||||
let _ = config.features.enable(Feature::BuiltInMcp);
|
||||
let _ = config.features.enable(Feature::MemoryTool);
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
|
||||
let mcp_config = config.to_mcp_config(&plugins_manager).await;
|
||||
|
||||
assert_eq!(
|
||||
mcp_config.builtin_mcp_servers,
|
||||
vec![codex_mcp::BuiltinMcpServer::Memories]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sqlite_home_defaults_to_codex_home_for_workspace_write() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
@@ -4255,87 +4192,6 @@ async fn to_mcp_config_preserves_apps_feature_from_config() -> std::io::Result<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
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::default(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.await?;
|
||||
let _ = config.features.enable(Feature::BuiltInMcp);
|
||||
let _ = config.features.enable(Feature::MemoryTool);
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
|
||||
let mcp_config = config.to_mcp_config(&plugins_manager).await;
|
||||
|
||||
assert_eq!(
|
||||
mcp_config.builtin_mcp_servers,
|
||||
vec![codex_mcp::BuiltinMcpServer::Memories]
|
||||
);
|
||||
assert!(
|
||||
!mcp_config
|
||||
.configured_mcp_servers
|
||||
.contains_key(codex_mcp::MEMORIES_MCP_SERVER_NAME)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn to_mcp_config_omits_builtin_mcps_when_feature_is_disabled() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.await?;
|
||||
let _ = config.features.enable(Feature::MemoryTool);
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
|
||||
let mcp_config = config.to_mcp_config(&plugins_manager).await;
|
||||
|
||||
assert!(mcp_config.builtin_mcp_servers.is_empty());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn to_mcp_config_reserves_enabled_builtin_mcp_names() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml {
|
||||
mcp_servers: HashMap::from([(
|
||||
codex_mcp::MEMORIES_MCP_SERVER_NAME.to_string(),
|
||||
http_mcp("https://user.example/memories"),
|
||||
)]),
|
||||
..ConfigToml::default()
|
||||
},
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.await?;
|
||||
let _ = config.features.enable(Feature::BuiltInMcp);
|
||||
let _ = config.features.enable(Feature::MemoryTool);
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
|
||||
let mcp_config = config.to_mcp_config(&plugins_manager).await;
|
||||
|
||||
assert_eq!(
|
||||
mcp_config.builtin_mcp_servers,
|
||||
vec![codex_mcp::BuiltinMcpServer::Memories]
|
||||
);
|
||||
assert!(
|
||||
!mcp_config
|
||||
.configured_mcp_servers
|
||||
.contains_key(codex_mcp::MEMORIES_MCP_SERVER_NAME)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_global_mcp_servers_rejects_inline_bearer_token() -> anyhow::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
@@ -66,9 +66,7 @@ use codex_features::FeaturesToml;
|
||||
use codex_features::MultiAgentV2ConfigToml;
|
||||
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::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;
|
||||
@@ -707,7 +705,7 @@ pub struct Config {
|
||||
/// Base URL for requests to ChatGPT (as opposed to the OpenAI API).
|
||||
pub chatgpt_base_url: String,
|
||||
|
||||
/// Optional path override for the built-in apps MCP server.
|
||||
/// Optional path override for the host-owned apps MCP server.
|
||||
pub apps_mcp_path_override: Option<String>,
|
||||
|
||||
/// Machine-local realtime audio device preferences used by realtime voice.
|
||||
@@ -1086,11 +1084,6 @@ 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 = enabled_builtin_mcp_servers(BuiltinMcpServerOptions {
|
||||
memories_enabled: self.features.enabled(Feature::BuiltInMcp)
|
||||
&& self.features.enabled(Feature::MemoryTool)
|
||||
&& self.memories.use_memories,
|
||||
});
|
||||
let mut configured_mcp_servers = self.mcp_servers.get().clone();
|
||||
for plugin in loaded_plugins
|
||||
.plugins()
|
||||
@@ -1111,13 +1104,9 @@ impl Config {
|
||||
&& mcp_requirements.value.is_empty()
|
||||
{
|
||||
// A present empty allowlist bans configurable MCPs, including plugin MCPs merged
|
||||
// above. Built-ins are product-owned and stay available regardless of admin
|
||||
// allowlists.
|
||||
// above.
|
||||
filter_mcp_servers_by_requirements(&mut configured_mcp_servers, Some(mcp_requirements));
|
||||
}
|
||||
for builtin_server in &builtin_mcp_servers {
|
||||
configured_mcp_servers.remove(builtin_server.name());
|
||||
}
|
||||
|
||||
McpConfig {
|
||||
chatgpt_base_url: self.chatgpt_base_url.clone(),
|
||||
@@ -1134,7 +1123,6 @@ 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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use anyhow::Result;
|
||||
use codex_config::types::McpServerConfig;
|
||||
use codex_config::types::McpServerTransportConfig;
|
||||
use codex_features::Feature;
|
||||
use codex_mcp::MEMORIES_MCP_SERVER_NAME;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::dynamic_tools::DynamicToolSpec;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
@@ -448,92 +447,6 @@ async fn mcp_call_marks_thread_memory_mode_polluted_when_configured() -> Result<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn builtin_memories_mcp_call_does_not_mark_thread_memory_mode_polluted_when_configured()
|
||||
-> Result<()> {
|
||||
let server = start_mock_server().await;
|
||||
let call_id = "call-123";
|
||||
let namespace = format!("mcp__{MEMORIES_MCP_SERVER_NAME}__");
|
||||
mount_sse_once(
|
||||
&server,
|
||||
responses::sse(vec![
|
||||
ev_response_created("resp-1"),
|
||||
responses::ev_function_call_with_namespace(call_id, &namespace, "list", "{}"),
|
||||
ev_completed("resp-1"),
|
||||
]),
|
||||
)
|
||||
.await;
|
||||
mount_sse_once(
|
||||
&server,
|
||||
responses::sse(vec![
|
||||
responses::ev_assistant_message("msg-1", "memories list tool completed."),
|
||||
ev_completed("resp-2"),
|
||||
]),
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut builder = test_codex().with_config(|config| {
|
||||
config
|
||||
.features
|
||||
.enable(Feature::Sqlite)
|
||||
.expect("test config should allow feature update");
|
||||
config
|
||||
.features
|
||||
.enable(Feature::BuiltInMcp)
|
||||
.expect("test config should allow feature update");
|
||||
config
|
||||
.features
|
||||
.enable(Feature::MemoryTool)
|
||||
.expect("test config should allow feature update");
|
||||
config.memories.use_memories = true;
|
||||
config.memories.disable_on_external_context = true;
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
let db = test.codex.state_db().expect("state db enabled");
|
||||
let thread_id = test.session_configured.thread_id;
|
||||
let cwd = test.cwd_path().to_path_buf();
|
||||
let (sandbox_policy, permission_profile) =
|
||||
turn_permission_fields(PermissionProfile::read_only(), cwd.as_path());
|
||||
|
||||
test.codex
|
||||
.submit(Op::UserTurn {
|
||||
environments: None,
|
||||
items: vec![UserInput::Text {
|
||||
text: "call the memories list tool".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd,
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: None,
|
||||
sandbox_policy,
|
||||
permission_profile,
|
||||
model: test.session_configured.model.clone(),
|
||||
effort: None,
|
||||
summary: None,
|
||||
service_tier: None,
|
||||
collaboration_mode: None,
|
||||
personality: None,
|
||||
})
|
||||
.await?;
|
||||
wait_for_event(&test.codex, |event| {
|
||||
matches!(event, EventMsg::McpToolCallEnd(_))
|
||||
})
|
||||
.await;
|
||||
wait_for_event_match(&test.codex, |event| match event {
|
||||
EventMsg::Error(err) => Some(Err(anyhow::anyhow!(err.message.clone()))),
|
||||
EventMsg::TurnComplete(_) => Some(Ok(())),
|
||||
_ => None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
assert_ne!(
|
||||
db.get_thread_memory_mode(thread_id).await?.as_deref(),
|
||||
Some("polluted")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn tool_call_logs_include_thread_id() -> Result<()> {
|
||||
let server = start_mock_server().await;
|
||||
|
||||
@@ -134,8 +134,6 @@ pub enum Feature {
|
||||
Sqlite,
|
||||
/// Enable startup memory extraction and file-backed memory consolidation.
|
||||
MemoryTool,
|
||||
/// Enable product-owned built-in MCP servers.
|
||||
BuiltInMcp,
|
||||
/// Enable the Chronicle sidecar for passive screen-context memories.
|
||||
Chronicle,
|
||||
/// Append additional AGENTS.md guidance to user instructions.
|
||||
@@ -152,7 +150,7 @@ pub enum Feature {
|
||||
Apps,
|
||||
/// Enable MCP apps.
|
||||
EnableMcpApps,
|
||||
/// Use the new path for the built-in apps MCP server.
|
||||
/// Use the new path for the host-owned apps MCP server.
|
||||
AppsMcpPathOverride,
|
||||
/// Enable the tool_search tool for apps.
|
||||
ToolSearch,
|
||||
@@ -797,12 +795,6 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
},
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::BuiltInMcp,
|
||||
key: "builtin_mcp",
|
||||
stage: Stage::UnderDevelopment,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::Chronicle,
|
||||
key: "chronicle",
|
||||
|
||||
@@ -145,13 +145,6 @@ fn responses_websocket_response_processed_is_under_development() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_mcp_is_under_development() {
|
||||
assert_eq!(Feature::BuiltInMcp.stage(), Stage::UnderDevelopment);
|
||||
assert_eq!(Feature::BuiltInMcp.default_enabled(), false);
|
||||
assert_eq!(feature_for_key("builtin_mcp"), Some(Feature::BuiltInMcp));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminal_resize_reflow_is_experimental_and_enabled_by_default() {
|
||||
assert_eq!(
|
||||
|
||||
@@ -10,8 +10,8 @@ Runtime orchestration for Phase 1 and Phase 2 still lives in `codex-core` under
|
||||
- `codex-rs/memories/read` (`codex-memories-read`) owns the read path:
|
||||
memory developer-instruction injection, memory citation parsing, and
|
||||
read-usage telemetry classification.
|
||||
- `codex-rs/memories/mcp` (`codex-memories-mcp`) exposes the read-only memory
|
||||
filesystem through the built-in MCP surface.
|
||||
- `codex-rs/memories/mcp` (`codex-memories-mcp`) owns the read-only memory
|
||||
filesystem MCP server implementation.
|
||||
- `codex-rs/memories/write` (`codex-memories-write`) owns the write path:
|
||||
Phase 1 and Phase 2 prompt rendering, filesystem artifact helpers,
|
||||
workspace diff helpers, and extension resource pruning.
|
||||
|
||||
Reference in New Issue
Block a user