MCP 还是 CLI?给 AI Agent 设计工具
我平时让 AI Agent 干活,工具入口基本就两种,MCP 和 CLI。两个解决的是同一件事,给 Agent 一个能干活的口子,但走的路完全不同。
MCP 是专门为 AI 设计的协议。工具的名字、参数、返回结构都用 schema 写清楚,模型读了 schema 就知道怎么调,标准统一,不同客户端都能接。CLI 是 shell 时代传下来的老东西,本来给人用,命令、参数、帮助文档都摆在工具外面,要用的时候自己去翻。没有统一协议,也没有谁规定它该长什么样。
这个差别听起来只是协议层面的事,落到真实场景里,差得最明显的地方是 token 成本。
两组数据
第一组来自 Anthropic 自己的工程博客 Advanced tool use。
| 场景 | 工具定义 token | 备注 |
|---|---|---|
| 传统 MCP,5 个 server / 58 个工具全量预加载 | 约 55,000 | GitHub 35 个工具 ~26K + Slack 11 个工具 ~21K + Sentry/Grafana/Splunk 共 ~8K |
| 再接上 Jira | 100,000+ | 仅 Jira 一项约 17,000 |
| Tool Search Tool(只预加载搜索入口,工具定义按需展开) | 约 8,700 | 对比传统方式节省约 95% 上下文 |
| Programmatic Tool Calling(模型写代码编排调用,中间结果留在执行环境里) | 27,297(对比传统 43,588) | 复杂研究任务,减少 37% |
数据来自 Anthropic 的 Advanced tool use,2025。
第二组来自社区。MCP 官方仓库的一篇讨论里,有人做了一个覆盖 112 个 GitHub tools 的 MCP server,对比了传统暴露方式和 code-first 模式。
| 模式 | 工具定义 token | 典型对话 token | 简单查询 token |
|---|---|---|---|
| 传统 MCP(全量 tool schema 常驻上下文) | 约 150,000 | 约 80,000 | 约 75,000 |
code-first(只暴露 execute_code,按需发现工具) | 约 1,200(↓99.2%) | 约 6,000(↓92.5%) | 约 2,000(↓97.3%) |
数据来自 MCP Discussion #629,Production Results: MCP Server for GitHub Validates Anthropic’s Code-First Pattern。
两组数据说的是同一件事。工具数量上来以后,把全量 tool schema 常驻在上下文里,代价很高。Anthropic 自己的工程团队和社区的生产案例,结论都差不多,按需发现、code 执行风格的入口,token 成本明显更低。
从上面这两组数据能看出来,相比 MCP,CLI 在节省 token 上有非常明显的优势。“按需翻文档、按需发现”这件事,CLI 天生就是这么干的,不需要把所有说明书提前塞进上下文。
CLI 是什么
一个能在终端里跑的程序。敲一行命令,它读参数、做点事,把结果打印到 stdout,错误打印到 stderr,结束后给一个 exit code 告诉你成功还是失败。
这套东西从 Unix 时代就在用,本来是设计给人的。但”命令、参数、帮助文档都摆在工具外面,需要时再翻”这个特性,恰好对 Agent 也很友好。不用一次性把所有说明书塞进上下文,按需 --help,按需 list。这跟前面 Tool Search Tool、code-first 的思路是一回事,只是 CLI 这套约定已经存在了几十年,工具链和习惯都很成熟。
命令一跑,结果从 stdout 回来。人能用,脚本能用,CI 能用,Agent 也能用。它不见得比 MCP 高级,但很多时候更轻、更直接,token 也省。
于是我想到另一件事。手里那些内部服务,还有一些没提供 CLI 的 API,是不是可以先给它们弄一个命令行工具?不急着写 MCP server,也不急着写 SDK。先让它有一个好用的 CLI,就已经能很快接入 AI 了。
CLI 该怎么设计,clig.dev 早写好了
“好用”这两个字得有具体标准。这方面不用自己摸索,我后来翻了 Command Line Interface Guidelines(业界公认的 CLI 设计指南,下面简称 clig.dev),发现它讲的东西放到 Agent 场景里,分量反而更重了。
clig.dev 开篇就定了基调:“If a command is going to be used primarily by humans, it should be designed for humans first.”。CLI 首先要让人看得懂,同时让程序能稳定接得上。下面对着 GitHub 官方的 gh 逐条看,这是我天天在用的 CLI,成熟度足够,几乎每一条都能找到对应的例子。
帮助要可发现。 clig.dev 说 “Lead with examples. Users tend to use examples over other forms of documentation.”。帮助文本要带例子,光罗列参数没人看。不带参数跑 gh,会直接列出按资源分组的子命令。gh pr --help 再把 create / list / view / merge / close / checkout 这些动作和例子列清楚。这跟 Anthropic 给 MCP 加的 Tool Search Tool 是同一个思路,只预加载一个搜索入口,具体工具的定义按需展开。
输出要人机分开。 clig.dev 的说法是 “Human-readable output is paramount. Humans come first, machines second.”,同时要给机器留一条稳定的路。gh pr list 默认给一张表格,看着舒服。加上 --json 配 --jq,stdout 就变成稳定的结构化结果,能直接喂给脚本或 Agent。错误、提示这些放 stderr,别污染 stdout。管道接起来的时候,这些信息只给人看,不会被下一个命令当输入吃掉。
参数要显式,少让人猜。 clig.dev 的原则是 “Prefer flags to args. It’s much clearer what is going on.”。gh pr create --title "x" --body "y" --base main 全是显式 flag,没有一堆容易记混的位置参数。啰嗦一点,歧义少很多,对 Agent 尤其重要。clig.dev 还有两条容易被忽略的配套约定,密钥类参数别直接走 flag(命令行会泄露到 ps 和 shell history),交互提示只在 stdin 是 TTY 时才弹,脚本和 Agent 场景给 --no-input 兜底。
错误要能恢复,危险操作要有边界。 clig.dev 说 “Catch errors and rewrite them for humans. Think of it like a conversation.”。没登录直接跑 gh pr list,它不会甩一个裸的网络错误,会直接告诉你该运行 gh auth login。真正有副作用的操作也有边界,gh repo delete 这种命令必须显式确认才会真的执行。clig.dev 把这类危险操作分了等级,越危险,确认门槛越高,比如要求输入资源名称做二次确认。
接口要经得起时间。 clig.dev 的说法是 “Make changes additive where you can. Rather than modify flag behavior incompatibly, add new flags.”。gh 的命令名、JSON 字段、exit code 已经稳定了很多年,大量 CI 脚本和 Agent 工作流长期依赖它们。人类还能临时适应改动,自动化可不会。
把这些合起来,CLI 设计的最佳实践大概能收成这几条。
- 无参数/
--help都要给清楚的说明,例子放在最前面 - stdout 给结果,stderr 给提示和错误,提供
--json/--plain给机器用 - 能用显式 flag 就别用位置参数,密钥类参数别直接走 flag(会泄露到 ps/history)
- 错误信息要告诉用户哪里错了、怎么修;危险操作分级加确认或
--dry-run - 命令名、flag、输出字段定下来就尽量不破坏性修改,要变也走加法式演进
- 默认值要让大多数场景不用多想;只在 stdin 是 TTY 时才弹交互提示,提供
--no-input给脚本和 Agent 兜底
自动生成的命令能把一个 API 快速调起来,但那只是起点。上面这套设计,才决定这个 CLI 是不是适合长期交给人和 Agent 一起用。