From 6d2ee2472f9c97c93265718c969a0a2b00e29c7f Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 8 Jul 2026 16:29:37 +0800 Subject: [PATCH] fix(provider): re-project Codex MCP after unified-session toggle rewrites live config Toggling unify_codex_session_history rewrites the current official provider's live config.toml in full (intended design), which drops the [mcp_servers] projection -- and nothing put it back, so enabled MCP servers silently vanished until the next provider switch (#C2). Re-project after the rewrite, with two deliberate choices: - Project Codex only (new McpService::sync_enabled_for_app) instead of sync_all_enabled: the all-apps sync short-circuits in AppType::all() order, so a corrupt ~/.claude.json would error before Codex is ever reached and the freshly wiped [mcp_servers] would stay missing. Only Codex's live file was rewritten here, so only Codex needs re-projection. - Degrade projection failure to a warning: by this point the live file already carries the new bucket state, so the toggle has taken effect. Propagating the error would make save_settings roll back the setting, creating the exact "setting=old, live=new bucket" session split the rollback exists to prevent. The projection self-heals on the next switch or any MCP toggle. --- src-tauri/src/lib.rs | 1 + src-tauri/src/services/mcp.rs | 37 +++-- src-tauri/src/services/provider/mod.rs | 12 ++ src-tauri/tests/provider_service.rs | 197 +++++++++++++++++++++++++ 4 files changed, 238 insertions(+), 9 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 98ff01073..38ac49e5c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -55,6 +55,7 @@ pub use prompt::Prompt; pub use provider::{Provider, ProviderMeta}; pub use services::{ profile::{ProfilePayload, ProfileScope, ProfileService}, + provider::reapply_current_codex_official_live, skill::{migrate_skills_to_ssot, ImportSkillSelection}, ConfigService, EndpointLatency, McpService, PromptService, ProviderService, ProxyService, SkillService, SpeedtestService, diff --git a/src-tauri/src/services/mcp.rs b/src-tauri/src/services/mcp.rs index ed07c56fe..67bb2b66e 100644 --- a/src-tauri/src/services/mcp.rs +++ b/src-tauri/src/services/mcp.rs @@ -181,16 +181,35 @@ impl McpService { let servers = Self::get_all_servers(state)?; for app in AppType::all() { - if matches!(app, AppType::OpenClaw | AppType::ClaudeDesktop) { - continue; - } + Self::project_servers_to_app(state, &servers, &app)?; + } - for server in servers.values() { - if server.apps.is_enabled_for(&app) { - Self::sync_server_to_app(state, server, &app)?; - } else { - Self::remove_server_from_app(state, &server.id, &app)?; - } + Ok(()) + } + + /// 只把启用状态投影到单个应用。某个应用的 live 被整体重写后用它做 + /// 定向重投影:sync_all_enabled 按 AppType::all() 顺序逐应用短路, + /// 排在前面的无关应用 live 损坏(如 ~/.claude.json 坏 JSON)会阻断 + /// 后面应用的投影。 + pub fn sync_enabled_for_app(state: &AppState, app: &AppType) -> Result<(), AppError> { + let servers = Self::get_all_servers(state)?; + Self::project_servers_to_app(state, &servers, app) + } + + fn project_servers_to_app( + state: &AppState, + servers: &IndexMap, + app: &AppType, + ) -> Result<(), AppError> { + if matches!(app, AppType::OpenClaw | AppType::ClaudeDesktop) { + return Ok(()); + } + + for server in servers.values() { + if server.apps.is_enabled_for(app) { + Self::sync_server_to_app(state, server, app)?; + } else { + Self::remove_server_from_app(state, &server.id, app)?; } } diff --git a/src-tauri/src/services/provider/mod.rs b/src-tauri/src/services/provider/mod.rs index 6d3368c1a..c804db291 100644 --- a/src-tauri/src/services/provider/mod.rs +++ b/src-tauri/src/services/provider/mod.rs @@ -81,6 +81,18 @@ pub fn reapply_current_codex_official_live(state: &AppState) -> Result