MCP 协议详解

讲清 MCP(Model Context Protocol):为什么需要它、Host/Client/Server 三角架构、Tools/Resources/Prompts 三类能力、JSON-RPC 消息与传输方式、与 Function Calling 和 Skill 的区别,以及在 OpenCode 里接入一个 MCP 服务器的实际步骤。

前面几篇讲过 Agent 怎么调用工具(工作原理篇第三节),也讲过 OpenCode 的 Skill 机制。但有一个问题还没回答:工具从哪来? 每个产品都自己写一套文件读写、数据库查询、网页抓取,彼此不通用。MCP 就是为解决这个问题而生的协议。

一、问题:N 个模型 × M 个工具的集成爆炸

没有统一协议时,每个 AI 应用要接每个外部系统,都得写一遍适配代码:

  • 三个 AI 客户端 × 十个数据源 = 三十套集成;
  • 数据源升级一次接口,所有适配都要改;
  • 工具只属于某个应用,换个客户端就得重写。

这和早年的打印机驱动一个道理:设备厂商各自为政,操作系统要为每台设备写驱动。USB 出现后,设备只要说同一种协议,所有主机都能用。MCP 想做的就是 AI 世界的 USB-C:工具提供方实现一次 MCP 服务器,任何支持 MCP 的客户端都能直接用。

MCP 由 Anthropic 在 2024 年底开源发布,现已被多家 AI 客户端与工具厂商采纳。它的规范公开,核心消息格式是成熟的 JSON-RPC 2.0。

二、三角架构:Host、Client、Server

flowchart LR
    U[用户] --> H[Host 宿主应用]
    H --> C1[Client 连接 1]
    H --> C2[Client 连接 2]
    C1 --> S1[Server 文件系统]
    C2 --> S2[Server 数据库]

三个角色各司其职:

  • Host(宿主):你直接使用的 AI 应用,如 OpenCode、Claude Desktop、IDE 插件。它负责与大模型对话、决定何时用工具、并对用户负责;
  • Client(客户端):Host 内部为每个 MCP 服务器建立的一条连接。一个 Host 可以同时挂多个 Client,每个 Client 独立管理自己那条连接;
  • Server(服务器):提供能力的一方,可以是本地进程,也可以是远程服务。它只关心“把自己的能力暴露出去”,并不知道背后是哪个模型。

这种分层的关键收益:Server 写一次,任何 Host 都能接;Host 不用知道每个系统的细节,只认 MCP 这一种语言。

三、三类能力:Tools、Resources、Prompts

Server 向外暴露的能力分为三类,区别在于谁来决定调用:

能力谁来触发本质例子
Tools 工具模型自主决定可执行的动作,有参数、有副作用查询数据库、创建 issue、发送消息
Resources 资源应用或用户选择可读取的数据,按 URI 寻址某个文件内容、某张表的结构、某个文档
Prompts 提示模板用户主动选择预先写好的提示词模板,可带参数“按我们的规范审查这段代码”

理解这三类的关键是控制权:Tools 交给模型,所以必须设计好描述与权限;Resources 让应用决定塞什么进上下文,避免模型乱读;Prompts 让用户把常用流程显式地“点”出来。这和推理逻辑篇讲的上下文预算管理一脉相承:谁有权决定进入窗口,决定了系统的安全与成本。

此外,客户端一侧还可以向 Server 提供一些能力,如采样(让 Server 借用 Host 的模型)、根目录声明(告诉 Server 可以访问哪些路径)等,属于进阶用法。

四、消息与传输

消息格式

MCP 用 JSON-RPC 2.0:每条消息是一个带方法名和参数的 JSON 对象,请求有 id、响应带同样的 id。一次工具调用大致长这样:

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "query_db",
    "arguments": { "sql": "SELECT count(*) FROM posts" }
  }
}

连接建立时还有一次能力协商:双方交换各自支持的功能,之后只使用共同支持的部分。这让协议可以平滑演进,新旧版本互不崩溃。

传输方式

同一套消息可以跑在不同的管道上:

  • stdio:Host 把 Server 当成子进程启动,通过标准输入输出通信。本地工具的首选,零网络配置,权限继承自宿主进程;
  • Streamable HTTP:远程服务的标准方式,用 HTTP 发请求、用流式响应推送结果。适合共享服务与云端能力,可以配合认证与限流;早期版本的 HTTP+SSE 方案已被它取代。

一个实用判断:本地文件、本地命令用 stdio;团队共享的服务用远程 HTTP。

五、与 Function Calling、Skill 的区别

这三个词经常被混在一起,但层次不同:

概念层次解决什么
Function Calling模型能力模型如何输出结构化的工具调用 JSON(见工作原理篇)
MCP连接协议工具和数据如何被发现、描述、调用,跨应用通用
Skill流程知识完成某类任务的说明书,告诉模型该怎么做(见Skills 机制篇)

一句话概括:Function Calling 是模型的“手势”,MCP 是手和外部世界之间的“接口标准”,Skill 是“做事的步骤说明”。三者可以叠加使用——Skill 告诉模型“先查数据库再写报告”,模型通过 Function Calling 发出调用,MCP 把调用路由到数据库服务器。

六、在 OpenCode 里接入 MCP 服务器

OpenCode 的 MCP 配置放在 mcp.servers 下。官方推荐用 CLI 添加,它会保留配置里的其他内容:

opencode mcp add <name> --global --url <remote-url>
opencode mcp list
  • --global 表示对你本人所有项目生效;省略则只作用于当前项目;
  • 远程服务默认使用 OAuth 登录。mcp list 显示“待认证”时,在 OpenCode 界面输入 /mcps,选中服务器后按提示登录;
  • 认证凭据存放在 OpenCode 配置之外。能用 OAuth 就不要把 API Key 写进配置;确实需要时用环境变量替换,如 {env:MCP_API_KEY},避免密钥入库。

接入后,服务器提供的 Tools 会和内置工具一起出现在模型的可用列表里,模型按需调用。安全上仍要配权限:和权限门控一样,应该对会写入、会删除的工具设置 ask 或 deny,而不是放任全部放行。

接入之后能做什么,看一个真实例子。本站自己写了一个行情 MCP 服务器接入 OpenCode,会话里只说了一句“赛力斯的股价最近10日曲线绘制出来”——Agent 自主完成了三步:search_symbol 把“赛力斯”解析成证券代码,get_history 取回近 10 个交易日的行情,再按真实数据坐标渲染成 K 线图:

Agent 通过自建 stocks MCP 查询赛力斯行情并绘制的近 10 个交易日 K 线图

值得注意的不是图本身,而是分工:表达意图的只有一句话,数据发现、数据获取走 MCP 工具,绘图是 Agent 拿到结构化数据后用自己的代码能力完成的——外部工具负责“拿到真实世界的数据”,模型的生成能力负责“把数据变成用户要的形态”。这正是 Client 一次接入、Server 自由组合的意义。

七、一个典型的 MCP 服务器长什么样

以一个“查询博客文章元数据”的服务器为例,它的职责可以拆成三件事:

  1. 声明工具:list_posts(列出文章)、get_post(读取单篇),每个工具写清参数与描述;
  2. 实现处理函数:收到 tools/call 后执行真正的文件读取或数据库查询,结果以结构化文本返回;
  3. 返回可行动的错误:找不到文章时返回“未找到 slug 为 xxx 的文章,可先调用 list_posts 查看”,而不是一段裸堆栈——这和工具流水线里讲的错误归一化是同一个道理。

官方提供了 TypeScript、Python 等 SDK,把上面三步封装成几十行代码即可跑起来。

八、小结

概念一句话
MCPAI 世界的统一接口标准,工具写一次、处处可用
三角架构Host 调度、Client 管连接、Server 提供能力
Tools / Resources / Prompts模型决定 / 应用选择 / 用户点选
传输本地 stdio,远程 Streamable HTTP
与其他概念的关系Function Calling 是手势,MCP 是接口,Skill 是步骤说明
实战opencode mcp add 接入,OAuth 登录,写操作务必设权限

系列现在有七篇:从大语言模型基础出发,到工具层的 OpenCode、Skills 与本篇 MCP——工具从哪来、怎么用、怎么管,三者在这里闭环。