1.model context protocol
这个模型上下文协议,这个显然是一套协议,但是说实话,这个定义对我来说挺抽象的。我也很难去理解。我接下来的表述 也只是我近期对它理解程度的一种表达。
就是我不理解什么叫模型上下文协议,可能当程序员这些年,一碰到叫什么上下文的,或者叫context的我,就像触发了我都职业病一样,慌张。每个字都懂,连起来就是不知道啥意思。
但是我理解当大模型决定接下来要调用工具的时候,(实际上大模型本身不会直接调用工具),它只是返回一个结构化调用指令,真正调用工具的是我们的应用,或者说是我们的Agent。 我们以openai的Chat Completions API 为例,如果模型判断需要调用工具,返回的 assistant 消息大致是这样的:
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"北京\"}"
}
}
]
}
你的应用层代码解析 tool_calls,找到 function.name 和 function.arguments,然后真正去调用天气接口、数据库、搜索服务或其他工具,模型本身不执行工具,它只是返回调用意图。
也就是说没有mcp之前,我们的应用在拿到这样一段结构化调用指令之后,要去执行指令,并且拿到指令执行的结果在反馈给大模型,想必不同的应用一定会有不同的实现方式。因为它势必涉及到应用到底如何解析这段输出,然后将其转化成对工具的调用,而mcp的出现恰好解决的第一个层面我能理解的问题就是:
- mcp规范了工具调用方式
也就是说当模型返回这段结构化调用指令后,应用层不用自己再为每个工具写一套调用逻辑,而是通过 MCP 协议把指令转成标准请求,把这次工具的调用抽象成一种标准化的请求,协议定义了请求的方式是什么,请求的结构是什么。 也就是他要求实现这个工具的服务(我们称之为server端)必须能接收这种请求,同样能处理这种请求。 这是我想表述的我能理解的第一个层次:就是任何一个敢叫自己是mcpserser的,它都必须要满足这种协议。
我对模型上下文协议能理解的第二个层面:这个协议也要求工具的调用结果要有统一的返回格式,这个我认为也挺对的,以前我们自己封装工具,返回值想怎么返回就怎么返回,那是因为再解析的时候,我们是自己写代码去解析返回值。而mcp协议解决的恰好是这个问题:他要求格式统一,也就是按照协议规定好的结构返回工具调用结果,那调用工具的这段代码就能被标准化(后面我们都称之为mcp客户端)。我们的应用再也不会为不同的工具适配不同的解析结果代码。 所以说我能理解的第二个关于上下文协议的一种表述是:
2.mcp规范了工具调用结果的返回
近期的一段时间我都在使用langchain,也习惯了用langchain的@tool注解去包装一个工具,一直也都觉得这或许已经将工具封装的很完美了。因为如果是直接使用openai的http请求访问,相比自己去写大量的tools工具集合,定义工具名称,参数schmma,description。显然@tool已经帮我们做了很多底层的事情了。
POST /v1/chat/completions
Content-Type: application/json
Authorization: Bearer <API_KEY>
{
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "福州现在天气怎么样?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,例如福州、北京"
}
},
"required": ["location"],
"additionalProperties": false
}
}
}
],
"tool_choice": "auto"
}
但是每当一个新的应用出现的时候,重复的工作就来了,需要再新的应用中重复定义工具,然后将工具绑定到LLM上。尽管@tool这个注解已经很方便的将我们的func转成schema,但是其实这部分也是硬编码的重复工作。所以mcp协议中也规定了 mcp的server如何暴露自己的工具集和schema,也规定了mcp的客户端通过何种方式能获得工具列表和参数,这样大家都按照各自的标准去实现,服务器按标准暴露,客户端按标准接入。就能够实现从server动态发现工具,而不用硬编码在代码中。
这就有点像那种插件一样,一个AI应用说我想要扩展那个mcpserver的工具, 应用只需要配置好参数,客户端就会按照确定好的协议,到指定的地方获取到服务端暴露的工具。 就目前来说我理解的只有这种层次,所以 我想表达的就是第三点:
3.mcp规范了模型如何发现工具
mcp 架构角色
mcp架构中涉及到三种角色,从上文我们也能隐隐约约的感觉到,我反复提到的几个关键词
- MCPServer
- MCPCLient
- MCPHost
mcp协议本质上在解决一个什么问题:本质上我个人理解解决的是如何规范化Agent和Tool之间的调用方式。也就是说不用mcp之前工具怎么定义?答案是瞎特么定义。agent怎么调用工具啊?答案是瞎特么调用,有用反射调用的,有用方法名称遍历调用的,五花八门吧。
现在用了mcp ,我们统一把工具定义在一个地方管理和运行,这个就叫MCPserver,也就是说运行Tool的服务叫做MCPserver ,Agent想调用这些工具怎么办呢,集成MCPClient,也就是一个Agent需要通过集成MCPClient去实现工具的调用,这就有点像什么呢,比如你想要在应用中链接kafka,你需要在项目的依赖中引入kafaka的客户端工具。
其实对MCPHost 说实话我理解不深,它很抽象。我现在的知识面也就只能认为他就是我们的agent,我们的AI应用。 你看官网给出的答案 :大概意思也是ai应用,并且协调和管理我们的客户端。
MCP Host: The AI application that coordinates and manages one or multiple MCP clients
client server之间的通信机制(通信模式)
上面的截图是langchain在创建客户端的代码。其实就有点类似像我们的应用在集成kafka的时候一样,然后调用客户端暴露的方法,比如获取工具集合。但是底层客户端和服务端如何通信的,都被SDK给掩盖封装了。作为开发人员,确实本质上不需要关注底层的细节。但是深挖底层细节,有助于开发人员在工程架构上整体去把握,我对技术是有焦虑的,我其实不太允许当我把一项技术应用到我的项目中,而这项技术对我来说跟黑盒一样。
截止到我写这篇文章的时候,我甚至都还没理解透彻传输层协议,但是我还是决定把它写出来,以此来记录我此刻的心得,说不定下一篇我还有新的思路。
说实话 一提客户端和服务端通信,我第一时间脑子里想到的单词就是transport 还有 protocol。因为我以前在学习其他框架,我用Dubbo举例,里面就似乎涉及到transport层,和 protocol层。
transport层:传输层,见名知意它解决的是一个数据如何传输的问题,它解决的实际是底层的通信问题,怎么建立连接啊。
而protocol:协议层 我更愿意称之为数据层,它解决的是依托于传输层,应用层的数据是如何组织的。
我借鉴一段官网的简介
Transport layer The transport layer manages communication channels and authentication between clients and servers. It handles connection establishment, message framing, and secure communication between MCP participants.
大概意思就是它解决的问题是 连接的建立,消息的帧结构frame,也就是消息的边界,还有认证安全相关。
目前支持两种传输层协议:
-
stdio 标准输入/输出:客户端以子进程的方式启动MCP服务器。
-
streamable HTTP 远程 HTTP 通信: Streamable HTTP 是把 MCP 消息放在 HTTP POST 里传输
-
早期还有HTTP + SSE 通信方式,关于SSE,我想单独写一篇文章记录,我还是比较有兴趣的对这个通信方式。
上面有一点就是消息边界:就是传输协议可以把消息的边界划分出来,但是划分出来的每个消息,到底是什么结构呢,这就是接下来讲解的数据层:JSON-RPC 2.0
报文格式 JSON-RPC 2.0
以我的理解,见名知意嘛 就是用json的方式实现远程方法调用。也就是说,我调用哪个方法,都是通过把方法名通过json格式的参数传递到服务器端。MCP 协议本身只有一个 JSON-RPC 消息通道。 也就是说mcp协议是通过JSON-RPC2.0的格式进行交互的。
借着HTTP的外壳,传输JSON-RPC2.0格式的数据。
JSON-RPC 2.0 是什么?
JSON-RPC 2.0 是一个轻量级的远程过程调用(RPC)协议,使用 JSON 格式来编码消息。简单说,它就是一种让客户端调用服务器上的函数/方法的标准格式。
1. JSON-RPC2.0中定义了请求消息结构:
{
"jsonrpc": "2.0",
"method": "xxx",
"params": {},
"id": 1
}
成功响应 Success Response
{
"jsonrpc": "2.0",
"result": {},
"id": 1
}
3. 错误响应 Error Response
{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "Method not found",
"data": null
},
"id": 1
}
4. 通知 Notification
通知本质上是“不需要响应的请求”,所以它没有 id。
{
"jsonrpc": "2.0",
"method": "xxx",
"params": {}
}
总结
截至目前,我对mcp的基础理解就整理到这里。还有很多更深入的了解,我需要单独专门讲解,比如mcpserver到底暴露了哪些,streamable怎么流式,认证与安全等等。目的是全方面的深入,如果感兴趣敬请期待。