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:
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
Reference in New Issue
Block a user