Skip to main content

cc_switch_lib/services/
mcp.rs

1use std::collections::HashMap;
2
3use crate::app_config::{AppType, McpServer, MultiAppConfig};
4use crate::error::AppError;
5use crate::mcp;
6use crate::store::AppState;
7
8/// MCP 相关业务逻辑(v3.7.0 统一结构)
9pub struct McpService;
10
11impl McpService {
12    /// 获取所有 MCP 服务器(统一结构)
13    pub fn get_all_servers(state: &AppState) -> Result<HashMap<String, McpServer>, AppError> {
14        let cfg = state.config.read()?;
15
16        // 如果是新结构,直接返回
17        if let Some(servers) = &cfg.mcp.servers {
18            return Ok(servers.clone());
19        }
20
21        // 理论上不应该走到这里,因为 load 时会自动迁移
22        Err(AppError::localized(
23            "mcp.old_structure",
24            "检测到旧版 MCP 结构,请重启应用完成迁移",
25            "Old MCP structure detected, please restart app to complete migration",
26        ))
27    }
28
29    /// 添加或更新 MCP 服务器
30    pub fn upsert_server(state: &AppState, server: McpServer) -> Result<(), AppError> {
31        let (server_id, apps_to_remove) = {
32            let mut cfg = state.config.write()?;
33
34            let servers = cfg.mcp.servers.get_or_insert_with(HashMap::new);
35            let server_id = server.id.clone();
36
37            let apps_to_remove = servers
38                .get(&server_id)
39                .map(|existing| {
40                    existing
41                        .apps
42                        .enabled_apps()
43                        .into_iter()
44                        .filter(|app| !server.apps.is_enabled_for(app))
45                        .collect::<Vec<_>>()
46                })
47                .unwrap_or_default();
48
49            // 插入或更新
50            servers.insert(server_id.clone(), server.clone());
51
52            (server_id, apps_to_remove)
53        };
54
55        state.save()?;
56
57        // 如果是更新:对“由启用变为禁用”的应用,清理对应 live 配置
58        for app in apps_to_remove {
59            Self::remove_server_from_app(state, &server_id, &app)?;
60        }
61
62        // 同步到各个启用的应用
63        Self::sync_server_to_apps(state, &server)?;
64
65        Ok(())
66    }
67
68    /// 删除 MCP 服务器
69    pub fn delete_server(state: &AppState, id: &str) -> Result<bool, AppError> {
70        let server = {
71            let mut cfg = state.config.write()?;
72
73            if let Some(servers) = &mut cfg.mcp.servers {
74                servers.remove(id)
75            } else {
76                None
77            }
78        };
79
80        if let Some(server) = server {
81            state.save()?;
82
83            // 从所有应用的 live 配置中移除
84            Self::remove_server_from_all_apps(state, id, &server)?;
85            Ok(true)
86        } else {
87            Ok(false)
88        }
89    }
90
91    /// 切换指定应用的启用状态
92    pub fn toggle_app(
93        state: &AppState,
94        server_id: &str,
95        app: AppType,
96        enabled: bool,
97    ) -> Result<(), AppError> {
98        let server = {
99            let mut cfg = state.config.write()?;
100
101            if let Some(servers) = &mut cfg.mcp.servers {
102                if let Some(server) = servers.get_mut(server_id) {
103                    server.apps.set_enabled_for(&app, enabled);
104                    Some(server.clone())
105                } else {
106                    None
107                }
108            } else {
109                None
110            }
111        };
112
113        if let Some(server) = server {
114            state.save()?;
115
116            // 同步到对应应用
117            if enabled {
118                Self::sync_server_to_app(state, &server, &app)?;
119            } else {
120                Self::remove_server_from_app(state, server_id, &app)?;
121            }
122        }
123
124        Ok(())
125    }
126
127    /// 将 MCP 服务器同步到所有启用的应用
128    fn sync_server_to_apps(state: &AppState, server: &McpServer) -> Result<(), AppError> {
129        let cfg = state.config.read()?;
130
131        for app in server.apps.enabled_apps() {
132            Self::sync_server_to_app_internal(&cfg, server, &app)?;
133        }
134
135        Ok(())
136    }
137
138    /// 将 MCP 服务器同步到指定应用
139    fn sync_server_to_app(
140        state: &AppState,
141        server: &McpServer,
142        app: &AppType,
143    ) -> Result<(), AppError> {
144        let cfg = state.config.read()?;
145        Self::sync_server_to_app_internal(&cfg, server, app)
146    }
147
148    fn sync_server_to_app_internal(
149        cfg: &MultiAppConfig,
150        server: &McpServer,
151        app: &AppType,
152    ) -> Result<(), AppError> {
153        match app {
154            AppType::Claude => {
155                mcp::sync_single_server_to_claude(cfg, &server.id, &server.server)?;
156            }
157            AppType::Codex => {
158                mcp::sync_single_server_to_codex(cfg, &server.id, &server.server)?;
159            }
160            AppType::Gemini => {
161                mcp::sync_single_server_to_gemini(cfg, &server.id, &server.server)?;
162            }
163            AppType::OpenCode => {
164                mcp::sync_single_server_to_opencode(cfg, &server.id, &server.server)?;
165            }
166            AppType::OpenClaw => {}
167            AppType::Hermes => {
168                mcp::sync_single_server_to_hermes(cfg, &server.id, &server.server)?;
169            }
170        }
171        Ok(())
172    }
173
174    /// 从所有曾启用过该服务器的应用中移除
175    fn remove_server_from_all_apps(
176        state: &AppState,
177        id: &str,
178        server: &McpServer,
179    ) -> Result<(), AppError> {
180        // 从所有曾启用的应用中移除
181        for app in server.apps.enabled_apps() {
182            Self::remove_server_from_app(state, id, &app)?;
183        }
184        Ok(())
185    }
186
187    fn remove_server_from_app(_state: &AppState, id: &str, app: &AppType) -> Result<(), AppError> {
188        match app {
189            AppType::Claude => mcp::remove_server_from_claude(id)?,
190            AppType::Codex => mcp::remove_server_from_codex(id)?,
191            AppType::Gemini => mcp::remove_server_from_gemini(id)?,
192            AppType::OpenCode => mcp::remove_server_from_opencode(id)?,
193            AppType::OpenClaw => {}
194            AppType::Hermes => mcp::remove_server_from_hermes(id)?,
195        }
196        Ok(())
197    }
198
199    /// 手动同步所有启用的 MCP 服务器到对应的应用
200    pub fn sync_all_enabled(state: &AppState) -> Result<(), AppError> {
201        let servers = Self::get_all_servers(state)?;
202
203        for app in AppType::all() {
204            if matches!(app, AppType::OpenClaw) {
205                continue;
206            }
207
208            for server in servers.values() {
209                if server.apps.is_enabled_for(&app) {
210                    Self::sync_server_to_app(state, server, &app)?;
211                } else {
212                    Self::remove_server_from_app(state, &server.id, &app)?;
213                }
214            }
215        }
216
217        Ok(())
218    }
219
220    // ========================================================================
221    // 兼容层:支持旧的 v3.6.x 命令(已废弃,将在 v4.0 移除)
222    // ========================================================================
223
224    /// [已废弃] 获取指定应用的 MCP 服务器(兼容旧 API)
225    #[deprecated(since = "3.7.0", note = "Use get_all_servers instead")]
226    pub fn get_servers(
227        state: &AppState,
228        app: AppType,
229    ) -> Result<HashMap<String, serde_json::Value>, AppError> {
230        let all_servers = Self::get_all_servers(state)?;
231        let mut result = HashMap::new();
232
233        for (id, server) in all_servers {
234            if server.apps.is_enabled_for(&app) {
235                result.insert(id, server.server);
236            }
237        }
238
239        Ok(result)
240    }
241
242    /// [已废弃] 设置 MCP 服务器在指定应用的启用状态(兼容旧 API)
243    #[deprecated(since = "3.7.0", note = "Use toggle_app instead")]
244    pub fn set_enabled(
245        state: &AppState,
246        app: AppType,
247        id: &str,
248        enabled: bool,
249    ) -> Result<bool, AppError> {
250        Self::toggle_app(state, id, app, enabled)?;
251        Ok(true)
252    }
253
254    /// [已废弃] 同步启用的 MCP 到指定应用(兼容旧 API)
255    #[deprecated(since = "3.7.0", note = "Use sync_all_enabled instead")]
256    pub fn sync_enabled(state: &AppState, app: AppType) -> Result<(), AppError> {
257        let servers = Self::get_all_servers(state)?;
258
259        for server in servers.values() {
260            if server.apps.is_enabled_for(&app) {
261                Self::sync_server_to_app(state, server, &app)?;
262            }
263        }
264
265        Ok(())
266    }
267
268    /// 从 Claude 导入 MCP(v3.7.0 已更新为统一结构)
269    pub fn import_from_claude(state: &AppState) -> Result<usize, AppError> {
270        let mut cfg = state.config.write()?;
271        let count = mcp::import_from_claude(&mut cfg)?;
272        drop(cfg);
273        state.save()?;
274        Ok(count)
275    }
276
277    /// 从 Codex 导入 MCP(v3.7.0 已更新为统一结构)
278    pub fn import_from_codex(state: &AppState) -> Result<usize, AppError> {
279        let mut cfg = state.config.write()?;
280        let count = mcp::import_from_codex(&mut cfg)?;
281        drop(cfg);
282        state.save()?;
283        Ok(count)
284    }
285
286    /// 从 Gemini 导入 MCP(v3.7.0 已更新为统一结构)
287    pub fn import_from_gemini(state: &AppState) -> Result<usize, AppError> {
288        let mut cfg = state.config.write()?;
289        let count = mcp::import_from_gemini(&mut cfg)?;
290        drop(cfg);
291        state.save()?;
292        Ok(count)
293    }
294
295    /// 从 OpenCode 导入 MCP
296    pub fn import_from_opencode(state: &AppState) -> Result<usize, AppError> {
297        let mut cfg = state.config.write()?;
298        let count = mcp::import_from_opencode(&mut cfg)?;
299        drop(cfg);
300        state.save()?;
301        Ok(count)
302    }
303
304    /// 从 Hermes 导入 MCP
305    pub fn import_from_hermes(state: &AppState) -> Result<usize, AppError> {
306        let mut cfg = state.config.write()?;
307        let count = mcp::import_from_hermes(&mut cfg)?;
308        drop(cfg);
309        state.save()?;
310        Ok(count)
311    }
312}