diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index a78a9ab91c..c754b0d13e 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -3158,7 +3158,10 @@ dependencies = [ "codex-core", "codex-exec-server", "codex-extension-api", + "codex-features", + "codex-goal-extension", "codex-login", + "codex-otel", "codex-protocol", "codex-shell-command", "codex-utils-absolute-path", diff --git a/codex-rs/mcp-server/Cargo.toml b/codex-rs/mcp-server/Cargo.toml index 29b2d6c7d7..d6469706b8 100644 --- a/codex-rs/mcp-server/Cargo.toml +++ b/codex-rs/mcp-server/Cargo.toml @@ -23,7 +23,10 @@ codex-config = { workspace = true } codex-core = { workspace = true } codex-exec-server = { workspace = true } codex-extension-api = { workspace = true } +codex-features = { workspace = true } +codex-goal-extension = { workspace = true } codex-login = { workspace = true } +codex-otel = { workspace = true } codex-protocol = { workspace = true } codex-utils-cli = { workspace = true } codex-utils-json-to-toml = { workspace = true } diff --git a/codex-rs/mcp-server/src/message_processor.rs b/codex-rs/mcp-server/src/message_processor.rs index 9e536d930c..f65a231a6b 100644 --- a/codex-rs/mcp-server/src/message_processor.rs +++ b/codex-rs/mcp-server/src/message_processor.rs @@ -6,7 +6,8 @@ use codex_core::StateDbHandle; use codex_core::ThreadManager; use codex_core::config::Config; use codex_exec_server::EnvironmentManager; -use codex_extension_api::empty_extension_registry; +use codex_extension_api::ExtensionRegistryBuilder; +use codex_features::Feature; use codex_login::AuthManager; use codex_login::default_client::USER_AGENT_SUFFIX; use codex_login::default_client::get_codex_user_agent; @@ -63,18 +64,31 @@ impl MessageProcessor { /*enable_codex_api_key_env*/ false, ) .await; - let thread_manager = Arc::new(ThreadManager::new( - config.as_ref(), - auth_manager, - SessionSource::Mcp, - environment_manager, - empty_extension_registry(), - /*analytics_events_client*/ None, - codex_core::thread_store_from_config(config.as_ref(), state_db.clone()), - state_db.clone(), - installation_id, - /*attestation_provider*/ None, - )); + let thread_store = codex_core::thread_store_from_config(config.as_ref(), state_db.clone()); + let thread_manager = Arc::new_cyclic(|thread_manager| { + let mut extensions = ExtensionRegistryBuilder::::new(); + if let Some(state_db) = state_db.clone() { + codex_goal_extension::install_with_backend( + &mut extensions, + state_db, + codex_otel::global(), + thread_manager.clone(), + |config: &Config| config.features.enabled(Feature::Goals), + ); + } + ThreadManager::new( + config.as_ref(), + auth_manager.clone(), + SessionSource::Mcp, + Arc::clone(&environment_manager), + Arc::new(extensions.build()), + /*analytics_events_client*/ None, + Arc::clone(&thread_store), + state_db.clone(), + installation_id.clone(), + /*attestation_provider*/ None, + ) + }); Self { outgoing, initialized: false, diff --git a/codex-rs/mcp-server/tests/suite/codex_tool.rs b/codex-rs/mcp-server/tests/suite/codex_tool.rs index d9f290c3c5..23ec5b7cb6 100644 --- a/codex-rs/mcp-server/tests/suite/codex_tool.rs +++ b/codex-rs/mcp-server/tests/suite/codex_tool.rs @@ -414,6 +414,18 @@ async fn codex_tool_passes_base_instructions() -> anyhow::Result<()> { let requests = server.received_requests().await.unwrap(); let request = requests[0].body_json::()?; + let tool_names = request["tools"] + .as_array() + .expect("responses request should include tools") + .iter() + .filter_map(|tool| tool.get("name").and_then(serde_json::Value::as_str)) + .collect::>(); + assert!( + tool_names.contains(&"create_goal") + && tool_names.contains(&"get_goal") + && tool_names.contains(&"update_goal"), + "expected MCP-hosted Codex session to advertise goal tools, got {tool_names:?}" + ); let instructions = request["instructions"] .as_str() .expect("responses request should include instructions");