mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[mcp] Fix plugin MCP approval policy. (#19537)
Plugin MCP servers are loaded from plugin manifests rather than top-level `[mcp_servers]`, so their tool approval preferences need to be stored and applied through the owning plugin config. Without this, choosing "Always allow" for a plugin MCP tool could write a preference that was not reliably used on later tool calls. ## Summary - Add plugin-scoped MCP policy config under `plugins.<plugin>.mcp_servers`, including server enablement, tool allow/deny lists, server defaults, and per-tool approval modes. - Overlay plugin MCP policy onto manifest-provided server configs when plugins are loaded. - Route persistent "Always allow" writes for plugin MCP tools back to the owning `plugins.<plugin>.mcp_servers.<server>.tools.<tool>` config entry. - Reload user config after persisting an approval and make the plugin load cache config-aware so stale plugin MCP policy is not reused after `config.toml` changes. - Regenerate the config schema and add coverage for plugin MCP policy loading, approval lookup, persistence, and stale-cache prevention. ## Testing - `cargo test -p codex-config` - `cargo test -p codex-core-plugins` - `cargo test -p codex-core --lib plugin_mcp`
This commit is contained in:
@@ -12,6 +12,7 @@ use codex_config::ConfigLayerStack;
|
||||
use codex_config::HooksFile;
|
||||
use codex_config::types::McpServerConfig;
|
||||
use codex_config::types::PluginConfig;
|
||||
use codex_config::types::PluginMcpServerConfig;
|
||||
use codex_core_skills::SkillMetadata;
|
||||
use codex_core_skills::config_rules::SkillConfigRules;
|
||||
use codex_core_skills::config_rules::resolve_disabled_skill_paths;
|
||||
@@ -175,6 +176,7 @@ pub fn remote_installed_plugins_to_config(
|
||||
plugin_id.as_key(),
|
||||
PluginConfig {
|
||||
enabled: plugin.enabled,
|
||||
mcp_servers: HashMap::new(),
|
||||
},
|
||||
))
|
||||
})
|
||||
@@ -575,7 +577,10 @@ async fn load_plugin(
|
||||
let mut mcp_servers = HashMap::new();
|
||||
for mcp_config_path in plugin_mcp_config_paths(plugin_root.as_path(), manifest_paths) {
|
||||
let plugin_mcp = load_mcp_servers_from_file(plugin_root.as_path(), &mcp_config_path).await;
|
||||
for (name, config) in plugin_mcp.mcp_servers {
|
||||
for (name, mut config) in plugin_mcp.mcp_servers {
|
||||
if let Some(policy) = plugin.mcp_servers.get(&name) {
|
||||
apply_plugin_mcp_server_policy(&mut config, policy);
|
||||
}
|
||||
if mcp_servers.insert(name.clone(), config).is_some() {
|
||||
warn!(
|
||||
plugin = %plugin_root.display(),
|
||||
@@ -599,6 +604,25 @@ async fn load_plugin(
|
||||
loaded_plugin
|
||||
}
|
||||
|
||||
fn apply_plugin_mcp_server_policy(config: &mut McpServerConfig, policy: &PluginMcpServerConfig) {
|
||||
config.enabled = policy.enabled;
|
||||
if let Some(approval_mode) = policy.default_tools_approval_mode {
|
||||
config.default_tools_approval_mode = Some(approval_mode);
|
||||
}
|
||||
if let Some(enabled_tools) = &policy.enabled_tools {
|
||||
config.enabled_tools = Some(enabled_tools.clone());
|
||||
}
|
||||
if let Some(disabled_tools) = &policy.disabled_tools {
|
||||
config.disabled_tools = Some(disabled_tools.clone());
|
||||
}
|
||||
for (tool_name, tool_policy) in &policy.tools {
|
||||
let tool_config = config.tools.entry(tool_name.clone()).or_default();
|
||||
if let Some(approval_mode) = tool_policy.approval_mode {
|
||||
tool_config.approval_mode = Some(approval_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ResolvedPluginSkills {
|
||||
pub skills: Vec<SkillMetadata>,
|
||||
|
||||
Reference in New Issue
Block a user