抓包理解MCP模型上下文协议

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

抓包理解MCP模型上下文协议

通过抓包分析,我们清晰地了解了 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

汇智网翻译整理,转载请标明出处