Compare commits

...

3 Commits

3 changed files with 126 additions and 0 deletions

View File

@@ -5,6 +5,11 @@ pub fn merge_toml_values(base: &mut TomlValue, overlay: &TomlValue) {
if let TomlValue::Table(overlay_table) = overlay
&& let TomlValue::Table(base_table) = base
{
if overlay_table.is_empty() {
base_table.clear();
return;
}
for (key, value) in overlay_table {
if let Some(existing) = base_table.get_mut(key) {
merge_toml_values(existing, value);
@@ -16,3 +21,75 @@ pub fn merge_toml_values(base: &mut TomlValue, overlay: &TomlValue) {
*base = overlay.clone();
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use toml::toml;
#[test]
fn empty_overlay_table_replaces_existing_table() {
let mut base = TomlValue::Table(toml! {
[mcp_servers.docs]
command = "uvx"
[mcp_servers.logs]
command = "tail"
});
let overlay = TomlValue::Table(toml! {
mcp_servers = {}
});
merge_toml_values(&mut base, &overlay);
assert_eq!(base, TomlValue::Table(toml! { mcp_servers = {} }));
}
#[test]
fn empty_overlay_table_only_clears_targeted_nested_table() {
let mut base = TomlValue::Table(toml! {
[model_providers.default]
name = "provider-a"
[model_providers.default.extra]
endpoint = "https://example-a"
[model_providers.other]
name = "provider-b"
});
let overlay = TomlValue::Table(toml! {
model_providers = { default = {} }
});
merge_toml_values(&mut base, &overlay);
assert_eq!(
base,
TomlValue::Table(toml! {
model_providers = { default = {}, other = { name = "provider-b" } }
})
);
}
#[test]
fn non_empty_overlay_table_keeps_recursive_merge_behavior() {
let mut base = TomlValue::Table(toml! {
[mcp_servers.docs]
command = "uvx"
disabled = false
});
let overlay = TomlValue::Table(toml! {
mcp_servers = { docs = { disabled = true } }
});
merge_toml_values(&mut base, &overlay);
assert_eq!(
base,
TomlValue::Table(toml! {
mcp_servers = { docs = { command = "uvx", disabled = true } }
})
);
}
}

View File

@@ -53,3 +53,40 @@ fn apply_toml_override(root: &mut TomlValue, path: &str, value: TomlValue) {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::merge::merge_toml_values;
use pretty_assertions::assert_eq;
use toml::toml;
#[test]
fn build_cli_overrides_layer_preserves_empty_table_values() {
let cli_overrides = vec![(
"mcp_servers".to_string(),
TomlValue::Table(toml::map::Map::new()),
)];
let layer = build_cli_overrides_layer(&cli_overrides);
assert_eq!(layer, TomlValue::Table(toml! { mcp_servers = {} }));
}
#[test]
fn empty_table_cli_override_clears_existing_map() {
let cli_overrides = vec![(
"mcp_servers".to_string(),
TomlValue::Table(toml::map::Map::new()),
)];
let cli_layer = build_cli_overrides_layer(&cli_overrides);
let mut merged = TomlValue::Table(toml! {
[mcp_servers.docs]
command = "uvx"
});
merge_toml_values(&mut merged, &cli_layer);
assert_eq!(merged, TomlValue::Table(toml! { mcp_servers = {} }));
}
}

View File

@@ -12,6 +12,18 @@ Codex can connect to MCP servers configured in `~/.codex/config.toml`. See the c
- https://developers.openai.com/codex/config-reference
### Clearing map-valued settings from the CLI
`--config` / `-c` overrides can clear map-valued settings by assigning an explicit empty table.
For example, to disable all configured MCP servers for a single run:
```bash
codex --config 'mcp_servers={}'
```
This replacement behavior is generic, so any map-valued config key can be reset the same way.
## Apps (Connectors)
Use `$` in the composer to insert a ChatGPT connector; the popover lists accessible