CLI、MCP、Skills 同时在场 —— 从 8 个 Agent 源码看到的
作为 Codex、Claude Code 或者 OpenClaw、Hermes 等 Agent 的用户,你应该每天都在跟 Skills、MCP、CLI 这些概念打交道,可能也随时能看到一些媒体文章,讨论谁更好、该怎么选、该怎么配。
过去一年半,这样的讨论经历了好几波,好在今年初开始有一些相对客观的表达,尝试说明 MCP、Skills、CLI 各自的特点和定位。不过,这些讨论大多停留在定性层面:
> - **2024.11–2025.5**:Anthropic 发布 MCP,Cursor、OpenAI、Google、微软相继接入,国内 BAT 全线跟进,一些媒体称之为 "AI 的 HTTP 协议"
> - **2025.10–12**:Anthropic 推出 Skills, "渐进式披露"、"按需加载"、"token 开销远低于 MCP"成为新的关注点
> - **2026.1–3**:OpenClaw 作者 Peter Steinberger 指出 CLI 天然适配 Agent;3 月 Perplexity CTO 宣布内部从 MCP 转向 CLI,YC 掌门 Garry Tan 公开指出 MCP 的上下文膨胀问题,国内开发者社区随之展开讨论 —— 也就是这个时候,CLI 这个大家每天都在用的东西,被重新放进了 "Agent 扩展能力"的讨论框架里
不知道你会不会好奇:在你使用的真实的 Agent 环境里 —— CLI、MCP、Skills 这些都算是标准的扩展能力同时存在同时可用 —— Agent 究竟会怎么协调它们的?谁先谁后?谁管谁?是不是谁会更好、谁会替代谁?我们尝试从 8 个常见的 Agent 的源码入手 —— Codex、Gemini CLI、Kimi Code、MiMo Code、OpenClaw、Hermes、OpenCode、ClaudeCode(排名不分先后)—— 把一个问题拆成四个递进的子问题来验证:**当 CLI、MCP、Skills 同时在一个 Agent 里,面对同一个需求时,它们到底怎么分工、谁覆盖谁、谁管谁。**
## 调研样本
| 来源 | Agent | 代码仓库 | 代码版本 |
|-------------------|---------------------|---------------------------------------------|------------------|
| GitHub 官方 repo | Codex ( OpenAI ) | [github.com/openai/codex](http://github.com/openai/codex) | `4c43465` (2026-07-25) |
| GitHub 官方 repo | Gemini CLI ( Google ) | [github.com/google-gemini/gemini-cli](http://github.com/google-gemini/gemini-cli) | `3818efb` (2026-07-24) |
| GitHub 官方 repo | Kimi Code (月之暗面) | [github.com/MoonshotAI/kimi-code](http://github.com/MoonshotAI/kimi-code) | `c497af6` (2026-07-25) |
| GitHub 官方 repo | MiMo Code (基于 OpenCode ) | [github.com/XiaomiMiMo/MiMo-Code](http://github.com/XiaomiMiMo/MiMo-Code) | `29321aa` (2026-07-25) |
| GitHub 官方 repo | OpenCode | [github.com/anomalyco/opencode](http://github.com/anomalyco/opencode) | `0a6637e` (2026-07-25) |
| GitHub 官方 repo | OpenClaw | [github.com/openclaw/openclaw](http://github.com/openclaw/openclaw) | `6e604438` (2026-07-25) |
| GitHub | Hermes ( NousResearch )| [github.com/NousResearch/hermes-agent](http://github.com/NousResearch/hermes-agent) | `760112a` (2026-07-25) |
| GitHub | ClaudeCode ( Anthropic ) | 早期泄露版本 | `a99de1b` (2026-03-31) |
MiMo Code 基于 OpenCode fork ,两个仓库有直接可比性。下文会在分析中标注哪些设计来自 OpenCode 原有代码,哪些是 MiMo Code 独立添加的。
## 一、问:工具池里,谁覆盖谁?
当一个 CLI 工具和 MCP 工具都能做同一件事,Agent 的实现代码怎么处理冲突?
这是一个纯代码层的问题,不看 prompt,不看模型意图,就看工具注册表。
### Codex:核心工具名保留
**Codex** 把所有工具 —— ShellCommandHandler、ExecCommandHandler、MCP 工具 —— 统一存入同一个 `ToolRegistry`(`HashMap<ToolName, CoreToolRuntime>`)。核心工具先注册,占据工具名。当扩展/MCP 工具尝试注册同名工具时,直接跳过:
```rust
if !reserved_tool_names.insert(tool_name.clone()) {
warn!("Skipping extension tool `{tool_name}`: tool already registered");
continue;
}
```
### ClaudeCode:built-in 优先
**ClaudeCode** 的 `assembleToolPool` 里,内置工具在前,MCP 工具在后,`uniqBy` 按名去重。注释直接写了 "built-ins win on name conflict":
```typescript
return uniqBy(
[...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),
'name',
)
```
两个 Agent,两种语言,不同的代码结构。结论出奇一致:**CLI 作为内置工具,同名时覆盖 MCP。**没有例外。
全样本对照:
| Agent | CLI 工具注册 | MCP 工具注册 | 同名冲突处理 |
|--------------|-----------------------|--------------------|------------------------|
| Codex | ShellCommandHandler / ExecCommandHandler → ToolRegistry | McpHandler → ToolRegistry | 核心工具先注册,MCP/扩展同名时跳过 |
| Gemini CLI | Shell tool → ToolRegistry | MCP tools → ToolRegistry | 并列注册,无显式优先级规则 |
| Kimi Code | BashTool → builtinTools Map | mcpTools 独立 Map | 三个 Map 分别管理,loopTools 合并 |
| MiMo Code | bash (BashTool) → tool registry | MCP 通过 toolScriptMcp 注入 | 内置工具先注册 |
| OpenCode | shell (ShellTool) → tool registry | MCP 在 SessionTools 外部组装 | 内置先于 MCP |
| OpenClaw | exec 作为一等内置工具 | MCP tools 进入同一策略管道 | 无显式优先级 |
| Hermes | terminal 作为内置工具 | MCP tools 注册进 tool registry | 内置工具同名优先 MCP |
| ClaudeCode | BashTool 进入内置工具池 | MCP 在 assembleToolPool 合并 | uniqBy 保留插入顺序,builtins win |
## 二、问:工具什么时候让模型看见?
覆盖是写代码时的事。另一个问题是:工具在什么时间点暴露给模型。
### 三种可见性策略
**Kimi Code** 给出了一种最明确的回答:不是所有工具都平等可见。CLI 工具(`BashTool`)始终在顶层 `tools[]` 中,模型每一步都能直接调用。MCP 工具则被排除在顶层之外 —— 当 `toolSelectEnabled` 为 true 时,模型需要先通过 `<tools_added>` / `<tools_removed>` 系统消息发现 MCP 工具的名称,再调用 `select_tools` 工具加载完整定义,之后才能调用。这被 Kimi 称为**渐进式披露** —— MCP 工具不做 "插上就能用",而是 "发现 → 加载 → 调用" 三步走。
这种做法影响的是模型看见工具的时机。更大的主动性被交给了模型 —— 它根据当前需求决定加载哪些工具,而不是被迫在一堆可能无关的工具定义里做选择。
两个直接效果:一是减少了上下文占用,一定程度缓解了 token 浪费;二是更重要的是,工具不再是大家经常吐槽的那种 "一股脑全部暴露" 的方式。
**Gemini CLI** 对 Skills 也做了类似的处理。它的 `ActivateSkillTool` 要求模型在看到 available skills 列表后,调用激活工具加载 skill 完整内容,并且需要用户确认。Skills 在 Gemini CLI 里不是自动激活的。
**OpenCode** 代表的是最简单的情况:Skills 通过 prompt 注入、按名自动匹配,MCP 和 CLI 工具始终可用,不加任何渐进披露层。这个基线是 MiMo Code fork 的起点,下文会看到它在上面加了什么。
**ClaudeCode** 的做法更复杂。它的 `SkillTool` prompt 写了:Skill 命中时是 **BLOCKING REQUIREMENT**,必须先调用 Skill 再生成其他响应。Skills 的匹配是自动的 —— 模型看到 skill 的 name 和 description,由 prompt 规则驱动判断,不需要像 Gemini 那样先激活。但在 MCP 这边,ClaudeCode 也有类似 Kimi Code 的机制:它有一个 `ToolSearchTool`,MCP 工具默认是 **deferred**(延迟加载)状态 —— 模型只看到工具名,需要调用 `ToolSearchTool` 加载完整 schema 才能使用。MCP server 可以通过 `_meta['anthropic/alwaysLoad']` 主动 opt-out 。也就是说 ClaudeCode 也有 MCP 的渐进披露机制。Kimi Code 后续在 `select_tools` 中采用了近似的实现。
### MiMo Code 的设计特例
而 **MiMo Code** 的做法最值得回味。我们知道标准 Skills 的运作方式本身就是渐进式披露:模型只看到 name 和 description,按需决定是否加载完整内容 —— 这已经是第一层 "不全量暴露"。但 MiMo Code fork 后添加了 `SkillSearchTool`,为 Skills 又加了一层门控:连 name + description 也不是直接全部给模型看的,而是先经 BM25 相关性搜索 —— 高置信度匹配自动加载,低置信度则返回排序结果让模型选择,不匹配的直接不出现。也就是说,MiMo Code 里 Skills 做了**两层渐进式披露**:第一层是标准的 "不暴露正文",第二层是 "连目录也要先搜再给"。
但正如那几次集中讨论所展现的那样,MCP 是被吐槽占用上下文最多的能力形式,我们却没有在 MiMo Code 的代码里找到类似 Kimi Code 已经采用的 `select_tools` 这类实现,没有给 MCP 加任何渐进披露。我们在 MiMo Code 源码样本里可以看到,MCP 工具从连接到注入是全量 "盲注" —— 所有 server 的所有工具定义直接进入模型上下文,没有 select_tools、没有搜索、没有门控。可是已经具备渐进式披露特性的 Skills,MiMo Code 为它再加了一层门控。我们没能在 MiMo Code 官方 repo 的 Issues 或 PRs 里找到有关的信息,对这个设计取舍的原因还不得而知。
一个可能相关的解释来自代码里的一个细节:MiMo Code 的 skill search 在 Claude 和 GPT 模型上是被**显式禁用的**。我尝试将该 Skills 渐进披露理解为一种 "弱模型能力补丁" —— 强模型不需要帮忙做技能选择,但也仅限于猜测。加上 MiMo 自家模型有 100 万 token 的上下文窗口,MCP 工具定义的 token 开销或许算不上是瓶颈。
## 三、问:Skill 能不能管工具?
前两个问题讲的是工具层内部的关系。再往上一层:Skill —— 作为工作流描述 —— 有没有能力约束 CLI 和 MCP 的使用。
这个问题在各 Agent 之间产生了最大的分歧。
### ClaudeCode:硬授权
**ClaudeCode** 是目前唯一在代码层实现了 "Skill 硬授权 CLI" 的。它的 Skill frontmatter 支持 `allowed-tools` 字段,Skill 作者可以声明 "这个流程允许执行哪些命令"。当 Skill 被激活时,`allowedTools` 被注入为 `alwaysAllowRules.command`,预授权特定的 bash 命令模式。这不是 prompt 建议,是权限层的硬操作。Skill 的 `shell` frontmatter 还可以指定用 `bash` 还是 `powershell`,虽然 skill 作者的选择不能覆盖用户的运行时开关。
### Codex:声明式依赖
**Codex** 的 Skill 可以声明工具依赖,但不算硬约束。它的 `SkillToolDependency` 结构体支持 `type: "cli"` 和 `type: "mcp"` 两种依赖类型,告诉系统 "这个 Skill 需要 gh CLI" 或 "这个 Skill 需要 GitHub MCP server"。但这更像声明关系,不强制权限。
### 其他 Agent:纯 prompt
**Kimi Code、MiMo Code、OpenClaw、Hermes** 的 Skills 是纯 prompt 机制。Skill 被注入为一段 markdown 文本,能**建议**模型做什么,不能**阻止**模型做什么。在这些 Agent 里,"Skill 管不了工具"不是设计缺陷,是当前架构的共识设定。
OpenClaw 的 [AGENTS.md](http://agents.md/) 把这个边界写成了显式的哲学立场:"Skills own workflows; root owns hard policy and routing"——Skill 负责工作流,但硬性策略和路由归 root(也就是 Agent 应用本身)。Skill 可以告诉模型 "该怎么做",但不能替模型决定 "能做哪些"。这是一种清醒的分工:流程归你,权限归我。
## 四、问:有没有显式的设计框架?
前三个问题看的是代码行为。最后一个问题看的是设计自觉 —— 有没有 Agent 把 CLI、MCP、Skills 之间的取舍逻辑写成显式的规则,而不是让它从代码里 "自然长出来"。
大部分 Agent 没有。但有两个做了,而且方式不同。
### Hermes:Footprint Ladder
**Hermes** 的 [AGENTS.md](http://agents.md/) 里有一个 "Footprint Ladder" (足迹阶梯,第 182-206 行),按 "新增永久表面积" 从小到大排列所有能力扩展方式。这里的 "表面积" 是软件维护的概念 —— 每新增一个工具、一个 API、一条代码路径,就多了一块需要持续维护测试安全审计的面积。阶梯越往上,引入的 "表面积" 越小:
1. 扩展现有代码 ——零新增表面积
2. **CLI command + skill** —— Agent 执行 `hermes <subcommand>`,由 skill 提供流程指导。零模型工具表面积。这是订阅任务、定时任务、服务设置的默认选择
3. Service-gated tool —— 需要结构化参数但仅在前提条件满足时出现
4. Plugin —— 第三方能力,不进 core
5. **MCP server** —— 如果能力确实需要成为工具(结构化 I/O),但不核心,优先做成 MCP server 而非新增 core tool
6. New core tool —— 最后手段
CLI+skill 在第二级,MCP server 在第五级。这个排序不是基于 "谁更好" —— Footprint Ladder 的唯一标准是新增表面积。Hermes 明确说:"选择最高(表面积最小)的可行阶梯。"
通俗地讲:**能用 CLI 和 skill 解决的事,不用 MCP;能用别人写的 MCP server 解决的事,不自己写 core tool。**
### OpenClaw:结构性职责划分
**OpenClaw** 没有 Hermes 那么细的阶梯,但给了一个简洁的结构性描述:AGENTS.md 开头第一句就是 "Skills own workflows; root owns hard policy and routing"( Skills 拥有工作流,root 拥有硬策略和路由)。三层关系一目了然 —— **Skills 管怎么做,Agent 管怎么排,工具(不管是 CLI 还是 MCP)是被编排的**。
这两个通用型 Agent 的框架,跟前面 Coding Agent 的代码实现指向同一个方向:**三者不是平级竞争关系。CLI 直接干活,MCP 接外部系统,Skills 管理流程。**区别在于,Hermes 和 OpenClaw 把这件事写成了可以指导后续开发的显式规则,而其他 Agent 是让代码行为自然形成分层 —— 有效果,但少了一份 "我们想清楚了" 的自觉。
## 五、ClaudeCode:把事情串起来的样本
看完各 Agent 在一两个子问题上的处理,可以发现 ClaudeCode 的特别之处在于它在**四个子问题上都有独特的设计**:
**工具冲突**:`assembleToolPool` 里 built-in 优先于 MCP,CLI 显然权重高于 MCP 的。
**可见性**:Skill 命中是 BLOCKING REQUIREMENT,必须先加载 Skill,再决定用什么工具。同时 MCP 工具默认 deferred,需通过 `ToolSearchTool` 加载 schema 后才能调用。MCP server 还可以通过 `_meta['anthropic/alwaysLoad']` 让特定工具跳过 deferred,直接暴露完整 schema —— 这相当于一个配置层面的门控开关。
**Skill 管工具**:`allowed-tools` 字段让 Skill 可以硬授权 CLI 命令 —— 全样本唯一的硬约束。
**完整设计**:MCP server 可以提供 Skill(通过 `skill://` resources),但 MCP Skill 被标记为不可信 —— 不执行 inline shell command ,本地 Skill 优先于同名 MCP Skill(`SkillTool.ts:82-93` 的 `uniqBy([...localCommands, ...mcpSkills], 'name')`)。也就是说,MCP 可以是 Skill 的分发渠道,但 Agent 保留最终控制权。
ClaudeCode 不是每一项都做得最激进,但它是把 CLI、MCP、Skills 三层之间的硬交互做得最全面的。这可能跟它作为早期版本有关 —— 实验更多,规矩少一些。但不管原因是什么,它提供了一个很完整的参考样板。
需要说明的是,本文分析的 ClaudeCode 代码来自 2026 年 3 月的泄露版本(`a99de1b`)。ClaudeCode 不开源,后续版本可能已经调整了上述设计 —— 比如 ToolSearchTool 的触发条件、MCP deferred 的默认行为等,都有可能发生变化。以上分析仅反映该版本的状态。
## 六、从源码看到的
四个子问题看完,呈现的图景不复杂。
没有任何一个 Agent 实现了一个硬编码的 `cli > mcp > skill` 优先级数值。各 Agent 的分层机制 —— 工具注册顺序、渐进式披露、权限规则、prompt 约束 —— 加在一起形成的是一个**结构性分层**:
| 层 | 职责 | 各 Agent 的实现方式 |
|------------------|------------------------------|-----------------------------------------|
| **Skills** | 工作流路由:匹配任务、决定流程、授权工具 | ClaudeCode 的 BLOCKING REQUIREMENT;Codex 的 "must use" rule;OpenClaw 的 "Skills own workflows";MiMo Code 的 BM25 搜索式渐进披露 |
| **CLI** | 系统执行:直接跑命令、操作本地环境 | 所有 Agent 的内置工具,同名时优先于 MCP ,始终立即可用 |
| **MCP** | 外部连接:调 API、查数据库、访问 SaaS | 有序加载(Kimi Code select_tools、ClaudeCode ToolSearchTool),统一注册(Codex),独立治理(Hermes MCP catalog) |
| **Agent 应用** | 治理:信任、权限、优先级、审计 | Hermes Footprint Ladder、OpenClaw 的策略路由、ClaudeCode 的 allowed-tools + skill:// 安全边界 |
更简洁地说:**CLI 负责跑命令,MCP 负责接系统,Skills 负责编排两者,Agent 应用决定边界。**
## 七、日常使用的参考
从源码分析回到日常使用,下面这张表可以作为参考:
| 你想让 Agent 做什么 | 参考方向 | 例子 |
|--------------------------------|--------|----------------------------|
| 执行本地命令、管理文件、操作 git | CLI | `git commit`、`npm install`、`ls` |
| 访问外部系统或 SaaS | MCP | 连接 GitHub API、Notion、Linear、数据库 |
| 按固定流程重复做事 | Skills | 代码审查流程、周报格式、发布 checklist |
| 用外部系统完成稳定流程 | MCP + Skill| GitHub MCP 读 PR ,Code Review Skill 定义审查流程 |
| 用本地命令完成稳定流程 | CLI + Skill | `gh` CLI 操作 GitHub ,Skill 定义审查步骤和授权命令 |
MCP server 装得多并不等于 Agent 能力强 —— Hermes 的 Footprint Ladder 从维护表面积的角度解释了这一点,每增加一个工具都是模型决策负担的增加。但从另一个角度看,确实需要 MCP 的场景(权限治理、结构化 I/O、跨平台复用),CLI 也代替不了。
### 一个具体案例:Playwright 的三种形态
Playwright 算是典型的同一个产品同时提供 CLI、MCP、Skills 三种扩展形态的案例(截至 2026 年 7 月,`@playwright/mcp` v0.0.75 / Playwright 1.60 )。三种形态是独立安装的,不是打包在一起:
- **MCP**(`npx @playwright/mcp@latest`):50+ 工具,通过结构化无障碍树操作浏览器,单次会话约消耗 114K tokens
- **CLI**(`npm install -g @playwright/cli`):命令行工具,单次会话约消耗 27K tokens —— 比 MCP 少约 4 倍
- **Skills**(`playwright-cli install --skills`):安装 markdown 技能文件,教 Agent 怎么用 CLI 命令 —— 专门跟 CLI 配对,不跟 MCP 配对
面对 "打开网页并截图" 这样一个任务,如果只装了 MCP,Agent 走 MCP 工具(`browser_navigate` + `browser_screenshot`);如果装了 CLI + Skills,Agent 读取技能后走 CLI 命令(`playwright-cli open` + `playwright-cli screenshot`);如果两种都装了,Agent 自己判断 —— Playwright 官方的建议是 "CLI 适合需要 token 效率的编码 Agent ,MCP 适合需要持续状态和迭代推理的自动化流程"。甚至从 Playwright 1.59 起,MCP 和 CLI 可以通过 `browser.bind()` 共享同一个浏览器实例。
这个案例说明的不是 "哪种更好",而是**同一个产品可以同时提供三种形态,由 Agent 和用户根据场景选择**。三层不是非此即彼。
## 八、对开发者
对开发者来说,选择 CLI、MCP 还是 Skills,涉及的维度更多 —— 能力粒度、维护成本、权限模型、信任链路、命名空间、跨 Agent 可移植性。一个相关的问题是 Plugin:ClaudeCode、Kimi Code、Codex 的 Plugin 系统可以把 Skills、MCP servers 、Hooks 打包在一个 manifest 里安装,但安装之后每种能力各自走 Agent 原有的加载通道,Plugin 本身不运行代码 —— 它是包装盒,不是执行器。OpenClaw 是个例外,它的原生 Plugin 会执行自己的 `register(api)` 代码来动态注册能力。文章已经很长了,这些留到之后另开一篇。
## 九、写在最后
AI 把一切都加速了,包括这些能力形式本身的演进。我们没有展开的地方也还很多 —— 开发者视角下 CLI/MCP/Skills 的技术选型、MCP 协议即将到来的重构对 Agent 设计的影响、插件(Plugin)体系如何在三种扩展能力之上再加一层编排。我们在 ClaudeCode 里已经看到 MCP server 可以提供 Skills —— 三层之间的边界本身就在变化。
不知道你在具体使用哪些 Agent,是否发现跟上文分析一致或不一致的地方,我们可以一起聊聊。
---
原文链接:[点击查看](https://www.v2ex.com/t/1229795)
过去一年半,这样的讨论经历了好几波,好在今年初开始有一些相对客观的表达,尝试说明 MCP、Skills、CLI 各自的特点和定位。不过,这些讨论大多停留在定性层面:
> - **2024.11–2025.5**:Anthropic 发布 MCP,Cursor、OpenAI、Google、微软相继接入,国内 BAT 全线跟进,一些媒体称之为 "AI 的 HTTP 协议"
> - **2025.10–12**:Anthropic 推出 Skills, "渐进式披露"、"按需加载"、"token 开销远低于 MCP"成为新的关注点
> - **2026.1–3**:OpenClaw 作者 Peter Steinberger 指出 CLI 天然适配 Agent;3 月 Perplexity CTO 宣布内部从 MCP 转向 CLI,YC 掌门 Garry Tan 公开指出 MCP 的上下文膨胀问题,国内开发者社区随之展开讨论 —— 也就是这个时候,CLI 这个大家每天都在用的东西,被重新放进了 "Agent 扩展能力"的讨论框架里
不知道你会不会好奇:在你使用的真实的 Agent 环境里 —— CLI、MCP、Skills 这些都算是标准的扩展能力同时存在同时可用 —— Agent 究竟会怎么协调它们的?谁先谁后?谁管谁?是不是谁会更好、谁会替代谁?我们尝试从 8 个常见的 Agent 的源码入手 —— Codex、Gemini CLI、Kimi Code、MiMo Code、OpenClaw、Hermes、OpenCode、ClaudeCode(排名不分先后)—— 把一个问题拆成四个递进的子问题来验证:**当 CLI、MCP、Skills 同时在一个 Agent 里,面对同一个需求时,它们到底怎么分工、谁覆盖谁、谁管谁。**
## 调研样本
| 来源 | Agent | 代码仓库 | 代码版本 |
|-------------------|---------------------|---------------------------------------------|------------------|
| GitHub 官方 repo | Codex ( OpenAI ) | [github.com/openai/codex](http://github.com/openai/codex) | `4c43465` (2026-07-25) |
| GitHub 官方 repo | Gemini CLI ( Google ) | [github.com/google-gemini/gemini-cli](http://github.com/google-gemini/gemini-cli) | `3818efb` (2026-07-24) |
| GitHub 官方 repo | Kimi Code (月之暗面) | [github.com/MoonshotAI/kimi-code](http://github.com/MoonshotAI/kimi-code) | `c497af6` (2026-07-25) |
| GitHub 官方 repo | MiMo Code (基于 OpenCode ) | [github.com/XiaomiMiMo/MiMo-Code](http://github.com/XiaomiMiMo/MiMo-Code) | `29321aa` (2026-07-25) |
| GitHub 官方 repo | OpenCode | [github.com/anomalyco/opencode](http://github.com/anomalyco/opencode) | `0a6637e` (2026-07-25) |
| GitHub 官方 repo | OpenClaw | [github.com/openclaw/openclaw](http://github.com/openclaw/openclaw) | `6e604438` (2026-07-25) |
| GitHub | Hermes ( NousResearch )| [github.com/NousResearch/hermes-agent](http://github.com/NousResearch/hermes-agent) | `760112a` (2026-07-25) |
| GitHub | ClaudeCode ( Anthropic ) | 早期泄露版本 | `a99de1b` (2026-03-31) |
MiMo Code 基于 OpenCode fork ,两个仓库有直接可比性。下文会在分析中标注哪些设计来自 OpenCode 原有代码,哪些是 MiMo Code 独立添加的。
## 一、问:工具池里,谁覆盖谁?
当一个 CLI 工具和 MCP 工具都能做同一件事,Agent 的实现代码怎么处理冲突?
这是一个纯代码层的问题,不看 prompt,不看模型意图,就看工具注册表。
### Codex:核心工具名保留
**Codex** 把所有工具 —— ShellCommandHandler、ExecCommandHandler、MCP 工具 —— 统一存入同一个 `ToolRegistry`(`HashMap<ToolName, CoreToolRuntime>`)。核心工具先注册,占据工具名。当扩展/MCP 工具尝试注册同名工具时,直接跳过:
```rust
if !reserved_tool_names.insert(tool_name.clone()) {
warn!("Skipping extension tool `{tool_name}`: tool already registered");
continue;
}
```
### ClaudeCode:built-in 优先
**ClaudeCode** 的 `assembleToolPool` 里,内置工具在前,MCP 工具在后,`uniqBy` 按名去重。注释直接写了 "built-ins win on name conflict":
```typescript
return uniqBy(
[...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),
'name',
)
```
两个 Agent,两种语言,不同的代码结构。结论出奇一致:**CLI 作为内置工具,同名时覆盖 MCP。**没有例外。
全样本对照:
| Agent | CLI 工具注册 | MCP 工具注册 | 同名冲突处理 |
|--------------|-----------------------|--------------------|------------------------|
| Codex | ShellCommandHandler / ExecCommandHandler → ToolRegistry | McpHandler → ToolRegistry | 核心工具先注册,MCP/扩展同名时跳过 |
| Gemini CLI | Shell tool → ToolRegistry | MCP tools → ToolRegistry | 并列注册,无显式优先级规则 |
| Kimi Code | BashTool → builtinTools Map | mcpTools 独立 Map | 三个 Map 分别管理,loopTools 合并 |
| MiMo Code | bash (BashTool) → tool registry | MCP 通过 toolScriptMcp 注入 | 内置工具先注册 |
| OpenCode | shell (ShellTool) → tool registry | MCP 在 SessionTools 外部组装 | 内置先于 MCP |
| OpenClaw | exec 作为一等内置工具 | MCP tools 进入同一策略管道 | 无显式优先级 |
| Hermes | terminal 作为内置工具 | MCP tools 注册进 tool registry | 内置工具同名优先 MCP |
| ClaudeCode | BashTool 进入内置工具池 | MCP 在 assembleToolPool 合并 | uniqBy 保留插入顺序,builtins win |
## 二、问:工具什么时候让模型看见?
覆盖是写代码时的事。另一个问题是:工具在什么时间点暴露给模型。
### 三种可见性策略
**Kimi Code** 给出了一种最明确的回答:不是所有工具都平等可见。CLI 工具(`BashTool`)始终在顶层 `tools[]` 中,模型每一步都能直接调用。MCP 工具则被排除在顶层之外 —— 当 `toolSelectEnabled` 为 true 时,模型需要先通过 `<tools_added>` / `<tools_removed>` 系统消息发现 MCP 工具的名称,再调用 `select_tools` 工具加载完整定义,之后才能调用。这被 Kimi 称为**渐进式披露** —— MCP 工具不做 "插上就能用",而是 "发现 → 加载 → 调用" 三步走。
这种做法影响的是模型看见工具的时机。更大的主动性被交给了模型 —— 它根据当前需求决定加载哪些工具,而不是被迫在一堆可能无关的工具定义里做选择。
两个直接效果:一是减少了上下文占用,一定程度缓解了 token 浪费;二是更重要的是,工具不再是大家经常吐槽的那种 "一股脑全部暴露" 的方式。
**Gemini CLI** 对 Skills 也做了类似的处理。它的 `ActivateSkillTool` 要求模型在看到 available skills 列表后,调用激活工具加载 skill 完整内容,并且需要用户确认。Skills 在 Gemini CLI 里不是自动激活的。
**OpenCode** 代表的是最简单的情况:Skills 通过 prompt 注入、按名自动匹配,MCP 和 CLI 工具始终可用,不加任何渐进披露层。这个基线是 MiMo Code fork 的起点,下文会看到它在上面加了什么。
**ClaudeCode** 的做法更复杂。它的 `SkillTool` prompt 写了:Skill 命中时是 **BLOCKING REQUIREMENT**,必须先调用 Skill 再生成其他响应。Skills 的匹配是自动的 —— 模型看到 skill 的 name 和 description,由 prompt 规则驱动判断,不需要像 Gemini 那样先激活。但在 MCP 这边,ClaudeCode 也有类似 Kimi Code 的机制:它有一个 `ToolSearchTool`,MCP 工具默认是 **deferred**(延迟加载)状态 —— 模型只看到工具名,需要调用 `ToolSearchTool` 加载完整 schema 才能使用。MCP server 可以通过 `_meta['anthropic/alwaysLoad']` 主动 opt-out 。也就是说 ClaudeCode 也有 MCP 的渐进披露机制。Kimi Code 后续在 `select_tools` 中采用了近似的实现。
### MiMo Code 的设计特例
而 **MiMo Code** 的做法最值得回味。我们知道标准 Skills 的运作方式本身就是渐进式披露:模型只看到 name 和 description,按需决定是否加载完整内容 —— 这已经是第一层 "不全量暴露"。但 MiMo Code fork 后添加了 `SkillSearchTool`,为 Skills 又加了一层门控:连 name + description 也不是直接全部给模型看的,而是先经 BM25 相关性搜索 —— 高置信度匹配自动加载,低置信度则返回排序结果让模型选择,不匹配的直接不出现。也就是说,MiMo Code 里 Skills 做了**两层渐进式披露**:第一层是标准的 "不暴露正文",第二层是 "连目录也要先搜再给"。
但正如那几次集中讨论所展现的那样,MCP 是被吐槽占用上下文最多的能力形式,我们却没有在 MiMo Code 的代码里找到类似 Kimi Code 已经采用的 `select_tools` 这类实现,没有给 MCP 加任何渐进披露。我们在 MiMo Code 源码样本里可以看到,MCP 工具从连接到注入是全量 "盲注" —— 所有 server 的所有工具定义直接进入模型上下文,没有 select_tools、没有搜索、没有门控。可是已经具备渐进式披露特性的 Skills,MiMo Code 为它再加了一层门控。我们没能在 MiMo Code 官方 repo 的 Issues 或 PRs 里找到有关的信息,对这个设计取舍的原因还不得而知。
一个可能相关的解释来自代码里的一个细节:MiMo Code 的 skill search 在 Claude 和 GPT 模型上是被**显式禁用的**。我尝试将该 Skills 渐进披露理解为一种 "弱模型能力补丁" —— 强模型不需要帮忙做技能选择,但也仅限于猜测。加上 MiMo 自家模型有 100 万 token 的上下文窗口,MCP 工具定义的 token 开销或许算不上是瓶颈。
## 三、问:Skill 能不能管工具?
前两个问题讲的是工具层内部的关系。再往上一层:Skill —— 作为工作流描述 —— 有没有能力约束 CLI 和 MCP 的使用。
这个问题在各 Agent 之间产生了最大的分歧。
### ClaudeCode:硬授权
**ClaudeCode** 是目前唯一在代码层实现了 "Skill 硬授权 CLI" 的。它的 Skill frontmatter 支持 `allowed-tools` 字段,Skill 作者可以声明 "这个流程允许执行哪些命令"。当 Skill 被激活时,`allowedTools` 被注入为 `alwaysAllowRules.command`,预授权特定的 bash 命令模式。这不是 prompt 建议,是权限层的硬操作。Skill 的 `shell` frontmatter 还可以指定用 `bash` 还是 `powershell`,虽然 skill 作者的选择不能覆盖用户的运行时开关。
### Codex:声明式依赖
**Codex** 的 Skill 可以声明工具依赖,但不算硬约束。它的 `SkillToolDependency` 结构体支持 `type: "cli"` 和 `type: "mcp"` 两种依赖类型,告诉系统 "这个 Skill 需要 gh CLI" 或 "这个 Skill 需要 GitHub MCP server"。但这更像声明关系,不强制权限。
### 其他 Agent:纯 prompt
**Kimi Code、MiMo Code、OpenClaw、Hermes** 的 Skills 是纯 prompt 机制。Skill 被注入为一段 markdown 文本,能**建议**模型做什么,不能**阻止**模型做什么。在这些 Agent 里,"Skill 管不了工具"不是设计缺陷,是当前架构的共识设定。
OpenClaw 的 [AGENTS.md](http://agents.md/) 把这个边界写成了显式的哲学立场:"Skills own workflows; root owns hard policy and routing"——Skill 负责工作流,但硬性策略和路由归 root(也就是 Agent 应用本身)。Skill 可以告诉模型 "该怎么做",但不能替模型决定 "能做哪些"。这是一种清醒的分工:流程归你,权限归我。
## 四、问:有没有显式的设计框架?
前三个问题看的是代码行为。最后一个问题看的是设计自觉 —— 有没有 Agent 把 CLI、MCP、Skills 之间的取舍逻辑写成显式的规则,而不是让它从代码里 "自然长出来"。
大部分 Agent 没有。但有两个做了,而且方式不同。
### Hermes:Footprint Ladder
**Hermes** 的 [AGENTS.md](http://agents.md/) 里有一个 "Footprint Ladder" (足迹阶梯,第 182-206 行),按 "新增永久表面积" 从小到大排列所有能力扩展方式。这里的 "表面积" 是软件维护的概念 —— 每新增一个工具、一个 API、一条代码路径,就多了一块需要持续维护测试安全审计的面积。阶梯越往上,引入的 "表面积" 越小:
1. 扩展现有代码 ——零新增表面积
2. **CLI command + skill** —— Agent 执行 `hermes <subcommand>`,由 skill 提供流程指导。零模型工具表面积。这是订阅任务、定时任务、服务设置的默认选择
3. Service-gated tool —— 需要结构化参数但仅在前提条件满足时出现
4. Plugin —— 第三方能力,不进 core
5. **MCP server** —— 如果能力确实需要成为工具(结构化 I/O),但不核心,优先做成 MCP server 而非新增 core tool
6. New core tool —— 最后手段
CLI+skill 在第二级,MCP server 在第五级。这个排序不是基于 "谁更好" —— Footprint Ladder 的唯一标准是新增表面积。Hermes 明确说:"选择最高(表面积最小)的可行阶梯。"
通俗地讲:**能用 CLI 和 skill 解决的事,不用 MCP;能用别人写的 MCP server 解决的事,不自己写 core tool。**
### OpenClaw:结构性职责划分
**OpenClaw** 没有 Hermes 那么细的阶梯,但给了一个简洁的结构性描述:AGENTS.md 开头第一句就是 "Skills own workflows; root owns hard policy and routing"( Skills 拥有工作流,root 拥有硬策略和路由)。三层关系一目了然 —— **Skills 管怎么做,Agent 管怎么排,工具(不管是 CLI 还是 MCP)是被编排的**。
这两个通用型 Agent 的框架,跟前面 Coding Agent 的代码实现指向同一个方向:**三者不是平级竞争关系。CLI 直接干活,MCP 接外部系统,Skills 管理流程。**区别在于,Hermes 和 OpenClaw 把这件事写成了可以指导后续开发的显式规则,而其他 Agent 是让代码行为自然形成分层 —— 有效果,但少了一份 "我们想清楚了" 的自觉。
## 五、ClaudeCode:把事情串起来的样本
看完各 Agent 在一两个子问题上的处理,可以发现 ClaudeCode 的特别之处在于它在**四个子问题上都有独特的设计**:
**工具冲突**:`assembleToolPool` 里 built-in 优先于 MCP,CLI 显然权重高于 MCP 的。
**可见性**:Skill 命中是 BLOCKING REQUIREMENT,必须先加载 Skill,再决定用什么工具。同时 MCP 工具默认 deferred,需通过 `ToolSearchTool` 加载 schema 后才能调用。MCP server 还可以通过 `_meta['anthropic/alwaysLoad']` 让特定工具跳过 deferred,直接暴露完整 schema —— 这相当于一个配置层面的门控开关。
**Skill 管工具**:`allowed-tools` 字段让 Skill 可以硬授权 CLI 命令 —— 全样本唯一的硬约束。
**完整设计**:MCP server 可以提供 Skill(通过 `skill://` resources),但 MCP Skill 被标记为不可信 —— 不执行 inline shell command ,本地 Skill 优先于同名 MCP Skill(`SkillTool.ts:82-93` 的 `uniqBy([...localCommands, ...mcpSkills], 'name')`)。也就是说,MCP 可以是 Skill 的分发渠道,但 Agent 保留最终控制权。
ClaudeCode 不是每一项都做得最激进,但它是把 CLI、MCP、Skills 三层之间的硬交互做得最全面的。这可能跟它作为早期版本有关 —— 实验更多,规矩少一些。但不管原因是什么,它提供了一个很完整的参考样板。
需要说明的是,本文分析的 ClaudeCode 代码来自 2026 年 3 月的泄露版本(`a99de1b`)。ClaudeCode 不开源,后续版本可能已经调整了上述设计 —— 比如 ToolSearchTool 的触发条件、MCP deferred 的默认行为等,都有可能发生变化。以上分析仅反映该版本的状态。
## 六、从源码看到的
四个子问题看完,呈现的图景不复杂。
没有任何一个 Agent 实现了一个硬编码的 `cli > mcp > skill` 优先级数值。各 Agent 的分层机制 —— 工具注册顺序、渐进式披露、权限规则、prompt 约束 —— 加在一起形成的是一个**结构性分层**:
| 层 | 职责 | 各 Agent 的实现方式 |
|------------------|------------------------------|-----------------------------------------|
| **Skills** | 工作流路由:匹配任务、决定流程、授权工具 | ClaudeCode 的 BLOCKING REQUIREMENT;Codex 的 "must use" rule;OpenClaw 的 "Skills own workflows";MiMo Code 的 BM25 搜索式渐进披露 |
| **CLI** | 系统执行:直接跑命令、操作本地环境 | 所有 Agent 的内置工具,同名时优先于 MCP ,始终立即可用 |
| **MCP** | 外部连接:调 API、查数据库、访问 SaaS | 有序加载(Kimi Code select_tools、ClaudeCode ToolSearchTool),统一注册(Codex),独立治理(Hermes MCP catalog) |
| **Agent 应用** | 治理:信任、权限、优先级、审计 | Hermes Footprint Ladder、OpenClaw 的策略路由、ClaudeCode 的 allowed-tools + skill:// 安全边界 |
更简洁地说:**CLI 负责跑命令,MCP 负责接系统,Skills 负责编排两者,Agent 应用决定边界。**
## 七、日常使用的参考
从源码分析回到日常使用,下面这张表可以作为参考:
| 你想让 Agent 做什么 | 参考方向 | 例子 |
|--------------------------------|--------|----------------------------|
| 执行本地命令、管理文件、操作 git | CLI | `git commit`、`npm install`、`ls` |
| 访问外部系统或 SaaS | MCP | 连接 GitHub API、Notion、Linear、数据库 |
| 按固定流程重复做事 | Skills | 代码审查流程、周报格式、发布 checklist |
| 用外部系统完成稳定流程 | MCP + Skill| GitHub MCP 读 PR ,Code Review Skill 定义审查流程 |
| 用本地命令完成稳定流程 | CLI + Skill | `gh` CLI 操作 GitHub ,Skill 定义审查步骤和授权命令 |
MCP server 装得多并不等于 Agent 能力强 —— Hermes 的 Footprint Ladder 从维护表面积的角度解释了这一点,每增加一个工具都是模型决策负担的增加。但从另一个角度看,确实需要 MCP 的场景(权限治理、结构化 I/O、跨平台复用),CLI 也代替不了。
### 一个具体案例:Playwright 的三种形态
Playwright 算是典型的同一个产品同时提供 CLI、MCP、Skills 三种扩展形态的案例(截至 2026 年 7 月,`@playwright/mcp` v0.0.75 / Playwright 1.60 )。三种形态是独立安装的,不是打包在一起:
- **MCP**(`npx @playwright/mcp@latest`):50+ 工具,通过结构化无障碍树操作浏览器,单次会话约消耗 114K tokens
- **CLI**(`npm install -g @playwright/cli`):命令行工具,单次会话约消耗 27K tokens —— 比 MCP 少约 4 倍
- **Skills**(`playwright-cli install --skills`):安装 markdown 技能文件,教 Agent 怎么用 CLI 命令 —— 专门跟 CLI 配对,不跟 MCP 配对
面对 "打开网页并截图" 这样一个任务,如果只装了 MCP,Agent 走 MCP 工具(`browser_navigate` + `browser_screenshot`);如果装了 CLI + Skills,Agent 读取技能后走 CLI 命令(`playwright-cli open` + `playwright-cli screenshot`);如果两种都装了,Agent 自己判断 —— Playwright 官方的建议是 "CLI 适合需要 token 效率的编码 Agent ,MCP 适合需要持续状态和迭代推理的自动化流程"。甚至从 Playwright 1.59 起,MCP 和 CLI 可以通过 `browser.bind()` 共享同一个浏览器实例。
这个案例说明的不是 "哪种更好",而是**同一个产品可以同时提供三种形态,由 Agent 和用户根据场景选择**。三层不是非此即彼。
## 八、对开发者
对开发者来说,选择 CLI、MCP 还是 Skills,涉及的维度更多 —— 能力粒度、维护成本、权限模型、信任链路、命名空间、跨 Agent 可移植性。一个相关的问题是 Plugin:ClaudeCode、Kimi Code、Codex 的 Plugin 系统可以把 Skills、MCP servers 、Hooks 打包在一个 manifest 里安装,但安装之后每种能力各自走 Agent 原有的加载通道,Plugin 本身不运行代码 —— 它是包装盒,不是执行器。OpenClaw 是个例外,它的原生 Plugin 会执行自己的 `register(api)` 代码来动态注册能力。文章已经很长了,这些留到之后另开一篇。
## 九、写在最后
AI 把一切都加速了,包括这些能力形式本身的演进。我们没有展开的地方也还很多 —— 开发者视角下 CLI/MCP/Skills 的技术选型、MCP 协议即将到来的重构对 Agent 设计的影响、插件(Plugin)体系如何在三种扩展能力之上再加一层编排。我们在 ClaudeCode 里已经看到 MCP server 可以提供 Skills —— 三层之间的边界本身就在变化。
不知道你在具体使用哪些 Agent,是否发现跟上文分析一致或不一致的地方,我们可以一起聊聊。
---
原文链接:[点击查看](https://www.v2ex.com/t/1229795)
· 0 个赞
· 0 个赞
· 0 个赞
· 0 个赞