fix(mcp): resolve compilation errors and add backward compatibility
## Compilation Fixes - Add deprecated compatibility methods to McpService: * get_servers() - filters servers by app * set_enabled() - delegates to toggle_app() * sync_enabled() - syncs enabled servers for specific app * import_from_claude/codex/gemini() - wraps mcp:: functions - Fix toml_edit type conversion in sync_single_server_to_codex(): * Add json_server_to_toml_table() helper function * Manually construct toml_edit::Table instead of invalid serde conversion - Fix get_codex_config_path() calls (returns PathBuf, not Result) - Update upsert_mcp_server_in_config() to work with unified structure: * Converts old per-app API to unified McpServer structure * Preserves existing server data when updating * Supports sync_other_side parameter for multi-app enable - Update delete_mcp_server_in_config() to ignore app parameter ## Backward Compatibility - All old v3.6.x commands continue to work with deprecation warnings - Frontend migration can be done incrementally - Old commands transparently use new unified backend ## Status ✅ Backend compiles successfully (cargo check passes) ⚠️ 16 warnings (8 deprecation + 8 unused functions - expected)
This commit is contained in:
@@ -66,6 +66,7 @@ pub async fn get_mcp_config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 在 config.json 中新增或更新一个 MCP 服务器定义
|
/// 在 config.json 中新增或更新一个 MCP 服务器定义
|
||||||
|
/// [已废弃] 该命令仍然使用旧的分应用API,会转换为统一结构
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn upsert_mcp_server_in_config(
|
pub async fn upsert_mcp_server_in_config(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
@@ -74,8 +75,58 @@ pub async fn upsert_mcp_server_in_config(
|
|||||||
spec: serde_json::Value,
|
spec: serde_json::Value,
|
||||||
sync_other_side: Option<bool>,
|
sync_other_side: Option<bool>,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
|
use crate::app_config::McpServer;
|
||||||
|
|
||||||
let app_ty = AppType::from_str(&app).map_err(|e| e.to_string())?;
|
let app_ty = AppType::from_str(&app).map_err(|e| e.to_string())?;
|
||||||
McpService::upsert_server(&state, app_ty, &id, spec, sync_other_side.unwrap_or(false))
|
|
||||||
|
// 读取现有的服务器(如果存在)
|
||||||
|
let existing_server = {
|
||||||
|
let cfg = state.config.read().map_err(|e| e.to_string())?;
|
||||||
|
if let Some(servers) = &cfg.mcp.servers {
|
||||||
|
servers.get(&id).cloned()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 构建新的统一服务器结构
|
||||||
|
let mut new_server = if let Some(mut existing) = existing_server {
|
||||||
|
// 更新现有服务器
|
||||||
|
existing.server = spec.clone();
|
||||||
|
existing.apps.set_enabled_for(&app_ty, true);
|
||||||
|
existing
|
||||||
|
} else {
|
||||||
|
// 创建新服务器
|
||||||
|
let mut apps = crate::app_config::McpApps::default();
|
||||||
|
apps.set_enabled_for(&app_ty, true);
|
||||||
|
|
||||||
|
// 尝试从 spec 中提取 name,否则使用 id
|
||||||
|
let name = spec.get("name")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or(&id)
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
McpServer {
|
||||||
|
id: id.clone(),
|
||||||
|
name,
|
||||||
|
server: spec,
|
||||||
|
apps,
|
||||||
|
description: None,
|
||||||
|
homepage: None,
|
||||||
|
docs: None,
|
||||||
|
tags: Vec::new(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果 sync_other_side 为 true,也启用其他应用
|
||||||
|
if sync_other_side.unwrap_or(false) {
|
||||||
|
new_server.apps.claude = true;
|
||||||
|
new_server.apps.codex = true;
|
||||||
|
new_server.apps.gemini = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
McpService::upsert_server(&state, new_server)
|
||||||
|
.map(|_| true)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,11 +134,10 @@ pub async fn upsert_mcp_server_in_config(
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn delete_mcp_server_in_config(
|
pub async fn delete_mcp_server_in_config(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app: String,
|
_app: String, // 参数保留用于向后兼容,但在统一结构中不再需要
|
||||||
id: String,
|
id: String,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
let app_ty = AppType::from_str(&app).map_err(|e| e.to_string())?;
|
McpService::delete_server(&state, &id).map_err(|e| e.to_string())
|
||||||
McpService::delete_server(&state, app_ty, &id).map_err(|e| e.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 设置启用状态并同步到客户端配置
|
/// 设置启用状态并同步到客户端配置
|
||||||
|
|||||||
@@ -825,14 +825,79 @@ pub fn remove_server_from_claude(id: &str) -> Result<(), AppError> {
|
|||||||
crate::claude_mcp::set_mcp_servers_map(¤t)
|
crate::claude_mcp::set_mcp_servers_map(¤t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper: 将 JSON MCP 服务器规范转换为 toml_edit::Table
|
||||||
|
fn json_server_to_toml_table(spec: &Value) -> Result<toml_edit::Table, AppError> {
|
||||||
|
use toml_edit::{Array, Item, Table};
|
||||||
|
|
||||||
|
let mut t = Table::new();
|
||||||
|
let typ = spec.get("type").and_then(|v| v.as_str()).unwrap_or("stdio");
|
||||||
|
t["type"] = toml_edit::value(typ);
|
||||||
|
|
||||||
|
match typ {
|
||||||
|
"stdio" => {
|
||||||
|
let cmd = spec.get("command").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
|
t["command"] = toml_edit::value(cmd);
|
||||||
|
|
||||||
|
if let Some(args) = spec.get("args").and_then(|v| v.as_array()) {
|
||||||
|
let mut arr_v = Array::default();
|
||||||
|
for a in args.iter().filter_map(|x| x.as_str()) {
|
||||||
|
arr_v.push(a);
|
||||||
|
}
|
||||||
|
if !arr_v.is_empty() {
|
||||||
|
t["args"] = Item::Value(toml_edit::Value::Array(arr_v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(cwd) = spec.get("cwd").and_then(|v| v.as_str()) {
|
||||||
|
if !cwd.trim().is_empty() {
|
||||||
|
t["cwd"] = toml_edit::value(cwd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(env) = spec.get("env").and_then(|v| v.as_object()) {
|
||||||
|
let mut env_tbl = Table::new();
|
||||||
|
for (k, v) in env.iter() {
|
||||||
|
if let Some(s) = v.as_str() {
|
||||||
|
env_tbl[&k[..]] = toml_edit::value(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !env_tbl.is_empty() {
|
||||||
|
t["env"] = Item::Table(env_tbl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"http" => {
|
||||||
|
let url = spec.get("url").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
|
t["url"] = toml_edit::value(url);
|
||||||
|
|
||||||
|
if let Some(headers) = spec.get("headers").and_then(|v| v.as_object()) {
|
||||||
|
let mut h_tbl = Table::new();
|
||||||
|
for (k, v) in headers.iter() {
|
||||||
|
if let Some(s) = v.as_str() {
|
||||||
|
h_tbl[&k[..]] = toml_edit::value(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !h_tbl.is_empty() {
|
||||||
|
t["headers"] = Item::Table(h_tbl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(t)
|
||||||
|
}
|
||||||
|
|
||||||
/// 将单个 MCP 服务器同步到 Codex live 配置
|
/// 将单个 MCP 服务器同步到 Codex live 配置
|
||||||
pub fn sync_single_server_to_codex(
|
pub fn sync_single_server_to_codex(
|
||||||
_config: &MultiAppConfig,
|
_config: &MultiAppConfig,
|
||||||
id: &str,
|
id: &str,
|
||||||
server_spec: &Value,
|
server_spec: &Value,
|
||||||
) -> Result<(), AppError> {
|
) -> Result<(), AppError> {
|
||||||
|
use toml_edit::Item;
|
||||||
|
|
||||||
// 读取现有的 config.toml
|
// 读取现有的 config.toml
|
||||||
let config_path = crate::codex_config::get_codex_config_path()?;
|
let config_path = crate::codex_config::get_codex_config_path();
|
||||||
|
|
||||||
let mut doc = if config_path.exists() {
|
let mut doc = if config_path.exists() {
|
||||||
let content = std::fs::read_to_string(&config_path)
|
let content = std::fs::read_to_string(&config_path)
|
||||||
@@ -858,11 +923,10 @@ pub fn sync_single_server_to_codex(
|
|||||||
doc["mcp"]["servers"] = toml_edit::table();
|
doc["mcp"]["servers"] = toml_edit::table();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将服务器转换为 TOML 格式并插入
|
// 将 JSON 服务器规范转换为 TOML 表
|
||||||
let toml_value = serde_json::from_value::<toml_edit::Value>(server_spec.clone())
|
let toml_table = json_server_to_toml_table(server_spec)?;
|
||||||
.map_err(|e| AppError::McpValidation(format!("无法将 MCP 服务器转换为 TOML: {e}")))?;
|
|
||||||
|
|
||||||
doc["mcp"]["servers"][id] = toml_edit::value(toml_value);
|
doc["mcp"]["servers"][id] = Item::Table(toml_table);
|
||||||
|
|
||||||
// 写回文件
|
// 写回文件
|
||||||
std::fs::write(&config_path, doc.to_string())
|
std::fs::write(&config_path, doc.to_string())
|
||||||
@@ -873,7 +937,7 @@ pub fn sync_single_server_to_codex(
|
|||||||
|
|
||||||
/// 从 Codex live 配置中移除单个 MCP 服务器
|
/// 从 Codex live 配置中移除单个 MCP 服务器
|
||||||
pub fn remove_server_from_codex(id: &str) -> Result<(), AppError> {
|
pub fn remove_server_from_codex(id: &str) -> Result<(), AppError> {
|
||||||
let config_path = crate::codex_config::get_codex_config_path()?;
|
let config_path = crate::codex_config::get_codex_config_path();
|
||||||
|
|
||||||
if !config_path.exists() {
|
if !config_path.exists() {
|
||||||
return Ok(()); // 文件不存在,无需删除
|
return Ok(()); // 文件不存在,无需删除
|
||||||
|
|||||||
@@ -186,4 +186,82 @@ impl McpService {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 兼容层:支持旧的 v3.6.x 命令(已废弃,将在 v4.0 移除)
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/// [已废弃] 获取指定应用的 MCP 服务器(兼容旧 API)
|
||||||
|
#[deprecated(since = "3.7.0", note = "Use get_all_servers instead")]
|
||||||
|
pub fn get_servers(
|
||||||
|
state: &AppState,
|
||||||
|
app: AppType,
|
||||||
|
) -> Result<HashMap<String, serde_json::Value>, AppError> {
|
||||||
|
let all_servers = Self::get_all_servers(state)?;
|
||||||
|
let mut result = HashMap::new();
|
||||||
|
|
||||||
|
for (id, server) in all_servers {
|
||||||
|
if server.apps.is_enabled_for(&app) {
|
||||||
|
result.insert(id, server.server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [已废弃] 设置 MCP 服务器在指定应用的启用状态(兼容旧 API)
|
||||||
|
#[deprecated(since = "3.7.0", note = "Use toggle_app instead")]
|
||||||
|
pub fn set_enabled(
|
||||||
|
state: &AppState,
|
||||||
|
app: AppType,
|
||||||
|
id: &str,
|
||||||
|
enabled: bool,
|
||||||
|
) -> Result<bool, AppError> {
|
||||||
|
Self::toggle_app(state, id, app, enabled)?;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [已废弃] 同步启用的 MCP 到指定应用(兼容旧 API)
|
||||||
|
#[deprecated(since = "3.7.0", note = "Use sync_all_enabled instead")]
|
||||||
|
pub fn sync_enabled(state: &AppState, app: AppType) -> Result<(), AppError> {
|
||||||
|
let servers = Self::get_all_servers(state)?;
|
||||||
|
|
||||||
|
for server in servers.values() {
|
||||||
|
if server.apps.is_enabled_for(&app) {
|
||||||
|
Self::sync_server_to_app(state, server, &app)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [已废弃] 从 Claude 导入 MCP(兼容旧 API)
|
||||||
|
#[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)?;
|
||||||
|
drop(cfg);
|
||||||
|
state.save()?;
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [已废弃] 从 Codex 导入 MCP(兼容旧 API)
|
||||||
|
#[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)?;
|
||||||
|
drop(cfg);
|
||||||
|
state.save()?;
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [已废弃] 从 Gemini 导入 MCP(兼容旧 API)
|
||||||
|
#[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)?;
|
||||||
|
drop(cfg);
|
||||||
|
state.save()?;
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user