前言
上文[【MCP-01】JSON-RPC2.0协议]阐述了MCP服务端和客户端交互的基本数据结构,这里简单说明下MCP不同的传输方式的技术细节,侧重关注RemoteMCP场景下的HTTP+SSE(社区已经废弃)和StreamableHttp两种方式,Stdio主要是进程通信,这里不再说明,具体可以参考官方文档。
为什么不直接HTTP?
1)HTTP为单向请求,无法实现服务端主动推送消息,比如LLM任务完成事件,如果是HTTP请求则需要客户端不断轮询。
2)短连接和无状态性,无法应对对话式会话的需求,客户端和LLM大模型交互需要在多次对话中保持连接,已确保LLM大模型能读取交互的上下文信息。
3)HTTP无法满足实时性需求,LLM大模型通常是一个一个字符的流式返回结果。
4)同步阻塞模式,LLM大模型在处理耗时的任务下,HTTP会长时间阻塞。
综上所述:在AI的场景下,纯Http传输方式无法满足异步、实时性、流式返回、服务器消息推送、会话上下文保持等痛点,因此MCP依次提供了HTTP+SSE和StreamableHttp方式。
HTTP+SSE
SSE解决了那些问题?
MCP选择HTTP+[SSE]作为初版的传输方式解决了那些问题?
1)提高实时性,可以避免客户端使用http协议做频繁的轮询,SSE允许服务器在数据生成时逐步传输,客户端可逐步处理。
2)实现服务器主动推送,HTTP 是无状态的请求 - 响应模式,服务器不能主动向客户端发送数据,SSE 则通过建立 HTTP 长连接,支持服务器主动向客户端推送事件,比如MCPServer往MCPClient推送tools、prompt、resources变更,LLM任务完成等信息。
3)减少连接开销,如果使用http需要频繁创建和释放连接,SSE基于HTTP长连接,一次连接可多次传输数据,避免了频繁建立连接的开销,节省了系统资源,提高了通信效率。
4)简化实现,SSE也是基于HTTP协议,客户端只要EventSource API则可以接收SSE事件,服务端只要支持长链接建立也非常方便。
5)流式传输支持,在LLM大模型返回大量数据结构返回响应的时候,不需要等待整个数据集传输完毕,提高用户体验。
交互流程
一些说明:
HTTP+SSE传输方式MCPServer和MCPClient交互流程。
1)连接建立:客户端首先请求建立SSE连接,服务端“同意”,然后生成并推送唯一的Session ID。
2)请求发送:客户端通过HTTP POST发送JSON-RPC2.0请求(请求中会带有Session ID和Request ID信息)。
3)请求接收确认:服务端接收请求后立即返回202 (Accepted)状态码,表示已接受请求。
4)异步处理:服务端应用框架会自动处理请求,根据请求中的参数,决定调用某个工具或资源。
5)结果推送:处理完成后,服务端通过SSE通道推送JSON-RPC2.0 响应,其中带有对应的Request ID。
6)结果匹配:客户端的SSE连接侦听接收到数据流后,会根据RequestID将接收到的响应与之前的请求匹配。
7)重复处理:循环2-6这个过程。这里面包含一个MCP的初始化过程。
8)连接断开:在客户端完成所有请求后,可以选择断开SSE连接,会话结束。
综上所述,为解决纯HTTP请求方式下的痛点,核心在于在HTTP的方式上加上了SSE,解决思路主要是。
1)建立SSE通道:连接建立后保持长连接;
2)请求响应异步:通过RequestID实现客户端异步请求/响应匹配;
3)异步非阻塞交互:服务端返回202后立即释放请求线程;
4)伪双向通信:HTTP POST用于请求 + SSE通道用于推送,HTTP Post(客户端->服务端) + HTTP SSE(服务端->客户端)的伪双工通信模式,一个HTTP Post通道,用于客户端发送请求。比如调用MCP Server中的Tools并传递参数,一个HTTP SSE通道,用于服务端推送数据,比如返回调用结果或更新进度,两个通道通过session_id来关联,而请求与响应则通过消息中的id来对应;
需要注意的是,通过上图和上文可以了解到,存在两个endpoint,/sse是用于会话的开始建立SSE连接,创建连接后响应结果会返回一个新的连接/messages?sessionId=xxx,/messages?sessionId=xxx则用于后续的客户端Post请求处理。
那官方为何废弃SSE?
既然SSE协议已经能解决纯http传输方式下单向请求,短连接和无状态性,低实时性,同步阻塞等痛点,那为什么近期还要废弃掉SSE协议呢?
具体看这个PR:[[RFC] Replace HTTP+SSE with new "Streamable HTTP" transport][github.com/modelcontextprotocol/modelcontextprotocol/pull/206]
SSE存在的一些问题
PR中总结的一些问题
1)Does not support resumability。(不支持恢复功能,客户端和服务器之间的SSE连接中断后,只能重新创建新的连接,之前的上下文可能会丢失。)
2)Requires the server to maintain a long-lived connection with high availability。(要求服务器维持具有高可用性的长期连接,服务器必须一直保持一个稳定、不中断的SSE长连接,否则通信就中断。)
3)Can only deliver server messages over SSE。(只能通过服务器发送事件SSE来传递服务器消息,无法在已有的请求之外,主动地发送消息给客户端,除了通过专门的/sse通道)
业内大佬补充的一些问题
4)大多数现代浏览器每个域总共允许最多六个SSE连接。
5)企业网络安全限制,由于SSE因是GET方法且无请求体,本身不需要 Content-Length,一些代理和防火墙不会允许远程连接SSE,企业级交付有安全挑战。
6)高并发下,长链接比较消耗资源(这点个人感觉也不一定,频繁创建短连接更加消耗性能)。
7)不支持长期的serverless和stateless架构部署,serverless和stateless架构通常自动扩缩容,不适合长时间连接,而 SSE 需要维持持久连接。
8)其他等等。
上文说到SSE在解决了纯HTTP传输模型下异步、实时性、流式返回、服务器消息推送、会话上下文保持等痛点,但是也是估计当前MCP没想到会这么火起来嘛,所以HTTP+SSE的解决方案看起来比较粗糙,也出现了上文说到的一些问题。因此[[RFC] Replace HTTP+SSE with new "Streamable HTTP" transport]这个PR提出了StreamableHTTP的概念,以方便解决上文说到的HTTP+SSE的那几个痛点。才开始看到以为和HTTP+SSE同样的思路,只是加了Streamable这个能力,以解决AI场景下流式输出的要求。不过仔细来看,StreamableHTTP更像是对HTTP+SSE做了一个智能化的降级(细化对的AI不同场景处理能力,服务器按场景选择是否要做SSE返回,避免了一股脑的全部走SSE长链接),统一端点endpoint和优化会话管理。
那StreamableHTTP相对HTTP+SSE有什么优势?
PR中总结的一些优势
1)Stateless servers are now possible—eliminating the requirement for high availability long-lived connections。(实现无状态服务器,从而消除了对高可用性长连接的需求。)
2)Plain HTTP implementation—MCP can be implemented in a plain HTTP server without requiring SSE。(纯HTTP实现方式 ——MCP可以在纯HTTP服务器中实现,SSE服务器发送事件非必须。)
3)Infrastructure compatibility—it's "just HTTP," ensuring compatibility with middleware and infrastructure。(基础架构兼容性 —— 因为“只是HTTP”,所以能确保与中间件和基础架构的兼容性。)
4)Backwards compatibility—this is an incremental evolution of our current transport。(向后兼容性 —— 这是我们当前传输方式的渐进式演变。)
5)Flexible upgrade path—servers can choose to use SSE for streaming responses when needed。(灵活的升级路径 —— 服务器可以根据需要选择使用SSE进行流式响应。)
业内大佬补充
6)支持Last-Event-ID机制,支持将JSON-PRC的message存储到第三方库中,断开连接后通过Last-Event-ID恢复和消息重传,用户体验较好。
7)会话管理更加标准化和清晰。
8)只要维护一个endpoint端点,默认为/mcp。
9)智能降级,在网络条件不佳时,自动降级到标准的HTTP模式(这个有点吹水,没在源码中找到相应的降级流程,不过是可以手动通过stateless_http控制是否创建sse流)。
交互流程
一些说明:
上图来源于官网Streamable HTTP->Sequence Diagram。StreamableHTTP可以看到核心还是走HTTP+SSE,只是在调用流程上做了一些优化以及场景细化。大体流程如下:
1)初始化(含连接建立):客户端发初始化请求,服务端响应并生成返回唯一SessionID,客户端再发初始化完成通知,服务端以202确认,完成类似SSE连接建立与会话初始化。
2)请求发送:客户端通过POST发送带SessionID的请求(如业务请求等 )。
3)请求接收确认:服务端收请求后立即返202状态码,确认已接受。
4)异步处理:服务端框架自动处理请求,按需调用工具/资源。
5)结果推送:处理完,服务端通过SSE通道(若开启)推送带对应标识(如关联请求信息 )的响应。
6)结果匹配:客户端监听SSE事件,依据标识(如类似RequestID )匹配响应与请求。
7)循环处理:重复“请求发送-接收确认-异步处理-结果推送-结果匹配” 流程,覆盖客户端请求、通知/响应及服务端请求等交互。
8)连接断开:客户端完成所有交互后,可断开连接,会话结束。
综上所述这不是和HTTP+SSE没多大区别吗?实际上通过参考源码Python-SDK。确实是按PR定的新的协议规划有做对应的优化,只是官方给的这个时序图没有将所有情况描述出来。除了统一端点(默认为/mcp)和Last-Event-ID机制,最重要的是StreamableHTTP MCPServer添加了两个参数。
**stateless_http和json_response,两个参数默认都为False,最重要的是stateless_http这个参数,控制是否开启SSE通道和是否对对客户端会话进行管理。json_response主要是为了控制Post请求响应结果数据结构是否用JSON还是SSE事件数据流(不是走SSE通道,只是用SSE事件数据格式)**。因此这两个参数相互组合可以达到上文中说的细化对待不同的AI业务场景。
stateless_http和json_response组合
四种组合关系以及适合的场景可以参考[claudemcp文档][claudemcp.com/blog/mcp-streamable-http]。
综上所述,StreamableHTTP传输模式,除了统一端点(默认为/mcp)和Last-Event-ID机制,还提供了适应不同AI场景下的参数配置,尤其是stateless_http=True, json_response=True**非常适合之前OpenAPI的微服务(stateless),可以复用微服务的可用性和可维护性建设。不需要像HTTP+SSE那样一股脑全部走异步、实时流推送的长链接SSE方式,当然业内也说明在性能上能做到大大的提升,不过这个个人认为按场景考虑。
Demo
这里简单使用官方的[FastMCP][github.com/modelcontextprotocol/python-sdk]编写一个MCPServer的demo。
mcpserver.py
from mcp.server.fastmcp import FastMCP
import logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
MCP_SERVER_NAME = "ShMcpServer1"
logger = logging.getLogger(MCP_SERVER_NAME)
# 初始化FastMCP服务器
mcp = FastMCP(MCP_SERVER_NAME)
@mcp.tool(name="add", description="对两个数字进行加法")
def add(a: float, b: float) -> float:
"""
Add two numbers.
Parameters:
- a (float): First number (required)
- b (float): Second number (required)
Returns:
- float: The result of a + b
"""
return a + b
@mcp.tool(name="subtract", description="对两个数字进行减法")
def subtract(a: float, b: float) -> float:
"""
Subtract two numbers.
Parameters:
- a (float): The number to subtract from (required)
- b (float): The number to subtract (required)
Returns:
- float: The result of a - b
"""
return a - b
@mcp.tool(name="multiply", description="对两个数字进行乘法")
def multiply(a: float, b: float) -> float:
"""
Multiply two numbers.
Parameters:
- a (float): First number (required)
- b (float): Second number (required)
Returns:
- float: The result of a * b
"""
return a * b
@mcp.tool(name="divide", description="对两个数字进行除法")
def divide(a: float, b: float) -> float:
"""
Divide two numbers.
Parameters:
- a (float): Numerator (required)
- b (float): Denominator (required, must not be zero)
Returns:
- float: The result of a / b
"""
if b == 0:
raise ValueError("Division by zero is not allowed")
return a / b
if __name__ == "__main__":
mcp.settings.host = "0.0.0.0"
mcp.settings.port = 8000
mcp.settings.log_level = "INFO"
# stateless_http和json_response,两个参数默认都为False
# stateless_http
# 控制是否开启SSE通道和是否对对客户端会话进行管理
# json_response
# 控制Post请求响应结果数据结构是否用JSON还是SSE事件数据流(不是走SSE通道,只是用SSE事件数据格式)
# mcp.settings.json_response = True
# mcp.settings.stateless_http = True
# 初始化并运行服务器
print("Starting MCPServer...")
# transport改成sse则mcp使用HTTP+SSE传输模式,不过因为官方已经废弃HTTP+SSE方式,这里不在赘述
# mcp.run(transport='sse')
mcp.run(transport="streamable-http")
mcpclient.py
"""
github.com/modelcontextprotocol/python-sdk
Run from the repository root:
uv run examples/snippets/clients/streamable_basic.py
"""
import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
"""
程序入口,设置配置并启动 MCP 客户端。
"""
mcpUrl="127.0.0.1:8000/mcp"
# Connect to a streamable HTTP server
async with streamablehttp_client(mcpUrl) as (
read_stream,
write_stream,
_,
):
# Create a session using the client streams
async with ClientSession(read_stream, write_stream) as session:
# Initialize the connection
await session.initialize()
# List available tools
tools = await session.list_tools()
print(f"Available tools: {[(tool.name,tool.description) for tool in tools.tools]}")
if __name__ == "__main__":
asyncio.run(main())
总结
1,MCP官方初步HTTP+SSE方案确实解决了在AI的场景下异步、实时性、流式返回、服务器消息推送、会话上下文保持等痛点。核心在于在HTTP的方式上加上了SSE,实现了伪双向通信,达到了异步,服务器流式输出的基本效果。
2,而在MCP协议在业内被接受的情况下,大范围的使用也出现了一些问题,因此StreamableHTTP对HTTP+SSE做了智能化的降级(细化对的AI不同场景处理能力,服务器按场景选择是否要做SSE返回,避免了一股脑的全部走SSE长链接),统一端点endpoint和优化会话管理,Last-Event-ID机制等改进策略。
3,在StreamableHTTP传输模式下,提供了stateless_http和json_response两个重要参数,尤其是stateless_http=True, json_response=True非常适合之前OpenAPI的微服务(stateless),可以复用微服务的可用性和可维护性建设。
4,MCP在成为业内标准后StreamableHTTP将是后续的主要传输协议,HTTP+SSE传输协议将废弃,后续重点将在StreamableHTTP上。