MCP 架构解密:那些你看不见的 Client 和 Server

技术架构评审会开到一半,老李突然把激光笔往桌上一放。

“我昨晚看了 MCP 的官网,”他拧开保温杯,“这不就是包装了 JSON-RPC 的 REST API 吗?Client 发请求,Server 返回结果,跟我们现有的 API 网关没什么区别。为什么要额外引入一个新协议?”

会议室里几个同事齐刷刷看向小王。小王不慌不忙站起来,走到白板前。

“老李,你家有几个遥控器?”

老李一愣:“电视一个,空调一个,投影仪一个,风扇还有一个。”

“每个遥控器是不是都能‘发送指令’?按一下,设备就动。但为什么你没法用电视遥控器开空调?”

“那当然不行,红外码不一样。”

“对。REST API 就像红外遥控器——每家设备的‘码’都不一样。你能说它‘就是发送一个请求’吗?能。但你的电视遥控器和空调遥控器,能互换吗?不能。”小王拿起记号笔,“MCP 不是又一个遥控器,它是把红外协议标准化了。任何遥控器,都能开任何设备。”


MCP 不只是“发请求”,它有三大原生能力

“咱们先画架构全景图。”小王在白板上画了三个框。

图:MCP 架构全景——Client、Transport、Server 三大板块

“REST API 的核心抽象只有一个——‘资源’。你对 URL 做增删改查。但 AI 应用需要的远不止这些。”小王指着 Server 的三个框。

Resources——这是你最熟悉的部分,类似 REST 的资源。数据库里的一张表、文件系统里的一个目录、一段文档内容。Client 可以读这些资源,把它们的上下文喂给 LLM。但和 REST 不同,MCP 的 Resource 是结构化的 URI,而且支持订阅更新——文件被修改了,Server 能主动通知 Client。”

Tools——这是 AI 的‘手’。不是只读的数据,而是可以执行的动作。查数据库、发邮件、调 API、操作浏览器。Tools 和 Resources 最大的区别是:Resources 不会改变世界,Tools 会。”

Prompts——这是最容易忽视的一个。它是一套预定义的提示词模板,带参数。比如一个‘代码审查’的 Prompt 模板,接受 languagecode 两个参数,返回一段精心设计的系统指令。这意味着,最佳实践可以被封装成 Prompt 模板,在团队内共享。”

老李的激光笔在手指间转了一圈:“Resources 就是 GET 请求,Tools 就是 POST 请求,Prompts 是什么?一个模板引擎?”

“比模板引擎深。Prompts 封装的是使用 AI 的经验。”小王在白板上写了个例子,“比如你们团队最会写 SQL 的老张,他每次让 AI 生成 SQL 时,都会加一句‘请确保所有查询都使用索引列,避免全表扫描’。有了 Prompts,这句话不需要每次手动输入,老张定义好一个 sql-review 的 Prompt,全团队直接用。”


能力发现:Client 怎么知道 Server 能干嘛?

老李靠在椅背上:“那 Client 连接上 Server 之后,怎么知道这个 Server 提供了哪些 Resources、哪些 Tools?总不能翻文档吧。”

“这就是 MCP 和 REST API 最关键的区别之一——能力协商。”小王转身在白板上画了一个时序图。

“REST API 的能力通常靠文档描述,OpenAPI Spec、Swagger——但这些都是给人看的,机器不会自动读。MCP 不同。Client 连上 Server 后,第一步就是发 initialize 请求,Server 返回自己的身份和能力清单。第二步 Client 发 tools/listresources/listprompts/list,Server 把自家有什么全列出来。这个过程完全自动,不需要人手动配置任何东西。”

图:MCP 初始化流程——自动发现所有能力

“这就好比你插一个 U 盘到电脑上,操作系统自动识别它是存储设备、显示容量、安装驱动。你不需要手动告诉电脑‘这是个 256G 的 U 盘,请用 FAT32 挂载’——这一切都是自动协商完成的。”

老李若有所思:“那我们内部如果有十几个不同的数据源,每个包一层 MCP Server,Client 连上之后就能自动发现所有数据库、所有 API 工具?”

“对。你在 Claude Desktop 里打开 MCP 配置,能看到一个工具列表,上面列着 query_users_dbsearch_knowledge_basesend_slack_message——这些都是 Claude 从各个 Server 自动发现来的。不是开发者手动注册的。”


JSON-RPC 2.0:协议的消息格式

“底层通信协议用的是 JSON-RPC 2.0。”小王在白板上写了几行字,“为什么选它?三个原因——简单双向成熟。”

“每条消息要么是请求,要么是响应,要么是通知。请求带 id,响应带对应的 id,通知不带 id(Server 不需要回复)。格式统一,解析简单。而且 JSON-RPC 支持双向通信——Client 可以调用 Server 的方法,Server 也可以主动向 Client 发通知。”

小王打开笔记本,展示了一段消息示例:

typescript
// Client 请求:列出所有可用工具
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {}
}

// Server 响应:返回工具列表
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "query_database",
        "description": "执行只读 SQL 查询",
        "inputSchema": {
          "type": "object",
          "properties": {
            "sql": { "type": "string", "description": "SELECT 语句" }
          },
          "required": ["sql"]
        }
      }
    ]
  }
}

// Server 主动通知:某个 Resource 更新了
{
  "jsonrpc": "2.0",
  "method": "notifications/resources/updated",
  "params": {
    "uri": "file:///docs/api-guide.md"
  }
}

“第三条消息特别关键,”小王强调,“这是 Server 主动发给 Client 的通知。意味着当知识库文档更新、数据库记录变更时,Server 不需要等 Client 来轮询,直接推过去。Client 收到通知后,可以立即刷新它存在 LLM 上下文里的缓存内容。这是 REST API 做不到的——REST 只能 Client 主动请求,Server 没法主动推送。”

老李若有所思:“WebSocket 也能双向推送,为什么不直接用 WebSocket?”

“好问题。WebSocket 是传输层,JSON-RPC 是消息格式层——它们不冲突。实际上 MCP 的 HTTP+SSE Transport 就用了 Server-Sent Events 来做服务器推送。但 JSON-RPC 的好处是传输无关——你可以跑在 stdio 上,可以跑在 HTTP 上,也可以跑在 WebSocket 上。换传输方式,消息格式不变。这让 MCP 既能用于本地进程通信,也能用于远程服务调用。”


请求的完整生命周期

“一条请求从 Client 到 Server 再到返回,中间经过什么?”老李追问。

小王在白板上画了完整流程:

第一步,Client 发起请求。 比如 LLM 决定调用 query_database 工具,参数是 {"sql": "SELECT COUNT(*) FROM users"}。Client 把这个包装成 JSON-RPC 请求,通过 Transport 发给 Server。

第二步,Server 解析并校验。 Server 收到请求后,先检查 JSON-RPC 格式是否正确,然后找到对应的工具定义,用 inputSchema 校验参数。参数不符合 Schema,直接返回错误,不需要人工写校验代码。

第三步,执行工具逻辑。 Server 执行实际的数据库查询。这里可以做权限校验、速率限制、日志记录。

第四步,返回结果。 Server 把查询结果包装成标准格式返回。成功返回 {result: ...},失败返回 {error: {code: -32000, message: "..."}}。错误码有标准定义,Client 能统一处理。

第五步,Client 处理响应。 Client 收到结果后,把内容注入 LLM 的上下文窗口,让 AI 基于真实数据生成回答。”

老李把激光笔放回桌上,沉默了一会儿。


“嗯……还有点意思。”他终于开口,“我收回‘这就是个 REST API’这句话。能力自动发现、双向推送、标准化错误处理——这些确实是 REST 没有的东西。”

“老李,你这就——”

“我没说认可。我是说,下不为例啊。”老李端起保温杯喝了一口,枸杞已经凉透了。“那什么,你说 Server 能主动推送通知,如果 Client 断线重连之后,怎么知道错过了哪些更新?有没有类似 Kafka offset 的机制?给我看看 MCP 的状态同步方案。”

小王咧嘴笑了,把笔记本的屏幕转向老李。白板上的架构图被夕阳打上一层金边,那些箭头和方框在光里闪闪发光,像一个正在呼吸的有机系统。

30 分钟搭建你的第一个 MCP Server(Python 版)
MCP 是什么?AI 应用的"USB-C 接口"