内容
<div align="center">
<img src="./assets/hero.svg" alt="mcps-manager" width="820"/>
<h1>mcps-manager</h1>
<p><b>把散在 6 个 AI 编码工具、3 种不同格式里的 MCP 配置,收进一个中央库统一管理</b></p>
<p><i>One central UMS repo · six host adapters · lossless format conversion</i></p>
<p>
<a href="LICENSE"><img src="https://img.shields.io/github/license/tinymindkin/mcps-manager?style=flat-square&color=blue" alt="license"/></a>
<img src="https://img.shields.io/badge/platform-macOS-000000?style=flat-square&logo=apple" alt="macOS"/>
<img src="https://img.shields.io/badge/tauri-2-24C8DB?style=flat-square&logo=tauri&logoColor=white" alt="tauri 2"/>
<img src="https://img.shields.io/badge/rust-1.97+-B7410E?style=flat-square&logo=rust&logoColor=white" alt="rust"/>
<img src="https://img.shields.io/badge/frontend-vanilla_JS-F7DF1E?style=flat-square&logo=javascript&logoColor=black" alt="vanilla JS"/>
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen?style=flat-square" alt="PRs welcome"/>
<a href="https://github.com/tinymindkin/mcps-manager/stargazers"><img src="https://img.shields.io/github/stars/tinymindkin/mcps-manager?style=flat-square&color=yellow" alt="stars"/></a>
</p>
<p>
<a href="#-为什么做这个">Why</a> ·
<a href="#-核心特性">Features</a> ·
<a href="#-快速开始">Install</a> ·
<a href="#-支持的-host">Hosts</a> ·
<a href="#-架构">Architecture</a> ·
<a href="#-路线图">Roadmap</a> ·
<a href="#-贡献一个新-host">Contributing</a>
</p>
</div>
---
## 🤔 为什么做这个
你同时在用 **Claude Code / Codex / Cursor / Gemini / OpenCode / Qwen / Kimi** 里的两个以上?那你多半已经遇到过下面这些事:
- 同一个 MCP,要在**六个工具里手动配置六遍**;
- 六个工具的配置文件**散在六个不同目录**,还是**三种截然不同的格式**——Claude 是 JSON,Codex 是 **TOML**,OpenCode 又是把 `command` 和 `args` 压成一个数组的另一种 JSON;
- 字段名还各不相同:`mcpServers` vs `mcp`、`env` vs `environment`;
- 想把 Claude 里配好的 MCP 复制到 Codex?手动改 JSON → TOML,**一次就够劝退**;
- 一份配置到处复制,也是 [GitHub 上上千个泄露 Anthropic API Key 事故](https://github.com/search?q=claude_desktop_config&type=code) 的直接原因。
**mcps-manager** 用一个中央 UMS 仓库 + 六个 adapter 把这件事收拾了:
在 `~/.mcps-manager/` 里维护一份配置,Push 到任意 host 时自动做格式转换、保留 host 私有字段、绝不动 host 文件里的非 MCP 内容,写入前自动备份 + 原子写。
---
## ✨ 核心特性
| | |
|---|---|
| 🎯 **只做一件事** | 只管 MCP。不做 Skills / Plugins / Hooks / Memory / Rules。深度 > 广度。 |
| 🔄 **双向格式转换** | 中央 UMS ⇄ 6 种原生 host 格式,自动、无损。 |
| 🛡 **不搞坏你的 host 文件** | 89KB 的 `.claude.json`、356 行的 `.codex/config.toml` 里其它字段一个字节都不动。Codex 用 `toml_edit` 保留注释和键顺序。 |
| 💾 **host 私有字段无损往返** | 比如 Codex 的 `[mcp_servers.foo.tools.<tool>] approval_mode`,import 时保留在 UMS `hostExtras.codex`,push 回同一 host 时原样还原。 |
| 🔒 **写入安全** | 备份到 `~/.mcps-manager/backups/<时间戳>/` + 原子 `tmp → fsync → rename` + 写前 round-trip parse 校验。 |
| 🧩 **架构解耦** | 每个 host 一个文件,硬编码路径 / 格式 / schema 集中在顶部常量。新增 host = 3 个文件的事。 |
| ⚡ **轻量** | Tauri 2,打包 ~10MB。前端零 npm 依赖,纯 HTML/CSS/JS。 |
| 🧪 **有测试** | 4 个集成 round-trip 测试守着「不动 host 非 MCP 字段」这条红线。 |
---
## 📸 界面预览
<div align="center">
<i>三 pane:左边 hosts / 中间 servers / 右边 UMS 详情 + Push 面板</i>
<br/><br/>
<sub>(截图 v0.3 上线后补,当前请自行 <code>cargo tauri dev</code> 起来看)</sub>
</div>
---
## 📦 快速开始
### 依赖
- macOS 12+(Apple Silicon 已验证;Intel 未测)
- Rust 1.97+(下面的命令会装)
- Node **不需要**——前端零依赖
### 一次性准备
```bash
# 装 Rust(装到 ~/.cargo,可随时 rustup self uninstall)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
source "$HOME/.cargo/env"
# 装 Tauri CLI
cargo install tauri-cli --version '^2.0' --locked
```
### 跑起来
```bash
git clone git@github.com:tinymindkin/mcps-manager.git
cd mcps-manager/src-tauri
cargo tauri dev
```
首次编译约 3–5 分钟,之后热重载秒开。前端在 `dist/`,改完文件在窗口内 `⌘R` 刷新即可(无需重启后端)。
### 目录结构(第一次启动后)
```
~/.mcps-manager/
├── servers/ # 每个 MCP 一个 UMS JSON 文件(git-friendly)
│ ├── filesystem.json
│ ├── notion.json
│ └── ...
├── backups/ # 每次写 host 文件前自动备份到这里
│ └── 2026-07-22T14-30-00/
│ └── codex__config.toml
└── profiles/ # (v0.4) 场景打包
```
---
## 🛠 支持的 host
| Host | 配置路径 | 格式 | MCP 根键 | 特殊字段 |
|---|---|---|---|---|
| **Claude Code** | `~/.claude.json` | JSON | `mcpServers` | — |
| **Codex** | `~/.codex/config.toml` | **TOML** | `mcp_servers` | `[mcp_servers.<x>.tools.<tool>] approval_mode` 嵌套子表;inline env |
| **Gemini CLI** | `~/.gemini/settings.json` | JSONC(带 `//` 注释) | `mcpServers` | — |
| **OpenCode** | `~/.config/opencode/opencode.json` | JSON | `mcp` | `command` 是数组;env 键叫 `environment`;每条要 `type: "local"` |
| **Qwen Code** | `~/.qwen/settings.json` | JSON | `mcpServers` | — |
| **Kimi CLI** | `~/.kimi-code/mcp.json` | JSON | `mcpServers` | 可选 `transport: "stdio" \| "sse" \| "http"` |
> **未支持**:Cursor、Windsurf、Copilot、Roo Code、TRAE 等。想要?[开 issue](../../issues) 或直接 PR——见下面的 [贡献一个新 host](#-贡献一个新-host)。
---
## 🏗 架构
<div align="center">
<img src="./assets/architecture.svg" alt="architecture" width="900"/>
</div>
四层,从上到下:
1. **UI Layer**:Tauri 2 窗口 + 纯 HTML/CSS/JS 三 pane。
2. **Application Commands**:`list_hosts` / `import_from_host` / `export_to_host` / `write_repo_server` 等 tauri 命令。
3. **Domain**:UMS schema、Repo(`~/.mcps-manager/servers/*.json`)、`HostAdapter` trait、Registry。**host 无关**。
4. **Adapter Layer**:每个 host 一个文件,硬编码常量集中在顶部;共享的 JSON / TOML helper 抽在 `adapters/mod.rs` 和 `adapters/toml_common.rs`。
完整设计见 [DESIGN.md](./DESIGN.md)。
---
## 🔄 格式转换(举例)
<div align="center">
<img src="./assets/formats.svg" alt="format conversion" width="900"/>
</div>
真实的翻译细节:
- **Codex TOML** 用 [`toml_edit`](https://crates.io/crates/toml_edit) —— 保留原文件的注释、键顺序、空行,而不是「反序列化再序列化」那种会毁排版的做法。
- **Gemini 的 JSONC** 用 `json5` fallback 解析,能吃掉 `// 注释掉的服务器`。
- **OpenCode** 自动把 `command + args` 合并成一个 array,env 键改叫 `environment`,加上 `type: "local"`。
- **Kimi 的 `transport` 字段**智能保留:`stdio` 不 emit(默认省略),`sse` / `http` 才写出——匹配 Kimi 官方文档的风格。
---
## 🛡 host 文件安全的三道保险
写任何 host 配置文件前,`mcps-manager` 会:
1. **只碰 MCP 子树**:`mcpServers` / `mcp_servers` / `mcp` 之外的所有顶层键、注释、格式全部原样保留。你 89KB 的 `.claude.json` 里的 `numStartups` / `projects` / `cachedGrowthBookFeatures` 不会被动。
2. **备份**:整个原文件复制到 `~/.mcps-manager/backups/<ISO 时间戳>/`。
3. **原子写 + round-trip 校验**:`.tmp → fsync → rename`;写入前先 parse 一遍自己即将写的内容,解析失败就 abort,不覆盖原文件。
`src-tauri/tests/roundtrip.rs` 里 4 个集成测试专门守着这些不变量:
```
running 4 tests
test claude_json_shape_preserves_other_keys ... ok
test codex_toml_preserves_other_sections_and_writes_subtable ... ok
test kimi_json_shape_handles_stdio_sse_and_explicit_transport ... ok
test opencode_shape_uses_command_array_and_environment_and_type ... ok
```
---
## 🗺 路线图
- [x] **v0.1** — 只读 import:6 个 host adapter,UMS repo,三 pane UI
- [x] **v0.2** — 双向:`export_to_host` + 格式转换 + 备份 + 原子写
- [x] **v0.2.4** — Kimi JSON + 可选 `transport` 字段支持
- [ ] **v0.3** — Diff 视图、UMS 编辑器(当前只能 delete 后新建)
- [ ] **v0.4** — Profile:一组 MCP 打包,一键切换场景
- [ ] **v0.5** — CLI(`mcps-manager export --host codex ...`)+ Keychain secret
- [ ] **v0.6** — 项目级配置(Claude 的 `.mcp.json`、OpenCode 的项目 `opencode.json`)
- [ ] **v1.0** — Apple 公证的 `.dmg`、Cursor / Windsurf / Copilot adapter
---
## 🤝 贡献一个新 host
理论上是 3 个文件、约 50 行代码的事:
```rust
// 1. src-tauri/src/adapters/cursor.rs
use super::{parse_json_servers, read_json_lenient, write_json_host,
HostAdapter, JsonAdapterShape, WriteReport};
use crate::ums::UmsServer;
use anyhow::Result;
use std::path::PathBuf;
pub struct CursorAdapter;
const SHAPE: JsonAdapterShape = JsonAdapterShape {
root_key: "mcpServers",
env_key: "env",
command_is_array: false,
emit_type_field: false,
transport_key: None,
known_fields: &["command", "args", "env", "url", "headers", "type"],
};
impl HostAdapter for CursorAdapter {
fn id(&self) -> &'static str { "cursor" }
fn display_name(&self) -> &'static str { "Cursor" }
fn primary_path(&self) -> PathBuf {
dirs::home_dir().unwrap().join(".cursor/mcp.json")
}
fn format_label(&self) -> &'static str { "JSON (mcpServers)" }
fn list_servers(&self) -> Result<Vec<UmsServer>> {
let path = self.primary_path();
if !path.exists() { return Ok(vec![]); }
let root = read_json_lenient(&path)?;
let Some(map) = root.get(SHAPE.root_key).and_then(|v| v.as_object())
else { return Ok(vec![]); };
Ok(parse_json_servers(map, &SHAPE, self.id()))
}
fn write_servers(&self, upserts: &[UmsServer]) -> Result<WriteReport> {
write_json_host(&self.primary_path(), &SHAPE, self.id(), upserts)
}
}
```
然后:
```rust
// 2. src-tauri/src/adapters/mod.rs
pub mod cursor; // + 一行
// ...
Arc::new(cursor::CursorAdapter), // + Registry::default_set() 里加一行
```
```rust
// 3. src-tauri/tests/roundtrip.rs — 加一个测试守着这个 host 的 shape 不变
```
UI 会自动出现这个 host。**没有硬编码在前端**。
---
## ❓ FAQ
<details>
<summary><b>它跟 HarnessKit / skills-manager 有什么区别?</b></summary>
- **HarnessKit**(@赛博Ditto):Skills + MCP + Plugin + Hook + CLI + memory + rules 全家桶,7 个 agent。「什么都想管」。
- **[skills-manager](https://github.com/xingkongliang/skills-manager)**(xingkongliang):主打 Skills,MCP 是附赠。
- **mcps-manager**(本项目):**只做 MCP,六个工具,三种格式**。窄而深。想要 skill 管理去看前两个;只被 MCP 折磨来看这个。
</details>
<details>
<summary><b>为什么选 Tauri 而不是 Electron?</b></summary>
- 体积:Tauri ~10MB,Electron 150MB+。这类小工具用户对体积敏感。
- Rust 核心可复用给未来的 CLI(v0.5 里程碑)。
- Codex 的 TOML 需要 `toml_edit` 这个能保留注释和键顺序的库,Node 生态里没有同级替代品。
</details>
<details>
<summary><b>Windows / Linux 支持?</b></summary>
代码本身是跨平台的(Rust + Tauri)。路径常量目前假设 macOS(`~/.claude.json` 等),Windows 需要 override。**欢迎 PR 补 Windows / Linux 的默认路径。**
</details>
<details>
<summary><b>安全性:我的 API Key 会不会被上传?</b></summary>
- 完全本地工具,没有任何网络请求(除了你自己配的 MCP server 运行时会发的)。
- Secret 存明文(v0.5 会接 Keychain)。**不要**把 `~/.mcps-manager/servers/*.json` push 到公开 git 仓库。
- 写 host 文件前会先备份,你可以随时从 `~/.mcps-manager/backups/` 恢复。
</details>
<details>
<summary><b>macOS 打开被 Gatekeeper 拦怎么办?</b></summary>
`v1.0` 前不会做 Apple 公证。Gatekeeper 拦是正常的:
- 打开 **系统设置 → 隐私与安全性**
- 找到 mcps-manager 的提示,点 **仍要打开**
(不放心的话可以自己 `cargo tauri dev` 从源码起——第一原则永远是「代码自己看过」。)
</details>
---
## 🙏 致谢
灵感来自:
- **HarnessKit**(@赛博Ditto) —— Skills/MCP/Plugin/Hook 全家桶思路
- **[xingkongliang/skills-manager](https://github.com/xingkongliang/skills-manager)** —— 中央 Skills 库 + 多工具分发
- **[toml_edit](https://crates.io/crates/toml_edit)** —— 让 Codex TOML 的注释保留成为可能
## 📝 License
[MIT](./LICENSE) © 2026 tinymindkin
---
<div align="center">
<sub><b>如果它帮到了你,点个 ⭐ 让我知道。</b></sub><br/>
<sub><a href="../../issues">Issues</a> · <a href="../../pulls">Pull Requests</a> · <a href="./DESIGN.md">Design Doc</a></sub>
</div>
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器