diff --git a/codex-rs/app-server/src/config_manager_service.rs b/codex-rs/app-server/src/config_manager_service.rs index 8c8ff227e2..8509aa648d 100644 --- a/codex-rs/app-server/src/config_manager_service.rs +++ b/codex-rs/app-server/src/config_manager_service.rs @@ -20,7 +20,6 @@ use codex_config::ConfigLayerStackOrdering; use codex_config::ConfigRequirementsToml; use codex_config::config_toml::ConfigToml; use codex_config::merge_toml_values; -use codex_config::validate_config_toml_layer; use codex_core::config::deserialize_config_toml_with_base; use codex_core::config::edit::ConfigEdit; use codex_core::config::edit::ConfigEditsBuilder; @@ -294,25 +293,23 @@ impl ConfigManager { parsed_segments.push(segments); } - validate_config_toml_layer(user_config.clone(), self.codex_home()).map_err(|err| { + // MCP server entries may be completed by lower-precedence config layers, + // while feature requirements must still be checked against the user's + // explicit settings before the write is persisted. + let mut user_config_for_feature_validation = user_config.clone(); + if let Some(table) = user_config_for_feature_validation.as_table_mut() { + table.remove("mcp_servers"); + } + let user_config_toml = deserialize_config_toml_with_base( + user_config_for_feature_validation, + self.codex_home(), + ) + .map_err(|err| { ConfigManagerError::write( ConfigWriteErrorCode::ConfigValidationError, format!("Invalid configuration: {err}"), ) })?; - - let mut user_config_without_mcp_servers = user_config.clone(); - if let Some(table) = user_config_without_mcp_servers.as_table_mut() { - table.remove("mcp_servers"); - } - let user_config_toml = - deserialize_config_toml_with_base(user_config_without_mcp_servers, self.codex_home()) - .map_err(|err| { - ConfigManagerError::write( - ConfigWriteErrorCode::ConfigValidationError, - format!("Invalid configuration: {err}"), - ) - })?; validate_feature_requirements_for_config_toml( &user_config_toml, layers.requirements().feature_requirements.as_ref(), diff --git a/codex-rs/config/src/lib.rs b/codex-rs/config/src/lib.rs index f857e1d7a0..1cbbbaf0a9 100644 --- a/codex-rs/config/src/lib.rs +++ b/codex-rs/config/src/lib.rs @@ -147,7 +147,6 @@ pub use state::ConfigLayerStackOrdering; pub use state::ConfigLoadOptions; pub use state::LoaderOverrides; pub use strict_config::config_error_from_ignored_toml_fields; -pub use strict_config::validate_config_toml_layer; pub use thread_config::NoopThreadConfigLoader; pub use thread_config::RemoteThreadConfigLoader; pub use thread_config::SessionThreadConfig; diff --git a/codex-rs/config/src/strict_config.rs b/codex-rs/config/src/strict_config.rs index 0ca82203b2..8e246ab197 100644 --- a/codex-rs/config/src/strict_config.rs +++ b/codex-rs/config/src/strict_config.rs @@ -10,7 +10,6 @@ use crate::diagnostics::span_for_config_path; use crate::diagnostics::span_for_toml_key_path; use crate::diagnostics::text_range_from_span; use codex_features::is_known_feature_key; -use codex_utils_absolute_path::AbsolutePathBufGuard; use serde::Deserialize; use serde::de::DeserializeOwned; use std::collections::HashMap; @@ -60,26 +59,6 @@ pub(crate) fn config_error_from_config_toml_layer_for_source_name( ) } -/// Validate one config layer without requiring an MCP server fragment to -/// contain a transport. MCP server definitions are merged recursively across -/// config layers, so transport completeness is validated on the effective -/// config after composition. -pub fn validate_config_toml_layer( - mut value: TomlValue, - base_dir: &Path, -) -> Result<(), toml::de::Error> { - let _guard = AbsolutePathBufGuard::new(base_dir); - let mcp_servers = value - .as_table_mut() - .and_then(|table| table.remove("mcp_servers")); - - let _: ConfigToml = value.try_into()?; - if let Some(mcp_servers) = mcp_servers { - let _: ConfigTomlMcpServersLayer = mcp_servers_layer(mcp_servers).try_into()?; - } - Ok(()) -} - /// Strictly validate one config layer while allowing MCP server fragments to /// receive their transport from a lower-precedence layer. fn config_error_from_config_toml_layer_for_source(