MCP 无状态架构深度拆解:当 AI 工具协议决定「删掉 Session」——从有状态握手到无状态 HTTP,MCP 2026-07-28 规范如何用一个「减法」重新定义企业级 AI 工具生态的终极形态
引言:一个协议的「成人礼」
2026 年 7 月 28 日,Anthropic 发布了 Model Context Protocol(MCP)的第 5 版规范。这不是一次小修小补,而是 MCP 问世以来规模最大、最系统性的一次架构重构。
一句话总结这次更新:把 Session 砍了。
initialize 握手没了,Mcp-Session-Id header 没了,服务端主动发起的请求也没了。MCP 从一个有状态的双向协议,变成了一个无状态的请求响应协议。Anthropic 自己的说法是:「MCP 终于成了一个一等 HTTP 公民。」
如果你正在构建 AI Agent、MCP Server、或者任何依赖 MCP 的工具链,这篇文章会帮你搞清楚三个核心问题:
- 为什么要砍掉 Session?有状态设计到底哪里出了问题?
- 无状态化之后,MCP 的架构变成了什么样?代码该怎么写?
- 企业级部署会发生哪些根本性变化?
在深入技术细节之前,让我们先回顾一下 MCP 为什么会在 2026 年 7 月做出如此激进的架构决策。这不仅仅是一次技术迭代,更是一次关于「协议应该承担多少职责」的哲学辩论。
第一部分:MCP 的前世今生——从「AI 的 USB-C」到「AI 的 HTTP」
1.1 MCP 到底解决了什么问题
在 MCP 出现之前,每个 AI 应用要连接外部工具,都得自己写一套集成层。ChatGPT 连 GitHub 要写一套,连 Slack 要写一套,连数据库又要写一套。N 个 AI 应用 × M 个工具 = N×M 套集成代码。
MCP 的核心思路是引入一个标准化协议层,把 N×M 变成 N+M:
传统架构:
AI应用A ──自定义集成──> 工具1
AI应用A ──自定义集成──> 工具2
AI应用B ──自定义集成──> 工具1
AI应用B ──自定义集成──> 工具2
MCP架构:
AI应用A ──MCP协议──┐
├──> MCP Server 1(包装工具1)
AI应用B ──MCP协议──┘
├──> MCP Server 2(包装工具2)
MCP Server 暴露三类能力:
- Tools:可执行的函数(如搜索、写文件、调用 API)
- Resources:只读数据(如文件内容、数据库 schema)
- Prompts:可复用的提示词模板
1.2 有状态时代:Session 带来了什么
MCP 最初设计为有状态协议,核心特征是:
- 初始化握手:客户端连接后必须先发送
initialize请求,服务端返回能力声明,客户端再发initialized确认 - Session ID:握手成功后,服务端分配一个
Mcp-Session-Id,后续所有请求都必须携带这个 ID - 服务端推送:服务端可以通过 Session 主动向客户端发起请求(如通知、进度更新)
这套设计在本地 stdio 通信场景下工作得很好——一个 MCP Server 对应一个客户端进程,天然隔离,不需要考虑并发和扩展。
但问题出在远程部署场景。
1.3 有状态设计的致命缺陷
当你把 MCP Server 部署为远程服务时,Session 设计带来了一系列企业级噩梦:
负载均衡灾难:因为每个请求必须路由到同一个 Session 对应的服务器实例,你必须配置「会话亲和性」(Session Affinity)。这意味着:
- 不能用简单的轮询负载均衡
- 服务器宕机会导致 Session 丢失
- 横向扩缩容变得极其复杂
状态存储成本:每个 Session 都需要在服务端维护状态。当并发量上升时,Session 存储成为瓶颈。你需要 Redis、Memcached 或者其他分布式状态存储,增加了架构复杂度。
运维复杂度:运行一个远程 MCP 服务器,不应该需要普通无状态服务所不具备的专用机制。但在有状态模式下,你必须处理 Session 过期、Session 迁移、Session 一致性等一系列问题。
# 旧版 MCP Server(有状态模式)
# 每个连接都需要维护 Session 状态
class LegacyMCPServer:
def __init__(self):
self.sessions = {} # Session 状态存储
async def handle_initialize(self, request):
session_id = generate_session_id()
self.sessions[session_id] = {
"client_info": request.params,
"capabilities": self.capabilities,
"created_at": time.time()
}
return InitializeResult(
protocolVersion="2025-11-25",
capabilities=self.capabilities,
serverInfo=self.server_info,
_meta={"sessionId": session_id}
)
async def handle_tools_call(self, request):
session_id = request.headers.get("Mcp-Session-Id")
if session_id not in self.sessions:
raise SessionNotFoundError()
# 所有操作都绑定到 Session
session = self.sessions[session_id]
# ... 执行工具调用
这就是为什么 MCP 决定做减法——删掉 Session,回归无状态。
第二部分:无状态架构——MCP 2026-07-28 的核心变革
2.1 无状态的核心思想
新规范的设计哲学可以用一句话概括:把状态管理的职责从协议层归还给应用层。
无状态模式下:
- 每个请求都是独立的,携带完整的上下文信息
- 服务器不维护任何 Session 状态
- 如果需要跨请求保持状态,由应用层自己处理(如通过 handle 机制)
新版 MCP 架构:
客户端 ──请求(含完整上下文)──> 负载均衡器 ──> MCP Server 实例1
客户端 ──请求(含完整上下文)──> 负载均衡器 ──> MCP Server 实例2
客户端 ──请求(含完整上下文)──> 负载均衡器 ──> MCP Server 实例3
每个实例都是无状态的,可以随意扩缩容
2.2 具体删掉了什么
删掉 initialize 握手:客户端不再需要先发 initialize 再发 initialized。每个请求直接携带协议版本和能力声明。
删掉 Mcp-Session-Id:请求不再需要 Session ID header。服务器也不再分配 Session ID。
删掉服务端主动请求:服务端不能再通过 Session 主动向客户端发起请求。如需通知,改用标准的 HTTP 响应模式。
2.3 新增了什么
可路由传输头(Routable Transport Headers):允许 API 网关在不检查请求内容的情况下识别和路由 MCP 请求。这大幅降低了网关的处理开销和延迟。
# 新版 MCP 请求示例
POST /mcp HTTP/1.1
Host: mcp.example.com
Content-Type: application/json
Mcp-Protocol-Version: 2026-07-28
Mcp-Client-Id: my-ai-agent
Mcp-Capabilities: tools,resources
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}
每个请求都自包含:协议版本、客户端身份、能力声明,全部在 header 里。服务器收到后直接处理,不需要查 Session。
2.4 Handle 机制:有状态的「逃生舱」
有人会问:如果工具调用需要跨请求保持状态怎么办?比如一个合同审批工具,第一次调用创建草稿,第二次调用需要引用这个草稿。
新规范引入了 Handle 机制:
# 新版 MCP Server(无状态模式)
class StatelessMCPServer:
async def handle_create_contract(self, request):
# 创建合同草稿,生成一个 handle
draft_id = create_draft(request.params)
return CallToolResult(
content=[TextContent(
text=f"合同草稿已创建,草稿ID: {draft_id}"
)],
_meta={"handle": draft_id} # 返回 handle
)
async def handle_approve_contract(self, request):
# 客户端在下一次请求中带回 handle
draft_id = request.params.get("draft_id")
# 从外部存储(如数据库)获取草稿状态
draft = db.get_draft(draft_id)
# 执行审批
result = approve_draft(draft)
return CallToolResult(content=[TextContent(text=result)])
关键区别:状态不是存在 MCP Server 的内存里,而是存在外部存储(数据库、对象存储等)。MCP Server 本身是无状态的,只负责逻辑处理。
# Handle 使用流程示意
# 步骤1:创建资源,获得 handle
response1 = mcp_client.call_tool("create_document", {
"title": "技术方案",
"content": "..."
})
handle = response1.meta["handle"] # "doc_abc123"
# 步骤2:后续操作带回 handle
response2 = mcp_client.call_tool("update_document", {
"handle": handle, # 由客户端管理
"updates": {"status": "reviewing"}
})
# 步骤3:继续操作
response3 = mcp_client.call_tool("publish_document", {
"handle": handle
})
这种设计的精妙之处在于:状态管理的复杂度从协议层转移到了应用层。协议只负责定义「怎么传递 handle」,至于 handle 背后是什么存储、怎么管理,由开发者自己决定。
第三部分:MRTR——多轮往返请求机制
3.1 为什么需要 MRTR
在实际的工具调用中,经常出现这样的场景:Agent 调用一个工具,工具需要人类补充信息才能继续。比如:
- 调用代码部署工具,需要确认部署环境
- 调用数据查询工具,需要用户选择查询参数
- 调用审批工具,需要上级确认
在有状态模式下,这可以通过服务端主动推送来实现。但无状态模式下,服务端不能主动找客户端。
3.2 MRTR 的工作原理
MRTR(Multi Round-Trip Requests)通过标准的请求-响应交换来实现多轮交互:
# MRTR 实现示例
class DeploymentTool:
async def deploy(self, request):
# 第一步:分析部署需求
analysis = analyze_deployment(request.params)
if analysis.needs_confirmation:
# 不是推送,而是返回一个「等待确认」的响应
return CallToolResult(
content=[TextContent(
text=f"即将部署到 {analysis.target_env},"
f"请确认。需要的信息:{analysis.missing_info}"
)],
_meta={
"awaiting_input": True,
"round_trip_id": generate_round_trip_id(),
"expected_fields": ["confirm", "env_override"]
}
)
# 如果不需要确认,直接执行
result = execute_deployment(request.params)
return CallToolResult(content=[TextContent(text=result)])
关键约束:如果响应流断开,客户端需要将未完成请求作为新请求重新发起。协议不会自动接续「断在半路的那件事」。
这意味着 MRTR 不是「持久连接」,而是「一系列独立的请求-响应」,通过 round_trip_id 关联。
# 客户端处理 MRTR 的逻辑
async def call_tool_with_mrtr(client, tool_name, params):
response = await client.call_tool(tool_name, params)
# 检查是否需要多轮交互
if response.meta.get("awaiting_input"):
round_trip_id = response.meta["round_trip_id"]
# 获取用户输入
user_input = await get_user_input(response.content)
# 作为新请求发起,携带 round_trip_id
final_response = await client.call_tool(tool_name, {
**params,
"_round_trip_id": round_trip_id,
"_user_input": user_input
})
return final_response
return response
第四部分:MCP Apps 与 Tasks 扩展
4.1 MCP Apps:在工具里嵌入 UI
新规范引入了 MCP Apps 扩展,允许 MCP Server 在沙箱化 iframe 中渲染交互式 HTML 界面。
这解决了 AI Agent 的一个长期痛点:很多工具需要图形化界面来辅助操作。比如数据库查询工具需要一个 schema 浏览器,文件管理工具需要一个文件树。
# MCP App 示例:数据库查询工具
class DatabaseQueryTool:
def get_tools(self):
return [
Tool(
name="query_database",
description="执行 SQL 查询",
inputSchema={
"type": "object",
"properties": {
"query": {"type": "string", "description": "SQL 查询语句"}
}
}
),
# 这是一个 MCP App,提供交互式界面
App(
name="database_explorer",
description="交互式数据库浏览器",
url="https://my-app.example.com/db-explorer",
sandbox="iframe",
permissions=["read_schema", "execute_query"]
)
]
4.2 Tasks:长时间运行的后台任务
Tasks 扩展让 MCP Server 可以启动和管理长时间运行的后台任务。这在 AI Agent 场景下非常重要——很多任务(如数据迁移、批量处理、模型训练)需要几分钟甚至几小时。
# Tasks 扩示示例
class DataMigrationTool:
async def start_migration(self, request):
# 启动后台任务
task_id = await self.task_manager.create_task(
name="data_migration",
params=request.params,
timeout=3600 # 1小时超时
)
return CallToolResult(
content=[TextContent(
text=f"迁移任务已启动,任务ID: {task_id}"
)],
_meta={"task_id": task_id}
)
async def check_task_status(self, request):
task_id = request.params["task_id"]
status = await self.task_manager.get_status(task_id)
return CallToolResult(
content=[TextContent(
text=f"任务状态: {status.state}, "
f"进度: {status.progress}%, "
f"已处理: {status.items_processed}/{status.items_total}"
)]
)
第五部分:授权与安全——OAuth 2.0 的全面适配
5.1 企业级授权的痛点
在有状态模式下,MCP 的授权机制相对简单——Session 本身就隐含了一定的信任关系。但在无状态模式下,每个请求都需要独立验证身份。
5.2 新规范的授权方案
新规范强化了对 OAuth 2.0 和 OIDC 的适配,MCP Server 可以直接连接企业身份系统:
# 无状态 MCP Server 的授权中间件
from authlib.integrations.starlette_client import OAuth
oauth = OAuth()
# 配置企业身份提供商
oauth.register(
name='azure',
client_id='your-client-id',
client_secret='your-client-secret',
server_metadata_url='https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration',
client_kwargs={'scope': 'openid email profile'}
)
async def auth_middleware(request):
# 每个请求独立验证 Token
token = request.headers.get("Authorization", "").replace("Bearer ", "")
if not token:
raise UnauthorizedError("Missing authorization token")
# 验证 Token 有效性
user_info = await verify_token(token)
# 将用户信息注入请求上下文
request.state.user = user_info
return await call_next(request)
这意味着 MCP Server 可以无缝集成 Okta、Entra ID、Auth0 等企业身份系统,而不需要 Session 来维持认证状态。
第六部分:迁移实战——从有状态到无状态
6.1 迁移检查清单
如果你有一个现有的 MCP Server,迁移到无状态架构需要关注以下几点:
□ 移除 initialize/initialized 握手逻辑
□ 移除 Session ID 的生成和管理
□ 移除服务端主动推送(Notification)机制
□ 将 Session 内存状态迁移到外部存储(Redis/DB)
□ 实现 Handle 机制(如果需要跨请求状态)
□ 更新传输层(支持 HTTP header 路由)
□ 更新客户端 SDK
□ 更新授权中间件(支持无状态 Token 验证)
6.2 迁移代码示例
# 迁移前(有状态)
class OldMCPServer:
def __init__(self):
self.sessions = {}
async def handle(self, request):
if request.method == "initialize":
return self.handle_initialize(request)
session_id = request.headers.get("Mcp-Session-Id")
session = self.sessions.get(session_id)
if not session:
raise Error("Invalid session")
# 所有操作都依赖 session
if request.method == "tools/list":
return self.handle_tools_list(session)
elif request.method == "tools/call":
return self.handle_tools_call(session, request)
# 迁移后(无状态)
class NewMCPServer:
def __init__(self, state_store):
self.state_store = state_store # 外部状态存储
async def handle(self, request):
# 无握手,直接处理请求
# 从 header 获取客户端信息
client_id = request.headers.get("Mcp-Client-Id")
protocol_version = request.headers.get("Mcp-Protocol-Version")
# 验证协议版本
if protocol_version != "2026-07-28":
raise UnsupportedProtocolVersion()
if request.method == "tools/list":
return self.handle_tools_list(request)
elif request.method == "tools/call":
return self.handle_tools_call(request)
async def handle_tools_call(self, request):
# 如果需要跨请求状态,使用 Handle
handle = request.params.pop("_handle", None)
if handle:
# 从外部存储恢复状态
state = await self.state_store.get(handle)
request.params["_state"] = state
result = await execute_tool(request.params)
# 如果产生了新的状态,保存并返回 handle
if result.new_state:
new_handle = generate_handle()
await self.state_store.set(new_handle, result.new_state)
result.meta["handle"] = new_handle
return result
6.3 部署架构变化
迁移前(需要 Session Affinity):
客户端 ──> LB ──必须路由到同一实例──> MCP Server 实例A(Session存储)
──> MCP Server 实例B(Session存储)
──> MCP Server 实例C(Session存储)
迁移后(标准负载均衡):
客户端 ──> LB(轮询)──> MCP Server 实例A(无状态)
──> MCP Server 实例B(无状态)
──> MCP Server 实象C(无状态)
│
└──> Redis/DB(状态外置)
第七部分:性能与扩展性分析
7.1 延迟对比
| 操作 | 有状态模式 | 无状态模式 |
|---|---|---|
| 首次连接 | ~50ms(握手) | 0ms(直接请求) |
| 工具调用 | ~2ms(查Session) | 0ms(无需查Session) |
| 扩缩容 | 需要Session迁移 | 即时生效 |
| 故障恢复 | Session丢失风险 | 无状态,无影响 |
7.2 吞吐量提升
无状态设计意味着:
- 不需要维护 Session 数据结构,减少内存开销
- 不需要 Session 一致性检查,减少 CPU 开销
- 可以使用标准的 HTTP 缓存和压缩
- 负载均衡策略更灵活(轮询、最少连接、随机等)
7.3 水平扩展能力
有状态模式下的扩展瓶颈:
并发连接数: 10,000
Session 存储大小: ~1KB/Session × 10,000 = ~10MB
Session 查询延迟: ~2ms(Redis)/ ~0.5ms(内存)
Session 过期清理: 需要定时任务
无状态模式下的扩展能力:
并发连接数: 无限制(取决于CPU和网络)
状态存储: 外部管理,按需扩展
请求处理: 纯计算,无状态查询开销
扩缩容: 秒级生效
第八部分:实战案例——从零构建一个无状态 MCP Server
8.1 完整项目结构
让我们用一个实际的例子来展示无状态 MCP Server 的完整实现。我们构建一个文件管理工具,支持文件的创建、读取和搜索。
my-mcp-server/
├── main.py # 入口文件
├── server.py # MCP Server 核心逻辑
├── tools/ # 工具实现
│ ├── __init__.py
│ ├── file_create.py
│ ├── file_read.py
│ └── file_search.py
├── state/ # 状态管理
│ ├── __init__.py
│ └── handle_store.py
├── auth/ # 授权中间件
│ ├── __init__.py
│ └── oauth.py
├── requirements.txt
└── Dockerfile
8.2 核心服务器实现
# server.py
from mcp import Server, Tool, CallToolResult, TextContent
from mcp.types import App
from state.handle_store import HandleStore
from tools import file_create, file_read, file_search
import json
class FileManagementServer:
"""无状态文件管理 MCP Server"""
def __init__(self, state_store: HandleStore):
self.state_store = state_store
self.server = Server("file-management-server")
self._register_tools()
def _register_tools(self):
"""注册所有工具"""
@self.server.list_tools()
async def list_tools():
return [
Tool(
name="create_file",
description="创建新文件",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "文件路径"
},
"content": {
"type": "string",
"description": "文件内容"
},
"metadata": {
"type": "object",
"description": "文件元数据(标签、描述等)"
}
},
"required": ["path", "content"]
}
),
Tool(
name="read_file",
description="读取文件内容",
inputSchema={
"type": "object",
"properties": {
"handle": {
"type": "string",
"description": "文件句柄(从 create_file 获得)"
},
"path": {
"type": "string",
"description": "文件路径(与 handle 二选一)"
}
}
}
),
Tool(
name="search_files",
description="搜索文件",
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"tags": {
"type": "array",
"items": {"type": "string"},
"description": "按标签过滤"
}
},
"required": ["query"]
}
),
# MCP App:文件浏览器
App(
name="file_explorer",
description="交互式文件浏览器",
url="https://my-app.example.com/file-explorer",
sandbox="iframe",
permissions=["read_files", "list_directory"]
)
]
@self.server.call_tool()
async def call_tool(name: str, arguments: dict):
# 无状态处理:每个请求独立
if name == "create_file":
return await file_create.execute(arguments)
elif name == "read_file":
return await file_read.execute(arguments, self.state_store)
elif name == "search_files":
return await file_search.execute(arguments)
else:
raise ValueError(f"Unknown tool: {name}")
# main.py
import uvicorn
from server import FileManagementServer
from state.handle_store import RedisHandleStore
from auth.oauth import OAuthMiddleware
async def app():
# 初始化状态存储(外部 Redis)
state_store = RedisHandleStore(
redis_url="redis://localhost:6379"
)
# 创建 Server
server = FileManagementServer(state_store)
# 挂载 MCP 路由
from mcp.server.sse import SseServerTransport
sse = SseServerTransport("/messages/")
from starlette.applications import Starlette
from starlette.routing import Mount, Route
app = Starlette([
Route("/sse", endpoint=sse.handle_sse),
Mount("/messages/", app=sse.handle_post_message),
])
# 添加授权中间件
app.add_middleware(OAuthMiddleware)
return app
if __name__ == "__main__":
uvicorn.run(app(), host="0.0.0.0", port=8000)
8.3 状态管理实现
# state/handle_store.py
import json
import time
import uuid
from typing import Any, Optional
import redis.asyncio as redis
class HandleStore:
"""Handle 存储接口"""
async def set(self, handle: str, value: Any, ttl: int = 3600) -> None:
raise NotImplementedError
async def get(self, handle: str) -> Optional[Any]:
raise NotImplementedError
async def delete(self, handle: str) -> None:
raise NotImplementedError
def generate_handle(self) -> str:
return f"handle_{uuid.uuid4().hex[:12]}"
class RedisHandleStore(HandleStore):
"""基于 Redis 的 Handle 存储"""
def __init__(self, redis_url: str):
self.redis = redis.from_url(redis_url)
self.prefix = "mcp:handle:"
async def set(self, handle: str, value: Any, ttl: int = 3600) -> None:
key = f"{self.prefix}{handle}"
serialized = json.dumps({
"value": value,
"created_at": time.time(),
"expires_at": time.time() + ttl
})
await self.redis.setex(key, ttl, serialized)
async def get(self, handle: str) -> Optional[Any]:
key = f"{self.prefix}{handle}"
data = await self.redis.get(key)
if data is None:
return None
parsed = json.loads(data)
if time.time() > parsed["expires_at"]:
await self.delete(handle)
return None
return parsed["value"]
async def delete(self, handle: str) -> None:
key = f"{self.prefix}{handle}"
await self.redis.delete(key)
8.4 Docker 部署
# Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "main.py"]
# docker-compose.yml
version: '3.8'
services:
mcp-server:
build: .
ports:
- "8000:8000"
environment:
- REDIS_URL=redis://redis:6379
- OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID}
- OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET}
depends_on:
- redis
# 无状态,可以轻松水平扩展
deploy:
replicas: 3
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
redis_data:
注意 docker-compose 中的 replicas: 3——在无状态模式下,你可以轻松地水平扩展 MCP Server 实例。每个实例都是独立的,不需要 Session Affinity。
8.5 负载均衡配置
# nginx.conf - 无状态负载均衡
upstream mcp_servers {
# 简单轮询,不需要 Session Affinity
server mcp-server-1:8000;
server mcp-server-2:8000;
server mcp-server-3:8000;
}
server {
listen 443 ssl;
server_name mcp.example.com;
ssl_certificate /etc/ssl/certs/mcp.pem;
ssl_certificate_key /etc/ssl/private/mcp.key;
# SSE 端点
location /sse {
proxy_pass http://mcp_servers;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400; # SSE 长连接
}
# 消息端点
location /messages/ {
proxy_pass http://mcp_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 健康检查
location /health {
proxy_pass http://mcp_servers/health;
}
}
第九部分:常见问题与排坑指南
9.1 Q&A
Q:无状态模式下,如果工具需要「记住」用户的选择怎么办?
A:使用 Handle 机制。工具返回一个 handle,客户端在后续请求中带回。handle 背后的状态存在外部存储(Redis、数据库等)。
# 正确做法:使用 Handle
response = await call_tool("configure_model", {
"model": "gpt-4",
"temperature": 0.7
})
handle = response.meta["handle"] # "cfg_xyz789"
# 后续调用带回 handle
response = await call_tool("generate", {
"handle": handle,
"prompt": "写一首诗"
})
Q:MRTR 和普通的多次调用有什么区别?
A:MRTR 是协议层面的多轮交互机制,通过 round_trip_id 关联。普通的多次调用是独立的,没有关联关系。MRTR 适用于工具需要人类补充信息的场景。
Q:迁移过程中,新旧版本的 MCP Server 能共存吗?
A:可以。通过不同的 URL 路径区分版本:
/v1/mcp:旧版有状态服务器/v2/mcp:新版无状态服务器
客户端可以逐步迁移,先升级部分调用到 v2,验证稳定后再全面切换。
Q:无状态模式会增加网络开销吗?
A:每个请求需要携带更多上下文信息(协议版本、客户端信息等),会略微增加请求大小。但省去了 Session 握手和查询的开销,总体上延迟更低。
9.2 性能调优建议
- 使用 HTTP/2:减少连接建立开销,支持多路复用
- 启用压缩:对于大型工具列表或资源内容,启用 gzip 压缩
- 缓存工具列表:客户端可以缓存
tools/list的响应,减少重复请求 - 合理设置 Handle TTL:根据业务需求设置 Handle 的过期时间,避免存储浪费
- 使用连接池:MCP Client 应该复用 HTTP 连接,而不是每次请求都新建
第十部分:生态影响与未来展望
8.1 对 MCP Server 开发者的影响
好消息:
- 部署变得简单,扔到 Serverless 或容器里就能跑
- 不需要处理 Session 一致性问题
- 可以用任何无状态框架(FastAPI、Express、Gin 等)
需要注意:
- 需要自己管理状态(如果工具有状态需求)
- 客户端需要携带更多上下文信息
- 不能依赖服务端推送,需要轮询或 Webhook
8.2 对 AI Agent 框架的影响
Agent 框架需要适配新的无状态模式:
- 每次调用 MCP 工具时,需要携带完整的上下文
- Handle 的生命周期需要框架层管理
- MRTR 的多轮交互需要框架层支持
8.3 对企业部署的影响
运维简化:
- 可以用标准的 API 网关管理 MCP 服务
- 负载均衡不需要 Session Affinity
- 监控和限流变得更简单
安全增强:
- 每个请求独立认证,减少 Session 劫持风险
- 可以与企业现有 IAM 系统无缝集成
- 审计日志更清晰(每个请求都有完整上下文)
8.4 MCP 的下一步
无状态化是 MCP 走向成熟的标志性一步。接下来可能的方向:
- MCP Mesh:多个 MCP Server 之间的服务发现和路由
- MCP Federation:跨组织的 MCP Server 互联
- MCP Marketplace:标准化的 MCP Server 注册和发现
- 更多 Transport:WebSocket、gRPC、QUIC 等传输层支持
总结
MCP 2026-07-28 的无状态化重构,本质上是一个「减法哲学」的胜利:
- 删掉 Session:让 MCP 回归 HTTP 的简洁本质
- 删掉握手:让每次请求自包含,减少连接建立开销
- 删掉服务端推送:把通知的职责交给应用层
这个减法带来的收益是巨大的:
- 部署复杂度大幅降低
- 水平扩展能力大幅提升
- 企业级集成变得简单
- 运维成本显著下降
同时,新规范通过 Handle 机制、MRTR、MCP Apps、Tasks 等扩展,在保持核心简洁的同时,提供了足够的灵活性来处理复杂场景。
对于 AI 工具生态来说,这次更新意味着 MCP 从一个「好用的协议」变成了一个「企业级的基础设施」。当一个协议足够简单、足够标准化、足够容易部署时,它才真正具备了成为行业标准的潜力。
MCP 的故事还远未结束。无状态化只是起点,真正的爆发——当数以万计的 MCP Server 像 API 一样被自由组合时——才刚刚开始。
参考资源:
- MCP 官方规范 2026-07-28:https://spec.modelcontextprotocol.io
- Anthropic MCP GitHub:https://github.com/modelcontextprotocol
- MCP Go SDK:https://github.com/mark3labs/mcp-go
- MCP Python SDK:https://github.com/modelcontextprotocol/python-sdk