Download codex-rs/codex-mcp/src/catalog_tests.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 23.6 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/codex-mcp/src/catalog_tests.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/codex-mcp/src/catalog_tests.rs
-
curl -L -o catalog_tests.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/codex-mcp/src/catalog_tests.rs
23.6 kB
| use std::collections::BTreeMap; | |
| use std::collections::HashMap; | |
| use std::time::Duration; | |
| use codex_config::AppToolApproval; | |
| use codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID; | |
| use codex_config::McpServerAuth; | |
| use codex_config::McpServerConfig; | |
| use codex_config::McpServerDisabledReason; | |
| use codex_config::McpServerIdpOAuthConfig; | |
| use codex_config::McpServerToolConfig; | |
| use codex_config::McpServerTransportConfig; | |
| use codex_config::types::PluginMcpServerEmaAuthConfig; | |
| use codex_protocol::mcp_policy::EnvironmentMcpPolicy; | |
| use codex_protocol::mcp_policy::PluginMcpRequirements; | |
| use codex_utils_path_uri::PathUri; | |
| use pretty_assertions::assert_eq; | |
| use crate::CODEX_APPS_MCP_SERVER_NAME; | |
| use crate::McpProtocolMode; | |
| use super::McpEnvironmentAuthority; | |
| use super::McpPluginAttribution; | |
| use super::McpServerConflict; | |
| use super::McpServerConflictAction; | |
| use super::McpServerRegistration; | |
| use super::McpServerSource; | |
| use super::ResolvedMcpCatalog; | |
| use super::ResolvedMcpServer; | |
| fn server(url: &str) -> McpServerConfig { | |
| McpServerConfig { | |
| auth: Default::default(), | |
| transport: McpServerTransportConfig::StreamableHttp { | |
| url: url.to_string(), | |
| bearer_token_env_var: None, | |
| http_headers: None, | |
| env_http_headers: None, | |
| http_headers_helper: None, | |
| }, | |
| environment_id: DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(), | |
| enabled: true, | |
| required: true, | |
| supports_parallel_tool_calls: true, | |
| omit_tools_from: None, | |
| disabled_reason: None, | |
| startup_timeout_sec: Some(Duration::from_secs(7)), | |
| tool_timeout_sec: Some(Duration::from_secs(11)), | |
| default_tools_approval_mode: Some(AppToolApproval::Prompt), | |
| enabled_tools: Some(vec!["read".to_string()]), | |
| disabled_tools: Some(vec!["write".to_string()]), | |
| scopes: None, | |
| oauth: None, | |
| oauth_resource: None, | |
| tools: HashMap::from([( | |
| "read".to_string(), | |
| McpServerToolConfig { | |
| approval_mode: Some(AppToolApproval::Approve), | |
| ..Default::default() | |
| }, | |
| )]), | |
| } | |
| } | |
| fn plugin(plugin_id: &str) -> McpPluginAttribution { | |
| McpPluginAttribution::new(plugin_id.to_string(), plugin_id.to_string()) | |
| } | |
| fn plugin_source(plugin_id: &str) -> McpServerSource { | |
| McpServerSource::Plugin(plugin(plugin_id)) | |
| } | |
| fn selected_plugin_source(plugin_id: &str) -> McpServerSource { | |
| McpServerSource::SelectedPlugin(plugin(plugin_id)) | |
| } | |
| fn compatibility_source(id: &str) -> McpServerSource { | |
| McpServerSource::Compatibility { id: id.to_string() } | |
| } | |
| fn extension_source(id: &str) -> McpServerSource { | |
| McpServerSource::Extension { | |
| id: id.to_string(), | |
| host_owned_apps: false, | |
| } | |
| } | |
| fn register(source: McpServerSource) -> McpServerConflictAction { | |
| McpServerConflictAction::Register(source) | |
| } | |
| fn remove(source: McpServerSource) -> McpServerConflictAction { | |
| McpServerConflictAction::Remove(source) | |
| } | |
| fn plugin_host_root_is_retained_in_catalog_identity() { | |
| let original_root = PathUri::parse("file:///plugins/original").expect("valid plugin root URI"); | |
| let replacement_root = | |
| PathUri::parse("file:///plugins/replacement").expect("valid plugin root URI"); | |
| let catalog_for_root = |root| { | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_plugin( | |
| "docs".to_string(), | |
| plugin("plugin@test").with_host_root(root), | |
| /*plugin_order*/ 0, | |
| server("https://plugin.example/mcp"), | |
| )); | |
| builder.build() | |
| }; | |
| let original = catalog_for_root(original_root.clone()); | |
| let replacement = catalog_for_root(replacement_root); | |
| let Some(McpServerSource::Plugin(attribution)) = | |
| original.server("docs").map(ResolvedMcpServer::source) | |
| else { | |
| panic!("expected host-discovered plugin registration"); | |
| }; | |
| assert_eq!(attribution.host_root(), Some(&original_root)); | |
| assert!(!original.has_same_servers(&replacement)); | |
| } | |
| fn ema_policy_survives_rebuilds_and_rebinds_materialized_servers() { | |
| let idp = McpServerIdpOAuthConfig { | |
| issuer: "https://idp.example".to_string(), | |
| client_id: "enterprise-client".to_string(), | |
| }; | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.enable_ema(idp.clone()); | |
| let enabled_empty = builder.build(); | |
| assert!(!enabled_empty.has_same_servers(&ResolvedMcpCatalog::default())); | |
| let mut builder = enabled_empty.to_builder(); | |
| let mut config = server("https://resource.example/mcp"); | |
| config.auth = McpServerAuth::EmaAuth; | |
| builder.register(McpServerRegistration::from_config( | |
| "enterprise".to_string(), | |
| config, | |
| )); | |
| let original = builder.build(); | |
| assert!(original.has_same_servers(&original.to_builder().build())); | |
| let mut servers = original.configured_servers(); | |
| let config = servers.get_mut("enterprise").expect("EMA server"); | |
| let McpServerTransportConfig::StreamableHttp { url, .. } = &mut config.transport else { | |
| panic!("expected HTTP server"); | |
| }; | |
| *url = "https://resource.example/revised".to_string(); | |
| let materialized = original.with_materialized_servers(servers); | |
| let config = materialized | |
| .server("enterprise") | |
| .expect("materialized EMA server") | |
| .config(); | |
| let registration = config | |
| .ema_registration() | |
| .expect("finalized EMA registration"); | |
| assert_eq!( | |
| ( | |
| config.enabled, | |
| registration.idp(), | |
| registration.server_url() | |
| ), | |
| (true, &idp, "https://resource.example/revised") | |
| ); | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_config( | |
| "enterprise".to_string(), | |
| config.clone(), | |
| )); | |
| let denied = builder.build(); | |
| let config = denied | |
| .server("enterprise") | |
| .expect("denied EMA server") | |
| .config(); | |
| assert_eq!((config.enabled, config.ema_registration()), (false, None)); | |
| } | |
| fn rejected_plugin_ema_registration_does_not_veto_hosted_apps() { | |
| let idp = McpServerIdpOAuthConfig { | |
| issuer: "https://idp.example".to_string(), | |
| client_id: "enterprise-client".to_string(), | |
| }; | |
| for (url, resource) in [ | |
| ("https://other.example/mcp", "https://resource.example"), | |
| ("https://resource.example/mcp", " "), | |
| ] { | |
| for initially_enabled in [true, false] { | |
| let mut rejected = server(url); | |
| rejected.enabled = initially_enabled; | |
| let policy = PluginMcpServerEmaAuthConfig { | |
| url: "https://resource.example/mcp".to_string(), | |
| client_id: "resource-client".to_string(), | |
| authorization_server_issuer: "https://as.example".to_string(), | |
| scopes: Vec::new(), | |
| resource: resource.to_string(), | |
| }; | |
| policy.apply(&mut rejected); | |
| assert!(rejected.resolve_ema_registration(&idp).is_err()); | |
| assert_eq!( | |
| ( | |
| rejected.enabled, | |
| rejected.auth.clone(), | |
| rejected.ema_registration() | |
| ), | |
| (false, McpServerAuth::EmaAuth, None), | |
| ); | |
| assert_eq!( | |
| rejected.disabled_reason, | |
| initially_enabled.then_some(McpServerDisabledReason::EmaRegistration), | |
| ); | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.enable_ema(idp.clone()); | |
| builder.register(McpServerRegistration::from_plugin( | |
| CODEX_APPS_MCP_SERVER_NAME.to_string(), | |
| plugin("plugin@test"), | |
| /*plugin_order*/ 0, | |
| rejected.clone(), | |
| )); | |
| let catalog = builder.build(); | |
| assert_eq!( | |
| catalog.server(CODEX_APPS_MCP_SERVER_NAME).unwrap().config(), | |
| &rejected, | |
| ); | |
| let materialized = catalog.with_materialized_servers(catalog.configured_servers()); | |
| for catalog in [catalog, materialized] { | |
| let mut builder = catalog.to_builder(); | |
| let mut expected = server("https://chatgpt.com/mcp"); | |
| builder.register(McpServerRegistration::from_hosted_apps( | |
| "apps", | |
| /*contribution_order*/ 0, | |
| expected.clone(), | |
| )); | |
| expected.enabled = initially_enabled; | |
| assert_eq!( | |
| builder.build().server(CODEX_APPS_MCP_SERVER_NAME), | |
| Some(&ResolvedMcpServer { | |
| source: McpServerSource::Extension { | |
| id: "apps".to_string(), | |
| host_owned_apps: true, | |
| }, | |
| config: expected, | |
| protocol_mode: None, | |
| }), | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| fn source_precedence_preserves_the_winning_registration() { | |
| let extension = server("https://extension.example/mcp"); | |
| let mut plugin_server = server("https://plugin.example/mcp"); | |
| plugin_server.enabled = false; | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_extension( | |
| "docs".to_string(), | |
| "hosted", | |
| /*contribution_order*/ 0, | |
| extension.clone(), | |
| )); | |
| builder.register(McpServerRegistration::from_plugin( | |
| "docs".to_string(), | |
| plugin("plugin@test"), | |
| /*plugin_order*/ 0, | |
| plugin_server, | |
| )); | |
| builder.register(McpServerRegistration::from_plugin( | |
| "docs".to_string(), | |
| plugin("other-plugin@test"), | |
| /*plugin_order*/ 1, | |
| server("https://other-plugin.example/mcp"), | |
| )); | |
| builder.register(McpServerRegistration::from_compatibility( | |
| "docs".to_string(), | |
| "legacy", | |
| server("https://compatibility.example/mcp"), | |
| )); | |
| builder.register(McpServerRegistration::from_config( | |
| "docs".to_string(), | |
| server("https://config.example/mcp"), | |
| )); | |
| let catalog = builder.build(); | |
| let resolved = catalog.server("docs").expect("resolved server"); | |
| assert_eq!( | |
| resolved.source(), | |
| &McpServerSource::Extension { | |
| id: "hosted".to_string(), | |
| host_owned_apps: false, | |
| } | |
| ); | |
| assert_eq!(resolved.config(), &extension); | |
| assert!(catalog.plugin_attributions_by_server_name().is_empty()); | |
| assert_eq!( | |
| catalog.conflicts(), | |
| &[McpServerConflict { | |
| name: "docs".to_string(), | |
| outcome: register(extension_source("hosted")), | |
| contenders: vec![ | |
| register(plugin_source("other-plugin@test")), | |
| register(plugin_source("plugin@test")), | |
| ], | |
| }] | |
| ); | |
| } | |
| fn disabled_veto_only_disables_the_winning_registration() { | |
| let extension = server("https://extension.example/mcp"); | |
| let mut expected = extension.clone(); | |
| expected.enabled = false; | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_extension( | |
| "docs".to_string(), | |
| "hosted", | |
| /*contribution_order*/ 0, | |
| extension, | |
| )); | |
| builder.disable("docs".to_string()); | |
| let actual = builder | |
| .build() | |
| .server("docs") | |
| .expect("resolved server") | |
| .config() | |
| .clone(); | |
| assert_eq!(actual, expected); | |
| } | |
| fn disabled_winner_remains_a_veto_when_the_catalog_is_extended() { | |
| let mut disabled = server("https://config.example/mcp"); | |
| disabled.enabled = false; | |
| let mut expected = server("https://extension.example/mcp"); | |
| expected.enabled = false; | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_config( | |
| "docs".to_string(), | |
| disabled, | |
| )); | |
| let mut builder = builder.build().to_builder(); | |
| builder.register(McpServerRegistration::from_extension( | |
| "docs".to_string(), | |
| "hosted", | |
| /*contribution_order*/ 0, | |
| server("https://extension.example/mcp"), | |
| )); | |
| let resolved = builder.build(); | |
| assert_eq!( | |
| resolved.server("docs"), | |
| Some(&super::ResolvedMcpServer { | |
| source: extension_source("hosted"), | |
| config: expected, | |
| protocol_mode: None, | |
| }) | |
| ); | |
| } | |
| fn disabled_discovered_plugin_remains_a_veto_for_runtime_overlays() { | |
| let mut disabled = server("https://plugin.example/mcp"); | |
| disabled.enabled = false; | |
| let mut expected = server("https://extension.example/mcp"); | |
| expected.enabled = false; | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_plugin( | |
| "docs".to_string(), | |
| plugin("plugin@test"), | |
| /*plugin_order*/ 0, | |
| disabled, | |
| )); | |
| let mut builder = builder.build().to_builder(); | |
| builder.register(McpServerRegistration::from_extension( | |
| "docs".to_string(), | |
| "hosted", | |
| /*contribution_order*/ 0, | |
| server("https://extension.example/mcp"), | |
| )); | |
| let resolved = builder.build(); | |
| assert_eq!( | |
| resolved.server("docs"), | |
| Some(&super::ResolvedMcpServer { | |
| source: extension_source("hosted"), | |
| config: expected, | |
| protocol_mode: None, | |
| }) | |
| ); | |
| } | |
| fn earlier_plugin_wins_with_an_explicit_conflict() { | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_plugin( | |
| "docs".to_string(), | |
| plugin("alpha@test"), | |
| /*plugin_order*/ 0, | |
| server("https://alpha.example/mcp"), | |
| )); | |
| builder.register(McpServerRegistration::from_plugin( | |
| "docs".to_string(), | |
| plugin("beta@test"), | |
| /*plugin_order*/ 1, | |
| server("https://beta.example/mcp"), | |
| )); | |
| let catalog = builder.build(); | |
| assert_eq!( | |
| catalog.plugin_attributions_by_server_name(), | |
| HashMap::from([("docs".to_string(), plugin("alpha@test"))]) | |
| ); | |
| assert_eq!( | |
| catalog.conflicts(), | |
| &[McpServerConflict { | |
| name: "docs".to_string(), | |
| outcome: register(plugin_source("alpha@test")), | |
| contenders: vec![ | |
| register(plugin_source("beta@test")), | |
| register(plugin_source("alpha@test")), | |
| ], | |
| }] | |
| ); | |
| } | |
| fn selected_plugins_override_discovered_plugins_but_not_config() { | |
| let selected = server("https://selected-alpha.example/mcp"); | |
| let mut discovered = server("https://local.example/mcp"); | |
| discovered.enabled = false; | |
| discovered.default_tools_approval_mode = Some(AppToolApproval::Auto); | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_plugin( | |
| "docs".to_string(), | |
| plugin("local@test"), | |
| /*plugin_order*/ 0, | |
| discovered, | |
| )); | |
| builder.register(McpServerRegistration::from_selected_plugin( | |
| "docs".to_string(), | |
| plugin("selected-beta"), | |
| /*selection_order*/ 1, | |
| server("https://selected-beta.example/mcp"), | |
| )); | |
| builder.register(McpServerRegistration::from_selected_plugin( | |
| "docs".to_string(), | |
| plugin("selected-alpha"), | |
| /*selection_order*/ 0, | |
| selected.clone(), | |
| )); | |
| let catalog = builder.build(); | |
| assert_eq!( | |
| catalog.server("docs"), | |
| Some(&super::ResolvedMcpServer { | |
| source: selected_plugin_source("selected-alpha"), | |
| config: selected, | |
| protocol_mode: None, | |
| }) | |
| ); | |
| assert_eq!( | |
| catalog.plugin_attributions_by_server_name(), | |
| HashMap::from([("docs".to_string(), plugin("selected-alpha"))]) | |
| ); | |
| assert_eq!( | |
| catalog.conflicts(), | |
| &[McpServerConflict { | |
| name: "docs".to_string(), | |
| outcome: register(selected_plugin_source("selected-alpha")), | |
| contenders: vec![ | |
| register(selected_plugin_source("selected-beta")), | |
| register(selected_plugin_source("selected-alpha")), | |
| ], | |
| }] | |
| ); | |
| let refreshed = server("https://refreshed.example/mcp"); | |
| let catalog = | |
| catalog.with_materialized_servers(HashMap::from([("docs".to_string(), refreshed.clone())])); | |
| assert_eq!( | |
| catalog.server("docs"), | |
| Some(&super::ResolvedMcpServer { | |
| source: selected_plugin_source("selected-alpha"), | |
| config: refreshed, | |
| protocol_mode: None, | |
| }) | |
| ); | |
| let mut builder = catalog.to_builder(); | |
| let configured = server("https://config.example/mcp"); | |
| builder.register(McpServerRegistration::from_config( | |
| "docs".to_string(), | |
| configured.clone(), | |
| )); | |
| let catalog = builder.build(); | |
| assert_eq!( | |
| catalog.server("docs"), | |
| Some(&super::ResolvedMcpServer { | |
| source: McpServerSource::Config, | |
| config: configured, | |
| protocol_mode: None, | |
| }) | |
| ); | |
| } | |
| fn disabled_selected_plugin_does_not_veto_runtime_overlays() { | |
| let mut disabled = server("https://selected.example/mcp"); | |
| disabled.enabled = false; | |
| let extension = server("https://extension.example/mcp"); | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_selected_plugin( | |
| "docs".to_string(), | |
| plugin("selected"), | |
| /*selection_order*/ 0, | |
| disabled, | |
| )); | |
| let mut builder = builder.build().to_builder(); | |
| builder.register(McpServerRegistration::from_extension( | |
| "docs".to_string(), | |
| "hosted", | |
| /*contribution_order*/ 0, | |
| extension.clone(), | |
| )); | |
| let resolved = builder.build(); | |
| assert_eq!( | |
| resolved.server("docs"), | |
| Some(&super::ResolvedMcpServer { | |
| source: extension_source("hosted"), | |
| config: extension, | |
| protocol_mode: None, | |
| }) | |
| ); | |
| } | |
| fn equal_precedence_uses_insertion_order_not_source_identity() { | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_compatibility( | |
| "docs".to_string(), | |
| "z-first", | |
| server("https://first.example/mcp"), | |
| )); | |
| builder.register(McpServerRegistration::from_compatibility( | |
| "docs".to_string(), | |
| "a-second", | |
| server("https://second.example/mcp"), | |
| )); | |
| let catalog = builder.build(); | |
| assert_eq!( | |
| catalog.server("docs"), | |
| Some(&super::ResolvedMcpServer { | |
| source: compatibility_source("a-second"), | |
| config: server("https://second.example/mcp"), | |
| protocol_mode: None, | |
| }) | |
| ); | |
| let mut builder = catalog.to_builder(); | |
| builder.remove_compatibility("docs".to_string(), "remove-last"); | |
| let catalog = builder.build(); | |
| assert_eq!(catalog.server("docs"), None); | |
| assert_eq!( | |
| catalog.conflicts(), | |
| &[McpServerConflict { | |
| name: "docs".to_string(), | |
| outcome: remove(compatibility_source("remove-last")), | |
| contenders: vec![ | |
| register(compatibility_source("z-first")), | |
| register(compatibility_source("a-second")), | |
| remove(compatibility_source("remove-last")), | |
| ], | |
| }] | |
| ); | |
| } | |
| fn extension_protocol_mode_follows_the_winner_through_materialization() { | |
| let config = server("https://apps.example/mcp"); | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register( | |
| McpServerRegistration::from_extension( | |
| "apps".to_string(), | |
| "loser", | |
| /*contribution_order*/ 0, | |
| config.clone(), | |
| ) | |
| .with_protocol_mode(McpProtocolMode::V20260728), | |
| ); | |
| builder.register(McpServerRegistration::from_extension( | |
| "apps".to_string(), | |
| "winner", | |
| /*contribution_order*/ 1, | |
| config.clone(), | |
| )); | |
| let without_override = builder.build(); | |
| assert_eq!( | |
| without_override | |
| .server("apps") | |
| .and_then(ResolvedMcpServer::protocol_mode), | |
| None | |
| ); | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register( | |
| McpServerRegistration::from_extension( | |
| "apps".to_string(), | |
| "winner", | |
| /*contribution_order*/ 1, | |
| config, | |
| ) | |
| .with_protocol_mode(McpProtocolMode::Legacy), | |
| ); | |
| let with_override = builder.build(); | |
| assert!(!without_override.has_same_servers(&with_override)); | |
| let refreshed = server("https://refreshed.example/mcp"); | |
| let materialized = with_override | |
| .with_materialized_servers(HashMap::from([("apps".to_string(), refreshed.clone())])); | |
| assert_eq!( | |
| materialized.server("apps"), | |
| Some(&ResolvedMcpServer { | |
| source: extension_source("winner"), | |
| config: refreshed, | |
| protocol_mode: Some(McpProtocolMode::Legacy), | |
| }) | |
| ); | |
| } | |
| fn environment_policy_exempts_only_explicitly_host_owned_apps() { | |
| let policy = EnvironmentMcpPolicy { | |
| servers: Some(BTreeMap::new()), | |
| plugins: None, | |
| }; | |
| for (registration, expected) in [ | |
| ( | |
| McpServerRegistration::from_extension( | |
| CODEX_APPS_MCP_SERVER_NAME.to_string(), | |
| "apps", | |
| /*contribution_order*/ 0, | |
| server("https://apps.example/mcp"), | |
| ), | |
| false, | |
| ), | |
| ( | |
| McpServerRegistration::from_hosted_apps( | |
| "apps", | |
| /*contribution_order*/ 0, | |
| server("https://apps.example/mcp"), | |
| ), | |
| true, | |
| ), | |
| ] { | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(registration); | |
| let catalog = builder | |
| .build_with_environment_authority(|_| McpEnvironmentAuthority::Restricted(&policy)); | |
| assert_eq!( | |
| catalog | |
| .server(CODEX_APPS_MCP_SERVER_NAME) | |
| .expect("Apps registration") | |
| .config() | |
| .enabled, | |
| expected | |
| ); | |
| } | |
| } | |
| fn environment_policy_preserves_selected_plugin_and_empty_server_allowlist_semantics() { | |
| let mut builder = ResolvedMcpCatalog::builder(); | |
| builder.register(McpServerRegistration::from_selected_plugin( | |
| "selected".to_string(), | |
| plugin("selected-plugin"), | |
| /*selection_order*/ 0, | |
| server("https://plugin.example/mcp"), | |
| )); | |
| let metadata_only_policy = EnvironmentMcpPolicy { | |
| servers: None, | |
| plugins: Some(BTreeMap::from([( | |
| "metadata-only-plugin".to_string(), | |
| PluginMcpRequirements { mcp_servers: None }, | |
| )])), | |
| }; | |
| let deny_all_policy = EnvironmentMcpPolicy { | |
| servers: Some(BTreeMap::new()), | |
| plugins: None, | |
| }; | |
| for (policy, expected) in [(&metadata_only_policy, true), (&deny_all_policy, false)] { | |
| let resolved = builder | |
| .clone() | |
| .build_with_environment_authority(|_| McpEnvironmentAuthority::Restricted(policy)); | |
| assert_eq!( | |
| resolved | |
| .server("selected") | |
| .expect("selected plugin") | |
| .config() | |
| .enabled, | |
| expected | |
| ); | |
| } | |
| } | |