
这段时间只要打开技术社区几乎每天都能看到同一个组合被反复提起CLI、MCP、Skill。很多人一脸疑惑CLI 不是早就被 GUI 淘汰了吗MCP 又是什么新协议Skill 听起来像是个提示词模板怎么这三样东西突然被绑在一起说还都跟 AI Agent 扯上了关系。先说我的结论这三者根本不是同一层的概念把它们并列本身就是个伪命题。CLI 是一种程序交互形态MCP 是一种工具连接协议Skill 是一种能力封装规范。但它们在 AI Agent 时代恰好构成了一条完整的链路——CLI 负责执行MCP 负责打通Skill 负责教会模型怎么用。这篇文章我把这条链路彻底拆开讲清楚不堆概念全部落到实际开发场景里。1. 先把“爆火”这件事拆清楚CLI 为什么回来了1.1 不是 CLI 复活是模型需要一种“看得懂”的接口CLI 从来没有死过只是过去十年它在普通用户视野里被 GUI 压得毫无存在感。真正发生变化的是现在跟你对话的不是人类是大语言模型。模型处理的是 token不是鼠标事件。GUI 界面对人很友好按钮、图标、拖拽交互但模型没法“看”屏幕去操作它——即便现在有多模态模型让模型去识别像素坐标点按钮依然又慢又脆。CLI 就不一样了。它的输入是字符串输出也是字符串错误码是一个数字结果还可以用 JSON 格式输出。这些全是模型最擅长的东西。一个模型“读”一个ls -la的输出和它读一段自然语言文本本质没有区别。所以当 Agent 要真正执行任务的时候CLI 成了最自然、成本最低的交互方式。这不是技术倒退是把接口形态拉回到模型的能力舒适区。我做过一个对比实验同一个 Agent 任务用 GUI 自动化方案成功率大概六成换成 CLI 方案能到九成以上而且速度更快排除错误也更容易——报错信息直接给你退出码和 stderr干净利落。1.2 CLI 的三个天然优势恰好命中 Agent 的痛点第一个优势是可组合性。CLI 程序遵循同一个范式标准输入进标准输出出。你可以在 shell 里用管道把一个程序的输出接给另一个程序这是几十年沉淀下来的 Unix 哲学。Agent 要处理复杂任务本质上就是在组合工具管道这种模式天然匹配。第二个优势是机器可读输出。现在主流 CLI 工具都支持--json或--output-format json参数日志走 stderr数据走 stdout严格分离。模型拿到纯数据之后自己解析、自己决策不需要从一个花花绿绿的界面上做 OCR。这一步省掉的工程量非常可观。第三个优势是低资源消耗。CLI 程序通常没有图形界面启动快、内存占用小适合在沙箱和容器里批量跑。Agent 很多时候要做的是高频的、并行的、试探性的操作几十个 CLI 进程同时起来毫无压力GUI 应用早就卡死了。所以我一直在想CLI 之所以在 2024 到 2025 年这个节点“爆了”不是因为开发者的审美回潮而是因为模型成了新的用户。当用户不再是人类而是模型时接口设计的第一原则就从“直观”变成了“结构化、可解析、可预测”。1.3 Agent 时代的 CLI 设计标准悄悄变了这也带来一个很实际的问题普通 CLI 工具并不等于 AI 友好的 CLI 工具。过去写 CLI参数少、报错靠人看、输出靠人读怎么方便怎么来。但 Agent 要用你的 CLI至少需要满足几个新标准必须有稳定的机器可读输出模式最好是--json退出码必须语义化0 成功、非 0 失败不能“打印个红色错误但退出码还是 0”需要完善的--help文档因为模型是真的会读 help 文本去推断参数参数命名要清晰一致避免缩写含糊比如-r到底是 recursive 还是 remote我见过很多团队直接把内部 Python 脚本丢给 Agent 用结果模型根本不会调用为什么因为脚本没写argparse参数全靠位置猜也没有--help。模型一猜就错一错就崩。所以现在新写 CLI 工具我都会提醒团队你是在给模型写 API不是在给同事写脚本。2. MCP 到底解决的是什么问题连接层面的“统一插头”2.1 MCP 诞生的背景工具接入的 N×M 困境在 MCP 出现之前让 AI 应用接入一个外部工具路径是这样的如果我要让我的 Agent 能查数据库需要写一段函数包装数据库驱动再写一段描述告诉模型这个函数干嘛用的再把函数注册到模型调用框架里。如果是两个 Agent 呢要写两套。如果是十个工具、三个 Agent 呢那就是三十个适配器。这就是经典的 N×M 连接问题每一对接入都是独立开发每改一个接口所有适配器跟着遭殃。某 AI 厂商开源的 MCPModel Context Protocol就是想把这个乱局理清楚。它的思路很直白定义一套通用协议工具方只需要把能力封装成一个 MCP ServerAI 应用方只需要支持 MCP Client两边按协议对接剩下的全部标准化。工具提供者写一次任意支持 MCP 的客户端都能用AI 应用接入一次任意 MCP Server 都能调。MCP 解决的不是“模型怎么思考”而是“模型怎么触达工具”。它是一个典型的互操作层价值在于把生态连接成本从 O(N×M) 降到 O(NM)。2.2 MCP 的核心机制Client、Server 与三大原语MCP 的架构是标准的客户端-服务器模式。MCP Server 暴露能力MCP Client也就是 AI 应用本身发起请求。两者之间通过 JSON-RPC 消息通信本地用 stdio 传输远程用 Streamable HTTP 传输。这组消息定义了三个核心原语原语作用生活化类比Tools工具可执行的函数式能力模型主动调用工具箱里的电钻你说“钻孔”它就开始干活并给你结果Resources资源可读取的数据内容相当于文件 API资料柜里的文件你可以翻阅但不会触发副作用Prompts提示模板预定义的高质量任务模板老师批好的作文提纲照着填空就能用对 Agent 来说Tools 用得最多。模型根据用户需求自主决定“现在该调用哪个 Tool”然后通过 MCP 协议把参数传过去拿到返回结果再继续推理。这个过程看起来就像是模型在使用自己的手但这只手是 MCP 接上的。2.3 MCP 与 CLI 的真实关系一个天然的双向适配现在关键问题来了MCP 和 CLI 到底什么关系直接说结论——它们不在同一层但结合非常紧密。第一种结合MCP Server 内部用 CLI 干活。很多 MCP Server 的实现方式就是包装一个现成的 CLI 工具Server 收到模型调用请求后在本地进程里执行对应的 CLI 命令解析 stdout返回结构化的结果。这种方案下 CLI 是底层执行者MCP 是上层协议封装。第二种结合MCP Server 本身就是 CLI 程序。本地 MCP Server 通过 stdio 通信启动方式就是一个命令行进程。你的 MCP Client 配置里写的就是command: python、args: [./server.py]这种形式。也就是说MCP Server 的宿主形态最初就是从 CLI 长出来的。第三种结合CLI 直接变成 MCP Client 的调用对象。有些团队嫌写 MCP Server 太重直接在 Agent 的工具调用层里配置一个“Shell 工具”模型生成命令沙箱里执行读取结果。这本质上就是裸用 CLI绕过了 MCP 的协议层。我个人看法如果只是临时用一两个工具裸 CLI 直接执行够用且快。但如果要构建一个稳定可维护的工具生态必须走 MCP——协议层能帮你处理参数校验、超时、错误重试、多个后端统一暴露裸 CLI 做这些得全自己造轮子。3. Skill 是干什么的能力复用从“说教”变成“打包”3.1 从“写提示词”到“构建技能包”早期让模型学会一个做事流程靠什么靠写提示词把步骤、规则、例子全塞进上下文里。问题是上下文有长度上限提示词越长模型越容易迷失重点而且每次调用都塞一堆固定文本又费 token 又难维护。Skill 的思路完全换了一个角度把“完成某类任务的完整方法”打包成一个独立单元包含描述文件、指令文本、参考脚本、资源文件像安装插件一样“装”给 Agent。模型遇到相关任务时不是从冗长的对话历史里翻找而是按需加载这个技能包里面的内容就是为这个任务专门优化的。简单说提示词是“现写现用的说教”Skill 是“结构化沉淀的教材”。3.2 Skill 的内部结构与触发机制一个标准 Skill 包在文件系统里通常长这样repo-review-skill/ ├── SKILL.md └── scripts/ ├── analyze.py └── report.pySKILL.md是这个技能包的核心头部用 YAML frontmatter 写元信息包括技能名称、描述、适用场景正文写完整的工作流程、决策规则、注意事项。而scripts/目录是与该技能配套的可执行脚本。触发机制很有意思。Skill 不是被“调用”的而是被“发现”的。Agent 会扫描可用技能列表根据任务描述和每个技能的 description 做匹配命中之后加载对应的 SKILL.md 和脚本。这就意味着SKILL.md里的 description 写得好不好直接决定模型在多大几率上会使用这个技能。写得太宽泛不该用的时候也用了写得太狭窄该用的时候又搜不到。这个度需要反复调。3.3 Skill、MCP、CLI 三者到底怎么分工把三者放到一起看其实是一条完整的分层链路CLI 是执行层具体干活的人。接收参数跑逻辑输出结果。MCP 是连接层把执行能力包装成标准化接口让 AI 应用能发现并调用。Skill 是方法论层告诉模型“什么时候该干这个活、怎么干、干完的结果怎么用”。我见过一个特别贴切的类比MCP 是插座CLI 是电器Skill 是说明书。插座统一了供电接口电器负责真实运转说明书告诉你什么场景下用哪个电器、怎么用最合适。三者缺一不可吗不缺。单有 CLI模型虽然有手但不知道该什么时候出手单有 MCP接口打通了但模型对接下来的流程没有章法单有 Skill知识和脚本都备好了但没有东西去真正执行。现阶段成熟的 Agent 产品基本都是三层都占了底层的工具用 CLI 实现工具的暴露走 MCP复杂的任务流程沉淀成 Skill 包。4. 用一个小而全的例子把三者串起来跑一遍4.1 场景设定做一个代码仓库健康度巡检工具说再多理论不如直接看一个能落地的小项目。我选一个很多团队都需要的场景给定一个本地代码仓库自动检查依赖过期、未提交文件、超长函数、重复代码最后生成一份健康度报告。这个任务完全可以用一个 CLI 工具实现但我们可以把它升级成一套标准的 Agent 能力让模型在对话里自主完成巡检并给出优化建议。整体架构是这样先用 Python 写一个 CLIrepo-check负责真实的仓库扫描和评分再用 MCP SDK 把这个 CLI 的能力封装成一个 Tool暴露给 AI 应用最后把“如何使用 repo-check、如何解读结果、如何输出优化建议”沉淀成一个 Skill 包。模型收到用户指令时通过 Skill 知道该怎么做通过 MCP 调用到底层的 CLI拿到数据后按 Skill 里的规则生成分析结论。4.2 第一步写一个 AI 友好的 CLI这个 CLI 不搞花活核心逻辑是扫描目录、计算指标、输出 JSON。关键不是功能多而是输出格式对 AI 友好。#!/usr/bin/env python3 # repo_check/cli.py import argparse import json import os import subprocess def scan_repo(path: str) - dict: # 这里简化处理实际可以跑 git status、npm audit、ast 分析等 result { path: path, uncommitted_files: 0, dependencies: { total: 0, outdated: 0 }, score: 0 } # 检查未提交文件 try: proc subprocess.run( [git, status, --porcelain], cwdpath, capture_outputTrue, textTrue, timeout10, ) lines [line for line in proc.stdout.splitlines() if line.strip()] result[uncommitted_files] len(lines) except (subprocess.TimeoutExpired, FileNotFoundError): result[uncommitted_files] -1 # 非 git 仓库 # 扫描依赖默认只数 package.json实际项目可扩展 pkg_file os.path.join(path, package.json) if os.path.exists(pkg_file): try: with open(pkg_file, r, encodingutf-8) as f: pkg json.load(f) deps {**pkg.get(dependencies, {}), **pkg.get(devDependencies: {})} result[dependencies][total] len(deps) except json.JSONDecodeError: result[dependencies][total] -1 # 计算健康分 score 100 if result[uncommitted_files] 5: score - 20 if result[dependencies][outdated] 10: score - 30 result[score] max(score, 0) return result def main(): parser argparse.ArgumentParser(descriptionScan a code repo for health issues) parser.add_argument(--path, requiredTrue, helpPath to the repository root) parser.add_argument(--json-output, actionstore_true, helpOutput in JSON format) args parser.parse_args() result scan_repo(args.path) if args.json_output: print(json.dumps(result, ensure_asciiFalse, indent2)) else: print(fScore: {result[score]}) print(fUncommitted files: {result[uncommitted_files]}) if __name__ __main__: main()这个 CLI 的设计原则有三点值得记住第一参数必须由argparse定义--help输出对模型非常友好第二JSON 输出和人类可读输出用参数分离不要让模型去解析混合文本第三退出码也好、stderr 也好都遵循最朴素的规范错误不能吞掉。4.3 第二步用 MCP 把 CLI 能力暴露给 Agent很多人以为“把 CLI 接进 MCP”等于直接在 Server 里用subprocess调命令我在实践里强烈不建议这样。更好的做法是把 CLI 的核心逻辑抽成可复用的函数MCP Server 直接调用那个函数而不是再起一个子进程。这样做的好处是参数校验在函数边界就完成错误类型可以直接传到上层不用解析命令行字符串。下面是一个基于官方 Python SDK 的最小实现# repo_check_server.py from mcp.server.fastmcp import FastMCP from repo_check.cli import scan_repo mcp FastMCP(repo-check) mcp.tool() def repo_health_check(repo_path: str) - str: Run health checks on a code repository and return a structured summary. Args: repo_path: Absolute path to the repository root. try: result scan_repo(repo_path) return json.dumps(result, ensure_asciiFalse) except Exception as e: return json.dumps({error: str(e)}) if __name__ __main__: mcp.run(transportstdio)就这么简单的十来行代码你的 AI 客户端就已经可以通过 MCP 协议调用仓库健康度检查了。关键点在这个函数的 docstring 上——它就是模型的“使用说明”写清楚参数含义、返回结构模型才能正确调用和解析。4.4 第三步写一个 Skill把使用方法和分析流程沉淀下来现在 CLI 有了、MCP 通了但模型还缺一个东西它知道“可以查仓库健康度”但不知道“查完之后怎么给优化建议”。所以我们要写一个 Skill 包把这一套流程固定下来。--- name: repo-health-review description: Use when user asks to review repository health, check code quality, find outdated dependencies, or get refactoring suggestions for a codebase. --- # Repository Health Review When the user asks for a repository review action, follow this process. ## 1. Trigger the MCP tool Call repo_health_check with the absolute repo path. ## 2. Interpret the result - uncommitted_files: if greater than 5, suggest committing or stashing local changes. - dependencies.outdated: if unknown (-1), check the lock file manually. - score: a score below 60 should be treated as a high-priority maintenance issue. ## 3. Generate recommendations Always output: - A score summary - Top 3 issues sorted by severity - Concrete next steps (command suggestions if available)这个 Skill 的价值在于它把“人类专家的使用方法”写进了模型能按需加载的知识包里。没有这个包模型即使能调用工具给出的建议也很可能泛泛而谈有了它模型的回答质量会接近一个真正跑过这个工具的工程师。4.5 三者协作的完整时序最后把这一整套跑起来的完整时序捋一遍方便大家对照自己的 Agent 项目用户在对话里说“帮我检查一下 /data/my-app 这个项目的健康度哪里要优化”Agent 先做意图判断认为这是一个代码仓库巡检类任务命中 Skill 中声明的触发场景。Agent 加载repo-health-review技能包看到流程第一步是调用repo_health_check。Agent 通过 MCP 协议向本地 Server 发送工具调用请求参数是repo_path/data/my-app。MCP Server 收到请求内部直接调用scan_repo()函数拿到字典结果序列化成 JSON 返回给 Agent。Agent 解析 JSON根据 Skill 里的解释规则生成分数总结、Top3 问题和下一步操作建议回复给用户。整个链路里模型做的事情是决策和表达CLI 做的事情是执行MCP 做的是握手和传输Skill 做的是流程和方法沉淀。缺任何一环这个任务都跑不出理想效果。5. 常见问题与避坑实录5.1 MCP Server 连接失败的排查思路MCP 本地 Server 最常见的故障就是“Client 说连不上”。我之前排查过很多次绝大多数原因集中在三处启动命令配错了。MCP Client 配置里需要写command、args很多人把 Python 虚拟环境的路径写错或者忘了写绝对路径导致进程根本没起来。排查时先手动在终端跑一遍同样的命令确认能启动再谈其他。stdio 传输时父进程带不动子进程。有些 MCP Server 里又启动了其他子进程stdio 被抢占消息传不过去。解决办法是不要让 MCP Server 内部再包装 CLI 进程直接调用函数。依赖环境不一致。Client 启动 Server 时用的解释器和 Server 依赖的包版本不一致启动即崩溃。解决方案是把 Server 做成独立的 venv并在配置里指定解释器路径而不是裸用python。5.2 模型不按 Skill 走怎么办Skill 写好了也加载了但模型就是不按里面的流程走。这种情况我遇到过好几次追下来发现原因基本是三个。第一是 description 写得不对。模型靠 description 做技能匹配如果听起来像任何任务都能用模型就会在不合适的场景乱用如果太具体又容易漏掉。第二是 Skill 正文太长模型加载后信息过载反而忽略了关键步骤。第三是正文顺序不对——第一步要看什么、第二步怎么判断模型是线性读取的你把最重要的指令放在后面它就容易在中途“自由发挥”。我的经验是每个 Skill 的正文控制在 100 行以内核心步骤控制在 5 步以内每步必须有明确的验收标准。超过这个限度效果会明显下降。5.3 裸 CLI 和 MCP 封装到底怎么选做 Agent 工具时我见过两种极端一种是什么都走 MCP连一个ls都想包一层另一种是完全裸用 Shell什么协议都不加。我的选择标准是看三个维度维度裸 CLIMCP 封装工具数量少数几个且很轻量成体系需要统一管理和发现调用频率低频实验性调用高频生产级调用协作方只有自己的 Agent 在用需要多个 Agent 或多个应用共享如果你只是在自己电脑上做个实验项目裸 CLI 更快不用引入任何框架但如果你在做产品工具会被不同场景反复调用MCP 的稳定性、统一错误处理和可观测性优势会立刻体现出来。5.4 给刚起步的人三条最实在的建议兜一圈下来最后给准备入这个方向的人三条建议都是踩过坑之后才明白的。第一先从 CLI 开始练手。把一个现有工具改造成 AI 友好的 CLI加上--json输出和完整 help等于完成了 80% 的 Agent 化工作。这一步不需要学任何协议立竿见影。第二再学 MCP但别从零手写 Server。直接用官方 SDK 或框架写一个 wrapper把现成函数暴露出去跑通一个端到端流程MCP 的核心概念就自然理解了。比死磕协议文档高效得多。第三最后做 Skill而且要从你实际反复在做的任务里提炼。比如团队每天都要做环境检查那就把这个检查流程沉淀成一个 Skill 包。它不是为了炫技是为了让你的 Agent 越用越“懂行”。我个人现在的工作方式已经变成了所有新工具默认设计 CLI 接口所有 Agent 需要的外部能力统一走 MCP所有高频复杂任务流程全部沉淀为 Skill。这三样东西组合在一起带来的不是单个环节的提速而是整个自动化能力的质变——模型不再是一个“能聊天的程序”而是真的长出了一套能干活的工具箱和一本方法论手册。