以Streamable HTTP方式访问mcp server的过程
一、mcp server 部署
使用fastmcp框架 部署 mcp server, 以下是源代码
# 引入 fastmcp 依赖包
from fastmcp import FastMCP# 新建fastmcp实例, 名字叫做 weather
mcp = FastMCP("weather")@mcp.tool(name="weather", tags={"weather"})
def weather(city: str) -> str:"""获取制定城市的天气信息:param city: 城市名称:return: 天气信息"""print(f"正在获取 {city} 的天气信息...")return f"{city} 的天气是晴天,温度 25 度。"if __name__ == '__main__':# mcp.run(transport="stdio")mcp.run(transport="streamable-http", host="0.0.0.0", port=8000, path="/weather")
运行python文件,启动 mcp server , 以下是启动成功界面
二、理解mcp服务调用原理
模型上下文协议 (MCP) 为客户端-服务器连接定义了严格的生命周期,以确保适当的功能协商和状态管理。
- 初始化 :能力协商和协议版本协议
- 操作 :正常协议通信
- Shutdown:正常终止连接
初始化
初始化阶段必须是客户端和服务器之间的第一次交互。在此阶段,客户端和服务器:
- 建立协议版本兼容性
- 交换和获取功能
- 获取实现详情
客户端必须通过发送包含以下内容的 initialize 请求来启动此阶段:
- 支持的协议版本
- 客户端功能
- 客户端实现信息
initialize request
{"jsonrpc": "2.0","id": 1,"method": "initialize","params": {"protocolVersion": "2025-03-26","capabilities": {"roots": {"listChanged": true},"sampling": {}},"clientInfo": {"name": "ExampleClient","version": "1.0.0"}}
}
initialize 请求不可以作为 JSON-RPC 的一部分 batch,因为在初始化完成之前,其他请求和通知是不可能的。这还允许向后兼容未明确支持 JSON-RPC 批处理的先前协议版本。
服务器必须使用自己的功能和信息进行响应:
{"jsonrpc": "2.0","id": 1,"result": {"protocolVersion": "2025-03-26","capabilities": {"logging": {},"prompts": {"listChanged": true},"resources": {"subscribe": true,"listChanged": true},"tools": {"listChanged": true}},"serverInfo": {"name": "ExampleServer","version": "1.0.0"},"instructions": "Optional instructions for the client"}
}
成功初始化后,客户端必须发送初始化通知,以指示它已准备好开始正常作:
initialize notifications
{"jsonrpc": "2.0","method": "notifications/initialized"
}
注意:
客户端 一定不能 在 服务器 响应 initialize 请求之前发送 ping 以外的请求。
服务器在接收初始化通知之前不应发送 ping 和 logging 以外的请求。
版本协商
在 initialize 请求中,客户端必须发送它支持的协议版本。这应该是客户端支持的最新版本 。
如果服务器支持请求的协议版本,则它必须使用相同的版本进行响应。否则,服务器必须使用它支持的另一个协议版本进行响应。这应该是服务器支持的最新版本 。
如果客户端不支持服务器响应中的版本,则它应该 断开。
能力协商
客户端和服务器功能确定在会话期间哪些可选协议功能将可用。
主要功能包括:
- roots: 提供文件系统 root 能力
- sampling: 支持 LLM采样请求
- experimental: 描述对非标准实验性功能的支持
- prompts: 提供提示模板
- resources: 提供可读资源
- tools: 公开可调用工具
- logging: 发出结构化日志消息
- completions: 支持参数自动补全
- experimental: 描述对非标准实验性功能的支持
- subscribe:支持订阅单个项目的更改(仅限资源)
功能对象可以描述子功能,例如:
listChanged:支持列表更改通知(用于提示、资源和工具)
subscribe:支持订阅单个项目的更改(仅限资源)
Operation
在操作阶段,客户端和服务端根据协商的能力进行消息交换。
双方都应该 :
- 遵循协商的协议版本
- 仅使用已成功协商的功能
Shutdown
在 shutdown 阶段,一端(通常是 client)干净地终止协议连接。没有定义特定的关闭消息,而是使用底层传输机制来发出连接终止信号:
stdio
对于 stdio 传输 ,客户端应通过以下方式启动关闭:
- 首先,关闭子进程(服务器)的输入流
- 等待服务器退出,如果服务器未在合理时间内退出,则发送 SIGTERM
- 如果服务器在 SIGTERM 之后的合理时间内未退出,则发送 SIGKILL
服务器可以通过关闭其对 Client 端的输出流并退出来启动关闭。
HTTP 协议
对于 HTTP 传输 ,通过关闭关联的 HTTP 连接来指示关闭。
Timeouts
实现应为所有发送的请求建立超时,以防止连接挂起和资源耗尽。当请求在超时期限内未收到成功或错误响应时,发送者应针对该请求发出取消通知并停止等待响应。
SDK 和其他中间件应允许按请求配置这些超时。
实现可以选择在收到与请求相对应的进度通知时重置超时时钟,因为这意味着工作实际上正在进行。但是,无论进度通知如何,实现都应始终强制执行最大超时,以限制行为异常的客户端或服务器的影响。
Error Handling
实现应该准备好处理这些错误情况:
- 协议版本不匹配
- 无法协商所需的功能
- 请求超时
初始化错误示例:
{"jsonrpc": "2.0","id": 1,"error": {"code": -32602,"message": "Unsupported protocol version","data": {"supported": ["2024-11-05"],"requested": "1.0.0"}}
}
三、请求案例
1. 首先发送初始化请求
调用工具之前, 首先需要初始化连接:
- 新增请求头: Accept: application/json, text/event-stream
- 构建请求体
{"jsonrpc": "2.0","method": "initialize","params": {"protocolVersion": "2025-07-17","capabilities": {},"clientInfo": {"name": "example-client","version": "1.0.0"}},"id": 0
}
- 请求测试
从相应头中,可以拿到 mcp-session-id:
2. 响应初始化
-
新增请求头: mcp-session-id:18251f0529ec4bef90da4d3ffb34b81b
-
构建请求体:
{"jsonrpc": "2.0","method": "notifications/initialized"
}
-检查响应, 服务器端返回的内容为空,正确
3. 在当前session下进行基本操作测试
3.1 获取可用工具列表
- 请求体:
{"jsonrpc": "2.0","id": 1,"method": "tools/list","params": {}
}
- 响应内容
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"weather","description":"获取制定城市的天气信息\n:param city: 城市名称\n:return: 天气信息","inputSchema":{"properties":{"city":{"title":"City","type":"string"}},"required":["city"],"type":"object"},"outputSchema":{"properties":{"result":{"title":"Result","type":"string"}},"required":["result"],"title":"_WrappedResult","type":"object","x-fastmcp-wrap-result":true}}]}}
3.2 调用工具
- 请求体:
{"jsonrpc": "2.0","id": 2,"method": "tools/call","params": {"name": "weather","arguments": {"city": "深圳"}}
}
}
- 响应内容
event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"深圳 的天气是晴天,温度 25 度。"}],"structuredContent":{"result":"深圳 的天气是晴天,温度 25 度。"},"isError":false}}
常见错误
1. Not Acceptable: Client must accept both application/json and text/event-stream
报错信息
{"jsonrpc": "2.0","id": "server-error","error": {"code": -32600,"message": "Not Acceptable: Client must accept both application/json and text/event-stream"}
}
原因
客户端请求头没有支持 application/json 和 text/event-stream
解决
新增请求头: Accept: application/json, text/event-stream
2. Bad Request: Missing session ID
报错信息
{
“jsonrpc”: “2.0”,
“id”: “server-error”,
“error”: {
“code”: -32600,
“message”: “Bad Request: Missing session ID”
}
}
原因
客户端请求头没有 session id
解决
请求头新增字段:mcp-session-id , 这个 session id 需要 请求 初始化方法, 从相应头中获取
3. Invalid request parameters
报错信息
event: message
data: {"jsonrpc":"2.0","id":1,"error":{"code":-32602,"message":"Invalid request parameters","data":""}}
原因
没有 调用相应初始化方法
解决
请求响应初始化方法
- 构建请求体:
{"jsonrpc": "2.0","method": "notifications/initialized"
}