refactor(mcp): complete v3.7.0 cleanup - remove legacy code and warnings

This commit finalizes the v3.7.0 unified MCP architecture migration by
removing all deprecated code paths and eliminating compiler warnings.

Frontend Changes (~950 lines removed):
- Remove deprecated components: McpPanel, McpListItem, McpToggle
- Remove deprecated hook: useMcpActions
- Remove unused API methods: importFrom*, syncEnabledTo*, syncAllServers
- Simplify McpFormModal by removing dual-mode logic (unified/legacy)
- Remove syncOtherSide checkbox and conflict detection
- Clean up unused imports and state variables
- Delete associated test files

Backend Changes (~400 lines cleaned):
- Remove unused Tauri commands: import_mcp_from_*, sync_enabled_mcp_to_*
- Delete unused Gemini MCP functions: get_mcp_status, upsert/delete_mcp_server
- Add #[allow(deprecated)] to compatibility layer commands
- Add #[allow(dead_code)] to legacy helper functions for future migration
- Simplify boolean expression in mcp.rs per Clippy suggestion

API Deprecation:
- Mark legacy APIs with @deprecated JSDoc (getConfig, upsertServerInConfig, etc.)
- Preserve backward compatibility for v3.x, planned removal in v4.0

Verification:
-  Zero TypeScript errors (pnpm typecheck)
-  Zero Clippy warnings (cargo clippy)
-  All code formatted (prettier + cargo fmt)
-  Builds successfully

Total cleanup: ~1,350 lines of code removed/marked
Breaking changes: None (all legacy APIs still functional)
This commit is contained in:
Jason
2025-11-14 22:43:25 +08:00
parent fafca841cb
commit 2f18d6ec00
19 changed files with 100 additions and 1420 deletions

View File

@@ -242,11 +242,7 @@ pub fn read_mcp_servers_map() -> Result<std::collections::HashMap<String, Value>
let servers = root
.get("mcpServers")
.and_then(|v| v.as_object())
.map(|obj| {
obj.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
})
.map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
.unwrap_or_default();
Ok(servers)

View File

@@ -141,7 +141,10 @@ pub async fn open_app_config_folder(handle: AppHandle) -> Result<bool, String> {
pub async fn get_claude_common_config_snippet(
state: tauri::State<'_, crate::store::AppState>,
) -> Result<Option<String>, String> {
let guard = state.config.read().map_err(|e| format!("读取配置锁失败: {e}"))?;
let guard = state
.config
.read()
.map_err(|e| format!("读取配置锁失败: {e}"))?;
Ok(guard.claude_common_config_snippet.clone())
}

View File

@@ -50,6 +50,7 @@ pub struct McpConfigResponse {
use std::str::FromStr;
#[tauri::command]
#[allow(deprecated)] // 兼容层命令,内部调用已废弃的 Service 方法
pub async fn get_mcp_config(
state: State<'_, AppState>,
app: String,
@@ -101,7 +102,8 @@ pub async fn upsert_mcp_server_in_config(
apps.set_enabled_for(&app_ty, true);
// 尝试从 spec 中提取 name否则使用 id
let name = spec.get("name")
let name = spec
.get("name")
.and_then(|v| v.as_str())
.unwrap_or(&id)
.to_string();
@@ -142,6 +144,7 @@ pub async fn delete_mcp_server_in_config(
/// 设置启用状态并同步到客户端配置
#[tauri::command]
#[allow(deprecated)] // 兼容层命令,内部调用已废弃的 Service 方法
pub async fn set_mcp_enabled(
state: State<'_, AppState>,
app: String,
@@ -152,48 +155,6 @@ pub async fn set_mcp_enabled(
McpService::set_enabled(&state, app_ty, &id, enabled).map_err(|e| e.to_string())
}
/// 手动同步:将启用的 MCP 投影到 ~/.claude.json
#[tauri::command]
pub async fn sync_enabled_mcp_to_claude(state: State<'_, AppState>) -> Result<bool, String> {
McpService::sync_enabled(&state, AppType::Claude)
.map(|_| true)
.map_err(|e| e.to_string())
}
/// 手动同步:将启用的 MCP 投影到 ~/.codex/config.toml
#[tauri::command]
pub async fn sync_enabled_mcp_to_codex(state: State<'_, AppState>) -> Result<bool, String> {
McpService::sync_enabled(&state, AppType::Codex)
.map(|_| true)
.map_err(|e| e.to_string())
}
/// 从 ~/.claude.json 导入 MCP 定义到 config.json
#[tauri::command]
pub async fn import_mcp_from_claude(state: State<'_, AppState>) -> Result<usize, String> {
McpService::import_from_claude(&state).map_err(|e| e.to_string())
}
/// 从 ~/.codex/config.toml 导入 MCP 定义到 config.json
#[tauri::command]
pub async fn import_mcp_from_codex(state: State<'_, AppState>) -> Result<usize, String> {
McpService::import_from_codex(&state).map_err(|e| e.to_string())
}
/// 手动同步:将启用的 MCP 投影到 ~/.gemini/settings.json
#[tauri::command]
pub async fn sync_enabled_mcp_to_gemini(state: State<'_, AppState>) -> Result<bool, String> {
McpService::sync_enabled(&state, AppType::Gemini)
.map(|_| true)
.map_err(|e| e.to_string())
}
/// 从 ~/.gemini/settings.json 导入 MCP 定义到 config.json
#[tauri::command]
pub async fn import_mcp_from_gemini(state: State<'_, AppState>) -> Result<usize, String> {
McpService::import_from_gemini(&state).map_err(|e| e.to_string())
}
// ============================================================================
// v3.7.0 新增:统一 MCP 管理命令
// ============================================================================
@@ -219,10 +180,7 @@ pub async fn upsert_mcp_server(
/// 删除 MCP 服务器
#[tauri::command]
pub async fn delete_mcp_server(
state: State<'_, AppState>,
id: String,
) -> Result<bool, String> {
pub async fn delete_mcp_server(state: State<'_, AppState>, id: String) -> Result<bool, String> {
McpService::delete_server(&state, &id).map_err(|e| e.to_string())
}
@@ -237,9 +195,3 @@ pub async fn toggle_mcp_app(
let app_ty = AppType::from_str(&app).map_err(|e| e.to_string())?;
McpService::toggle_app(&state, &server_id, app_ty, enabled).map_err(|e| e.to_string())
}
/// 手动同步所有启用的 MCP 服务器到对应的应用
#[tauri::command]
pub async fn sync_all_mcp_servers(state: State<'_, AppState>) -> Result<(), String> {
McpService::sync_all_enabled(&state).map_err(|e| e.to_string())
}

View File

@@ -38,24 +38,6 @@ fn write_json_value(path: &Path, value: &Value) -> Result<(), AppError> {
atomic_write(path, json.as_bytes())
}
/// 获取 Gemini MCP 状态
pub fn get_mcp_status() -> Result<McpStatus, AppError> {
let path = user_config_path();
let (exists, count) = if path.exists() {
let v = read_json_value(&path)?;
let servers = v.get("mcpServers").and_then(|x| x.as_object());
(true, servers.map(|m| m.len()).unwrap_or(0))
} else {
(false, 0)
};
Ok(McpStatus {
user_config_path: path.to_string_lossy().to_string(),
user_config_exists: exists,
server_count: count,
})
}
/// 读取 Gemini MCP 配置文件的完整 JSON 文本
pub fn read_mcp_json() -> Result<Option<String>, AppError> {
let path = user_config_path();
@@ -66,96 +48,7 @@ pub fn read_mcp_json() -> Result<Option<String>, AppError> {
Ok(Some(content))
}
/// 在 Gemini settings.json 中新增或更新一个 MCP 服务器
pub fn upsert_mcp_server(id: &str, spec: Value) -> Result<bool, AppError> {
if id.trim().is_empty() {
return Err(AppError::InvalidInput("MCP 服务器 ID 不能为空".into()));
}
// 基础字段校验(尽量宽松)
if !spec.is_object() {
return Err(AppError::McpValidation(
"MCP 服务器定义必须为 JSON 对象".into(),
));
}
let t_opt = spec.get("type").and_then(|x| x.as_str());
let is_stdio = t_opt.map(|t| t == "stdio").unwrap_or(true); // 兼容缺省(按 stdio 处理)
let is_http = t_opt.map(|t| t == "http").unwrap_or(false);
if !(is_stdio || is_http) {
return Err(AppError::McpValidation(
"MCP 服务器 type 必须是 'stdio' 或 'http'(或省略表示 stdio".into(),
));
}
// stdio 类型必须有 command
if is_stdio {
let cmd = spec.get("command").and_then(|x| x.as_str()).unwrap_or("");
if cmd.is_empty() {
return Err(AppError::McpValidation(
"stdio 类型的 MCP 服务器缺少 command 字段".into(),
));
}
}
// http 类型必须有 url
if is_http {
let url = spec.get("url").and_then(|x| x.as_str()).unwrap_or("");
if url.is_empty() {
return Err(AppError::McpValidation(
"http 类型的 MCP 服务器缺少 url 字段".into(),
));
}
}
let path = user_config_path();
let mut root = if path.exists() {
read_json_value(&path)?
} else {
serde_json::json!({})
};
// 确保 mcpServers 对象存在
{
let obj = root
.as_object_mut()
.ok_or_else(|| AppError::Config("settings.json 根必须是对象".into()))?;
if !obj.contains_key("mcpServers") {
obj.insert("mcpServers".into(), serde_json::json!({}));
}
}
let before = root.clone();
if let Some(servers) = root.get_mut("mcpServers").and_then(|v| v.as_object_mut()) {
servers.insert(id.to_string(), spec);
}
if before == root && path.exists() {
return Ok(false);
}
write_json_value(&path, &root)?;
Ok(true)
}
/// 删除 Gemini settings.json 中的一个 MCP 服务器
pub fn delete_mcp_server(id: &str) -> Result<bool, AppError> {
if id.trim().is_empty() {
return Err(AppError::InvalidInput("MCP 服务器 ID 不能为空".into()));
}
let path = user_config_path();
if !path.exists() {
return Ok(false);
}
let mut root = read_json_value(&path)?;
let Some(servers) = root.get_mut("mcpServers").and_then(|v| v.as_object_mut()) else {
return Ok(false);
};
let existed = servers.remove(id).is_some();
if !existed {
return Ok(false);
}
write_json_value(&path, &root)?;
Ok(true)
}
/// 读取 Gemini settings.json 中的 mcpServers 映射
pub fn read_mcp_servers_map() -> Result<std::collections::HashMap<String, Value>, AppError> {
@@ -168,11 +61,7 @@ pub fn read_mcp_servers_map() -> Result<std::collections::HashMap<String, Value>
let servers = root
.get("mcpServers")
.and_then(|v| v.as_object())
.map(|obj| {
obj.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
})
.map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
.unwrap_or_default();
Ok(servers)

View File

@@ -6,8 +6,8 @@ mod codex_config;
mod commands;
mod config;
mod error;
mod gemini_mcp;
mod gemini_config; // 新增
mod gemini_mcp;
mod init_status;
mod mcp;
mod prompt;
@@ -541,18 +541,11 @@ pub fn run() {
commands::upsert_mcp_server_in_config,
commands::delete_mcp_server_in_config,
commands::set_mcp_enabled,
commands::sync_enabled_mcp_to_claude,
commands::sync_enabled_mcp_to_codex,
commands::sync_enabled_mcp_to_gemini,
commands::import_mcp_from_claude,
commands::import_mcp_from_codex,
commands::import_mcp_from_gemini,
// v3.7.0: Unified MCP management
commands::get_mcp_servers,
commands::upsert_mcp_server,
commands::delete_mcp_server,
commands::toggle_mcp_app,
commands::sync_all_mcp_servers,
// Prompt management
commands::get_prompts,
commands::upsert_prompt,

View File

@@ -41,6 +41,7 @@ fn validate_server_spec(spec: &Value) -> Result<(), AppError> {
Ok(())
}
#[allow(dead_code)] // v3.7.0: 旧的验证逻辑,保留用于未来可能的迁移
fn validate_mcp_entry(entry: &Value) -> Result<(), AppError> {
let obj = entry
.as_object()
@@ -210,6 +211,7 @@ fn collect_enabled_servers(cfg: &McpConfig) -> HashMap<String, Value> {
out
}
#[allow(dead_code)] // v3.7.0: 旧的分应用 API保留用于未来可能的迁移
pub fn get_servers_snapshot_for(
config: &mut MultiAppConfig,
app: &AppType,
@@ -235,6 +237,7 @@ pub fn get_servers_snapshot_for(
(snapshot, normalized)
}
#[allow(dead_code)] // v3.7.0: 旧的分应用 API保留用于未来可能的迁移
pub fn upsert_in_config_for(
config: &mut MultiAppConfig,
app: &AppType,
@@ -273,6 +276,7 @@ pub fn upsert_in_config_for(
Ok(before.is_none())
}
#[allow(dead_code)] // v3.7.0: 旧的分应用 API保留用于未来可能的迁移
pub fn delete_in_config_for(
config: &mut MultiAppConfig,
app: &AppType,
@@ -286,6 +290,7 @@ pub fn delete_in_config_for(
Ok(existed)
}
#[allow(dead_code)] // v3.7.0: 旧的分应用 API保留用于未来可能的迁移
/// 设置启用状态(不执行落盘或文件同步)
pub fn set_enabled_flag_for(
config: &mut MultiAppConfig,
@@ -900,8 +905,8 @@ pub fn sync_single_server_to_codex(
let config_path = crate::codex_config::get_codex_config_path();
let mut doc = if config_path.exists() {
let content = std::fs::read_to_string(&config_path)
.map_err(|e| AppError::io(&config_path, e))?;
let content =
std::fs::read_to_string(&config_path).map_err(|e| AppError::io(&config_path, e))?;
content
.parse::<toml_edit::DocumentMut>()
.map_err(|e| AppError::McpValidation(format!("解析 Codex config.toml 失败: {e}")))?
@@ -915,10 +920,10 @@ pub fn sync_single_server_to_codex(
}
// 确保 [mcp.servers] 子表存在
if !doc["mcp"]
if doc["mcp"]
.as_table()
.and_then(|t| t.get("servers"))
.is_some()
.is_none()
{
doc["mcp"]["servers"] = toml_edit::table();
}
@@ -929,8 +934,7 @@ pub fn sync_single_server_to_codex(
doc["mcp"]["servers"][id] = Item::Table(toml_table);
// 写回文件
std::fs::write(&config_path, doc.to_string())
.map_err(|e| AppError::io(&config_path, e))?;
std::fs::write(&config_path, doc.to_string()).map_err(|e| AppError::io(&config_path, e))?;
Ok(())
}
@@ -943,8 +947,8 @@ pub fn remove_server_from_codex(id: &str) -> Result<(), AppError> {
return Ok(()); // 文件不存在,无需删除
}
let content = std::fs::read_to_string(&config_path)
.map_err(|e| AppError::io(&config_path, e))?;
let content =
std::fs::read_to_string(&config_path).map_err(|e| AppError::io(&config_path, e))?;
let mut doc = content
.parse::<toml_edit::DocumentMut>()
@@ -958,8 +962,7 @@ pub fn remove_server_from_codex(id: &str) -> Result<(), AppError> {
}
// 写回文件
std::fs::write(&config_path, doc.to_string())
.map_err(|e| AppError::io(&config_path, e))?;
std::fs::write(&config_path, doc.to_string()).map_err(|e| AppError::io(&config_path, e))?;
Ok(())
}

View File

@@ -163,11 +163,7 @@ impl McpService {
Ok(())
}
fn remove_server_from_app(
_state: &AppState,
id: &str,
app: &AppType,
) -> Result<(), AppError> {
fn remove_server_from_app(_state: &AppState, id: &str, app: &AppType) -> Result<(), AppError> {
match app {
AppType::Claude => mcp::remove_server_from_claude(id)?,
AppType::Codex => mcp::remove_server_from_codex(id)?,
@@ -236,7 +232,10 @@ impl McpService {
}
/// [已废弃] 从 Claude 导入 MCP兼容旧 API
#[deprecated(since = "3.7.0", note = "Import will be handled differently in unified structure")]
#[deprecated(
since = "3.7.0",
note = "Import will be handled differently in unified structure"
)]
pub fn import_from_claude(state: &AppState) -> Result<usize, AppError> {
let mut cfg = state.config.write()?;
let count = mcp::import_from_claude(&mut cfg)?;
@@ -246,7 +245,10 @@ impl McpService {
}
/// [已废弃] 从 Codex 导入 MCP兼容旧 API
#[deprecated(since = "3.7.0", note = "Import will be handled differently in unified structure")]
#[deprecated(
since = "3.7.0",
note = "Import will be handled differently in unified structure"
)]
pub fn import_from_codex(state: &AppState) -> Result<usize, AppError> {
let mut cfg = state.config.write()?;
let count = mcp::import_from_codex(&mut cfg)?;
@@ -256,7 +258,10 @@ impl McpService {
}
/// [已废弃] 从 Gemini 导入 MCP兼容旧 API
#[deprecated(since = "3.7.0", note = "Import will be handled differently in unified structure")]
#[deprecated(
since = "3.7.0",
note = "Import will be handled differently in unified structure"
)]
pub fn import_from_gemini(state: &AppState) -> Result<usize, AppError> {
let mut cfg = state.config.write()?;
let count = mcp::import_from_gemini(&mut cfg)?;