從抓包看 MCP:AI 工具調用背后的通信機制
TL;DR
通過抓包分析,我們清晰地了解了 MCP 通信的全過程:從建立 SSE 連接、三步初始化、工具調用操作到最終的連接終止??梢钥闯?,MCP 基于簡單的 SSE 協議搭建了一個功能強大的工具調用框架,使 AI 代理能夠便捷地調用外部工具完成復雜任務。
相比傳統的接口調用方式,MCP 更加靈活,能夠自動適應不同的工具集,讓 AI 代理 " 即插即用 " 地使用各種服務能力,這也是其設計的精妙之處。
當然,MCP 也并不是完美的,作為一個新興的協議,它仍然在不斷發展中。未來可能會有更多的功能和特性被添加進來,以滿足更復雜的需求。
背景
MCP 支持兩種標準的傳輸實現:標準輸入/輸出(stdio)和 Server-Sent Event[1](下稱 SSE)。stdio 基于命令行工具,多用于本地集成,通過進程通信來實現;SSE 基于客戶端和服務器的網絡通信,用于跨設備網絡的通信場景。
既然是用抓包來分析,我們就要選擇使用 SSE 傳輸 MCP server,然后通過工具進行網絡抓包分析。在抓包分析之前,我們必要對 SSE 協議進行簡單的了解。
SSE 協議
SSE 協議 是一種服務器推送技術,使客戶端能夠通過 HTTP 連接從服務器自動接受更新,通常用于服務器向客戶端發送消息更新或者連續的數據流(流信息 streaming)。
本質上,HTTP 協議是無法實現主動推送消息的,除非服務端“通知”客戶端接下來發送的是流信息。因此客戶端便不會斷開該連接,并持續從該連接上接收數據流。
看到這里你是否想到了 WebSocket 協議,二者看起來都是客戶端與服務端建立連接,然后服務端向客戶端推送數據??此葡嗤?,實際差別還挺大:
- SSE 是基于 HTTP 的輕量級協議;WebSocket 是獨立的協議。
- SSE 是基于 HTTP 請求
Accept: text/event-stream
;WebSocket 借助 HTTP 升級協議Upgrade: websocket
,之后使用獨立協議。 - SSE 是偽雙工,只支持服務端到客戶端的單向通信,客戶端到服務端的通信還需要另外發送 HTTP 請求進行;WebSocket 是全雙工的雙向通信。
- SSE 簡單、輕量,適合單向低頻推送;WebSocket 復雜度高、實時性強,適合雙向高頻交互。
從上面的對比不難看出 MCP 選擇 SSE 作為網絡傳輸協議的原因了。
了解了 SSE 協議之后,我們就可以開始了。
環境
- 抓包工具:Proxyman ,并安裝 CA 證書,方便處理 HTTPS 的請求。
- AI 應用:VSCode Insiders,安裝 Github Copilot 插件并開啟 Agent 模式。
- MCP Server:使用 上一篇文章[2]。
圖片
配置 MCP Server
在 settings.json 中添加 MCP Server 配置,為了能夠使用 Proxyman 的 HTTP Proxy 在 /etc/hosts 中添加 127.0.0.1 nio.local
。
{
"mcp": {
"servers": {
"spring-ai-mcp-sample": {
"type": "sse",
"url": "http://nio.local:8080/sse"
}
}
}
}
添加好之后就可以啟動 MCP Client 連接 Server 了。
圖片
MCP 通信
下面我們將通過抓包分析,詳細了解 MCP 通信的完整生命周期,包括建立連接、初始化、操作和終止四個階段。
當我們的 VSCode 成功連接到 MCP Server,此時從 Proxyman 已經可以看到多條通信了。
圖片
建立連接
由于不確定 Server 支持哪種方法,MCP Client 會同時發送 GET 和 POST 請求到我們配置的 Server 地址,嘗試建立連接。請求中的 Accept 是 text/event-stream,說明是與 Server 嘗試進行 SSE 通信。
這里配置的 Server 僅支持通過 GET 方式建立 SSE 通信,POST 請求收到 404 響應。而 GET請求的響應中,Server 端回傳了如下信息:
- 會話 id:3e19fbcd-51f4-4784-9f63-538c9a203859
- 事件 event :endpoint
- 數據 data:*/mcp/messages?sessinotallow=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?sessinotallow=3e19fbcd-51f4-4784-9f63-538c9a203859
這個 HTTP 連接作為后續 Server 向 Client 推送流信息的通道,所以在截圖中我們看到了其他的流信息。此時 MCP Client 與 Server 連接的聲明周期就開始了:
- 初始化
- 操作
- 終止
圖片
初始化
初始化階段必須是客戶端與服務器之間的首次交互,這個過程有點類似 TCP 的三次握手。
Client 發起初始化請求
從 Server 接收到后續的通信端點后,Client 會發送 initialize
- 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"
}
}
}
Server 響應初始化請求
同樣 Server 也回傳了流信息
- 相同的會話 id
- 事件類型 message
- 事件數據
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"}}}
在事件的數據部分,Server 也提供了與請求類似的內容(在下文中將直接展示流信息中的數據部分):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"logging": {},
"tools": {
"listChanged": true
}
},
"serverInfo": {
"name": "webmvc-mcp-server",
"version": "1.0.0"
}
}
}
初始化完成
在完成與 Server 端的信息交換,并協商(如版本兼容、功能支持)成功后,Client 發送請求完成初始化。
{
"method": "notifications/initialized",
"jsonrpc": "2.0"
}
這一次 Server 并不會有任何響應,像是 TCP 握手時客戶端發送了 ACK
操作
獲取 tool 列表
完成初始化后,Client 發送請求獲取 Server 支持的 tool 列表。
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
服務端通過 SSE 連接回傳 tool 列表,我們使用的示例 Server 中包含了 4 個 tool。在響應內容包含了如 tool 名字、輸入 schema 參數說明等信息??蛻舳耸盏竭@個響應后,會在本地緩存 tool 列表避免頻繁的請求。只有當 Server 端更新了列表并通知 Client 后才會更新緩存內容。
篇幅原因,沒有全部展示列表內容。
{
"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
}
},
//...
]
}
}
有了 tool 列表之后,我們便可以嘗試讓 Copilot 為了執行任務了。在 Copilot Agent 模式下輸入和上次一樣的任務:
First, help me check the user list to see if there is a user named Carson. If not, add a new user: Carson carson@gmail.com[3]; then check the list again to see if the new user was added successfully. Finally, say hello to Carson.
先來看執行結果。
在我發出任務請求后,VSCode 經過一通分析決定一次執行幾個 tool 來完成任務。這里我使用的是 GPT-4o。
圖片
如果切換到 Claude 3.7 Sonnet。
圖片
執行
回到 Proxyman 查看抓取的請求。
1.VScode 先請求 Copilot Server 時傳輸的請求內容比較長。以 GPT-4o 模型為例,請求大小為 49.7 KB,響應 1.34 KB。
請求中包含了:在響應中包含了經過分析任務后決定要調用的 tool:
{
"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"
}
- 一段非常長的系統 Prompt,有興趣的可以參考開發者整理的 GitHub Copilot Agent 官方 Prompt[4]
- 可用的 tool 列表,包括 VSCode 官方提供的系統 tool 以及配置的 MCP Server 提供的 tool
2.VSCode 根據響應的內容,調用 MCP Tool。
//http://nio.local:8080/mcp/messages?sessinotallow=3e19fbcd-51f4-4784-9f63-538c9a203859
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "getUsers",
"arguments": {}
}
}
MCP Server 在 SSE 連接中回傳 tool 的調用結果。
{
"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 進行處理,然后又得到一個要調用的 tool,以及需要提供的參數。
3.如此往復,直到最終完成任務的執行。在最右一個發送給 Copilot Server 的請求中,可以看到這個任務執行過程中所有調用的 tool 請求和響應的列表。也就是說,每次調用模型時,都會帶上此前調用的所有 tool 請求和響應,因此請求的 size 也是逐漸變大的。
終止
終止操作就簡單了,對于 SSE 傳輸類型的 MCP 交互來說,就是斷開相關的 HTTP 連接。
參考資料
[1]Server-Sent Event: https://en.wikipedia.org/wiki/Server-sent_events
[2]上一篇文章: https://mp.weixin.qq.com/s/jhXpxkMSyts_O5OuDsE9XA
[3]carson@gmail.com: vscode-file://vscode-app/Applications/Visual%20Studio%20Code%20-%20Insiders.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/workbench.html
[4]GitHub Copilot Agent 官方 Prompt: https://github.com/LouisShark/chatgpt_system_prompt/blob/main/prompts/official-product/github/github_copilot_agent.md