From c165c9ae0664372280eaa5cb823883cb0c4ec7c6 Mon Sep 17 00:00:00 2001 From: Alex Daley Date: Thu, 14 May 2026 15:32:19 -0400 Subject: [PATCH] Fix runtime Apps MCP path override enablement --- codex-rs/app-server/src/config_manager.rs | 71 +++++++++++++++++++ .../request_processors/config_processor.rs | 1 + .../suite/v2/experimental_feature_list.rs | 23 ++++-- 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/codex-rs/app-server/src/config_manager.rs b/codex-rs/app-server/src/config_manager.rs index 030829fa4b..6af2f50a00 100644 --- a/codex-rs/app-server/src/config_manager.rs +++ b/codex-rs/app-server/src/config_manager.rs @@ -8,6 +8,7 @@ use codex_config::loader::load_config_layers_state; use codex_core::config::Config; use codex_core::config::ConfigOverrides; use codex_exec_server::LOCAL_FS; +use codex_features::Feature; use codex_features::feature_for_key; use codex_login::AuthManager; use codex_login::default_client::set_default_client_residency_requirement; @@ -336,4 +337,74 @@ pub(crate) fn apply_runtime_feature_enablement( ); } } + + let apps_mcp_path_override_key = Feature::AppsMcpPathOverride.key(); + if !protected_features.contains(apps_mcp_path_override_key) { + config.apps_mcp_path_override = + config + .features + .enabled(Feature::AppsMcpPathOverride) + .then(|| { + config + .apps_mcp_path_override + .clone() + .unwrap_or_else(|| "/ps/mcp".to_string()) + }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[tokio::test] + async fn runtime_feature_enablement_defaults_apps_mcp_path_override() -> std::io::Result<()> { + let codex_home = tempfile::tempdir()?; + let manager = + ConfigManager::without_managed_config_for_tests(codex_home.path().to_path_buf()); + manager + .extend_runtime_feature_enablement([( + Feature::AppsMcpPathOverride.key().to_string(), + true, + )]) + .expect("runtime feature enablement should update"); + + let config = manager.load_latest_config(/*fallback_cwd*/ None).await?; + + assert_eq!(config.features.enabled(Feature::AppsMcpPathOverride), true); + assert_eq!(config.apps_mcp_path_override.as_deref(), Some("/ps/mcp")); + Ok(()) + } + + #[tokio::test] + async fn runtime_feature_enablement_preserves_configured_apps_mcp_path_override() + -> std::io::Result<()> { + let codex_home = tempfile::tempdir()?; + std::fs::write( + codex_home.path().join("config.toml"), + r#" +[features.apps_mcp_path_override] +enabled = true +path = "/custom/mcp" +"#, + )?; + let manager = + ConfigManager::without_managed_config_for_tests(codex_home.path().to_path_buf()); + manager + .extend_runtime_feature_enablement([( + Feature::AppsMcpPathOverride.key().to_string(), + false, + )]) + .expect("runtime feature enablement should update"); + + let config = manager.load_latest_config(/*fallback_cwd*/ None).await?; + + assert_eq!(config.features.enabled(Feature::AppsMcpPathOverride), true); + assert_eq!( + config.apps_mcp_path_override.as_deref(), + Some("/custom/mcp") + ); + Ok(()) + } } diff --git a/codex-rs/app-server/src/request_processors/config_processor.rs b/codex-rs/app-server/src/request_processors/config_processor.rs index b7f973b076..0f30dbd8d9 100644 --- a/codex-rs/app-server/src/request_processors/config_processor.rs +++ b/codex-rs/app-server/src/request_processors/config_processor.rs @@ -49,6 +49,7 @@ use std::path::PathBuf; const SUPPORTED_EXPERIMENTAL_FEATURE_ENABLEMENT: &[&str] = &[ "apps", + "apps_mcp_path_override", "memories", "mentions_v2", "plugins", diff --git a/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs b/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs index d50683cf80..34cb109fc7 100644 --- a/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs +++ b/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs @@ -166,13 +166,21 @@ async fn experimental_feature_enablement_set_applies_to_global_and_thread_config let mut mcp = McpProcess::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; - let actual = - set_experimental_feature_enablement(&mut mcp, BTreeMap::from([("apps".to_string(), true)])) - .await?; + let actual = set_experimental_feature_enablement( + &mut mcp, + BTreeMap::from([ + ("apps".to_string(), true), + ("apps_mcp_path_override".to_string(), true), + ]), + ) + .await?; assert_eq!( actual, ExperimentalFeatureEnablementSetResponse { - enablement: BTreeMap::from([("apps".to_string(), true)]), + enablement: BTreeMap::from([ + ("apps".to_string(), true), + ("apps_mcp_path_override".to_string(), true), + ]), } ); @@ -186,6 +194,13 @@ async fn experimental_feature_enablement_set_applies_to_global_and_thread_config .and_then(|features| features.get("apps")), Some(&json!(true)) ); + assert_eq!( + config + .additional + .get("features") + .and_then(|features| features.get("apps_mcp_path_override")), + Some(&json!(true)) + ); } Ok(())