MCP 还是 CLI:给 Agent 接工具的两条路
给 Agent 接工具,现在大致有两种做法。
一种是 MCP(Model Context Protocol):为每个服务写一个 MCP Server,把能力包装成结构化的工具,Agent 通过协议发现并调用它们。
另一种是 CLI:给 Agent 一个 shell,让它直接跑 git、gh、kubectl、psql、curl,就像一个工程师坐在终端前那样。
这两条路经常被拿来对立讨论,“MCP 已死,CLI 就够了”和“CLI 太危险,一切都该走 MCP”两种声音都有。我的看法是:它们解决的是不同层面的问题,选哪个取决于 Agent 运行在哪里、给谁用、要接什么。
先说清楚两者是什么
MCP:一个协议
MCP 是 Anthropic 在 2024 年底开源的协议,目标是让“模型应用”和“外部能力”之间有一个统一的接口,常被比作 AI 领域的 USB-C。
几个关键点:
- 基于 JSON-RPC 2.0,客户端(Agent 宿主,比如 Claude Code、Cursor)和服务端(MCP Server)之间交换结构化消息;
- 传输层可以是本地的
stdio(宿主拉起一个子进程),也可以是远程的 Streamable HTTP(连接一个云端服务); - 服务端暴露几类原语:Tools(可调用的函数)、Resources(可读取的数据)、Prompts(预设的提示模板);
- 每个 Tool 都有名字、描述和 JSON Schema 定义的参数,宿主把这些信息放进模型的上下文,模型据此决定调用哪个工具、传什么参数。
一次调用大概长这样:
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "create_issue",
"arguments": { "repo": "acme/web", "title": "登录页 500", "labels": ["bug"] }
}
}
CLI:一个环境
CLI 不是协议,而是一种工作方式。Agent 只有一个通用工具,比如 bash,然后它自己决定敲什么命令:
gh issue create --repo acme/web --title "登录页 500" --label bug
模型不需要预先知道有哪些工具。它知道 gh 是什么,是因为训练数据里有海量的 gh 用法;不知道某个参数时,它会去跑 gh issue create --help。
一句话概括:MCP 是把能力“递给”模型,CLI 是让模型自己“去拿”能力。
核心差异
一、上下文开销
这是最近讨论 CLI 时被提得最多的一点。
MCP 的工具定义需要预先放进上下文。一个 GitHub MCP Server 可能有几十个工具,每个工具带描述和 Schema,接上五六个 Server,还没开始干活,几万 token 就没了。工具一多,模型选错工具的概率也会上升。
CLI 的开销是按需的。模型只在用到时才去读 --help 或 man page,而且对于 git、docker、curl 这类常见工具,它根本不需要读文档。
不过这个差异正在被缩小:不少宿主已经支持工具的延迟加载和按需搜索,只在需要时才把完整 Schema 放进上下文。所以这一点更多是“当前实现”的问题,不是协议本身的限制。
二、可组合性
CLI 有 Unix 五十年积累下来的组合能力:
gh pr list --state merged --json number,title,mergedAt \
| jq -r '.[] | select(.mergedAt > "2026-10-01") | .title' \
| sort | uniq -c
一条命令完成“查询 → 过滤 → 聚合”,中间结果不经过模型的上下文。如果用 MCP,通常是模型调用 list_pull_requests,拿到一大坨 JSON 放进上下文,再自己“看”一遍做过滤,既慢又贵,还容易数错。
这也是为什么有人提出“让 Agent 写代码去调 MCP 工具”,而不是一个个直接调用:本质上是在 MCP 之上把可组合性找回来。
三、结构化与可靠性
反过来,MCP 的优势在于契约明确:
- 参数有 Schema,类型错了在调用前就能被拒绝;
- 返回值是结构化数据,不用去解析人类可读的输出;
- 工具的语义由服务提供方定义,比如
create_issue就只做一件事。
CLI 的输出是写给人看的,格式可能随版本变化、受 locale 影响、混着进度条和颜色码。好在越来越多的 CLI 提供 --json 或 --output json,能缓解很多问题;但对于那些只有表格输出的老工具,模型就得靠“读”来理解,出错的概率更高。
四、运行环境与安全边界
这是我认为最决定性的差异。
CLI 方式的前提是:Agent 有一个 shell。有了 shell,它能做的事就远不止你想让它做的那几件,gh 能跑,rm -rf 也能跑,curl 能调 API,也能把环境变量发到外面去。所以 CLI 方式几乎必然要配合沙箱、网络出口控制和凭证隔离(上一篇Agent 沙箱讲的就是这些)。
MCP 的能力边界则由 Server 决定。一个只暴露 search_docs 和 create_ticket 两个工具的 Server,Agent 就只能做这两件事。这让它可以用在没有 shell 的环境里:网页聊天界面、手机 App、企业内部的客服 Agent。在这些场景里,给模型一个 shell 既不可能也不合适。
当然,MCP 也不是天然安全。工具描述本身可能被投毒,Server 返回的内容同样会带来提示词注入,第三方 Server 的代码质量也参差不齐。但至少“能做什么”是可枚举、可审查的。
五、认证
CLI 用的是本地凭证:gh auth login 存下的 token、~/.aws/credentials、~/.kube/config。在开发者自己的机器上,这非常顺手,Agent 直接继承了你的身份。但这也意味着 Agent 拥有和你一样的权限,没有更细的划分。
远程 MCP Server 走的是 OAuth:用户在浏览器里授权一次,Server 拿到的是一个有范围限制、可吊销的 token。对多用户的 SaaS 场景来说,这是正确的模型:你不可能让每个用户把自己的 AWS 密钥文件上传上来。
六、生态覆盖
CLI 的生态是现成的:几乎所有基础设施工具和开发者服务都有 CLI,而且模型已经“学过”它们。
MCP 的生态在快速补齐,尤其是那些本来就没有 CLI 的服务:Notion、Figma、Slack、各类 SaaS 后台,以及公司内部的系统。为这些服务写一个 MCP Server,往往比先写一个 CLI 再教模型用它更直接。
对比一览
| 维度 | MCP | CLI |
|---|---|---|
| 本质 | 协议 | 运行环境 |
| 工具发现 | 预先注册,Schema 进上下文 | 按需,靠训练知识和 --help |
| 上下文开销 | 工具多时较大 | 较小 |
| 可组合性 | 弱,结果经过模型 | 强,管道直接串联 |
| 输入输出 | 结构化、有契约 | 文本为主,依赖 --json |
| 运行前提 | 宿主支持 MCP 即可 | 需要 shell,通常还需要沙箱 |
| 能力边界 | 由 Server 定义,可枚举 | 几乎无边界 |
| 认证 | OAuth,适合多用户 | 本地凭证,适合个人 |
| 生态 | SaaS、内部系统 | 开发与基础设施工具 |
什么时候用哪个
适合 CLI 的场景
- 编码 Agent、开发者工具:Agent 本来就要在代码仓库里工作、跑测试、装依赖,shell 是必需品。这时候再给
git、gh、docker包一层 MCP 往往是多余的; - 工具已经有成熟的 CLI,而且模型很熟悉它;
- 需要组合与批处理:数据过滤、日志分析、批量操作,用管道一行解决;
- 运行在已经隔离好的环境里:有沙箱、有出口代理,shell 的风险已经被控制住了。
适合 MCP 的场景
- 没有 shell 的宿主:聊天应用、移动端、浏览器里的 Agent;
- 面向多用户的产品:需要 OAuth、按用户隔离权限、审计每一次调用;
- 服务没有 CLI,或者 CLI 很难用:SaaS 产品、内部系统;
- 需要严格限定 Agent 能做什么:比如客服 Agent 只能查订单、退款,不能做别的;
- 同一个能力要提供给多个 Agent 宿主:写一次 Server,Claude、Cursor、各种 IDE 都能接。
混合使用
实际上,最常见的形态是两者并存。一个编码 Agent 在沙箱里用 CLI 处理代码、Git 和构建,同时通过 MCP 接入 Jira、Sentry、内部文档这些没有好用 CLI 的系统。
还有一种值得关注的中间形态:用 文档 + 脚本 的方式给 Agent 提供能力。比如写一份说明,告诉 Agent “要做 X,就去跑 scripts/x.sh,参数是这些”,Agent 在需要时才读这份说明。它兼具 CLI 的按需加载和可组合性,又比让模型从零摸索一个陌生工具更可靠。Claude 的 Skills 就是这个思路。
一些实践建议
如果你在写 MCP Server:
- 工具要少而精。不要把 REST API 一对一映射成几十个工具,按 Agent 的任务来设计,比如一个
search_and_summarize_issues往往比list_issues+get_issue好用得多; - 描述写给模型看。说清楚什么时候该用、什么时候不该用、参数的含义和常见值;
- 控制返回值大小。支持分页、字段筛选,别一次返回几百 KB 的 JSON;
- 错误信息要可操作。“参数
repo应为owner/name格式”比“400 Bad Request”有用得多。
如果你让 Agent 用 CLI:
- 优先用结构化输出,
--json配合jq; - 沙箱先行,网络出口和凭证的控制要在 shell 之外;
- 避免交互式命令,分页器、确认提示、
-i类参数都会让 Agent 卡住,设置GH_PAGER=cat、--yes这类开关; - 限制输出长度,一条命令打出几万行日志会直接撑爆上下文。
小结
- MCP 是协议,CLI 是环境,它们并不在同一个层面上竞争;
- 有 shell 的地方,CLI 通常更省、更灵活,尤其是开发者工具和基础设施;
- 没有 shell、多用户、需要限定能力的地方,MCP 是更合适的选择;
- 上下文开销是 MCP 当前最大的痛点,但正在被延迟加载、代码调用等方式缓解;
- 大多数真实系统会混合使用:CLI 处理“本地的事”,MCP 连接“外部的服务”。
选工具的方式,归根结底还是看 Agent 在哪里跑、为谁工作、你愿意给它多大的权力。这和上一篇讲沙箱时的结论其实是同一件事。