直观理解时下大热的 MCP 协议

得益于 Cursor 从 v0.45.x 开始支持 Anthropic MCP 协议,最近 MCP server 的概念很火热。我想聊聊对这个协议的感受。

MCP 是什么?

MCP = Model Context Protocol = 模型上下文协议

说白了,它就是个「插件协议」,严谨点加个限定词,「专供 LLM 应用的插件接口协议」。

Anthropic 官方说 MCP 是受微软的 LSP (Language Service Protocol) 的启发而制定,有朋友熟悉 LSP 协议的话,应该马上会发现这两者极为相似。

给不了解 LSP 的朋友介绍一下。VSCode 大家都熟,可以装各种插件。因为 VSCode 是用 JS 写的,插件要运行在 VSCode 之内,所以也必须用 JS 写。

但有一类插件比较特殊:编程语言支持类插件。比如你想在 VSCode 里写 rust,肯定要装 rust 相关插件。可问题是 rust 官方的语言支持(提供错误提示、代码自动补全之类的功能)肯定也是用 rust 写的,无法直接跑在 VSCode 的运行时里。别的语言 C#、Java、Python 情况也一样,怎么办呢?

为了解决这问题,LSP 制定了一套基于 JSON-RPC 2.0 的标准协议。RPC 顾名思义「远程调用」,那些语言工具你爱跑在哪都行,只要你按照这协议,能接受 RPC 请求,能给出正确返回数据格式,那么就能顺利接入 VSCode。

这套协议带来的价值有三个点:

  1. 这是个开放标准,市面上那么多 IDE 编辑器,都有语言支持需求,大家都用这套标准的话,很快可以形成开放插件生态。

  2. 把插件和消费它的客户端解耦合了。按照 LSP 标准写插件,你不需要关心你服务的客户端到底是 VSCode 还是 JetBrain 还是 Vim,只要这些客户端支持 LSP,那你的插件都能接入,不需要挨个适配。

  3. LSP 协议本身预设了很多跟编程语言支持相关的「标准功能 」。例如最常见的代码自动补全 "textDocument/completion",或者点击跳转到函数定义 "textDocument/definition" 等等。这些都是跨语言、广泛存在的需求,是编程语言业界多年积累下来的集体经验。假如你自己哪天创造了个新的编程语言,要写配套的语言支持工具,那么你不用闭门造车,对着 LSP 协议,把里列举的所有「标准功能」挨个实现一遍,这妥妥的就是「语言支持工具界的最佳实践」了。

所以 MCP 到底是什么...

之所以在 LSP 上费这么多字,是希望能借用一个大家熟悉的老概念,快速对 MCP 这个新概念建立起一个直观的认识。

回到 MCP,它也是一个基于 JSON-RPC 2.0 的标准协议,LSP 有的那些优点它也有:

  • 开放标准:语言无关,实现无关,有助形成开放生态

  • 解耦合:只要客户端支持,你的 MCP Server 都能接入,不用多次适配

  • 最佳实践:参考「标准功能」,能借鉴行业集体经验,少走弯路

我认为「标准功能」,官方称为「能力(capability)」,是 MCP 价值比较大的东西,尤其对于开发 LLM 应用的朋友来说,支持这些能力基本上就跟 Cursor 在底层的 agent 工具层面上对齐了。

MCP 不是什么

MCP 不是 agent 框架,MCP 也不是 RAG 框架,它甚至都不是框架!尽管官方有提供 SDK,但 MCP 本身只是一个标准协议,目的是构建一个给 LLM/agent 用的「外接能力插件生态」。

不过 MCP 的标准设计里没有考虑 RAG 能力,是让我比较困惑的点。

能力 Capability

理解这个小节,我建议脑子里可以想着 Cursor 作为「LLM 应用」的范本。

client 端能力

  • roots 当前项目路径列表,对标 IDE 里的 workspace/project 概念,主要用来通知 server 端更新 resources(见下文)

  • sampling 供 server 调用 client 侧 LLM 的能力

server 端能力

  • tools 任意的外部工具:计算器、代码运行、搜索引擎之类

  • prompts 提示词模版,设计目的是为了支持类似 Github Copilot Chat 聊天框 / 开头的快捷指令

  • resources 当前项目下有什么资源可访问(主要是文件啦)。Cursor/Cline 聊天框 @/foobar.txt 就可以用这项能力来实现

  • completion 自动补全,快捷指令和资源都需要,提升用户体验

  • logging 给到 client 的 log 信息推送,这个属于杂项,方便 debug 之类

其他

  • resources 不只能建模文件,也可以建模 git 历史,数据库表等其他资源,只需要 uri 上通过 "git://""db://" 来区分即可

  • 两端都支持自定义能力,通过 experimental namespace 来暴露。

  • 前面提过 MCP 没考虑 RAG 的用例,目前看来似乎可以通过 prompts + completion 能力来间接实现。

通讯模型

MCP 是一个 client/server 架构的 RPC 协议,需要关注两端的通讯模型。大致可分三段生命周期来看:初始化阶段,运行阶段,结束阶段。不过先铺垫两个前置知识,方便后面的理解。

前置知识

一、实体术语定义

一共有三类实体:host, client, server.

host 指「LLM 应用」的本体,它大概率是个 GUI 程序。在 MCP 的语境下,这就是一个容器,负责管理多个 client 实例,同时要集成 LLM,承接用户交互,特别是各种授权的工作。

每个 client 实例只负责与一个 server 建立有状态的连接,然后进行 RPC 通讯。server 是实际干活的、跑插件的线程,client 是留在 host 内负责 RPC 调用的一段简单的程序。

当前的协议版本下(版本号:2024-11-05,你没看错,它是用日期来做版本号的)

  • host 与 client 是一对多关系

  • client 与 server 是一对一关系

这里插一句,「一对一」的奇怪设定是暂时的。目前 MCP 只针对 client/server 都跑在本地的场景设计,官方 SDK 在使用 stdio 为传输信道的时候,更是做了个「由 client fork 子进程来跑 server」的强假设。好在 roadmap 里面有提,支持 remote server 是眼下的第一优先级,预计 2025 上半年会更新相关标准。

二、JSON-RPC 2.0 的三种信息类型

JSON-RPC 2.0 标准有三类 RPC 信息类型:request, response, notification. 注意几个点:

  • request 必须有对应 response, id 要对得上

  • response 的 resulterror 字段互斥,同时只可能有其一

  • response error.code 必须是整数,并且协议预留了一批错误码,代表特定含义(类似 HTTP status code)

  • notification 只比 request 少一个 id, 并且不要求有对应 response

ts 复制代码
type JsonRpcRequest = {
  jsonrpc: "2.0";
  id: string | number;
  method: string;
  params?: {
    [key: string]: unknown;
  };
};

type JsonRpcResponse = {
  jsonrpc: "2.0";
  id: string | number;
  // result 与 error 是互斥的
  result?: {
    [key: string]: unknown;
  };
  error?: {
    code: number;
    message: string;
    data?: unknown;
  };
};

type JsonRpcNotification = {
  jsonrpc: "2.0";
  method: string;
  params?: {
    [key: string]: unknown;
  };
};

三个通讯生命周期

一、初始化阶段 Initialization

client/server 需要握手协商,交换各自能力(capability)声明,跟 TCP 的三次握手基本一样。

第一次:client 向 server 发送 request,声明 client 侧提供的能力。

Client:「Server 老哥在吗?我能干这些,你能干啥?」

第二次:server 向 client 回复 response,声明 server 侧提供的能力。

Server:「Client 老弟,我在呢,我能干这些,需要干啥活你喊我哈!」

第三次:client 向 server 发送 notification,确认连接建立

Client:「得嘞,那我开始干活了,有事儿我再喊你。」

二、运行阶段 Operation

根据初始化阶段交换的能力声明,两端开始互相发送 RPC 信息。这里展示一段能力调用示例。

json 复制代码
// 1. server 在初始化阶段,第二次握手时,向 client 公布自己的 tools 能力
{
  "capabilities": {
    "tools": {
      "listChanged": true
    }
  }
}

// 2. client 初始化后,主动拉取 tools 列表
// Request:
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {
    // 可选参数,list 如果很长,可支持翻页
    "cursor": "optional-cursor-value"
  }
}
// Response:
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "get_weather",
        "description": "Get current weather information for a location",
        "inputSchema": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "City name or zip code"
            }
          },
          "required": ["location"]
        }
      }
    ],
    "nextCursor": "next-page-cursor"
  }
}

// 3. client 调用工具
// Request:
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "location": "New York"
    }
  }
}
// Response:
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
      }
    ],
    "isError": false
  }
}

// 4. 如果 server 端因为什么原因,可用 tools list 发生变化,应该通知 client 重新拉取
{
  "jsonrpc": "2.0",
  "method": "notifications/tools/list_changed"
}

三、结束阶段 Shutdown

标准只说任何一端(正常来说是 client 端)可以主动断开连接,没有硬性规定这个阶段的具体协议。因为传输层通常会有相关的断联信号,已经够用了,没必要再在上层协议重复建设。

但是实际写落地实现,开发者还是需要做一些处理的,比如 graceful shutdown, 或者错误重启之类的。

总结

目前整个 AI 应用范式没有固定下来,整个业界都在积极探索,摸着石头过河。这个背景下 MCP 相当于把 AI 应用厂商们拉了个大群,一起来总结业界的最佳实践,制定标准推广集体智慧。当前 MCP 的生态发展势头很不错,标准本身更新得也很紧跟潮流。最近当红炸子鸡 Cursor 的加入,可以说是对 MCP 的重大利好,势必会进一步刺激 MCP server(插件)生态的成长。

现在正在做 LLM 相关应用的朋友,我非常推荐拥抱这个协议标准,好处多多。

  1. 首先协议本身很薄不复杂,看不出有技术上的坑。同时官方也有 SDK 可用,支持的难度不高。

  2. 其次可以拥抱生态,快速接入第三方插件,增强自身产品竞争力。

  3. 最后,让自己的应用去支持协议要求,等于是跟进业界最佳实践了,避免闭门造车走死胡同。

如果觉得本文对你有帮助,欢迎转发和关注(微信公众号同名),我会持续分享在开发 multi-agent 系统过程中的第一手经验和心得。

相关推荐
我不吃饼干2 小时前
【AI初体验】用cursor复刻一个掘金网站
前端·cursor
孟健8 小时前
普通程序员 vs 提示词高手:同样的 AI,差距竟有 10 倍
aigc·cursor·ai 编程
不止会JS3 天前
cursor使用经验分享(java后端服务开发向)
java·开发语言·经验分享·cursor
seven1085 天前
cursor MCP server 如何AI 编程中实现动态数据获取
前端·cursor·mcp
Ironben6 天前
看好 MCP,但用不了 Claude,所以做了一款 MCP 客户端
人工智能·claude·mcp
MervynZ8 天前
人工智能生成的代码如何加剧技术债务
人工智能·cursor·trae
潘锦8 天前
AI 编程后,软件的本质复杂度会有变化吗?
cursor·trae
尽一份心出一份力8 天前
dify-dashboard 用Windsurf和Claude耗时两天开发的 DIFY 辅助项目
前端·claude·windsurf
!win !10 天前
Trae和Cursor小斗法
cursor·trae·ai ide