- 开发工具
- CLI
- AI 应用
【免费下载链接】zcf
Zero-Config Code Flow for Claude code & Codex
ZCF(Zero-Config Code Flow)是一套面向 Claude Code 与 Codex 的零配置初始化工具链,其中内置的 MCP(Model Context Protocol)服务配置是用户最常接触的能力之一。本文将基于仓库内.zcf/plan/history/mcp-services-refactor.md重构计划,完整复盘"把MCP_SERVICES从constants.ts中剥离、让业务配置与多语言文本彻底分离"这一重构工程,涵盖目标架构、配置数据结构、i18n 翻译体系、TDD 三阶段实施步骤以及完整调用链,读者可以借此掌握一种可复制的"配置模块 + i18n 分离 + 测试驱动"的重构方法论。
一、重构背景:业务配置与多语言文本的"混居"困境
在重构之前,ZCF 的 MCP 服务配置MCP_SERVICES直接内嵌在src/constants.ts中。这意味着同一个数组里同时承载了两类完全不同的职责:
- 业务配置:服务 ID、启动命令、参数、环境变量、是否需要 API Key 等与运行逻辑强相关的数据;
- 多语言文本:服务的展示名称(name)、描述(description)等纯展示层内容。
这种"混居"带来的直接问题在于:
- 职责耦合:修改文案需要改动业务配置,调整配置又容易误伤翻译,
constants.ts日益膨胀; - i18n 无法接入:名称与描述是硬编码的单一语言字符串,无法跟随系统语言动态切换;
- 可维护性差:新增一个 MCP 服务需要同时理解两套语义,容易埋下错误。
值得注意的是,ZCF 项目中并非首次遇到这种问题——workflows.ts(工作流配置)早已完成了同样的分离改造,形成了成熟可复用的模式。因此本次重构的上下文非常清晰:参照src/config/workflows.ts的既有模式,为 MCP 服务配置建立同样的"纯业务配置 + 翻译文件"双轨结构(相关上下文记录见 mcp-services-refactor.md)。
二、选定方案:完全分离式架构
重构计划明确选定了"方案 1:完全分离式",其核心设计如下:
- 创建
src/config/mcp-services.ts:存放纯业务配置(不含任何 i18n 文本); - 完善现有
src/i18n/locales/*/mcp.json翻译文件:名称、描述、API Key 提示等展示文本全部迁移至翻译文件; - 创建配置合并函数:提供统一接口,在运行时将业务配置与翻译动态合并,对外暴露完整的
McpService对象。
整个改造要求保持现有功能完全兼容,并遵循 TDD(测试驱动开发)模式分三个阶段推进。重构完成后,src/constants.ts中不再保留任何 MCP 服务相关内容(当前仓库中搜索MCP_SERVICES已无任何结果,可证实清理完成)。
三、新配置模块深度解析
3.1 业务配置数据结构:McpServiceConfig
重构后的src/config/mcp-services.ts定义了全新的纯业务配置接口,刻意不包含任何展示字段:
export interface McpServiceConfig { id: string requiresApiKey: boolean apiKeyEnvVar?: string config: McpServerConfig }字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 服务唯一标识,也是写入.mcp.json时使用的 key |
requiresApiKey | boolean | 是否需要用户提供 API Key |
apiKeyEnvVar | string(可选) | API Key 应注入的环境变量名,如EXA_API_KEY |
config | McpServerConfig | 底层 MCP Server 的启动配置 |
注意:接口中没有name和description字段——测试 mcp-services.test.ts 第 10-24 行专门断言了这一点(expect(config).not.toHaveProperty('name')),从测试层面强制保证"纯业务配置"的纯度。
3.2 内置 MCP 服务全景
MCP_SERVICE_CONFIGS是重构后的核心配置数组,当前仓库中共注册 7 个服务,完整配置如下(源码见 mcp-services.ts):
| ID | 启动方式 | 命令/地址 | 关键环境变量 | 需要 API Key |
|---|---|---|---|---|
context7 | stdio | npx -y @upstash/context7-mcp@latest | 无 | ❌ |
open-websearch | stdio | npx -y open-websearch@latest | MODE=stdio、DEFAULT_SEARCH_ENGINE=duckduckgo、ALLOWED_SEARCH_ENGINES=duckduckgo,bing,brave | ❌ |
spec-workflow | stdio | npx -y @pimzino/spec-workflow-mcp@latest | 无 | ❌ |
mcp-deepwiki | http | https://mcp.deepwiki.com/mcp | 无 | ❌ |
Playwright | stdio | npx -y @playwright/mcp@latest | 无 | ❌ |
exa | stdio | npx -y exa-mcp-server@latest | EXA_API_KEY(占位值YOUR_EXA_API_KEY) | ✅ |
serena | stdio | uvx --from git+https://github.com/oraios/serena serena start-mcp-server --context ide-assistant --enable-web-dashboard false | 无 | ❌ |
其中有几个细节值得注意:
open-websearch的配置最有代表性,通过三个环境变量默认启用 DuckDuckGo 搜索引擎,同时允许bing、brave作为备选,无需 API Key 即可开箱即用。测试 mcp-services.test.ts 对这段配置做了逐字段精确断言;mcp-deepwiki是唯一一个http类型服务,只配置url,没有command/args/env。类型安全测试(同文件第 228-231 行)专门验证了 http 类型的服务必须无 command;exa是唯一需要 API Key 的服务,apiKeyEnvVar指定为EXA_API_KEY,翻译文件中对应提供了apiKeyPrompt文案;serena使用uvx而非npx启动,且--context参数在安装到 Codex 时会被动态替换为codex(见下文的调用链分析)。
3.3 配置合并函数:getMcpServices与getMcpService
src/config/mcp-services.ts的核心价值在于提供了两个异步合并函数,将"静态翻译 + 动态运行时语言"与"纯业务配置"统一缝合:
export async function getMcpServices(): Promise<McpService[]> { ensureI18nInitialized() // 1. 静态声明 i18n 键(兼容 i18n-ally 插件自动提取) const mcpServiceList = [ { id: 'context7', name: i18n.t('mcp:services.context7.name'), description: i18n.t('mcp:services.context7.description') }, // ... 每个服务对应一组 name/description 键 ] // 2. 将 MCP_SERVICE_CONFIGS 与翻译按 id 匹配合并 return MCP_SERVICE_CONFIGS.map((config) => { /* ... */ }) } export async function getMcpService(id: string): Promise<McpService | undefined> { const services = await getMcpServices() return services.find(service => service.id === id) }合并逻辑的关键行为(源码第 136-158 行):
- 以
MCP_SERVICE_CONFIGS为主循环,按id从静态翻译列表中查找对应的name与description,未命中时用id兜底; - 仅当服务
requiresApiKey === true且翻译中存在apiKeyPrompt时才附加该字段; - 若配置声明了
apiKeyEnvVar,则一并注入返回对象。
调用ensureI18nInitialized()保证了在 i18n 未初始化时抛错而非静默返回空翻译,避免了"无文案可用"的隐性故障。
四、i18n 翻译体系:展示文本的完整迁移
4.1 翻译文件结构
名称与描述等展示文本全部迁移至按语言组织的 JSON 文件,由 i18next 的 fs-backend 按{{lng}}/{{ns}}.json路径加载:
- 中文:src/i18n/locales/zh-CN/mcp.json
- 英文:src/i18n/locales/en/mcp.json
mcp命名空间(namespace)的键设计采用扁平结构,以services.<服务id>.{name,description}为约定,例如:
{ "configureMcp": "是否配置 MCP 服务?", "selectMcpServices": "选择要安装的 MCP 服务", "services.context7.name": "Context7 文档查询", "services.context7.description": "查询最新的库文档和代码示例", "services.exa.apiKeyPrompt": "请输入 Exa API Key", "services.serena.name": "Serena 助手", "services.serena.description": "提供类似 IDE 的语义代码检索与编辑……" }英文文件保持同样的键结构,仅替换文案内容。除了各服务的name/description外,命名空间还承载了安装流程提示语(configureMcp、selectMcpServices、mcpBackupSuccess、mcpConfigSuccess)以及 API Key 相关错误文案(apiKeyPrompt、apiKeyApprovalFailed、primaryApiKeySetFailed)。
4.2 键分隔符的设计细节
i18n 初始化配置(src/i18n/index.ts)中有一处关键设定:keySeparator: false, nsSeparator: ':'。这意味着翻译键禁用点号分隔、仅用冒号切分命名空间,因此mcp:services.context7.name中的services.context7.name是一整个扁平键,而非嵌套对象路径。这也是getMcpServices内核对serviceInfo.apiKeyPrompt !== \mcp.services.${config.id}.apiKeyPrompt`` 这一兜底判断的背景:当翻译缺失返回原始键名时,可以识别出来并用空值规避。
4.3 多语言加载路径
翻译文件在开发与打包场景下通过多条候选路径定位(src/i18n/index.ts):开发期读取src/i18n/locales,npm 包安装后读取node_modules/zcf/dist/i18n/locales,生产构建读取./dist/i18n/locales,并依次探测直到找到zh-CN/common.json。所有命名空间在initI18n时逐一loadNamespaces预加载,确保getMcpServices执行时翻译立即可用。
五、类型系统:重构后的契约边界
重构并未改变公开类型契约,src/types/CLAUDE.md中记录了完整的类型定义:
export interface McpService { id: string name: { 'en': string, 'zh-CN': string } // 运行时被解析为 string description: { 'en': string, 'zh-CN': string } requiresApiKey: boolean apiKeyPrompt?: { 'en': string, 'zh-CN': string } apiKeyPlaceholder?: string apiKeyEnvVar?: string config: McpServerConfig } export interface McpServerConfig { type: 'stdio' | 'sse' // 当前仓库实际还使用 http 类型 command?: string args?: string[] url?: string env?: Record<string, string> }McpServerConfig是写入 Claude/Codex 配置文件中mcpServers字段的基本单元(mcpServers: Record<string, McpServerConfig>)。重构前调用方若直接依赖McpService的字符串name/description,重构后仍能获得同样形状的对象——这正是"保持完全兼容"的类型层保证。类型安全测试 mcp-services.test.ts 对stdio必须携带command+args、http必须携带url的约束做了系统性校验。
六、TDD 三阶段实施流程
重构计划为工程落地设计了严谨的三阶段 TDD 节奏(详见 mcp-services-refactor.md 的"实施步骤"章节):
阶段 1:测试驱动的配置模块创建
- 先写测试用例:覆盖
MCP_SERVICE_CONFIGS的结构约束(无硬编码 name/description)、服务 ID 全集、open-websearch与mcp-deepwiki的精确配置; - 再建模块:创建
src/config/mcp-services.ts,实现McpServiceConfig接口与MCP_SERVICE_CONFIGS; - 实现合并函数直至测试通过:迭代实现
getMcpServices/getMcpService,用红-绿-重构循环驱动。
阶段 2:测试驱动的 i18n 完善
- 编写翻译完整性测试:验证中英文服务数量一致、ID 列表一致、英文文案不含中文字符(测试中通过正则
/[\u4E00-\u9FA5]/检测); - 完善翻译文件:补齐
zh-CN/mcp.json与en/mcp.json的全部键; - 更新类型定义:同步
McpService等类型的可选字段与注释。
阶段 3:测试驱动的代码迁移
- 编写集成测试验证新旧兼容性:确认所有调用方拿到的
McpService对象字段完整; - 更新所有引用
MCP_SERVICES的代码:将src/commands/init.ts、src/utils/features.ts、src/utils/mcp-selector.ts、src/utils/code-tools/codex-configure.ts统一切换到新模块; - 清理
src/constants.ts:删除旧的MCP_SERVICES常量。
重构计划预期的最终状态是:业务配置与 i18n 完全分离、现有功能完全兼容、代码更清晰易维护、测试覆盖率 100%。当前仓库中src/constants.ts已无任何 MCP 内容、src/i18n/locales/*/mcp.json与tests/config/mcp-services.test.ts均已落地,可确认三个阶段全部完成。
七、完整调用链:从选择器到配置落盘
重构的价值最终体现在调用方是否真正获益。梳理仓库源码,MCP 服务配置的完整消费链路如下:
7.1 统一选择器selectMcpServices
src/utils/mcp-selector.ts 是重构的最大受益者:它只需调用getMcpServices()拿到带翻译的服务列表,就能用 inquirer 的 checkbox 渲染多选菜单(${service.name} - ${service.description}),彻底摆脱了对翻译文件的直接依赖:
export async function selectMcpServices(): Promise<string[] | undefined> { ensureI18nInitialized() const mcpServices = await getMcpServices() const choices = mcpServices.map(service => ({ name: `${service.name} - ${ansis.gray(service.description)}`, value: service.id, selected: false, })) // inquirer checkbox prompt... }7.2 安装流程init.ts的 MCP 步骤
src/commands/init.ts第 915-994 行展示了完整的安装逻辑:
- 调用
selectMcpServices()获取用户勾选的服务 ID 列表(支持--skip-prompt跳过); - 若已有 MCP 配置,先
backupMcpConfig()备份(输出mcp:mcpBackupSuccess文案); - 遍历每个选中服务,通过
getMcpServices()取回完整对象; - 针对
serena做特殊处理:根据当前代码工具类型(Codex 或 Claude Code)动态调整--context参数值; - 针对需要 API Key 的服务(如
exa):弹出service.apiKeyPrompt输入框,调用buildMcpServerConfig注入密钥; mergeMcpServers与现有配置合并 →fixWindowsMcpConfig修复 Windows 命令包装 →writeMcpConfig落盘(成功输出mcp:mcpConfigSuccess)。
同样的流程也复用在src/utils/features.ts(configureMcpFeature)中,二者共享selectMcpServices与getMcpServices,消除了重复实现。
7.3 API Key 注入的实现细节
buildMcpServerConfig(src/utils/claude-config.ts)承担了密钥注入职责,其处理优先级是:
- 深拷贝基础配置,避免污染
MCP_SERVICE_CONFIGS共享对象; - 平台适配:在 Windows 上若命令需要包装则改写为
cmd /c形式; - 若提供了
envVarName且配置带env:直接config.env[envVarName] = apiKey(如EXA_API_KEY)并提前返回——这是新式注入路径; - 否则走旧式占位符替换:把
YOUR_EXA_API_KEY等占位符替换进args和url。
测试 mcp-services.test.ts 验证了exa服务经getMcpService('exa')后同时携带apiKeyPrompt与apiKeyEnvVar: 'EXA_API_KEY',保证两条注入路径所需信息齐全。
7.4 Codex 侧的接入
src/utils/code-tools/codex-configure.ts也引入了新模块(import { getMcpServices ... }),说明这次重构同时为 Claude Code 与 Codex 两套目标工具的配置生成提供了统一的 MCP 服务数据源,印证了计划中"保持现有功能完全兼容"的达成。
八、测试保障:重构质量的量化证据
测试文件 tests/config/mcp-services.test.ts 是本次重构"测试先行"的直接产物,围绕四个维度构建了 10 余条用例:
| 测试维度 | 核心断言 |
|---|---|
| 配置纯度 | 每个配置项含id/requiresApiKey/config,且不含name/description |
| 配置正确性 | 服务 ID 全集、open-websearch的完整 env、mcp-deepwiki的 http 结构 |
| 翻译完整性 | 中英文服务数量与 ID 列表一致、英文文案无中文字符、name/description 非空 |
| 查询行为 | getMcpService按 ID 命中、未知 ID 返回undefined、API Key 字段正确附加 |
在init.ts、init-validation.test.ts、init-param-validation.test.ts等调用方测试中,src/config/mcp-services已成为标准的 mock 目标(例如vi.mock('../../../src/config/mcp-services')),说明新模块已成为测试基础设施中的一等公民,任何调用方测试都可独立 mock 该配置源。
九、重构收益与可复用方法论
对照重构计划的"预期结果",本次改造的实际收益可以归纳为:
- 职责彻底分离:业务配置与 i18n 文本分居 mcp-services.ts 与 mcp.json,
constants.ts回归纯粹; - 零兼容性破坏:
McpService/McpServerConfig公开契约不变,四个调用方平滑切换; - 翻译体系统一:新增服务只需"业务配置数组加一条 + 两种语言 JSON 各加一组键",规则明确;
- 测试驱动落地:从"先写测试"到"集成验证"全程有测试护航,测试文件同时充当配置的活文档;
- 模式可复用:
workflows.ts→mcp-services.ts证明该"配置 + i18n 分离 + 合并函数 + TDD"的路径在 ZCF 内已形成标准范式。
若要在自己的项目中复制这套模式,推荐路径是:先选定参照模块(如本项目的workflows.ts)→ 定义纯业务配置接口并先写纯度测试 → 迁移文案到命名空间 JSON → 实现合并函数直至全绿 → 逐个替换调用方并清理旧常量。
- 开发工具
- CLI
- AI 应用
【免费下载链接】zcf
Zero-Config Code Flow for Claude code & Codex
相关推荐
ZCF MCP 服务集成实战:为 Claude Code 与 Codex 一键配置七大 MCP 服务
ZCF MCP 服务集成实战:为 Claude Code 与 Codex 一键配置七大 MCP 服务 ZCF 内置了一批经过预配置的 MCP(Model Con
开发工具CLIAI 应用九网盘直链解析一键搞定:LinkSwift 多网盘直链下载助手完整解析
九网盘直链解析一键搞定:LinkSwift 多网盘直链下载助手完整解析 LinkSwift 是一个纯前端的网盘直链解析工具,一个油猴脚本就能从百度网盘、阿里云盘
前端ASP.NET Boilerplate 启动配置完全指南:PreInitialize 配置体系、模块配置点与服务替换实战
ASP.NET Boilerplate 启动配置完全指南:PreInitialize 配置体系、模块配置点与服务替换实战 导读 ASP.NET Boilerpl
后端Web框架依赖注入认证鉴权
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考