MCP协议说明
MCP 协议说明与接入设计
本文档用于说明 MCP(Model Context Protocol)的基本概念、连接方式、会话机制、认证授权流程,以及工具中心类平台中 MCP 服务的推荐实现方式。
- API 域名:
https://api.example.com - MCP 地址:
https://api.example.com/mcp - API Key:
tc_xxxxxxxx - 用户 ID:
user_id - Session ID:
mcp_sess_xxxxx
1. MCP 是什么
MCP 全称是 Model Context Protocol,可以理解为一种:
AI 客户端 / Agent 与外部工具服务之间的标准通信协议。
它解决的问题是:
- 客户端如何发现服务端有哪些工具;
- 客户端如何知道每个工具需要什么参数;
- 客户端如何调用工具;
- 服务端如何按照用户权限只暴露可用工具;
- 服务端如何将工具执行结果返回给客户端。
在工具中心类平台中,MCP 通常用于让外部 AI 客户端调用平台内的工具,例如:
- 文本处理工具;
- 文件解析工具;
- 视频解析工具;
- 数据查询工具;
- 内部自动化工具。
MCP 本身不是某一个简单 REST 接口,而是一套基于 JSON-RPC 的协议规范,常见核心方法包括:
initializetools/listtools/call
此外,很多平台会额外提供一个非标准但很实用的说明接口:
GET /mcp/info
2. MCP 与普通 HTTP 工具调用的区别
很多工具平台同时支持两种调用方式:
2.1 普通 HTTP 工具调用
典型调用方式如下:
POST /tools/run
X-API-Key: tc_xxxxxxxx
Content-Type: application/json
{
"tool_name": "text_summary",
"input": {
"text": "待处理文本"
}
}
这种方式直观、简单,适合脚本、后端服务、CI/CD 或普通业务系统调用。
2.2 MCP 工具调用
MCP 更偏向 AI 客户端协议化调用。客户端先和服务端建立协议会话,然后通过 JSON-RPC 发现和调用工具。
典型流程是:
initialize
↓
tools/list
↓
tools/call
相比普通 HTTP,MCP 更关注:
- 工具发现;
- 工具 schema;
- 客户端能力协商;
- 会话上下文;
- Agent 生态兼容。
3. MCP 常见传输方式
MCP 可以运行在不同传输方式上。常见方式包括:
| 传输方式 | 说明 | 常见场景 |
|---|---|---|
| Stdio | 客户端启动本地进程,通过标准输入输出通信 | 本地工具、本地桌面集成 |
| SSE + HTTP | 使用 SSE 建立服务端推送通道,再通过 HTTP 发送消息 | 早期远程 MCP 实现 |
| Streamable HTTP | 新版远程 MCP 推荐方式,通过 HTTP 请求和会话头维持上下文 | 云端工具服务、Web 服务 |
| WebSocket | 通过双向长连接传输 JSON-RPC 消息 | 实时双向通信场景 |
| Stateless HTTP | 每个请求独立携带 API Key,无持久会话 | 简化实现、调试或兼容模式 |
对于工具中心类平台,通常推荐:
Streamable HTTP + 服务端 Session
原因:
- 适合远程服务;
- 适合 Nginx / API Gateway 代理;
- 适合 Docker / Kubernetes 部署;
- 可通过 Redis 维护 session;
- 易于横向扩展;
- 相比 WebSocket,运维复杂度更低。
4. /mcp/info 是什么
GET /mcp/info 通常不是 MCP 标准 JSON-RPC 方法,而是平台提供的辅助说明接口。
它可以用于返回:
- MCP 服务名称;
- MCP 服务地址;
- 支持的传输方式;
- 认证方式;
- 客户端配置示例;
- 协议版本;
- 能力说明。
示例:
GET /mcp/info
可能返回:
{
"name": "tool-center-mcp",
"endpoint": "https://api.example.com/mcp",
"auth": {
"type": "api_key",
"header": "X-API-Key"
},
"transports": ["streamable-http"],
"protocolVersion": "2024-11-05"
}
4.1 /mcp/info 是否需要认证
取决于它返回什么内容。
如果只返回通用说明,可以公开:
可公开访问
如果返回用户相关信息,例如:
- 当前用户;
- 当前 API Key;
- 已订阅工具列表;
- 账号权限;
则必须校验:
X-API-Key: tc_xxxxxxxx
推荐做法:
/mcp/info 默认只返回通用说明;
带 API Key 时可额外返回当前 Key 可访问的摘要信息。
5. MCP 三个核心方法
MCP 的常见协议流程主要包含三个核心方法:
initializetools/listtools/call
5.1 initialize
initialize 是 MCP 会话的初始化方法。
客户端通过它告诉服务端:
- 客户端名称;
- 客户端版本;
- 支持的协议版本;
- 客户端能力。
示例请求:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {
"name": "example-client",
"version": "1.0.0"
}
}
}
示例响应:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {}
},
"serverInfo": {
"name": "tool-center",
"version": "1.0.0"
}
}
}
initialize 的作用不是调用工具,而是建立协议上下文。
5.2 tools/list
tools/list 用于获取当前会话可访问的工具列表。
请求示例:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
服务端应根据当前 session 对应的用户和 API Key 查询权限,只返回该用户已订阅、且当前可用的工具。
响应示例:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "text_summary",
"description": "对文本内容进行摘要",
"inputSchema": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "需要摘要的文本"
}
},
"required": ["text"]
}
}
]
}
}
注意:
tools/list 不能返回用户未订阅的工具。
5.3 tools/call
tools/call 用于调用某个具体工具。
请求示例:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "text_summary",
"arguments": {
"text": "这是一段需要摘要的文本。"
}
}
}
服务端处理逻辑:
1. 根据 session 获取 user_id / api_key_id;
2. 检查 API Key 是否仍然有效;
3. 检查用户是否订阅 params.name 对应工具;
4. 检查工具是否 active;
5. 校验 arguments 是否符合工具 schema;
6. 执行工具;
7. 返回结果。
响应示例:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "摘要结果..."
}
]
}
}
如果未订阅工具,应返回权限错误,例如:
{
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32003,
"message": "当前用户未订阅该工具"
}
}
6. 推荐执行顺序
严格来说,/mcp/info 不属于 MCP 会话本身,它是辅助信息接口。
推荐整体顺序如下:
可选:GET /mcp/info
↓
连接或请求 /mcp,携带 X-API-Key
↓
initialize
↓
tools/list
↓
tools/call
更完整地说:
1. 用户在控制台创建 API Key;
2. 用户将 API Key 配置到 MCP 客户端;
3. 客户端访问 /mcp/info 或直接连接 /mcp;
4. 服务端验证 X-API-Key;
5. 服务端创建或恢复 MCP session;
6. 客户端发送 initialize;
7. 服务端返回协议能力;
8. 客户端发送 tools/list;
9. 服务端返回当前用户已订阅工具;
10. 客户端发送 tools/call;
11. 服务端校验权限并执行工具。
7. MCP Session 是什么
MCP session 可以理解为一次客户端与 MCP 服务端之间的协议会话。
它保存的是:
- 当前连接属于哪个用户;
- 当前连接使用哪个 API Key;
- 是否已初始化;
- 客户端信息;
- 协议版本;
- 会话创建时间;
- 最后活跃时间。
示例结构:
{
"session_id": "mcp_sess_xxxxx",
"user_id": 10001,
"api_key_id": 20001,
"api_key_prefix": "tc_abcd",
"initialized": true,
"client_info": {
"name": "example-client",
"version": "1.0.0"
},
"protocol_version": "2024-11-05",
"created_at": "2026-01-01T00:00:00Z",
"last_seen_at": "2026-01-01T00:10:00Z"
}
注意:
session 不是浏览器登录 session,也不是 JWT。
它是 MCP 协议层的服务端上下文。
8. Session 由谁维护
MCP session 的核心状态应由 服务端维护。
客户端通常只保存:
session_id;- 或保持连接本身;
- 或保存
Mcp-Session-Id; - 或保存请求 ID 用于匹配响应。
服务端保存:
user_id;api_key_id;- 是否已认证;
- 是否已初始化;
- 客户端信息;
- 权限上下文;
- 会话 TTL;
- 长连接对象(如果是 SSE / WebSocket)。
不能把用户身份和权限完全交给客户端保存,因为客户端不可信。
9. 不同传输方式下的 Session 维护
9.1 SSE + HTTP
常见流程:
Client --GET /mcp/sse--> Server
Client --POST /mcp/messages?session_id=xxx--> Server
Server --SSE event--> Client
Session 维护方式:
| 内容 | 维护方 |
|---|---|
| session 状态 | 服务端 |
| session_id | 客户端保存并携带 |
| SSE 连接对象 | 服务端保存 |
| 用户身份与权限 | 服务端 |
9.2 Streamable HTTP
常见流程:
第一次请求:
POST /mcp
X-API-Key: tc_xxxxxxxx
后续请求:
POST /mcp
Mcp-Session-Id: mcp_sess_xxxxx
第一次请求后,服务端返回:
Mcp-Session-Id: mcp_sess_xxxxx
Session 维护方式:
| 内容 | 维护方 |
|---|---|
| session 数据 | 服务端 |
Mcp-Session-Id | 客户端保存并携带 |
| 用户身份与权限 | 服务端 |
| 请求/响应 | 每次 HTTP 请求 |
9.3 WebSocket
常见流程:
Client --WebSocket /mcp--> Server
headers: X-API-Key
WebSocket 连接本身就是会话载体。
服务端通常会在 socket 上保存上下文:
{
"session_id": "mcp_sess_xxxxx",
"user_id": 10001,
"api_key_id": 20001,
"initialized": true
}
Session 维护方式:
| 内容 | 维护方 |
|---|---|
| WebSocket 连接 | 双方保持 |
| session 状态 | 服务端 |
| 用户身份与权限 | 服务端 |
| 请求 ID | 客户端生成,双方匹配 |
9.4 Stateless HTTP
如果没有实现 session,也可以每次请求都携带 API Key:
POST /mcp
X-API-Key: tc_xxxxxxxx
服务端每次请求都验证 API Key。
优点:
- 实现简单;
- 无需 session 存储;
- 请求天然无状态。
缺点:
- 不易维护
initialized状态; - 不容易记录客户端信息;
- 不完全符合会话型 MCP 语义;
- 对复杂客户端兼容性较差。
10. 认证与授权
工具中心类平台通常有两套凭证:
| 场景 | 凭证 | 请求头 |
|---|---|---|
| 管理控制台 | JWT | Authorization: Bearer <token> |
| 工具调用 / MCP | API Key | X-API-Key: tc_xxxxxxxx |
原则:
管理台使用 JWT;
工具调用和 MCP 使用 API Key;
不要把 JWT 交给 MCP 客户端;
不要把 API Key 当成前端登录态使用。
11. API Key 应该在哪一步验证
11.1 /mcp/info
如果只返回通用服务说明,可以不验证。
如果返回用户相关信息,必须验证 API Key。
11.2 建立 /mcp 会话时
必须验证:
X-API-Key: tc_xxxxxxxx
服务端应检查:
1. API Key 是否存在;
2. API Key 是否 active;
3. API Key 是否过期;
4. API Key 所属用户是否 active;
5. 是否允许创建 MCP session。
验证成功后创建 session。
11.3 initialize
如果 session 已在连接入口创建,initialize 可复用 session 上下文。
它主要负责协议协商,不应承担主要权限判断。
11.4 tools/list
必须基于当前 session 的用户身份进行授权过滤。
逻辑:
session.user_id
↓
查询 active subscriptions
↓
只返回已订阅且 active 的工具
11.5 tools/call
必须再次进行实时授权校验。
即使 tools/list 已经过滤过工具,tools/call 仍不能信任客户端。
服务端应检查:
1. session 是否存在;
2. API Key 是否仍 active;
3. API Key 是否过期;
4. 用户是否仍 active;
5. 用户是否订阅 params.name 对应工具;
6. 工具是否 active;
7. arguments 是否符合工具 schema。
原因:
- 用户可能手写
tools/call; - 用户可能绕过
tools/list; - 订阅可能在 session 存活期间被取消;
- API Key 可能在 session 存活期间被吊销;
- 工具可能临时下线。
12. 推荐的 Session 存储方案
12.1 单实例开发环境
可以使用进程内内存:
Map<string, McpSession>
优点:
- 简单;
- 性能高;
- 易调试。
缺点:
- 服务重启 session 丢失;
- 多实例无法共享;
- 不适合生产横向扩容。
12.2 生产环境
推荐使用 Redis:
mcp:session:mcp_sess_xxxxx -> JSON
推荐 TTL:
30 分钟 ~ 2 小时
每次请求刷新 TTL。
Redis Value 示例:
{
"session_id": "mcp_sess_xxxxx",
"user_id": 10001,
"api_key_id": 20001,
"api_key_prefix": "tc_abcd",
"initialized": true,
"client_info": {
"name": "example-client",
"version": "1.0.0"
},
"protocol_version": "2024-11-05",
"created_at": "2026-01-01T00:00:00Z",
"last_seen_at": "2026-01-01T00:10:00Z"
}
13. 推荐实现:Streamable HTTP + Redis Session
推荐架构:
MCP Client
↓ HTTPS
API Gateway / Nginx
↓
MCP Server
↓
Redis: MCP Session
↓
Database: User / API Key / Subscription / Service
↓
Tool Worker
推荐流程:
GET /mcp/info
↓
返回 MCP 服务说明
POST /mcp initialize + X-API-Key
↓
验证 API Key
↓
创建 Redis session
↓
返回 Mcp-Session-Id
POST /mcp tools/list + Mcp-Session-Id
↓
读取 Redis session
↓
查询用户已订阅服务
↓
返回工具列表
POST /mcp tools/call + Mcp-Session-Id
↓
读取 Redis session
↓
实时校验 API Key / 订阅 / 工具状态
↓
执行工具
↓
返回结果
14. 客户端配置示例
{
"mcpServers": {
"tool-center": {
"url": "https://api.example.com/mcp",
"headers": {
"X-API-Key": "tc_xxxxxxxx"
}
}
}
}
说明:
url指向 MCP 入口;X-API-Key使用用户在控制台创建的 API Key;- 用户必须先订阅工具;
tools/list只返回已订阅工具;tools/call调用未订阅工具应返回权限错误。
15. Nginx 代理注意事项
如果使用远程 MCP,需要确保代理支持长连接或流式响应。
示例:
location /mcp {
proxy_pass http://backend_api;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
chunked_transfer_encoding off;
}
注意:
- SSE / Streamable HTTP 通常需要关闭 buffering;
- 长连接需要较长
proxy_read_timeout; - 如果不使用 WebSocket,
Upgrade头不是必须,但保留通常不影响; - 如果使用多实例,session 应放 Redis,不应只放本地内存。
16. 常见错误与处理
16.1 API Key 无效
建议返回:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "API Key 无效或已过期"
}
}
HTTP 状态可使用:
401 Unauthorized
16.2 未订阅工具
建议返回:
{
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32003,
"message": "当前用户未订阅该工具"
}
}
HTTP 状态可使用:
403 Forbidden
16.3 工具不存在
建议返回:
{
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32602,
"message": "工具不存在或不可用"
}
}
16.4 参数错误
建议返回:
{
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32602,
"message": "工具参数不合法"
}
}
17. 安全建议
-
API Key 只展示一次
- 创建成功时展示完整 Key;
- 后续只展示前缀;
- 不在前端持久化完整 Key。
-
MCP 不使用 JWT
- JWT 用于管理控制台;
- MCP / 工具调用使用 API Key。
-
tools/list 按订阅过滤
- 不暴露未订阅工具;
- 不暴露内部工具。
-
tools/call 必须实时校验权限
- 不能因为 session 已认证就跳过权限检查。
-
Session 设置 TTL
- 推荐 30 分钟到 2 小时;
- 每次请求刷新 TTL。
-
API Key 吊销应尽快生效
tools/call时检查 API Key 状态;- 可在吊销 Key 时清理相关 MCP session。
-
记录审计日志
- 记录调用时间;
- 记录 API Key 前缀;
- 记录用户 ID;
- 记录工具名称;
- 记录成功/失败状态;
- 不记录完整 API Key。
18. 推荐后端伪代码
18.1 初始化 session
async def handle_initialize(request):
api_key = request.headers.get("X-API-Key")
key_info = verify_api_key(api_key)
if not key_info:
return jsonrpc_error(request.id, -32001, "API Key 无效或已过期")
session_id = create_session_id()
session = {
"session_id": session_id,
"user_id": key_info.user_id,
"api_key_id": key_info.id,
"initialized": True,
"client_info": request.params.get("clientInfo"),
"protocol_version": request.params.get("protocolVersion"),
}
await redis.setex(f"mcp:session:{session_id}", 7200, json.dumps(session))
return response_with_header(
jsonrpc_result(request.id, {
"protocolVersion": "2024-11-05",
"capabilities": {"tools": {}},
"serverInfo": {"name": "tool-center", "version": "1.0.0"},
}),
headers={"Mcp-Session-Id": session_id},
)
18.2 获取工具列表
async def handle_tools_list(request):
session = await get_session(request.headers.get("Mcp-Session-Id"))
if not session:
return jsonrpc_error(request.id, -32002, "MCP session 不存在或已过期")
subscriptions = get_active_subscriptions(session["user_id"])
tools = build_tools_from_subscriptions(subscriptions)
return jsonrpc_result(request.id, {"tools": tools})
18.3 调用工具
async def handle_tools_call(request):
session = await get_session(request.headers.get("Mcp-Session-Id"))
if not session:
return jsonrpc_error(request.id, -32002, "MCP session 不存在或已过期")
tool_name = request.params["name"]
arguments = request.params.get("arguments", {})
if not is_api_key_active(session["api_key_id"]):
return jsonrpc_error(request.id, -32001, "API Key 已失效")
if not has_active_subscription(session["user_id"], tool_name):
return jsonrpc_error(request.id, -32003, "当前用户未订阅该工具")
validate_tool_arguments(tool_name, arguments)
result = await run_tool(tool_name, arguments)
return jsonrpc_result(request.id, {
"content": [
{"type": "text", "text": result}
]
})
19. 最佳实践总结
推荐设计:
传输方式:Streamable HTTP
认证方式:X-API-Key
Session 存储:Redis
Session 标识:Mcp-Session-Id
工具权限:基于用户订阅实时判断
推荐流程:
GET /mcp/info
可选,用于查看服务说明
POST /mcp initialize + X-API-Key
验证 API Key
创建 session
返回 Mcp-Session-Id
POST /mcp tools/list + Mcp-Session-Id
使用 session.user_id
返回已订阅工具
POST /mcp tools/call + Mcp-Session-Id
使用 session.user_id / api_key_id
再次校验 API Key 和订阅
执行工具
核心原则:
身份认证可复用 session;
关键操作授权必须实时校验。
20. 一句话说明
MCP 是 AI 客户端与工具服务之间的标准协议。对于远程工具平台,推荐使用 Streamable HTTP,通过 X-API-Key 完成首次认证,通过服务端 session 维持上下文,通过 tools/list 暴露已订阅工具,通过 tools/call 调用工具,并在每次关键操作时实时校验 API Key、订阅和工具状态。