经常在几个 AI 编程工具之间来回切换的人应该都有过一种相似的崩溃刚刚在 Claude Code 里调好的 Postgres MCP 工具切到 Cursor 要重新配一遍再打开 Codex又是另一种格式稍微一波操作下来半天时间就报废在复制粘贴 JSON 上了。我两周里手动写过不下 20 次 MCP 服务器配置于是决定做一个本机同步方案把 Claude Code、Codex、Cursor、Hermes Agent 这几个 agent 的 MCP 工具和技能全部收敛到同一个配置源不再为每个工具单独维护一份。折腾完以后现在的日常工作流从“开工具 → 配工具 → 调试”变成了“开工具 → 直接干活”。这篇文章把这套方案从设计到落地的完整过程都写出来你可以直接照着搭。1. 为什么四个 agent 要维护四份配置1.1 MCP 到底在解决什么问题MCPModel Context Protocol本质上是一套“把外部能力插进 AI agent”的开放协议。凡是接了 MCP 的服务比如本地文件检索、数据库查询、代码搜索、浏览器自动化都会被 agent 以标准化方式识别并调用。对开发者来说好处是不用等官方插件只要给出一个 server 地址和一段 JSON 描述agent 就能自己把工具用起来。问题出在“给谁看”这件事上。每个 agent 都有自己注册 MCP 的方式有的用命令行交互式添加有的要求写在全局 JSON 里有的放在项目目录下还有的走它自己的“技能加载目录”。同一个 server我在 Claude Code 里能正常用换到 Cursor 就要重新跑一遍初始化再到 Codex 又是另一种语法。最开始觉得“每个也就多花几分钟”可当 MCP server 超过五个再叠加环境变量、工作目录、启动参数这些细节维护成本已经不是线性增长是指数级增长。1.2 手工维护的真实成本我做过一个统计记录两周内因为多 agent 配置问题产生的无效操作。给 Claude Code 新增一个 MCP server5 分钟同步到 Codex翻了二十分钟文档改了配置后发现格式不完全兼容同步到 Cursor又花了十五分钟调 JSON最后到 Hermes Agent因为它读取的是独立配置文件我干脆放弃同步选择双份维护。这种消耗很隐蔽。它不报错、不出现红字只是反复占用注意力。更麻烦的是“技能”的形态也各不相同Claude Code 里技能是命令目录里的 markdown 文件Cursor 里是 rules 目录Codex 里是自定义指令。你没法用一套文件同时喂饱四个 agent技能库很快就分裂了。某次我在 Claude Code 里精心维护的代码审查提示词在 Cursor 里完全没有体现等于同一个技能方案在不同工具里表现不一致这种体验非常难受。2. 核心方案单一配置源加生成挂接2.1 什么是“单一配置源”我想了很久最后得出的结论很简单把所有 agent 需要的东西统一收敛到一个本地配置目录所有 agent 都从这个目录“派生”各自的配置。这个中心目录是整个体系的唯一可信来源我给它起了个代号叫 agent-sync目录结构如下。~/.agent-sync/ ├── mcp.yml # MCP server 清单唯一主配置 ├── skills/ # 所有共享技能markdown 文件 │ ├── review.md │ └── api_design.md ├── scripts/ │ ├── sync.py # 生成脚本 │ └── watch.sh # 监听变化自动同步 └── generated/ # 生成结果的暂存区不入 git这里的关键思维是不直接让四个 agent 去读同一份文件而是让他们读“由同一份源配置生成出来、放在自己老位置上的文件”。这样既满足各自格式要求又不破坏工具本身的更新逻辑。源配置只需要维护一份新增一个工具或技能全链路一次生效。2.2 为什么不直接靠 symlink 搞定一开始我也想过直接搞 symlink把.claude、.cursor这些目录指到同一个地方。实际跑完发现三个问题。第一agent 对配置目录的读写方式不一样。有的启动时会自动往配置目录里写缓存有的会做骚操作直接改文件结构。如果用 symlink这些改动会直接反映到中心目录污染源配置。第二配置格式差异很大。同样是 MCPClaude Code 喜欢mcpServers这种包裹结构Cursor 用.mcp.jsonCodex 又要求写在 TOML 里。让它们共享一个 JSON 文件等于逼它们做格式兼容现实中根本兼容不了。第三symlink 对“技能”这种需要结构转换的内容完全无效。同一个 markdown 技能文件在 Claude Code 里要多一截 YAML frontmatter在 Cursor 里要写成规则段落的风格在 Codex 里可能又要变成纯文本 prompt。这不只是文件路径的问题是内容格式的转换问题必须通过生成脚本来做。所以最终方案定为“源配置 生成器 落位”源配置里维护一套生成器按目标工具的格式输出把输出放到各工具的默认配置路径。symlink 只在极少数完全同步的场景下使用比如纯 markdown 的技能目录。2.3 “0 配置”到底指什么这里要澄清一下标题里的“0 配置”不是说我做了个东西装完就能自己同步而是指“日常增量维护接近零成本”。首次接入时要做一次初始化把生成结果放到各 agent 的配置目录这个动作只需要做一次。之后你添加一个新的 MCP server只需要在mcp.yml里加一段添加一个新技能只需要在skills/放一个 markdown 文件。保存触发监听脚本各 agent 的配置自动重新生成立刻就能用上。真正减少的是那些每天都可能发生的小动作加一个搜索工具、加一条审查规则、调整某个工具的启动参数。这些动作不再需要打开四个终端、改四份文件、担心格式错乱只需要在一个地方改一次。3. 实操落地从目录设计到自动同步3.1 先设计主配置文件 mcp.yml我选择了 YAML 作为人类主要维护的格式因为它比 JSON 更适合手写注释也方便。mcp.yml的结构分两层shared里放所有 agent 公用的 MCP serveroverrides里放某个 agent 特有的差异配置。shared: servers: memory: command: npx args: - -y - modelcontextprotocol/server-memory filesystem: command: npx args: - -y - modelcontextprotocol/server-filesystem - ~/workspace env: LOCAL_FS_ROOT: ~/workspace overrides: codex: servers: filesystem: env: LOCAL_FS_ROOT: /data/workspace这里新增一个 MCP server 的成本就是往servers里加一个 key。环境变量也在这个文件里集中管理从一开始就杜绝“环境变量漏配”这类隐形问题。相比打开四个工具分别粘贴配置这个维护成本几乎可以忽略。3.2 生成脚本 sync.py生成脚本是整个方案的核心。我用 Python 写了一个简单的同步脚本读取源配置分别渲染出 Claude Code、Cursor、Codex 各自格式的配置再写入对应的输出目录。#!/usr/bin/env python3 import json import yaml from pathlib import Path SYNC Path.home() / .agent-sync MCP_YAML SYNC / mcp.yml GENERATED SYNC / generated def load_config(): with open(MCP_YAML, r, encodingutf-8) as f: return yaml.safe_load(f) def build_servers(cfg, agent_name): shared cfg.get(shared, {}).get(servers, {}) override cfg.get(overrides, {}).get(agent_name, {}) result {} for name, spec in shared.items(): entry { command: spec[command], args: spec.get(args, []), env: {}, } if spec.get(env): entry[env].update(spec[env]) result[name] entry for name, spec in override.get(servers, {}).items(): if name not in result: result[name] {command: , args: []} result[name].update(spec) return result def emit_claude(cfg): data {mcpServers: build_servers(cfg, claude)} out GENERATED / claude / mcp.json out.parent.mkdir(parentsTrue, exist_okTrue) out.write_text(json.dumps(data, indent2, ensure_asciiFalse), encodingutf-8) def emit_cursor(cfg): data build_servers(cfg, cursor) out GENERATED / cursor / .cursor-mcp.json out.parent.mkdir(parentsTrue, exist_okTrue) out.write_text(json.dumps(data, indent2, ensure_asciiFalse), encodingutf-8) def emit_codex(cfg): servers build_servers(cfg, codex) lines [] for name, spec in servers.items(): lines.append(f[mcp.servers.{name}]) lines.append(fcommand {spec[command]}) if spec.get(args): lines.append(args [ , .join(f{a} for a in spec[args]) ]) lines.append() out GENERATED / codex / mcp.toml out.parent.mkdir(parentsTrue, exist_okTrue) out.write_text(\n.join(lines), encodingutf-8) if __name__ __main__: cfg load_config() emit_claude(cfg) emit_cursor(cfg) emit_codex(cfg) print(已生成 claude / cursor / codex 的 MCP 配置)脚本里的路径和格式我按当前常用版本写你实际使用前先跑一次看生成的格式和工具自带配置有没有出入。PowerShell 用户可以用同思路重写成 Python因为你完全不需要关心 shell 语法差异。3.3 技能的生成与落位技能的同步比 MCP 稍微复杂一点。我在skills/目录下放源文件每个文件用 YAML frontmatter 写元信息下面写正文。生成时分别转成 Claude Code 的 command 文件、Cursor 的 rules 文件、Codex 的 prompt 文本。--- name: review description: 让 agent 做一轮代码审查 --- 你是一个资深代码审查者。请重点检查 1. 资源泄漏和异常处理 2. 并发安全 3. 命名与代码结构 4. 测试覆盖对应生成的转换逻辑很简单主要是把不同的 frontmatter 字段映射成各工具认识的字段。def translate_skill_to_claude(skill_text: str): meta, body split_frontmatter(skill_text) return f# {meta[name]}\n\n{body}这里要提醒的是Cursor 的 rules 文件支持globs字段Claude Code 的 command 则更关注description字段。源目录中我会把这类差异统一写在 frontmatter 里生成时再按目标工具挑选对应字段。不要在纯技能正文里揉入工具特定语法否则一旦换工具就会全乱了。3.4 监听变化实现自动同步手动跑python3 sync.py没问题但离“0 配置”还差一步我希望保存文件后不需要任何操作。于是写了个监听脚本用文件监听工具来监控mcp.yml和skills/下的改动改动发生后自动重新生成所有配置。#!/bin/bash while true; do inotifywait -r -e modify,create,delete ~/.agent-sync/mcp.yml ~/.agent-sync/skills python3 ~/.agent-sync/scripts/sync.py donemacOS 上没有inotifywait可以用fswatch替代fswatch -o ~/.agent-sync/mcp.yml ~/.agent-sync/skills | while read; do python3 ~/.agent-sync/scripts/sync.py done我建议把生成脚本设置成开机启动或者直接放在当前 shell 的会话里挂后台。这样日常使用时只需要编辑源配置其他全部自动。3.5 首次接入一次性初始化假设你是从存量状态开始机器上已经有一堆工具配置。我的建议是不要把现有配置直接丢弃先做一次迁移。迁移分三步走。第一步把当前各个 agent 里能正常运行的 MCP server一条条抄到mcp.yml的shared区域。第二步逐项确认环境变量和工作目录是否一致特别关注某些 agent 特有的启动路径。第三步跑一次sync.py对比生成结果与原配置的差异确认没有差异后再决定要不要删除旧配置。我个人的习惯是保留旧配置备份一周确认无回退需求再清理。4. 坑点、排查与多环境迁移4.1 路径和环境变量的坑不管用哪种 agentMCP server 都会遇到“路径找不到”或者“环境变量丢失”的问题。比如在 Claude Code 里一切正常换到 Cursor 后突然报错找不到某个二进制十有八九是启动时的 PATH 不一致。Cursor 这类图形界面工具在 GUI 启动时并不会完整继承你 shell 里的环境变量。解决方法是把所有环境变量统一写进mcp.yml的env字段不要依赖系统 PATH。生成脚本统一注入。如果某个 agent 坚持不读你传的 env那就在工具的自定义配置文件里显式加一份同样的变量同时把这段变量也补回源配置避免下次生成覆盖丢失。4.2 技能格式不只是 markdown我最早以为技能就是 markdown 文件复制到哪个目录都一样。实际跑下来发现不是。Claude Code 里的 command 会产生一次子代理执行Cursor 的 rules 会被编辑器在后台作为背景提示Codex 更夸张它的指令是按会话上下文注入的。同样一段 markdown在不同工具里的行为完全不同。所以转换的重点不是文件后缀而是要理解目标工具的加载时机。如果一段技能在 Claude Code 里是“描述一个执行流程”在 Cursor 里就应该把它写成“编辑器内长期生效的规则列表”在 Codex 里则适合写成“带上输入输出的完整提示词”。我的方案是在源文件的 frontmatter 里写清楚使用场景让生成脚本按场景输出不同风格的正文而不是机械地整段平移。4.3 多机器同步我在家里和公司两台机器上跑同一套配置中间用过云盘同步也用过 git 仓库。最终推荐用 git因为配置的演进历史可见回滚方便。要注意两个处理一是把generated/目录加入.gitignore因为它包含本机路径和可能的环境变量引用不应该跨机器提交二是各机器的差异尽量写在overrides区域而不是直接改源文件。比如公司机器上的文件系统路径是/data/workspace家里是~/workspace这种差异用 overrides 表达最合适。generated/ *.log .DS_Store新机器上只需要 git clone复制mcp.yml和skills/然后跑一次sync.py初始化所有 agent 就能恢复一致的配置环境。5. 我目前的工作流和一点建议跑通这套方案之后我实际的体会是最明显的收益不是“省了配置时间”而是“不再打断心流”。以前在 Cursor 里思考问题突然想加一个搜索工具得停下来去翻文档写配置现在只是在mcp.yml里加一行保存再用的时候已经自动加载好了。处理技能的方式也自然了很多。我的skills/目录更像一个“个人提示词库”它独立于任何工具存在我在 Claude Code 里写好的审查规则换到 Codex 里也能用不用重写。唯一的建议是你不要一口气把所有技能都搬进中心目录先放两三个高频技能跑一段确认转换逻辑和加载时机符合预期再逐步迁移其他内容。最后再分享一个小技巧每次改完mcp.yml或技能文件后不要只信监听脚本偶尔手动跑一次sync.py然后故意在某个 agent 里触发一次工具调用。如果报错优先检查环境变量和路径这比看生成文件内容更直接。这个习惯帮我避开了好几次“配置看起来没问题实际跑起来就是不行”的隐患。