在作者上篇文章《大模型流式输出:七大底层传输技术对比探究》中,对大模型流式输出的底层传输技术完成系统性梳理与对比。
而立足2025年人工智能大模型领域,最受瞩目的核心热点当属MCP(模型控制协议)技术。因为对外部工具的调用能力,正是大模型从基础对话机器人向多功能助手AI Agent跨越的关键,而MCP技术恰恰依托大模型Function Calling的底层能力,凭借高效统一的开发规范,迅速成为行业关注的焦点。
结合业界主流大模型的接入建议,Streamable HTTP已成为对接MCP Server 的优选方式。
基于此,本文将从理论解析到代码实践,深入拆解Streamable HTTP MCP 工作原理,并进行具体实践,带您透彻理解其底层通信机制与核心运行逻辑。
一、Streamable HTTP 协议详解
Streamable HTTP 技术是一种基于标准HTTP协议的实时数据传输协议,通过单一端点实现请求发送和响应接收,服务器可以动态决定响应方式,既支持普通HTTP响应,也能根据需要升级为SSE流式传输。通过此设计解决了传统HTTP在实时通信方面的局限性,同时保持了与现有Web基础设施的兼容性。
1.1 统一端点设计
Streamable HTTP 采用统一的HTTP端点设计,同时支持HTTP POST和GET请求,客户端和服务器消息都可以通过同一个端点发送和接收。所有工具调用请求都通过同一个URL路径处理,这种设计显著简化了API设计和客户端实现:
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: text/event-stream, application/json
Mcp-Session-Id: sessionId123
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "generate_content",
"arguments": {"topic": "AI未来"}
}
}
1.2 动态响应模式
Streamable HTTP支持两种响应模式:即时响应和流式响应,服务器可以根据请求性质动态选择响应方式。
即时响应模式(适合简单请求):
HTTP/1.1 200 OK
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"result": {"status": "completed"}
}
流式响应模式(适合复杂请求):
HTTP/1.1 200 OK
Content-Type: text/event-stream
Transfer-Encoding: chunked
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"AI"}],"isComplete":false}}
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"的未来"}],"isComplete":false}}
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"充满潜力"}],"isComplete":true}}
1.3 会话管理与恢复
Streamable HTTP引入了Mcp-Session-Id和Last-Event-ID头,实现了会话管理与断线恢复功能。
GET /mcp HTTP/1.1
Mcp-Session-Id: sessionId123
Last-Event-ID: 42
客户端可以在重连时携带最后接收的Event-ID和会话ID,服务器根据这些信息从断点续传或返回增量更新,确保了断线恢复的可靠性。
import requests
import json
initial_response = requests.post(
'https://api.example.com/mcp',
headers={'Content-Type': 'application/json'},
json={
'toolName': 'mcp_test',
'args': { 'test_id': '1' },
'stream': True
}
)
# 获取会话ID
session_id = initial_response.headers.get('MCP-Session-ID')
# 假设连接中断,尝试恢复
recover_response = requests.post(
'https://api.example.com/mcp',
headers={
'Content-Type': 'application/json',
'MCP-Session-ID': session_id # 提供会话ID进行恢复
},
json={
'toolName': 'recover_mcp_test',
'args': { 'test_id': '1' },
'stream': True
}
)
1.4 安全增强机制
Streamable HTTP与MCP 2025-03-26版本紧密结合,引入了OAuth 2.1认证机制,强制使用PKCE和HTTPS,消除了隐式流风险,更适应AI工具的高权限场景。
GET /.well-known/oauth-authorization-server HTTP/1.1
Host: api.example.com
MCP-Protocol-Version: version123
HTTP/1.1 200 OK
{
"issuer": "https://api.example.com",
"authorization_endpoint": "https://auth.example.com/authorize",
"token_endpoint": "https://auth.example.com/token",
"capabilities": ["PKCE", "TOKEN_ROTATION"]
}
二、Streamable HTTP关键技术特性分析
Streamable HTTP技术具有多项关键技术特性,使其在通信领域具有显著优势。
2.1 动态协议协商机制
Streamable HTTP实现了智能的协议协商机制,能够根据客户端能力和网络环境动态调整通信参数:
-
能力探测:客户端与服务器首次通信时交换能力信息
-
参数协商:根据双方能力协商最优的传输参数
-
自适应调整:根据网络状况动态调整传输参数
协议协商过程使用JSON格式的能力描述对象,示例如下:
// 客户端能力描述
{
"version": "1.0",
"features": {
"streaming": true,
"batching": true,
"compression": ["gzip", "br"],
"maxChunkSize": 16384
},
"extensions": ["retry", "checksum"]
}
// 服务器能力描述
{
"version": "1.0",
"features": {
"streaming": true,
"batching": true,
"compression": ["gzip"],
"maxChunkSize": 32768
},
"extensions": ["retry", "auth_tokens"]
}
// 协商结果
{
"version": "1.0",
"features": {
"streaming": true,
"batching": true,
"compression": "gzip",
"maxChunkSize": 16384
},
"extensions": ["retry"]
}
2.2 双向通信隧道
在SSE流开启期间,客户端可通过附加HTTP POST发送新请求,服务端通过Mcp-Request-Id头部实现多路复用,确保多个请求之间不会混淆。
2 .3 批量请求处理
Streamable HTTP支持JSON-RPC 2.0批处理规范,允许在一个请求中发送多个方法调用,显著减少网络开销。
批量请求的JSON-RPC格式示例:
POST /mcp/v1/batch HTTP/1.1
Host: api.example.com
Content-Type: application/json
[
{
"id": "req1",
"method": "invoke",
"params": {
"toolName": "get_weather",
"args": {"location": "beijing"}
}
},
{
"id": "req2",
"method": "invoke",
"params": {
"toolName": "get_traffic",
"args": {"route": "airport"}
}
},
{
"id": "req3",
"method": "invoke",
"params": {
"toolName": "get_news",
"args": {"category": "tech"}
}
}
]
服务器响应:
[ { "id": "req1", "result": { "temperature": 25, "condition": "sunny", "humidity": 45 } }, { "id": "req2", "result": { "status": "heavy", "delay": 25, "alternatives": ["highway", "express"]
}
},
{
"id": "req3",
"result": {
"total": 10,
"articles": ["article1", "article2", ...]
}
}
]
2 .4 多模态支持
Streamable HTTP 支持多种数据类型和模态的传输,以适应不同应用场景的需求:
-
文本数据:支持JSON、XML、纯文本等格式
-
二进制数据:支持图片、音频、视频等二进制数据传输
-
混合数据:支持在同一请求或响应中传输多种类型的数据
多模态数据传输示例(音频流):
// 客户端发送音频流
async function streamAudio() {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });
mediaRecorder.ondataavailable = async (event) => {
if (event.data.size > 0) {
// 读取音频数据并发送
const arrayBuffer = await event.data.arrayBuffer();
const base64Audio = btoa(
new Uint8Array(arrayBuffer).reduce(
(data, byte) => data + String.fromCharCode(byte),
''
)
);
await fetch('/mcp', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
toolName: 'speech_recognition',
args: {
audio: base64Audio,
format: 'webm'
},
stream: true
})
});
}
};
mediaRecorder.start(1000); // 每1秒发送一次数据
}
三、基于Streamable HTTP MCP 的开发应用分析
在人工智能技术飞速发展的浪潮中,大语言模型(LLM)已经不在局限于对话问答,Function Calling技术让LLM的能力边界不断的拓展。如何让LLM高效地、可靠地与外部服务进行交互呢?Anthropic于2024年11月受语言服务器协议 ( LSP )启发提出模型上下文协议(Model Context Protocol,简称MCP) ,被誉为AI世界的 "USB-C 接口"。它通过规范化接口,使LLM能够无缝调用各类外部工具、访问数据源并执行复杂操作,从根本上打破了不同厂商插件接口互不兼容的困境 —— 在MCP出现前,开发者需为不同LLM和工具编写定制化连接代码,效率极低。2025年初Anthropic的Manus多智能体框架落地后,作为底层工具调用协议的MCP迅速成为行业焦点。
Streamable HTTP是MCP协议为解决原有"HTTP+SSE"传输机制缺陷而推出的全新传输层实现,2025年3月随 MCP v2025.03.26 版本正式引入。
下面将从MCP服务开发框架支持情况、MCP服务与客户端通信流程、MCP服务配置与部署以及MCP客户端集成等几个方面,讲解利用Stream HTTP技术结合MCP实现大模型通过Function Calling技术完成外部服务交互。
3.1 MCP 官方SDK及开发框架支持
随着Streamable HTTP的广泛采用,MCP 生态系统已经形成了一套完整的开发框架,支持多种编程语言和平台。
目前主流的官方SDK包括:
-
Python SDK:官方Python SDK,提供对Streamable HTTP的完整支持,提供了简单的API接口用于创建MCP Server和Client。
-
TypeScript SDK:官方TypeScript SDK,支持浏览器和Node.js环境
-
Java SDK:官方Java SDK,与Spring AI框架深度集成
-
C# SDK:官方C# SDK,支持.NET 平台
除了官方SDK外,还有一系列第三方框架和工具,进一步提升开发效率和体验:
-
Spring AI:Spring AI框架提供了McpClientTransport实现,支持Streamable HTTP模式,使得Java开发者可以方便地构建MCP应用。
-
FastAPI:基于Python的FastAPI框架被广泛用于构建高性能的Streamable HTTP MCP Server,支持异步请求处理和流式响应。
-
Higress:作为AI原生的API网关,Higress提供了完整的开源MCP Server托管解决方案,支持Streamable HTTP协议。
3.2 MCP 服务端与客户端通信流程
早在2025年3月26日,MCP官方github就出现HTTP流式传输服务器通信标准的提议用来代替现在HTTP SSE的通信方式。该提议详细说明了Streamable HTTP MCP 服务器与客户端之间的通信流程,以及外部工具调用信息同步格式与流程,如下图所示:
结合上图按照客户端首次启动>成功连接服务器>等待用户提问的过程来说明Streamable HTTP MCP详细的请求响应顺序,让大家明白每一步在做什么。
当客户端启动与服务端进行连接时,需要进行3步握手动作,此时用户还没有输入任何信息。
| 步骤 | HTTP | 方法 | 作用 | 响应 |
|---|---|---|---|---|
| 1 | POST /mcp | initialize | 协商协议版本&能力 | result.protocolVersion = 协议版本号 result.capabilities.tools.listChanged = true |
| 2 | POST /mcp | notifications/initialized | 客户端告诉服务器“我已就绪”(通知服务器只回 204 No Content) | HTTP 204 无包体 |
| 3 | POST /mcp | tools/list | 向服务器请求工具清单 | result.tools 数组 + nextCursor(下一流式点) |
当用户开始提问时,大模型判断要使用工具,客户端向服务端发起工具调用请求。
| 步骤 | HTTP | 方法 | 作用 | 响应 |
|---|---|---|---|---|
| 4 | POST /mcp | tools/call | 调用服务端服务 | params.name = get_weather``params.arguments.city 或 location |
| 5 | 流式响应 | stream/result | 服务器逐行推送响应结果 | result.content |
客户端在收到5的result.content后,会把文本回填到大模型对话记录中,大模型再输出给终端,此时就可以看到MCP服务执行的结果信息。
如果有多次的工具调用,那么会重复执行步骤4和5,每次请求的id都会发生改变。
3.3 MCP 服务端示例
在了解了MCP官方SDK及开发框架支持情况,以及明白了Streamable HTTP MCP详细的请求响应顺序后,以FastAPI为例,编写一个可以查询天气的Streamable HTTP MCP Server。查询天气的功能需要用到心知天气的免费API KEY(通过www.seniverse.com/products?ii… HTTP MCP服务器的示例代码如下:
- 导入所需python库并定义全局变量:
import argparse
import asyncio
import json
from typing import Any, AsyncIterator, Union
import requests
from fastapi import FastAPI, Request, Response, status
from fastapi.responses import StreamingResponse
import uvicorn
SERVER_NAME = "weather_server" # 定义服务器名称
SERVER_VERSION = "1.0.0" #定义服务器版本
PROTOCOL_VERSION = "version123" #定义协议版本号
API_KEY = "your_api_key" #申请的seniverse.com的KEY
2. 编写天气请求天气函数fetch_weather,编写天气流式响应函数stream_weather使用生成器将请求天气改写为流传输的形式,在传输jsonrpc协议中记录了req_id标识请求和响应:
# 编写请求天气函数
async def fetch_weather(city: str):
try:
url="https://api.seniverse.com/v3/weather/now.json"
params={
"key": API_KEY,
"location": city,
"language": "zh-Hans",
"unit": "c"
}
response = requests.get(url, params=params)
temperature = response.json()['results'][0]['now']
except Exception:
return "error"
return json.dumps(temperature)
# 构造天气流式响应, 使用生成器yield流式返回天气内容(符合MCP协议的逐行JSON)
async def stream_weather(city: str, req_id: Union[int, str]):
yield json.dumps({"jsonrpc": "2.0", "id": req_id, "stream": f"查询 {city} 天气中…"}).encode() + b"\n"
await asyncio.sleep(0.3)
data = await fetch_weather(city)
if data == "error":
yield json.dumps({"jsonrpc": "2.0", "id": req_id, "error": {"code": -32000, "message": data["error"]}}).encode() + b"\n"
return
yield json.dumps({
"jsonrpc": "2.0", "id": req_id,
"result": {
"content": [
{"type": "text", "text": data}
],
"isError": False
}
}).encode() + b"\n"
3. 定义服务器的工具列表,这里只有一个get_weather工具用来获取天气情况。因为是从底层编写,需要详细的JSON格式让天气函数被大模型Function Calling功能识别:
TOOLS_REGISTRY = {
"tools": [
{
"name": "get_weather",
"description": "用于进行天气信息查询的函数,输入城市英文名称,即可获得当前城市天气信息。",
"inputSchema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, e.g. 'Hangzhou'"
}
},
"required": ["city"]
}
}
]
4. 使用FastAPI模拟Streamable HTTP请求与响应的流程,结合【3.2中MCP服务端与客户端的通信流程】,更清晰直观的了解请求与响应的全过程:
app = FastAPI(title="Weather Streamble HTTP MCP SERVER")
@app.get("/mcp")
async def mcp_initialize_via_get():
# GET 请求也执行了 initialize 方法
return {
"jsonrpc": "2.0",
"id": 0,
"result": {
"protocolVersion": PROTOCOL_VERSION,
"capabilities": {
"streaming": True,
"tools": {"listChanged": True}
},
"serverInfo": {
"name": SERVER_NAME,
"version": SERVER_VERSION
},
"instructions": "Use the get_weather tool to fetch weather by city name."
}
}
@app.post("/mcp")
async def mcp_endpoint(request: Request):
try:
body = await request.json()
except Exception:
return {"jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": "Parse error"}}
req_id = body.get("id", 1)
method = body.get("method")
if method is None:
return {"jsonrpc": "2.0", "id": req_id, "result": {"status": "MCP server online."}}
if method == "notifications/initialized":
return Response(status_code=status.HTTP_204_NO_CONTENT)
if method == "initialize":
return {
"jsonrpc": "2.0",
"id": req_id,
"result": {
"protocolVersion": PROTOCOL_VERSION,
"capabilities": {
"streaming": True,
"tools": {"listChanged": True}
},
"serverInfo": {"name": SERVER_NAME, "version": SERVER_VERSION},
"instructions": "Use the get_weather tool to fetch weather by city name."
}
}
if method == "tools/list":
print(json.dumps(TOOLS_REGISTRY, indent=2, ensure_ascii=False))
return {"jsonrpc": "2.0", "id": req_id, "result": TOOLS_REGISTRY}
if method == "tools/call":
params = body.get("params", {})
tool_name = params.get("name")
args = params.get("arguments", {})
if tool_name != "get_weather":
return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32602, "message": "Unknown tool"}}
city = args.get("city")
if not city:
return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32602, "message": "Missing city"}}
return StreamingResponse(stream_weather(city, req_id), media_type="application/json")
return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32601, "message": "Method not found"}}
5. 解析命令行参数获得服务端要运行的IP和端口,启动服务端
def main() -> None:
parser = argparse.ArgumentParser(description="Weather Streamble HTTP MCP SERVER")
parser.add_argument("--host", default="127.0.0.1")
parser.add_argument("--port", type=int, default=8000)
args = parser.parse_args()
uvicorn.run(app, host=args.host, port=args.port, log_level="info")
if __name__ == "__main__":
main()
3.4 MCP 服务端配置与部署
Streamable HTTP MCP服务端的配置和部署非常灵活,可以根据不同的规模和需求进行调整:
-
单机部署:适用于开发和小规模测试场景
-
容器化 部署:支持Docker容器化部署,便于环境一致性和扩缩容
-
集群部署:支持多实例集群部署,提高可用性和性能
-
云原生部署:支持Kubernetes等云原生平台部署
以下是一个Docker Compose配置示例,用于部署Streamable HTTP MCP服务端:
version: 'version123'
services:
mcp-server:
build: .
ports:
- "8000:8000"
environment:
- NODE_ENV=production
- MCP_PORT=8000
- MCP_MAX_CONNECTIONS=1000
- MCP_STREAM_TIMEOUT=300000
depends_on:
- redis
restart: always
redis:
image: redis:6-alpine
volumes:
- redis-data:/data
restart: always
volumes:
redis-data:
3.5 MCP 客户端集成方式
Streamable HTTP MCP提供了多种客户端集成方式,适应不同的应用场景和开发环境:
-
官方 SDK 集成: 已封装协议细节(如会话管理、流式处理、错误重试),支持主流编程语言,开箱即用,是最常用的集成方式
- Python SDK集成:适用于Python后端、数据分析脚本或AI应用开发,支持Streamable HTTP的全特性
- TypeScript/JavaScript SDK集成:适用于Node.js后端或浏览器端,支持异步/await语法
-
原生 HTTP/ HTTPS 集成: 当官方SDK不支持目标语言或需自定义协议细节时,可直接通过HTTP/HTTPS请求实现MCP协议,需手动处理会话、流式解析等逻辑
- 无状态工具调用:适用于单次工具调用(无需上下文),直接发送POST请求到MCP端点
- 有状态流式调用:适用于多轮对话或长时间任务(如文件分析),需维护Mcp-Session-Id并处理SSE流式响应
-
框架插件集成(企业级场景,低侵入): 适用于已使用主流开发框架(如 Spring Boot、FastAPI)的项目,通过框架插件快速集成MCP,无需修改核心业务代码
-
Spring Boot集成(Java):通过spring-ai插件,将MCP能力注入Spring容器,支持依赖注入和自动配置
-
FastAPI 集成(Python):通过fastapi-mcp插件(第三方),将MCP工具注册为 API 接口,支持自动生成文档
-
3.6 MCP 客户端示例
通过对MCP客户端集成方式的认识,以deepseek大模型为例(通过platform.deepseek.com申请注册API KEY),模拟服务器通信流程,支持多轮对话,能自动识别是否需要调用工具,自动处理工具参数解析与调用逻辑,通过读取配置文件的方式支持多个服务器。以下是Python客户端集成示例:
- 导入相关包并定义客户端依赖大模型:
import asyncio
import json
import logging
import os
from contextlib import AsyncExitStack
from typing import Any, Dict, List, Optional
import httpx
from openai import OpenAI
class Configuration:
def __init__(self) -> None:
self.api_key = "you_api_key" # 修改为API的KEY
self.base_url = "https://api.deepseek.com" # 可修改为其他FC模型
self.model = "deepseek-chat"
# 添加mcp 配置文件
@staticmethod
def load_config(path: str) -> Dict[str, Any]:
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
2. 编写与单个Streambale HTTP MCP Server交互的类,模拟服务器通信流程, 支持四个核心操作, 包括发送连接请求、初始化连接请求、获取工具列表和工具调用流式读取:
class HTTPMCPServer:
def __init__(self, name: str, endpoint: str) -> None:
self.name = name
self.endpoint = endpoint.rstrip("/") # e.g. http://localhost:8000/mcp
self.session: Optional[httpx.AsyncClient] = None
self.protocol_version: str = "version123" # 与server.py中定义的协议版本一致
async def _post_json(self, payload: Dict[str, Any]) -> Dict[str, Any]:
assert self.session is not None
r = await self.session.post(self.endpoint, json=payload, headers={"Accept": "application/json"})
if r.status_code == 204 or not r.content:
return {} # ← 通知无响应体
r.raise_for_status()
return r.json()
async def initialize(self) -> None: #客户端发起
self.session = httpx.AsyncClient(timeout=httpx.Timeout(30.0))
# 1) 步骤1发送连接请求
init_req = {
"jsonrpc": "2.0",
"id": 0,
"method": "initialize",
"params": {
"protocolVersion": self.protocol_version,
"capabilities": {},
"clientInfo": {"name": "Streamable HTTP Client Demo", "version": "0.1"},
},
}
r = await self._post_json(init_req)
if "error" in r:
raise RuntimeError(f"Initialize error: {r['error']}")
# 2) 步骤二,发送请求初始化包,通知服务器已连接
await self._post_json({"jsonrpc": "2.0", "method": "notifications/initialized"})
# 步骤三 请求服务端 tools列表
async def list_tools(self) -> List[Dict[str, Any]]:
req = {"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}
res = await self._post_json(req)
return res["result"]["tools"]
# 步骤四 发起工具调用并将流式结果拼接为完整文本
async def call_tool_stream(self, tool_name: str, arguments: Dict[str, Any]) -> str:
req = {
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {"name": tool_name, "arguments": arguments},
}
assert self.session is not None
async with self.session.stream(
"POST", self.endpoint, json=req, headers={"Accept": "application/json"}
) as resp:
if resp.status_code != 200:
raise RuntimeError(f"HTTP {resp.status_code}")
collected_text: List[str] = []
async for line in resp.aiter_lines():
if not line:
continue
chunk = json.loads(line)
if "stream" in chunk:
continue # 中间进度
if "error" in chunk:
raise RuntimeError(chunk["error"]["message"])
if "result" in chunk:
# 根据协议,文本在 result.content[0].text
for item in chunk["result"]["content"]:
if item["type"] == "text":
collected_text.append(item["text"])
return "\n".join(collected_text)
async def close(self) -> None:
if self.session:
await self.session.aclose()
self.session = None
3. 编写类封装大模型对话的请求响应:
# 大模型客户端
class LLMClient:
def __init__(self, api_key: str, base_url: Optional[str], model: str) -> None:
self.client = OpenAI(api_key=api_key, base_url=base_url)
self.model = model
def chat(self, messages: List[Dict[str, Any]], tools: Optional[List[Dict[str, Any]]]):
return self.client.chat.completions.create(model=self.model, messages=messages, tools=tools)
4. 利用单服务器类与大模型类编写多服务MCP客户端,让Client更通用:
class MultiHTTPMCPClient:
def __init__(self, servers_conf: Dict[str, Any], api_key: str, base_url: Optional[str], model: str) -> None:
self.servers: Dict[str, HTTPMCPServer] = {
name: HTTPMCPServer(name, cfg["endpoint"]) for name, cfg in servers_conf.items()
}
self.llm = LLMClient(api_key, base_url, model)
self.all_tools: List[Dict[str, Any]] = []
async def start(self):
for srv in self.servers.values():
await srv.initialize()
tools = await srv.list_tools()
for t in tools:
full_name = f"{srv.name}_{t['name']}"
self.all_tools.append({
"type": "function",
"function": {
"name": full_name,
"description": t["description"],
"parameters": t["inputSchema"],
},
})
logging.info("已连接服务器并汇总工具:%s", [t["function"]["name"] for t in self.all_tools])
async def call_local_tool(self, full_name: str, args: Dict[str, Any]) -> str:
srv_name, tool_name = full_name.split("_", 1)
srv = self.servers[srv_name]
city = args.get("city")
if not city:
raise ValueError("Missing city/location")
return await srv.call_tool_stream(tool_name, {"city": city})
async def chat_loop(self):
print("HTTP MCP + Function Calling 客户端已启动,输入 quit 退出")
messages: List[Dict[str, Any]] = []
while True:
user = input("你: ").strip()
if user.lower() == "quit":
break
messages.append({"role": "user", "content": user})
resp = self.llm.chat(messages, self.all_tools)
choice = resp.choices[0]
if choice.finish_reason == "tool_calls":
tc = choice.message.tool_calls[0]
tool_name = tc.function.name
tool_args = json.loads(tc.function.arguments)
print(f"[调用工具] {tool_name} → {tool_args}")
tool_resp = await self.call_local_tool(tool_name, tool_args)
messages.append(choice.message.model_dump())
messages.append({"role": "tool", "content": tool_resp, "tool_call_id": tc.id})
resp2 = self.llm.chat(messages, self.all_tools)
print("AI:", resp2.choices[0].message.content)
messages.append(resp2.choices[0].message.model_dump())
else:
print("AI:", choice.message.content)
messages.append(choice.message.model_dump())
async def close(self):
for s in self.servers.values():
await s.close()
5. 编写main函数,读取MCP Server配置文件,启动客户端:
async def main():
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
conf = Configuration()
servers_conf = conf.load_config("./servers_config.json").get("mcpServers", {})
client = MultiHTTPMCPClient(servers_conf, conf.api_key, conf.base_url, conf.model)
try:
await client.start()
await client.chat_loop()
finally:
await client.close()
if __name__ == "__main__":
asyncio.run(main())
6. 新建servers_config.json文件用于写入Streamable HTTP MCP Server的传输服务器地址和名称,让客户端接入MCP服务端:
{
"mcpServers": {
"weather": {
"endpoint": "http://127.0.0.1:8000/mcp"
}
}
}
7. 启动server.py
- 启动client.py
可以看到日志输入了【3.2 MCP 服务端与客户端通信流程】中的1、2、3步骤中的动作,完成了服务器的连接并汇总了获取天气服务的工具,等待用户进行提问
提问后,可以看到日志输出了4、5步骤中的动作,完成了天气服务工具的调用,并以流的形式返回了结果并输出给了客户端。
四、Streamable HTTP ****MCP 优势分析
在Stream HTTP出现之前,MCP服务的通信方式有Stdio方式和HTTP SSE两种,下面将通过性能、资源效率、部署及运维复杂度以及企业级场景下的性能表现四个方面将HTTP协议下的SSE和Streamable进行对比,了解Streamable HTTP MCP优势。
4.1 性能测试数据对比
通过对比测试,Streamable HTTP在多个关键性能指标上显著优于传统HTTP+SSE模式。以下是基于Higress网关的官方测试数据:
| 技术指标 | HTTP+SSE | Streamable HTTP | 提升幅度 |
|---|---|---|---|
| 吞吐量 | 3,247 req/s | 8,934 req/s | 175% |
| 内存使用 | 892MB | 234MB | 74%节省 |
| 错误率 | 0.12% | 0.03% | 75%改善 |
| 连接建立时间 | 平均120ms | 平均35ms | 71%减少 |
| 响应延迟 | 平均450ms | 平均120ms | 73%减少 |
4.2 资源效率对比分析
Streamable HTTP在资源利用效率方面表现出色,主要体现在以下几个方面:
-
内存 占用减少:每个客户端连接在HTTP+SSE模式下平均占用约892MB内存,而在Streamable HTTP模式下仅需234MB,节省了74%的内存资源。
-
连接管理优化:HTTP+SSE需要维护两个长连接,而Streamable HTTP无需长期维护连接,仅在需要时建立临时连接,大大降低了服务器的连接管理负担。
-
基础设施友好:Streamable HTTP基于标准HTTP协议,兼容现有的CDN、Web防火墙等基础设施,无需特殊配置即可部署,降低了企业级应用的部署难度。
-
监控简化:Streamable HTTP使用标准HTTP指标,如http_request_duration_seconds、http_requests_total等,无需像HTTP+SSE那样定义大量自定义监控指标,减少了75%的监控规则。
4.3 部署与运维复杂度对比
Streamable HTTP在部署和运维方面也具有显著优势:
| 评估维度 | HTTP+SSE | Streamable HTTP | 优势幅度 |
|---|---|---|---|
| 开发复杂度 | 高 (127行客户端代码) | 低 (42行客户端代码) | 67%降低 |
| 部署复杂度 | 高 (42个配置项) | 低 (8个配置项) | 81%简化 |
| 运维复杂度 | 高 (42个监控规则) | 低 (8个监控规则) | 81%减少 |
这种复杂度的降低不仅减少了开发和维护成本,还提高了系统的稳定性和可靠性。以Nginx配置为例,HTTP+SSE需要特殊配置支持SSE流,而Streamable HTTP使用标准HTTP配置即可:
HTTP+SSE的Nginx配置:
upstream mcp_events {
server mcp-sse-server:3001;
keepalive 100; # SSE需要保持连接池
}
server {
location /events {
proxy_pass http://mcp_events;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_buffering off; # 关键:必须关闭缓冲
proxy_cache off; # 关键:必须关闭缓存
proxy_read_timeout 24h; # 长连接超时设置
}
}
Streamable HTTP的Nginx配置:
upstream mcp_backend {
server mcp-streamable-server:3000;
}
server {
location /mcp {
proxy_pass http://mcp_backend;
proxy_http_version 1.1;
# 标准HTTP配置,无需特殊设置
}
}
综上对比,Streamable HTTP在企业级场景中能提供更高效、更可靠的性能表现。
本文总结:本文深入分析Streamable HTTP协议的技术特性,说明其在MCP调用场景中的应用实践,并进行优势分析,让读者更好地理解在大模型LLM+MCP场景中,为何选择Streamable HTTP作为基础通讯协议。
本文作者:钟离离
本文原载:公众号“木昆子记录AI”