抓包理解MCP模型上下文协议
通过抓包分析,我们清晰地了解了 MCP 的完整通信流程:从建立 SSE 连接、三步初始化、工具调用操作,到最终的连接终止

通过抓包分析,我们清晰地了解了 MCP 的完整通信流程:从建立 SSE 连接、三步初始化、工具调用操作,到最终的连接终止。显而易见,MCP 基于简单的 SSE 协议构建了一个强大的工具调用框架,使 AI 代理能够便捷地调用外部工具完成复杂的任务。
相比传统的 API 调用方式,MCP 更加灵活,能够自动适配不同的工具集,使 AI 代理能够“即插即用”地使用各种服务能力,这正是其设计的巧妙之处。
当然,MCP 并非完美无缺,作为一个新兴协议,它仍在不断发展。未来可能会添加更多特性和功能,以满足日益复杂的需求。
1、背景
MCP 支持两种标准传输实现:标准输入/输出 (stdio) 和服务器发送事件 (SSE)。stdio 基于命令行工具,通常用于通过进程通信进行本地集成;SSE 基于客户端-服务器网络通信,用于跨设备联网场景。
由于我们通过抓包进行分析,因此需要选择一个采用 SSE 传输的 MCP 服务器,然后使用工具进行网络数据包分析。在深入研究数据包分析之前,有必要简要了解 SSE 协议。
2、SSE 协议
SSE 协议是一种服务器推送技术,允许客户端通过 HTTP 连接自动从服务器接收更新,通常用于服务器到客户端的消息更新或连续数据流(流式信息)。
本质上,除非服务器“通知”客户端它正在发送流信息,否则 HTTP 协议无法实现主动消息推送。这可以防止客户端断开连接,并允许其通过该连接持续接收数据流。
你可能想到的是 WebSocket 协议,因为两者都涉及在客户端和服务器之间建立连接,然后服务器将数据推送到客户端。虽然看似相似,但两者之间存在显著差异:
- SSE 是基于 HTTP 的轻量级协议;WebSocket 是一个独立的协议。
- SSE 基于 HTTP 请求,使用
Accept: text/event-stream
;WebSocket 利用 HTTP 协议升级,使用Upgrade: websocket
,然后使用独立的协议。 - SSE 是伪双工的,仅支持从服务器到客户端的单向通信;客户端到服务器的通信需要发送单独的 HTTP 请求。WebSocket 是全双工的,支持双向通信。
- SSE 简单、轻量,适用于单向低频推送;WebSocket 更复杂,实时性更强,适用于双向高频交互。
通过以上比较,不难理解 MCP 选择 SSE 作为网络传输协议的原因。
了解了 SSE 协议后,我们就可以开始分析了。
3、环境
- 抓包工具:Proxyman,已安装 CA 证书以处理 HTTPS 请求。
- AI 应用:VSCode Insiders,已安装 Github Copilot 插件并启用 Agent 模式。
- MCP 服务器:使用上一篇文章中的 Spring REST API 示例。

4、配置 MCP 服务器
在 settings.json
中添加 MCP 服务器配置。要使用 Proxyman 的 HTTP 代理,请在 /etc/hosts
中添加 127.0.0.1 nio.local
。
{
"mcp": {
"servers": {
"spring-ai-mcp-sample": {
"type": "sse",
"url": "http://nio.local:8080/sse"
}
}
}
}
添加配置后,我们就可以启动 MCP 客户端连接到服务器了。

5、MCP 通信
下面我们将通过抓包来分析 MCP 通信的完整生命周期,包括连接建立、初始化、运行和终止四个阶段。
当我们的 VSCode 成功连接到 MCP 服务器后,我们已经可以在 Proxyman 中看到多个通信。

6、建立连接
由于不确定服务器支持哪种方法,MCP 客户端会向我们配置的服务器地址发送 GET 和 POST 请求,尝试建立连接。请求中的 Accept 标头为 text/event-stream,表示尝试与服务器建立 SSE 通信。
配置的服务器仅支持通过 GET 方法建立 SSE 通信;POST 请求会收到 404 响应。在 GET 请求响应中,服务器返回以下信息:
- Session id:
3e19fbcd-51f4-4784-9f63-538c9a203859
- Event event:
endpoint
表示内容类型,它是客户端与服务器后续单向通信的端点。 - Data data:
/mcp/messages?sessionId=3e19fbcd-51f4–4784–9f63–538c9a203859
,其中/mcp/messages
由服务端配置为spring.ai.mcp.server.sse-message-endpoint: /mcp/messages
。
id:3e19fbcd-51f4-4784-9f63-538c9a203859
event:endpoint
data:/mcp/messages?sessionId=3e19fbcd-51f4-4784-9f63-538c9a203859
此 HTTP 连接作为后续服务端到客户端流信息推送的通道,这也是我们在截图中看到其他流信息的原因。此时,MCP 客户端-服务器连接的生命周期开始:
- 初始化
- 操作
- 终止

初始化
初始化阶段必须是客户端与服务器之间的首次交互,这个过程有点类似于 TCP 的三次握手。
客户端发起初始化请求
在收到服务器发送的后续通信端点后,客户端发送初始化请求以执行初始化、报告信息并协商功能。
- protocolVersion:协议版本
- capabilities:功能支持:listChanged 表示支持列表变更通知
- clientInfo:客户端信息
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {
"roots": {
"listChanged": true
}
},
"clientInfo": {
"name": "Visual Studio Code - Insiders",
"version": "1.100.0-insider"
}
}
}
服务器响应初始化请求
类似地,服务器也会返回流信息:
- 相同的会话 ID
- 事件类型:消息
- 事件数据
id:3e19fbcd-51f4-4784-9f63-538c9a203859
event:message
data:{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"logging":{},"tools":{"listChanged":true}},"serverInfo":{"name":"webmvc-mcp-server","version":"1.0.0"}}}
在事件的数据部分,服务器也提供了与请求类似的内容(下文中,我们将直接展示流信息的数据部分):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"logging": {},
"tools": {
"listChanged": true
}
},
"serverInfo": {
"name": "webmvc-mcp-server",
"version": "1.0.0"
}
}
}
初始化完成
客户端与服务器完成信息交互并协商成功后(例如版本兼容性、功能支持),发送请求完成初始化。
{
"method": "notifications/initialized",
"jsonrpc": "2.0"
}
此时服务器不会提供任何响应,类似于客户端在 TCP 握手过程中发送 ACK 后服务器不进行任何处理。
7、操作
7.1 获取工具列表
初始化完成后,客户端发送请求获取服务器支持的工具列表。
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
服务器通过 SSE 连接返回工具列表。我们的示例服务器包含 4 个工具。响应包含工具名称、输入架构参数描述等信息。客户端收到此响应后,会将工具列表缓存到本地,以避免频繁请求。缓存内容仅在服务器更新列表并通知客户端时更新。
由于篇幅限制,列表内容并未全部显示。
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "addUser",
"description": "Add a new user",
"inputSchema": {
"type": "object",
"properties": {
"arg0": {
"type": "object",
"properties": {
"email": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"email",
"name"
],
"description": "user to add"
}
},
"required": [
"arg0"
],
"additionalProperties": false
}
},
//...
]
}
}
有了工具列表,我们可以尝试让 Copilot 执行任务。在 Copilot Agent 模式下,输入与上次相同的任务:
首先,帮我检查用户列表,看看是否有名为 Carson 的用户。如果没有,则添加一个新用户:Carson, carson@gmail.com;然后再次检查列表,看看新用户是否添加成功。最后,向 Carson 打个招呼。
我们先来看看执行结果。
我发出任务请求后,VSCode 对其进行了分析,并决定执行多个工具来完成任务。我这里使用的是 GPT-4o 模型,它没有显示推理过程。如果我没有展开工具执行结果,我只能看到最终的响应。

如果切换到 Claude 3.7 Sonnet 模型,执行过程将包含推理过程,使整个过程更加清晰。

7.2 执行
回到 Proxyman 查看捕获的请求:
当 VSCode首先请求 Copilot 服务器,请求内容相当长。以 GPT-4o 模型为例,请求大小为 49.7 KB,响应大小为 1.34 KB。
该请求包含:
- 一个非常长的系统提示。有兴趣的可以参考开发者编写的 GitHub Copilot Agent 官方提示
- 可用工具列表,包括 VSCode 提供的系统工具和配置的 MCP 服务器提供的工具

响应中包含了分析任务后决定调用的工具:
{
"choices": [
{
"index": 0,
"delta": {
"content": null,
"role": "assistant",
"tool_calls": [
{
"function": {
"arguments": "",
"name": "bb7_getUsers"
},
"id": "call_nL7ToTNvrfLwUPYoqtUH8Yx3",
"index": 0,
"type": "function"
}
]
}
}
],
"created": 1745649196,
"id": "chatcmpl-BQTO863fJsOBHD4tU1LN3AEk5Uuo2",
"model": "gpt-4o-2024-11-20",
"system_fingerprint": "fp_ee1d74bde0"
}
VSCode 根据响应内容调用 MCP Tool。
//http://nio.local:8080/mcp/messages?sessionId=3e19fbcd-51f4-4784-9f63-538c9a203859
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "getUsers",
"arguments": {}
}
}
MCP Server 通过 SSE 连接返回工具调用结果。
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "[{\"name\":\"John\",\"email\":\"john@example.com\"},{\"name\":\"Jane\",\"email\":\"jane@example.com\"}]"
}
],
"isError": false
}
}
VSCode 随后将调用结果发送到 Copilot Server 进行处理,并接收另一个工具及其所需参数进行调用。

此循环重复,直到任务执行完成。在最右侧发送给 Copilot Server 的请求中,我们可以看到本次任务执行期间调用的所有工具请求和响应的列表。这意味着每次调用该模型时,它都会携带所有先前调用的工具请求和响应,这就是请求大小逐渐增加的原因。

7.3 终止
终止操作很简单——对于 SSE 传输类型的 MCP 交互,只需断开相关的 HTTP 连接即可。
原文链接:Understanding MCP Through Packet Capture: The Communication Mechanism Behind AI Tool Invocation
汇智网翻译整理,转载请标明出处
