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                mcp::sync_single_server_to_openclaw(cfg, &server.id, &server.server)?;
168            }
169            AppType::Hermes => {
170                mcp::sync_single_server_to_hermes(cfg, &server.id, &server.server)?;
171            }
172        }
173        Ok(())
174    }
175
176    /// 从所有曾启用过该服务器的应用中移除
177    fn remove_server_from_all_apps(
178        state: &AppState,
179        id: &str,
180        server: &McpServer,
181    ) -> Result<(), AppError> {
182        // 从所有曾启用的应用中移除
183        for app in server.apps.enabled_apps() {
184            Self::remove_server_from_app(state, id, &app)?;
185        }
186        Ok(())
187    }
188
189    fn remove_server_from_app(_state: &AppState, id: &str, app: &AppType) -> Result<(), AppError> {
190        match app {
191            AppType::Claude => mcp::remove_server_from_claude(id)?,
192            AppType::Codex => mcp::remove_server_from_codex(id)?,
193            AppType::Gemini => mcp::remove_server_from_gemini(id)?,
194            AppType::OpenCode => mcp::remove_server_from_opencode(id)?,
195            AppType::OpenClaw => mcp::remove_server_from_openclaw(id)?,
196            AppType::Hermes => mcp::remove_server_from_hermes(id)?,
197        }
198        Ok(())
199    }
200
201    /// 手动同步所有启用的 MCP 服务器到对应的应用
202    pub fn sync_all_enabled(state: &AppState) -> Result<(), AppError> {
203        let servers = Self::get_all_servers(state)?;
204
205        for app in AppType::all() {
206            for server in servers.values() {
207                if server.apps.is_enabled_for(&app) {
208                    Self::sync_server_to_app(state, server, &app)?;
209                } else {
210                    Self::remove_server_from_app(state, &server.id, &app)?;
211                }
212            }
213        }
214
215        Ok(())
216    }
217
218    // ========================================================================
219    // 兼容层:支持旧的 v3.6.x 命令(已废弃,将在 v4.0 移除)
220    // ========================================================================
221
222    /// [已废弃] 获取指定应用的 MCP 服务器(兼容旧 API)
223    #[deprecated(since = "3.7.0", note = "Use get_all_servers instead")]
224    pub fn get_servers(
225        state: &AppState,
226        app: AppType,
227    ) -> Result<HashMap<String, serde_json::Value>, AppError> {
228        let all_servers = Self::get_all_servers(state)?;
229        let mut result = HashMap::new();
230
231        for (id, server) in all_servers {
232            if server.apps.is_enabled_for(&app) {
233                result.insert(id, server.server);
234            }
235        }
236
237        Ok(result)
238    }
239
240    /// [已废弃] 设置 MCP 服务器在指定应用的启用状态(兼容旧 API)
241    #[deprecated(since = "3.7.0", note = "Use toggle_app instead")]
242    pub fn set_enabled(
243        state: &AppState,
244        app: AppType,
245        id: &str,
246        enabled: bool,
247    ) -> Result<bool, AppError> {
248        Self::toggle_app(state, id, app, enabled)?;
249        Ok(true)
250    }
251
252    /// [已废弃] 同步启用的 MCP 到指定应用(兼容旧 API)
253    #[deprecated(since = "3.7.0", note = "Use sync_all_enabled instead")]
254    pub fn sync_enabled(state: &AppState, app: AppType) -> Result<(), AppError> {
255        let servers = Self::get_all_servers(state)?;
256
257        for server in servers.values() {
258            if server.apps.is_enabled_for(&app) {
259                Self::sync_server_to_app(state, server, &app)?;
260            }
261        }
262
263        Ok(())
264    }
265
266    /// 从 Claude 导入 MCP(v3.7.0 已更新为统一结构)
267    pub fn import_from_claude(state: &AppState) -> Result<usize, AppError> {
268        let mut cfg = state.config.write()?;
269        let count = mcp::import_from_claude(&mut cfg)?;
270        drop(cfg);
271        state.save()?;
272        Ok(count)
273    }
274
275    /// 从 Codex 导入 MCP(v3.7.0 已更新为统一结构)
276    pub fn import_from_codex(state: &AppState) -> Result<usize, AppError> {
277        let mut cfg = state.config.write()?;
278        let count = mcp::import_from_codex(&mut cfg)?;
279        drop(cfg);
280        state.save()?;
281        Ok(count)
282    }
283
284    /// 从 Gemini 导入 MCP(v3.7.0 已更新为统一结构)
285    pub fn import_from_gemini(state: &AppState) -> Result<usize, AppError> {
286        let mut cfg = state.config.write()?;
287        let count = mcp::import_from_gemini(&mut cfg)?;
288        drop(cfg);
289        state.save()?;
290        Ok(count)
291    }
292
293    /// 从 OpenCode 导入 MCP
294    pub fn import_from_opencode(state: &AppState) -> Result<usize, AppError> {
295        let mut cfg = state.config.write()?;
296        let count = mcp::import_from_opencode(&mut cfg)?;
297        drop(cfg);
298        state.save()?;
299        Ok(count)
300    }
301
302    /// 从 OpenClaw 导入 MCP
303    pub fn import_from_openclaw(state: &AppState) -> Result<usize, AppError> {
304        let mut cfg = state.config.write()?;
305        let count = mcp::import_from_openclaw(&mut cfg)?;
306        drop(cfg);
307        state.save()?;
308        Ok(count)
309    }
310
311    /// 从 Hermes 导入 MCP
312    pub fn import_from_hermes(state: &AppState) -> Result<usize, AppError> {
313        let mut cfg = state.config.write()?;
314        let count = mcp::import_from_hermes(&mut cfg)?;
315        drop(cfg);
316        state.save()?;
317        Ok(count)
318    }
319}