1. 初始化是什么? #
客户端和 MCP 服务器建立连接后,第一件事必须是初始化(握手),然后才能调工具、读资源。
可以把它想成打电话:
| 步骤 | 打电话 | MCP 初始化 |
|---|---|---|
| 1 | 拨号接通 | 客户端连上服务器(stdio 或 HTTP) |
| 2 | 「听得见吗?我说中文」 | 客户端发 initialize,声明协议版本和能力 |
| 3 | 「听得见,我们说中文」 | 服务器回复支持的版本和能力 |
| 4 | 「好,开始聊」 | 客户端发 initialized 通知 |
| 5 | 正常聊天 | 调用 tools/list、tools/call 等 |
没有完成初始化就发业务请求,属于协议错误。
2. 初始化在交换什么? #
握手时双方交换三类信息,你只需知道含义,不必手写 JSON:
| 交换内容 | 谁发送 | 用途 |
|---|---|---|
| 协议版本 | 双方 | 确保说同一种「方言」(如 2025-11-25) |
| 能力(capabilities) | 双方 | 声明支持哪些功能(工具、资源、提示等) |
| 实现信息(name / version) | 双方 | 显示名称,方便调试和日志 |
2.1 服务器常见能力 #
| 能力字段 | 有了它才能… |
|---|---|
tools |
客户端调用 tools/list、tools/call |
resources |
客户端读取 resources/read |
prompts |
客户端使用 prompts/get |
2.2 用 FastMCP 时你需要做什么? #
服务器端:几乎不用管。 注册 `@mcp.tool()后,FastMCP 会在初始化响应里自动声明tools` 能力。
客户端:调用一行 await session.initialize(),SDK 自动完成三步握手。
3. 三步握手流程 #
sequenceDiagram
participant C as 客户端
participant S as 服务器
C->>S: ① initialize 请求
S-->>C: ② initialize 响应(版本 + 能力)
C->>S: ③ notifications/initialized
Note over C,S: 此后才能 list_tools / call_tool
| 步骤 | 方法 | 要不要等回复 |
|---|---|---|
| ① | initialize |
要,等服务器响应 |
| ② | (服务器响应) | — |
| ③ | notifications/initialized |
不要,发完即走 |
规则:服务器在收到
initialized之前,不应向客户端发业务请求(ping、日志除外)。
4. 消息长什么样? #
实际开发中由 SDK 自动生成。下面帮助你看懂 Inspector 里的原始 JSON。
4.1 客户端发的 initialize 请求 #
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": {
"name": "example-client",
"version": "1.0.0"
}
}
}4.2 服务器回的响应 #
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-11-25",
"capabilities": {
"tools": { "listChanged": true }
},
"serverInfo": {
"name": "hello-server",
"version": "1.0.0"
}
}
}4.3 客户端发的就绪通知 #
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}注意:通知没有 id 字段,也不会收到回复。
5. 服务器端用法 #
服务器不需要自己写 initialize 处理逻辑。写好工具、启动服务即可。
5.1 服务器 #
initialize_server.py
# 导入 FastMCP 模块,用于自动处理 initialize 握手
from mcp.server.fastmcp import FastMCP
# 创建 FastMCP 服务器实例,name 参数会显示在 serverInfo 里
mcp = FastMCP(name="hello-server")
# 使用装饰器注册 greet 工具,初始化时会自动声明 tools 能力
@mcp.tool()
def greet(name: str = "World") -> str:
# 返回问候语字符串
return f"Hello, {name}!"
# 判断是否作为主程序运行
if __name__ == "__main__":
# 以 stdio 模式启动服务器,等待客户端连接
mcp.run(transport="stdio")6. 客户端用法 #
客户端的核心就两步:连上服务器 → 调用 initialize()。
6.1 完整示例 #
# 官方客户端 API 是异步的
import asyncio
# 导入操作系统相关的模块,用于处理文件路径等
import os
# 导入系统相关的模块,用于获取解释器路径等
import sys
# 导入会话类和子进程启动参数,用于与服务器通信
from mcp import ClientSession, StdioServerParameters
# 导入 stdio 传输模块,用于通过标准输入输出连接服务器
from mcp.client.stdio import stdio_client
# 定义主异步函数
async def main() -> None:
# 获取当前文件所在目录的绝对路径
base_dir = os.path.dirname(os.path.abspath(__file__))
# 拼接服务器脚本的完整路径
server_path = os.path.join(base_dir, "initialize_server.py")
# 配置服务器子进程启动参数
server_params = StdioServerParameters(
# 指定 Python 解释器的路径
command=sys.executable,
# 指定启动的服务器脚本
args=[server_path],
)
# 建立 stdio 连接并自动启动子进程
async with stdio_client(server_params) as (read, write):
# 创建会话对象,与服务器进行通信
async with ClientSession(read, write) as session:
# ★ 进行初始化握手,必须最先调用
result = await session.initialize()
# 初始化成功后,输出协商得到的信息
print("协议版本:", result.protocolVersion)
# 判断服务器信息是否存在,存在就输出名称,否则输出“未知”
print("服务器名称:", result.serverInfo.name if result.serverInfo else "未知")
# 初始化完成后,可以调用业务接口
tools = await session.list_tools()
# 输出获取到的工具列表名称
print("工具列表:", [t.name for t in tools.tools])
# 如果当前模块为主程序则执行 main
if __name__ == "__main__":
asyncio.run(main())6.2 运行 #
uv run initialize_client.py期望输出:
协议版本: 2025-11-25
服务器名称: hello-server
工具列表: ['greet']6.3 initialize() 返回什么? #
await session.initialize() 返回 InitializeResult 对象,常用字段:
| 字段 | 含义 | 示例 |
|---|---|---|
protocolVersion |
协商后的协议版本 | "2025-11-25" |
capabilities |
服务器支持的能力 | 含 tools 等 |
serverInfo |
服务器名称和版本 | name="hello-server" |
instructions |
可选,给客户端的说明文字 | 部分服务器会提供 |
6.4 常见写法注意点 #
| 注意点 | 说明 |
|---|---|
必须用 async with |
stdio_client 和 ClientSession 都是异步上下文管理器 |
initialize() 在最前面 |
不初始化就 list_tools() 会失败 |
用 sys.executable |
Windows 下避免 python 指向错误解释器 |
7. 用 Inspector 验证初始化 #
7.1 启动 Inspector #
npx @modelcontextprotocol/inspector uv run initialize_server.py7.2 如何判断成功 #
浏览器打开后,看以下信号:
| 信号 | 说明 |
|---|---|
| 连接状态为 Connected | 握手完成 |
Tools 标签页能看到 greet |
说明 tools 能力协商成功 |
| 通知面板无初始化错误 | 没有版本不兼容等报错 |
7.3 在 Inspector 里观察消息顺序 #
展开通知/日志面板,正常顺序应为:
initialize请求 → 响应notifications/initialized- 之后才有
tools/list等
8. 初始化之后做什么? #
初始化只是「接通电话」,之后才能干正事:
flowchart LR
A[initialize 握手] --> B[tools/list 发现工具]
B --> C[tools/call 调用工具]
A --> D[resources/read 读资源]
A --> E[prompts/get 获取提示]
| 下一步 | 客户端方法 | 前提 |
|---|---|---|
| 列出工具 | await session.list_tools() |
服务器声明了 tools |
| 调用工具 | await session.call_tool(name, args) |
同上 |
| 读取资源 | await session.read_resource(uri) |
服务器声明了 resources |
| 获取提示 | await session.get_prompt(name, args) |
服务器声明了 prompts |