Compare commits

...

3 Commits

Author SHA1 Message Date
Alex Daley
c165c9ae06 Fix runtime Apps MCP path override enablement 2026-05-14 15:32:19 -04:00
Alex Daley
69cc07cdb7 Reuse apps MCP path override for rollout 2026-05-13 14:53:43 -04:00
Alex Daley
a40cf9b605 Add rollout flag for new apps MCP endpoint 2026-05-13 13:48:41 -04:00
5 changed files with 144 additions and 4 deletions

View File

@@ -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(())
}
}

View File

@@ -49,6 +49,7 @@ use std::path::PathBuf;
const SUPPORTED_EXPERIMENTAL_FEATURE_ENABLEMENT: &[&str] = &[
"apps",
"apps_mcp_path_override",
"memories",
"mentions_v2",
"plugins",

View File

@@ -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(())

View File

@@ -8769,6 +8769,58 @@ path = "/custom/mcp"
Ok(())
}
#[tokio::test]
async fn config_defaults_enabled_apps_mcp_path_override_to_plugin_service() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
let toml = r#"
model = "gpt-5.4"
[features]
apps_mcp_path_override = true
"#;
let cfg: ConfigToml =
toml::from_str(toml).expect("TOML deserialization should succeed for apps MCP feature");
let config = Config::load_from_base_config_with_overrides(
cfg,
ConfigOverrides::default(),
codex_home.abs(),
)
.await?;
assert!(config.features.enabled(Feature::AppsMcpPathOverride));
assert_eq!(config.apps_mcp_path_override.as_deref(), Some("/ps/mcp"));
Ok(())
}
#[tokio::test]
async fn config_preserves_explicit_apps_mcp_path_override_path() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
let toml = r#"
model = "gpt-5.4"
[features.apps_mcp_path_override]
enabled = true
path = "/custom/mcp"
"#;
let cfg: ConfigToml =
toml::from_str(toml).expect("TOML deserialization should succeed for apps MCP feature");
let config = Config::load_from_base_config_with_overrides(
cfg,
ConfigOverrides::default(),
codex_home.abs(),
)
.await?;
assert_eq!(
config.apps_mcp_path_override.as_deref(),
Some("/custom/mcp")
);
assert!(config.features.enabled(Feature::AppsMcpPathOverride));
Ok(())
}
#[tokio::test]
async fn config_loads_mcp_oauth_callback_url_from_toml() -> std::io::Result<()> {
let codex_home = TempDir::new()?;

View File

@@ -2615,6 +2615,7 @@ impl Config {
.and_then(|config| config.path.as_ref())
.or_else(|| base.and_then(|config| config.path.as_ref()))
.cloned()
.or_else(|| Some("/ps/mcp".to_string()))
} else {
None
};