MCP细节与原理分析-使用Python+SSE传输机制

部署运行你感兴趣的模型镜像

1. 相关总结

总结内容链接
MCP之大模型Function Calling开发与原理https://blog.youkuaiyun.com/a82514921/article/details/147860120
MCP概念与server开发及调试-使用Python+SSE传输机制https://blog.youkuaiyun.com/a82514921/article/details/147860221
MCP client开发与日志分析-使用Python+SSE传输机制https://blog.youkuaiyun.com/a82514921/article/details/147860518
MCP细节与原理分析-使用Python+SSE传输机制https://blog.youkuaiyun.com/a82514921/article/details/147860541
MCP server支持在不同Python脚本中实现工具方法https://blog.youkuaiyun.com/a82514921/article/details/147860559
MCP client支持同时连接多个MCP server-使用Python开发https://blog.youkuaiyun.com/a82514921/article/details/147860587

2. 说明

以下使用 Python+SSE 传输机制开发的 MCP 示例项目,结合抓包结果,对 MCP 的细节与原理进行分析

3. SSE 传输机制

SSE(Server-Sent Events)不是 MCP 首创的技术,SSE 在 2012 年就已经发布,相关规范可参考以下链接

https://www.w3.org/TR/2012/WD-eventsource-20120426/

https://html.spec.whatwg.org/multipage/server-sent-events.html

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events

SSE 用于支持服务器向浏览器 web 页面推送数据,需要使用长连接

SSE 的 MIME type 是 text/event-stream

服务器通过 SSE 发送的数据包括 event 与 data,可以使用不同的 event 类型,默认为“message”,数据格式如下所示

event: add
data: 73857293

4. MCP SSE 传输机制

MCP 使用 SSE 传输机制,使客户端使用 HTTP POST 请求服务器时,支持服务器到客户端的流式处理

4.1. MCP SSE 传输机制数据传递方式

MCP SSE 传输机制数据传递方式可参考以下链接

https://modelcontextprotocol.io/docs/concepts/architecture#transport-layer

https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse

https://modelcontextprotocol.io/specification/2024-11-05/basic/transports

MCP server 必须提供两个接入点

  • SSE 接入点:供 MCP client(与 MCP server)建立连接并接收 MCP server 推送消息
  • 标准 HTTP POST 接入点:供 MCP client 向 MCP server 发送消息

所有传输层(包括 SSE)均采用 JSON-RPC 2.0 协议进行消息交换

4.2. MCP SSE 连接初始化

MCP SSE 传输机制连接初始化的说明可参考以下文档

https://modelcontextprotocol.io/docs/concepts/architecture#1-initialization

https://modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle#initialization

MCP SSE 传输机制连接初始化过程如下

client 向 server 发送 initialize 请求,包含协议版本和支持的能力

sever 向 client 响应协议版本和支持的能力

client 向 server 发送 initialized notification 作为确认

开始交换正常消息

5. MCP 工具

工具是 MCP 中的核心组件,使服务器能够向客户端开放可执行功能,让大模型能够通过服务器执行操作,可参考以下文档

https://modelcontextprotocol.io/docs/concepts/tools

https://modelcontextprotocol.io/specification/2025-03-26/server/tools

5.1. 显示工具列表

MCP client 向 server 发送 tools/list 请求可以发现可用的工具

5.2. 执行工具

发送 tools/call 请求可以执行工具

5.3. 错误处理

若执行工具时出错,有两种实现机制

一是标准 JSON-RPC,包括未知工具、非法参数、服务器错误等,示例如下

{
  "jsonrpc": "2.0",
  "id": 3,
  "error": {
    "code": -32602,
    "message": "Unknown tool: invalid_tool_name"
  }
}

二是在工具执行返回的 result.isError=true,包括 API 失败、非法输入数据、业务逻辑错误等,示例如下

{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Failed to fetch weather data: API rate limit exceeded"
      }
    ],
    "isError": true
  }
}

5.4. 工具执行返回格式

MCP 工具可以返回不同类型的结果,包括文本、图片、嵌入式资源等

对于文本类型的返回结果,MCP 仅定义了通过 type 字段值为 text 表示属于文本类型,text 字段值为对应的返回结果,没有定义具体的返回数据格式,示例如下

{
  "type": "text",
  "text": "Tool result text"
}

即 MCP 不限制工具执行返回结果的类型,由大模型去理解具体的含义

6. MCP SSE 传输机制通信数据分析

6.1. MCP server 提供的两个接入点

MCP server 会提供两个接入点,可以认为是两个不同的 HTTP 服务路径,分别为 SSE 接入点与标准 HTTP POST 接入点

6.1.1. SSE 接入点

SSE 接入点使用 SSE 传输机制,使用 TCP 长连接

MCP server 向 client 返回数据时,都通过 SSE 接入点返回

对应以上 MCP SSE 通信过程图中的源端口为 62614 的 TCP 连接

6.1.2. 标准 HTTP POST 接入点

标准 HTTP POST 接入点提供一个允许 POST 的接口,可以使用一个或多个 TCP 连接(多次 HTTP 请求在 HTTP 头指定“Connection: keep-alive”且协议版本及服务器均支持时,可以使用同一个 TCP 连接)

当 MCP client 访问 MCP server 的标准 HTTP POST 接入点时(包括 method=initialize、notifications/initialized、tools/list、tools/call 等),MCP server 会通过 SSE 接入点对应连接向 MCP client 发送数据,在当前标准 HTTP POST 接入点只会返回“202 Accepted”

即标准 HTTP POST 接入点用于客户端向服务器发送数据,当服务器收到对应请求后,不会在当前 TCP 连接向客户端返回对应的数据,而是在 SSE 接入点的 TCP 连接向客户端发送对应的数据

对应以上 MCP SSE 通信过程图中的源端口为 62615 的 TCP 连接

以上两个接入点的数据通信方式的时序图如下所示

在这里插入图片描述

6.2. MCP SSE 连接初始化通信数据分析

一次常见的 MCP SSE 通信过程,包括连接初始化、显示工具列表、执行工具等,如下图所示

在这里插入图片描述

MCP SSE 传输机制连接初始化通信数据分析如下

6.2.1. client 访问 server SSE 接入点

对应以上 MCP SSE 通信过程图中的序号为 4 的包

client 访问 server SSE 接入点,GET /sse,在 HTTP 头指定“Accept: text/event-stream”

通信数据示例如下

GET /sse HTTP/1.1
Host: 127.0.0.1:8000
Accept-Encoding: gzip, deflate
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Accept: text/event-stream
Cache-Control: no-store

6.2.2. server 通过 SSE 向 client 返回 event=endpoint 数据

对应以上 MCP SSE 通信过程图中的序号为 6、8 的包

在 HTTP 头返回了 content-type: text/event-stream

在 HTTP body 返回 event=endpoint,在返回的 data 中指定了当前使用的 session_id,后续 MCP client 在当前会话会继续使用该 session_id

HTTP body 返回的 51 代表后续的 event、data 等内容的长度,51 是十六进制(0x51=81)

通信数据示例如下

HTTP/1.1 200 OK
date: Fri, 18 Apr 2025 15:49:10 GMT
server: uvicorn
cache-control: no-store
connection: keep-alive
x-accel-buffering: no
content-type: text/event-stream; charset=utf-8
Transfer-Encoding: chunked

51
event: endpoint
data: /messages/?session_id=422820f7d23b46ab9f818dcda3964b9e

6.2.3. client 通过 POST 向 server 发送 initialize 请求

对应以上 MCP SSE 通信过程图中的序号为 13、15 的包

访问标准 HTTP POST 接入点,URI 为/messages/,session_id 使用上一步 MCP server 返回的

HTTP body 中的 method=initialize,params 中包含了 protocolVersion、capabilities、clientInfo,即 MCP client 的协议版本、支持的能力,及客户端信息

通信数据示例如下

POST /messages/?session_id=422820f7d23b46ab9f818dcda3964b9e HTTP/1.1
Host: 127.0.0.1:8000
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Content-Length: 194
Content-Type: application/json

{"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"sampling":{},"roots":{"listChanged":true}},"clientInfo":{"name":"mcp","version":"0.1.0"}},"jsonrpc":"2.0","id":0}

6.2.4. server 在 POST 请求向 client 返回 202 Accepted

对应以上 MCP SSE 通信过程图中的序号为 17、19 的包

MCP server 在接收到 client 发送的 POST 数据后,返回“202 Accepted”

通信数据示例如下

HTTP/1.1 202 Accepted
date: Fri, 18 Apr 2025 15:49:10 GMT
server: uvicorn
content-length: 8

Accepted

6.2.5. sever 通过 SSE 向 client 返回协议版本和支持的能力

对应以上 MCP SSE 通信过程图中的序号为 21 的包

对于 MCP server 需要返回给 client 的数据,会通过 SSE 接入点返回,result 中包含了 protocolVersion、capabilities、serverInfo,即 MCP server 的协议版本、支持的能力,及服务器信息

通信数据示例如下

136
event: message
data: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2024-11-05","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"high_speed_railQuerySystem","version":"1.6.0"}}}

6.2.6. client 通过 POST 向 server 发送 initialized notification 作为确认

对应以上 MCP SSE 通信过程图中的序号为 23、25 的包

HTTP body 中的 method=notifications/initialized

通信数据示例如下

POST /messages/?session_id=422820f7d23b46ab9f818dcda3964b9e HTTP/1.1
Host: 127.0.0.1:8000
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Content-Length: 54
Content-Type: application/json

{"method":"notifications/initialized","jsonrpc":"2.0"}

6.2.7. server 在 POST 请求向 client 返回 202 Accepted

对应以上 MCP SSE 通信过程图中的序号为 27、29 的包

通信数据内容还是“202 Accepted”,略

6.3. 执行工具及返回结果通信数据分析

MCP 通过 SSE 传输机制执行工具及返回结果通信数据分析如下

6.3.1. client 通过 POST 要求 server 显示工具列表

对应以上 MCP SSE 通信过程图中的序号为 31、33 的包

HTTP body 中的 method=tools/list

通信数据示例如下

POST /messages/?session_id=422820f7d23b46ab9f818dcda3964b9e HTTP/1.1
Host: 127.0.0.1:8000
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Content-Length: 46
Content-Type: application/json

{"method":"tools/list","jsonrpc":"2.0","id":1}

6.3.2. server 在 POST 请求向 client 返回 202 Accepted

对应以上 MCP SSE 通信过程图中的序号为 35、37 的包

通信数据内容还是“202 Accepted”,略

6.3.3. server 通过 SSE 向 client 返回工具信息

对应以上 MCP SSE 通信过程图中的序号为 39 的包

返回数据中 result.tools 为工具信息,name 为工具名称,inputSchema 为工具参数信息

通信数据示例如下

6e7
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"get_all_lines","description":"获取所有高铁线路编号,需要首先调用当前接口","inputSchema":{"properties":{},"title":"get_all_linesArguments","type":"object"}},{"name":"query_stations","description":"根据高铁线路编号查询起始站点名称,可用于判断指定线路是否能从某个站点到达另一个站点","inputSchema":{"properties":{"line_name":{"description":"高铁线路编号如 G1/G2","title":"Line Name","type":"string"}},"required":["line_name"],"title":"query_stationsArguments","type":"object"}},{"name":"query_duration","description":"根据高铁线路编号查询运行时长","inputSchema":{"$defs":{"LineNameRequest1":{"properties":{"line_name":{"description":"高铁线路编号如 G1/G2","title":"Line Name","type":"string"}},"required":["line_name"],"title":"LineNameRequest1","type":"object"}},"properties":{"line_name_request":{"allOf":[{"$ref":"#/$defs/LineNameRequest1"}],"description":"高铁线路编号请求类 1"}},"required":["line_name_request"],"title":"query_durationArguments","type":"object"}},{"name":"query_ticket_price","description":"根据高铁线路编号查询最低票价","inputSchema":{"$defs":{"LineNameRequest2":{"properties":{"line_name":{"description":"高铁线路编号如 G1/G2","title":"Line Name","type":"string"},"not_used":{"default":"","description":"暂未使用的参数","title":"Not Used","type":"string"}},"required":["line_name"],"title":"LineNameRequest2","type":"object"}},"properties":{"line_name_request":{"allOf":[{"$ref":"#/$defs/LineNameRequest2"}],"description":"高铁线路编号请求类 2"}},"required":["line_name_request"],"title":"query_ticket_priceArguments","type":"object"}}]}}

6.3.3.1. 自定义类型参数返回信息

6.3.4. client 通过 POST 要求 server 执行工具

对应以上 MCP SSE 通信过程图中的序号为 41、43 的包

HTTP body 中的 method=tools/call

通信数据示例如下

POST /messages/?session_id=422820f7d23b46ab9f818dcda3964b9e HTTP/1.1
Host: 127.0.0.1:8000
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Content-Length: 110
Content-Type: application/json

{"method":"tools/call","params":{"name":"get_all_lines","arguments":{"properties":{}}},"jsonrpc":"2.0","id":2}

6.3.5. server 在 POST 请求向 client 返回 202 Accepted

对应以上 MCP SSE 通信过程图中的序号为 45、47 的包

通信数据内容还是“202 Accepted”,略

6.3.6. server 通过 SSE 向 client 返回工具执行结果

对应以上 MCP SSE 通信过程图中的序号为 49 的包

返回数据中 result.content 为工具执行结果,对于文本形式的执行结果,type 字段值为 text,text 字段为具体结果

通信数据示例如下

ad
event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"G1"},{"type":"text","text":"G2"},{"type":"text","text":"G3"}],"isError":false}}

7. Python 与 Java MCP server 返回的工具参数区别

Python MCP server 返回的工具参数的名称使用实际的参数名称

Java MCP server 返回的工具参数的名称不是实际的参数名称,而是 arg{序号},例如 arg0 等。这是因为 Java 代码编译为 class 文件时,方法参数的名称是可选的,不能保证方法参数名称一定存在,因此使用参数序号时,可以保证通过反射调用工具方法时不依赖参数的名称

Java MCP server 返回的工具信息示例如下

event:message
data:{"jsonrpc":"2.0","id":"c8c979b5-1","result":{"tools":[{"name":"testTool1","description":"Get data","inputSchema":{"type":"object","properties":{"arg0":{"type":"string","description":"user info"}},"required":["arg0"],"additionalProperties":false}}]}}

8. 验证 MCP SSE 传输机制数据传递方式

为了验证以上 MCP SSE 传输机制的数据传递方式,可以使用浏览器访问 MCP server 的 SSE 接入点,使用 curl 命令访问 MCP server 的标准 HTTP POST 接入点,通过观察浏览器显示的结果进行验证

使用 Chrome 浏览器访问 MCP server 的 SSE 接入点,例如“http://127.0.0.1:8000/sse”

可以观察到浏览器页面图标在不停转圈,说明当前访问在持续没有结束。假如打开开发者工具,Network,则能在 EventStream 及 Response 中看到 SSE 相关数据

浏览器页面会显示如下内容

event: endpoint
data: /messages/?session_id=762c6f734302436f92aa2804ee2e885f

使用 MCP server 返回的 session_id,依次执行以下 curl 命令,进行与 MCP server 的连接初始化,及获取工具与执行工具操作

session_id="762c6f734302436f92aa2804ee2e885f"

# 1. initialize
curl -X POST "http://127.0.0.1:8000/messages/?session_id=$session_id" \
  -H "Content-Type: application/json" \
  -d '{"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"sampling":{},"roots":{"listChanged":true}},"clientInfo":{"name":"mcp","version":"0.1.0"}},"jsonrpc":"2.0","id":0}'

# 2. notifications/initialized
curl -X POST "http://127.0.0.1:8000/messages/?session_id=$session_id" \
  -H "Content-Type: application/json" \
  -d '{"method":"notifications/initialized","jsonrpc":"2.0"}'

# 3. tools/list
curl -X POST "http://127.0.0.1:8000/messages/?session_id=$session_id" \
  -H "Content-Type: application/json" \
  -d '{"method":"tools/list","jsonrpc":"2.0","id":1}'

# 4. tools/call
curl -X POST "http://127.0.0.1:8000/messages/?session_id=$session_id" \
  -H "Content-Type: application/json" \
  -d '{"method":"tools/call","params":{"name":"get_all_lines","arguments":{"properties":{}}},"jsonrpc":"2.0","id":2}'

在浏览器页面会显示类似以下内容

event: message
data: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2024-11-05","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"high_speed_railQuerySystem","version":"1.6.0"}}}

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"get_all_lines","description":"获取所有高铁线路编号,需要首先调用当前接口","inputSchema":{"properties":{},"title":"get_all_linesArguments","type":"object"}},{"name":"query_stations","description":"根据高铁线路编号查询起始站点名称,可用于判断指定线路是否能从某个站点到达另一个站点","inputSchema":{"properties":{"line_name":{"description":"高铁线路编号如 G1/G2","title":"Line Name","type":"string"}},"required":["line_name"],"title":"query_stationsArguments","type":"object"}},{"name":"query_duration","description":"根据高铁线路编号查询运行时长","inputSchema":{"$defs":{"LineNameRequest1":{"properties":{"line_name":{"description":"高铁线路编号如 G1/G2","title":"Line Name","type":"string"}},"required":["line_name"],"title":"LineNameRequest1","type":"object"}},"properties":{"line_name_request":{"allOf":[{"$ref":"#/$defs/LineNameRequest1"}],"description":"高铁线路编号请求类 1"}},"required":["line_name_request"],"title":"query_durationArguments","type":"object"}},{"name":"query_ticket_price","description":"根据高铁线路编号查询最低票价","inputSchema":{"$defs":{"LineNameRequest2":{"properties":{"line_name":{"description":"高铁线路编号如 G1/G2","title":"Line Name","type":"string"},"not_used":{"default":"","description":"暂未使用的参数","title":"Not Used","type":"string"}},"required":["line_name"],"title":"LineNameRequest2","type":"object"}},"properties":{"line_name_request":{"allOf":[{"$ref":"#/$defs/LineNameRequest2"}],"description":"高铁线路编号请求类 2"}},"required":["line_name_request"],"title":"query_ticket_priceArguments","type":"object"}}]}}

event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"G1"},{"type":"text","text":"G2"},{"type":"text","text":"G3"}],"isError":false}}

可以验证 MCP SSE 传输机制确实会使用 SSE 接入点与标准 HTTP POST 接入点这两个接入点,且 server 向 client 返回的数据都通过 SSE 接入点返回

9. MCP server ping 机制

MCP Python SDK 中的 MCP server 会定期 ping MCP client

在以上浏览器页面中会显示以下 ping 相关的内容

event: endpoint
data: /messages/?session_id=547ef618565840498d11b44a928e0448

: ping - 2025-05-05 14:43:04.301749+00:00

: ping - 2025-05-05 14:43:19.326299+00:00

: ping - 2025-05-05 14:43:34.331170+00:00

: ping - 2025-05-05 14:43:49.352748+00:00

: ping - 2025-05-05 14:44:04.367248+00:00

: ping - 2025-05-05 14:44:19.385155+00:00

: ping - 2025-05-05 14:44:34.389978+00:00

: ping - 2025-05-05 14:44:49.391826+00:00

9.1. Python SDK MCP server 相关代码

在 Python SDK MCP server 的sse_starlette.sse.EventSourceResponse.__call__方法中,会调用 sse_starlette.sse.EventSourceResponse._ping 方法,该方法默认每 15 秒发送一次 ping 消息,以保持连接存活,与以上浏览器显示的两次 ping 的时间间隔相符

10. 使用 Nginx 作为 MCP SSE 反向代理

假如 MCP 使用 SSE 传输机制,MCP client 通过 Nginx 作为反向代理访问 MCP server,由于 MCP server 只有单个实际具有会话信息,因此需要在 Nginx 开启会话保持,保证 MCP client 与 server 建立连接后,后续都能继续访问之前的 MCP server 实例

Nginx 还需要进行其他配置,才能保证 SSE 传输机制正常运行,可搜索“Nginx SSE”“Nginx Server-Sent Events”查看相关文章

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值