mirror of
https://github.com/openai/codex.git
synced 2026-05-01 03:42:05 +03:00
Propagate MCP disabled reason (#9207)
Indicate why MCP servers are disabled when they are disabled by
requirements:
```
➜ codex git:(main) ✗ just codex mcp list
cargo run --bin codex -- "$@"
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/codex mcp list`
Name Command Args Env Cwd Status Auth
docs docs-mcp - - - disabled: requirements (MDM com.openai.codex:requirements_toml_base64) Unsupported
hello_world hello-world-mcp - - - disabled: requirements (MDM com.openai.codex:requirements_toml_base64) Unsupported
➜ codex git:(main) ✗ just c
cargo run --bin codex -- "$@"
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.90s
Running `target/debug/codex`
╭─────────────────────────────────────────────╮
│ >_ OpenAI Codex (v0.0.0) │
│ │
│ model: gpt-5.2 xhigh /model to change │
│ directory: ~/code/codex/codex-rs │
╰─────────────────────────────────────────────╯
/mcp
🔌 MCP Tools
• No MCP tools available.
• docs (disabled)
• Reason: requirements (MDM com.openai.codex:requirements_toml_base64)
• hello_world (disabled)
• Reason: requirements (MDM com.openai.codex:requirements_toml_base64)
```
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::auth::AuthCredentialsStoreMode;
|
||||
use crate::config::types::DEFAULT_OTEL_ENVIRONMENT;
|
||||
use crate::config::types::History;
|
||||
use crate::config::types::McpServerConfig;
|
||||
use crate::config::types::McpServerDisabledReason;
|
||||
use crate::config::types::McpServerTransportConfig;
|
||||
use crate::config::types::Notice;
|
||||
use crate::config::types::Notifications;
|
||||
@@ -19,6 +20,7 @@ use crate::config_loader::ConfigRequirements;
|
||||
use crate::config_loader::LoaderOverrides;
|
||||
use crate::config_loader::McpServerIdentity;
|
||||
use crate::config_loader::McpServerRequirement;
|
||||
use crate::config_loader::Sourced;
|
||||
use crate::config_loader::load_config_layers_state;
|
||||
use crate::features::Feature;
|
||||
use crate::features::FeatureOverrides;
|
||||
@@ -539,25 +541,32 @@ fn deserialize_config_toml_with_base(
|
||||
|
||||
fn filter_mcp_servers_by_requirements(
|
||||
mcp_servers: &mut HashMap<String, McpServerConfig>,
|
||||
mcp_requirements: Option<&BTreeMap<String, McpServerRequirement>>,
|
||||
mcp_requirements: Option<&Sourced<BTreeMap<String, McpServerRequirement>>>,
|
||||
) {
|
||||
let Some(allowlist) = mcp_requirements else {
|
||||
return;
|
||||
};
|
||||
|
||||
let source = allowlist.source.clone();
|
||||
for (name, server) in mcp_servers.iter_mut() {
|
||||
let allowed = allowlist
|
||||
.value
|
||||
.get(name)
|
||||
.is_some_and(|requirement| mcp_server_matches_requirement(requirement, server));
|
||||
if !allowed {
|
||||
if allowed {
|
||||
server.disabled_reason = None;
|
||||
} else {
|
||||
server.enabled = false;
|
||||
server.disabled_reason = Some(McpServerDisabledReason::Requirements {
|
||||
source: source.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn constrain_mcp_servers(
|
||||
mcp_servers: HashMap<String, McpServerConfig>,
|
||||
mcp_requirements: Option<&BTreeMap<String, McpServerRequirement>>,
|
||||
mcp_requirements: Option<&Sourced<BTreeMap<String, McpServerRequirement>>>,
|
||||
) -> ConstraintResult<Constrained<HashMap<String, McpServerConfig>>> {
|
||||
if mcp_requirements.is_none() {
|
||||
return Ok(Constrained::allow_any(mcp_servers));
|
||||
@@ -1707,6 +1716,7 @@ mod tests {
|
||||
use crate::config::types::HistoryPersistence;
|
||||
use crate::config::types::McpServerTransportConfig;
|
||||
use crate::config::types::Notifications;
|
||||
use crate::config_loader::RequirementSource;
|
||||
use crate::features::Feature;
|
||||
|
||||
use super::*;
|
||||
@@ -1728,6 +1738,7 @@ mod tests {
|
||||
cwd: None,
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: None,
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -1744,6 +1755,7 @@ mod tests {
|
||||
env_http_headers: None,
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: None,
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -1976,9 +1988,9 @@ trust_level = "trusted"
|
||||
(MATCHED_URL_SERVER.to_string(), http_mcp(GOOD_URL)),
|
||||
(DIFFERENT_NAME_SERVER.to_string(), stdio_mcp("same-cmd")),
|
||||
]);
|
||||
filter_mcp_servers_by_requirements(
|
||||
&mut servers,
|
||||
Some(&BTreeMap::from([
|
||||
let source = RequirementSource::LegacyManagedConfigTomlFromMdm;
|
||||
let requirements = Sourced::new(
|
||||
BTreeMap::from([
|
||||
(
|
||||
MISMATCHED_URL_SERVER.to_string(),
|
||||
McpServerRequirement {
|
||||
@@ -2011,20 +2023,29 @@ trust_level = "trusted"
|
||||
},
|
||||
},
|
||||
),
|
||||
])),
|
||||
]),
|
||||
source.clone(),
|
||||
);
|
||||
filter_mcp_servers_by_requirements(&mut servers, Some(&requirements));
|
||||
|
||||
let reason = Some(McpServerDisabledReason::Requirements { source });
|
||||
assert_eq!(
|
||||
servers
|
||||
.iter()
|
||||
.map(|(name, server)| (name.clone(), server.enabled))
|
||||
.collect::<HashMap<String, bool>>(),
|
||||
.map(|(name, server)| (
|
||||
name.clone(),
|
||||
(server.enabled, server.disabled_reason.clone())
|
||||
))
|
||||
.collect::<HashMap<String, (bool, Option<McpServerDisabledReason>)>>(),
|
||||
HashMap::from([
|
||||
(MISMATCHED_URL_SERVER.to_string(), false),
|
||||
(MISMATCHED_COMMAND_SERVER.to_string(), false),
|
||||
(MATCHED_URL_SERVER.to_string(), true),
|
||||
(MATCHED_COMMAND_SERVER.to_string(), true),
|
||||
(DIFFERENT_NAME_SERVER.to_string(), false),
|
||||
(MISMATCHED_URL_SERVER.to_string(), (false, reason.clone())),
|
||||
(
|
||||
MISMATCHED_COMMAND_SERVER.to_string(),
|
||||
(false, reason.clone()),
|
||||
),
|
||||
(MATCHED_URL_SERVER.to_string(), (true, None)),
|
||||
(MATCHED_COMMAND_SERVER.to_string(), (true, None)),
|
||||
(DIFFERENT_NAME_SERVER.to_string(), (false, reason)),
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -2041,11 +2062,14 @@ trust_level = "trusted"
|
||||
assert_eq!(
|
||||
servers
|
||||
.iter()
|
||||
.map(|(name, server)| (name.clone(), server.enabled))
|
||||
.collect::<HashMap<String, bool>>(),
|
||||
.map(|(name, server)| (
|
||||
name.clone(),
|
||||
(server.enabled, server.disabled_reason.clone())
|
||||
))
|
||||
.collect::<HashMap<String, (bool, Option<McpServerDisabledReason>)>>(),
|
||||
HashMap::from([
|
||||
("server-a".to_string(), true),
|
||||
("server-b".to_string(), true),
|
||||
("server-a".to_string(), (true, None)),
|
||||
("server-b".to_string(), (true, None)),
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -2057,16 +2081,22 @@ trust_level = "trusted"
|
||||
("server-b".to_string(), http_mcp("https://example.com/b")),
|
||||
]);
|
||||
|
||||
filter_mcp_servers_by_requirements(&mut servers, Some(&BTreeMap::new()));
|
||||
let source = RequirementSource::LegacyManagedConfigTomlFromMdm;
|
||||
let requirements = Sourced::new(BTreeMap::new(), source.clone());
|
||||
filter_mcp_servers_by_requirements(&mut servers, Some(&requirements));
|
||||
|
||||
let reason = Some(McpServerDisabledReason::Requirements { source });
|
||||
assert_eq!(
|
||||
servers
|
||||
.iter()
|
||||
.map(|(name, server)| (name.clone(), server.enabled))
|
||||
.collect::<HashMap<String, bool>>(),
|
||||
.map(|(name, server)| (
|
||||
name.clone(),
|
||||
(server.enabled, server.disabled_reason.clone())
|
||||
))
|
||||
.collect::<HashMap<String, (bool, Option<McpServerDisabledReason>)>>(),
|
||||
HashMap::from([
|
||||
("server-a".to_string(), false),
|
||||
("server-b".to_string(), false),
|
||||
("server-a".to_string(), (false, reason.clone())),
|
||||
("server-b".to_string(), (false, reason)),
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -2491,6 +2521,7 @@ trust_level = "trusted"
|
||||
cwd: None,
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: Some(Duration::from_secs(3)),
|
||||
tool_timeout_sec: Some(Duration::from_secs(5)),
|
||||
enabled_tools: None,
|
||||
@@ -2644,6 +2675,7 @@ bearer_token = "secret"
|
||||
cwd: None,
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: None,
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -2712,6 +2744,7 @@ ZIG_VAR = "3"
|
||||
cwd: None,
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: None,
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -2760,6 +2793,7 @@ ZIG_VAR = "3"
|
||||
cwd: Some(cwd_path.clone()),
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: None,
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -2806,6 +2840,7 @@ ZIG_VAR = "3"
|
||||
env_http_headers: None,
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: Some(Duration::from_secs(2)),
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -2868,6 +2903,7 @@ startup_timeout_sec = 2.0
|
||||
)])),
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: Some(Duration::from_secs(2)),
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -2942,6 +2978,7 @@ X-Auth = "DOCS_AUTH"
|
||||
)])),
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: Some(Duration::from_secs(2)),
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -2969,6 +3006,7 @@ X-Auth = "DOCS_AUTH"
|
||||
env_http_headers: None,
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: None,
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -3034,6 +3072,7 @@ url = "https://example.com/mcp"
|
||||
)])),
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: Some(Duration::from_secs(2)),
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -3051,6 +3090,7 @@ url = "https://example.com/mcp"
|
||||
cwd: None,
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: None,
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -3131,6 +3171,7 @@ url = "https://example.com/mcp"
|
||||
cwd: None,
|
||||
},
|
||||
enabled: false,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: None,
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: None,
|
||||
@@ -3173,6 +3214,7 @@ url = "https://example.com/mcp"
|
||||
cwd: None,
|
||||
},
|
||||
enabled: true,
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: None,
|
||||
tool_timeout_sec: None,
|
||||
enabled_tools: Some(vec!["allowed".to_string()]),
|
||||
|
||||
Reference in New Issue
Block a user