- Add McpConfig to MultiAppConfig and persist MCP servers in ~/.cc-switch/config.json - Add Tauri commands: get_mcp_config, upsert_mcp_server_in_config, delete_mcp_server_in_config, set_mcp_enabled, sync_enabled_mcp_to_claude, import_mcp_from_claude - Only write enabled MCPs to ~/.claude.json (mcpServers) and strip UI-only fields (enabled/source) - Frontend: update API wrappers and MCP panel to read/write via config.json; seed presets on first open; import from ~/.claude.json - Fix warnings (remove unused mut, dead code) - Verified with cargo check and pnpm typecheck
165 lines
5.0 KiB
Rust
165 lines
5.0 KiB
Rust
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
/// MCP 配置:集中存放于 ~/.cc-switch/config.json
|
||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||
pub struct McpConfig {
|
||
/// 以 id 为键的服务器定义(宽松 JSON 对象,包含 enabled/source 等 UI 辅助字段)
|
||
#[serde(default)]
|
||
pub servers: HashMap<String, serde_json::Value>,
|
||
}
|
||
|
||
use crate::config::{copy_file, get_app_config_dir, get_app_config_path, write_json_file};
|
||
use crate::provider::ProviderManager;
|
||
|
||
/// 应用类型
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
#[serde(rename_all = "lowercase")]
|
||
pub enum AppType {
|
||
Claude,
|
||
Codex,
|
||
}
|
||
|
||
impl AppType {
|
||
pub fn as_str(&self) -> &str {
|
||
match self {
|
||
AppType::Claude => "claude",
|
||
AppType::Codex => "codex",
|
||
}
|
||
}
|
||
}
|
||
|
||
impl From<&str> for AppType {
|
||
fn from(s: &str) -> Self {
|
||
match s.to_lowercase().as_str() {
|
||
"codex" => AppType::Codex,
|
||
_ => AppType::Claude, // 默认为 Claude
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 多应用配置结构(向后兼容)
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct MultiAppConfig {
|
||
#[serde(default = "default_version")]
|
||
pub version: u32,
|
||
/// 应用管理器(claude/codex)
|
||
#[serde(flatten)]
|
||
pub apps: HashMap<String, ProviderManager>,
|
||
/// MCP 配置
|
||
#[serde(default)]
|
||
pub mcp: McpConfig,
|
||
}
|
||
|
||
fn default_version() -> u32 {
|
||
2
|
||
}
|
||
|
||
impl Default for MultiAppConfig {
|
||
fn default() -> Self {
|
||
let mut apps = HashMap::new();
|
||
apps.insert("claude".to_string(), ProviderManager::default());
|
||
apps.insert("codex".to_string(), ProviderManager::default());
|
||
|
||
Self {
|
||
version: 2,
|
||
apps,
|
||
mcp: McpConfig::default(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl MultiAppConfig {
|
||
/// 从文件加载配置(处理v1到v2的迁移)
|
||
pub fn load() -> Result<Self, String> {
|
||
let config_path = get_app_config_path();
|
||
|
||
if !config_path.exists() {
|
||
log::info!("配置文件不存在,创建新的多应用配置");
|
||
return Ok(Self::default());
|
||
}
|
||
|
||
// 尝试读取文件
|
||
let content = std::fs::read_to_string(&config_path)
|
||
.map_err(|e| format!("读取配置文件失败: {}", e))?;
|
||
|
||
// 检查是否是旧版本格式(v1)
|
||
if let Ok(v1_config) = serde_json::from_str::<ProviderManager>(&content) {
|
||
log::info!("检测到v1配置,自动迁移到v2");
|
||
|
||
// 迁移到新格式
|
||
let mut apps = HashMap::new();
|
||
apps.insert("claude".to_string(), v1_config);
|
||
apps.insert("codex".to_string(), ProviderManager::default());
|
||
|
||
let config = Self {
|
||
version: 2,
|
||
apps,
|
||
mcp: McpConfig::default(),
|
||
};
|
||
|
||
// 迁移前备份旧版(v1)配置文件
|
||
let backup_dir = get_app_config_dir();
|
||
let ts = std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap_or_default()
|
||
.as_secs();
|
||
let backup_path = backup_dir.join(format!("config.v1.backup.{}.json", ts));
|
||
|
||
match copy_file(&config_path, &backup_path) {
|
||
Ok(()) => log::info!(
|
||
"已备份旧版配置文件: {} -> {}",
|
||
config_path.display(),
|
||
backup_path.display()
|
||
),
|
||
Err(e) => log::warn!("备份旧版配置文件失败: {}", e),
|
||
}
|
||
|
||
// 保存迁移后的配置
|
||
config.save()?;
|
||
return Ok(config);
|
||
}
|
||
|
||
// 尝试读取v2格式
|
||
serde_json::from_str::<Self>(&content).map_err(|e| format!("解析配置文件失败: {}", e))
|
||
}
|
||
|
||
/// 保存配置到文件
|
||
pub fn save(&self) -> Result<(), String> {
|
||
let config_path = get_app_config_path();
|
||
// 先备份旧版(若存在)到 ~/.cc-switch/config.json.bak,再写入新内容
|
||
if config_path.exists() {
|
||
let backup_path = get_app_config_dir().join("config.json.bak");
|
||
if let Err(e) = copy_file(&config_path, &backup_path) {
|
||
log::warn!("备份 config.json 到 .bak 失败: {}", e);
|
||
}
|
||
}
|
||
|
||
write_json_file(&config_path, self)?;
|
||
Ok(())
|
||
}
|
||
|
||
/// 获取指定应用的管理器
|
||
pub fn get_manager(&self, app: &AppType) -> Option<&ProviderManager> {
|
||
self.apps.get(app.as_str())
|
||
}
|
||
|
||
/// 获取指定应用的管理器(可变引用)
|
||
pub fn get_manager_mut(&mut self, app: &AppType) -> Option<&mut ProviderManager> {
|
||
self.apps.get_mut(app.as_str())
|
||
}
|
||
|
||
/// 确保应用存在
|
||
pub fn ensure_app(&mut self, app: &AppType) {
|
||
if !self.apps.contains_key(app.as_str()) {
|
||
self.apps
|
||
.insert(app.as_str().to_string(), ProviderManager::default());
|
||
}
|
||
}
|
||
|
||
/// 获取 MCP 配置(可变引用)
|
||
pub fn mcp_mut(&mut self) -> &mut McpConfig {
|
||
&mut self.mcp
|
||
}
|
||
}
|