fix(mcp): correct Codex MCP configuration format to [mcp_servers]

BREAKING CHANGE: The [mcp.servers] format was completely incorrect and not
any official Codex format. The only correct format is [mcp_servers] at the
top level of config.toml.

Changes:
- Remove incorrect [mcp.servers] nested table support
- Always use [mcp_servers] top-level table (official Codex format)
- Auto-migrate and cleanup erroneous [mcp.servers] entries on write
- Preserve error-tolerant import for migrating old incorrect configs
- Simplify sync logic by removing format selection branches (~60 lines)
- Update all documentation and tests to reflect correct format
- Add warning logs when detecting and cleaning incorrect format

Backend (Rust):
- mcp.rs: Simplify sync_enabled_to_codex by removing Target enum
- mcp.rs: sync_single_server_to_codex now always uses [mcp_servers]
- mcp.rs: remove_server_from_codex cleans both locations
- mcp.rs: Update import_from_codex comments to clarify format status
- tests: Rename test to sync_enabled_to_codex_migrates_erroneous_*
- tests: Update assertions to verify migration behavior

Frontend (TypeScript):
- tomlUtils.ts: Prioritize [mcp_servers] format in parsing
- tomlUtils.ts: Update error messages to guide correct format

Documentation:
- README.md: Correct MCP format reference to [mcp_servers]
- CLAUDE.md: Add comprehensive format specification with examples

All 79 tests pass. This ensures backward compatibility while enforcing
the correct Codex official standard going forward.

Refs: https://github.com/openai/codex/issues/3441
This commit is contained in:
Jason
2025-11-17 22:57:04 +08:00
parent 3051743bd3
commit 67bd8f5c11
10 changed files with 147 additions and 167 deletions

View File

@@ -127,8 +127,14 @@ fn import_mcp_from_claude_creates_config_and_enables_servers() {
let guard = state.config.read().expect("lock config");
// v3.7.0: 检查统一结构
let servers = guard.mcp.servers.as_ref().expect("unified servers should exist");
let entry = servers.get("echo").expect("server imported into unified structure");
let servers = guard
.mcp
.servers
.as_ref()
.expect("unified servers should exist");
let entry = servers
.get("echo")
.expect("server imported into unified structure");
assert!(
entry.apps.claude,
"imported server should have Claude app enabled"
@@ -182,10 +188,12 @@ fn set_mcp_enabled_for_codex_writes_live_config() {
// 创建 Codex 配置目录和文件
let codex_dir = home.join(".codex");
fs::create_dir_all(&codex_dir).expect("create codex dir");
fs::write(codex_dir.join("auth.json"), r#"{"OPENAI_API_KEY":"test-key"}"#)
.expect("create auth.json");
fs::write(codex_dir.join("config.toml"), "")
.expect("create empty config.toml");
fs::write(
codex_dir.join("auth.json"),
r#"{"OPENAI_API_KEY":"test-key"}"#,
)
.expect("create auth.json");
fs::write(codex_dir.join("config.toml"), "").expect("create empty config.toml");
let mut config = MultiAppConfig::default();
config.ensure_app(&AppType::Codex);
@@ -203,7 +211,7 @@ fn set_mcp_enabled_for_codex_writes_live_config() {
}),
apps: McpApps {
claude: false,
codex: false, // 初始未启用
codex: false, // 初始未启用
gemini: false,
},
description: None,