Fix plugin MCP requirements enforcement

This commit is contained in:
daveaitel-openai
2026-03-03 16:26:46 -05:00
parent 4ae60cf03c
commit 517ab90f93
2 changed files with 65 additions and 1 deletions

View File

@@ -727,7 +727,7 @@ fn load_model_catalog(
.transpose()
}
fn filter_mcp_servers_by_requirements(
pub(crate) fn filter_mcp_servers_by_requirements(
mcp_servers: &mut HashMap<String, McpServerConfig>,
mcp_requirements: Option<&Sourced<BTreeMap<String, McpServerRequirement>>>,
) {

View File

@@ -19,6 +19,7 @@ use serde_json::Value;
use crate::AuthManager;
use crate::CodexAuth;
use crate::config::Config;
use crate::config::filter_mcp_servers_by_requirements;
use crate::config::types::McpServerConfig;
use crate::config::types::McpServerTransportConfig;
use crate::features::Feature;
@@ -193,6 +194,14 @@ fn configured_mcp_servers(
for (name, plugin_server) in loaded_plugins.effective_mcp_servers() {
servers.entry(name).or_insert(plugin_server);
}
filter_mcp_servers_by_requirements(
&mut servers,
config
.config_layer_stack
.requirements()
.mcp_servers
.as_ref(),
);
servers
}
@@ -407,6 +416,8 @@ mod tests {
use super::*;
use crate::config::CONFIG_TOML_FILE;
use crate::config::ConfigBuilder;
use crate::config_loader::CloudRequirementsLoader;
use crate::config_loader::ConfigRequirementsToml;
use pretty_assertions::assert_eq;
use std::fs;
use std::path::Path;
@@ -613,6 +624,59 @@ mod tests {
assert_eq!(url, &expected_url);
}
#[tokio::test]
async fn configured_mcp_servers_apply_requirements_to_plugin_servers() {
let codex_home = tempfile::tempdir().expect("tempdir");
let plugin_root = codex_home.path().join("plugin-sample");
write_file(
&plugin_root.join(".codex-plugin/plugin.json"),
r#"{"name":"sample"}"#,
);
write_file(
&plugin_root.join(".mcp.json"),
r#"{
"mcpServers": {
"docs": {
"type": "http",
"url": "https://docs.example/mcp"
}
}
}"#,
);
write_file(
&codex_home.path().join(CONFIG_TOML_FILE),
&plugin_config_toml(&plugin_root),
);
let config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.cloud_requirements(CloudRequirementsLoader::new(async {
Ok(Some(ConfigRequirementsToml {
mcp_servers: Some(Default::default()),
..Default::default()
}))
}))
.build()
.await
.expect("config should load");
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(config.codex_home.clone())));
let configured = mcp_manager.configured_servers(&config);
let docs = configured.get("docs").expect("plugin server should exist");
assert!(
!docs.enabled,
"plugin server should be disabled by requirements"
);
assert!(
matches!(
docs.disabled_reason,
Some(crate::config::types::McpServerDisabledReason::Requirements { .. })
),
"plugin server should record requirements disabled reason"
);
}
#[tokio::test]
async fn effective_mcp_servers_include_plugins_without_overriding_user_config() {
let codex_home = tempfile::tempdir().expect("tempdir");