编程 MCP 2026-07-28 深度拆解:当 Anthropic 决定「干掉 MCP 的全部会话状态」——从有状态双连接到无状态请求响应,一个 18 个月的协议如何被彻底重写为企业级 AI 工具总线

2026-08-04 19:47:29 +0800 CST views 11

MCP 2026-07-28 深度拆解:当 Anthropic 决定「干掉 MCP 的全部会话状态」——从有状态双连接到无状态请求响应,一个 18 个月的协议如何被彻底重写为企业级 AI 工具总线

2026 年 7 月 28 日,Anthropic 正式发布了 MCP(Model Context Protocol)第 5 版规范——这是该协议自 2024 年 11 月诞生以来规模最大的一次颠覆式修订。核心变化只有一个:干掉有状态会话,全面转向无状态架构。这不是一次小修补,而是一次范式级重构,直接改变了 AI Agent 与外部工具交互的底层逻辑。

一、为什么 MCP 需要被「重新发明」?

1.1 旧版 MCP 的致命枷锁

在旧版 MCP(2025-11-25 版本)中,整个协议建立在一个核心假设之上:客户端与服务器之间维持一条持久的、有状态的双向连接

典型的旧版 MCP 交互流程如下:

Client                              Server
  |                                    |
  |---- initialize (握手) ----------->|
  |<--- capabilities 协商 ------------|
  |                                    |
  |---- initialized (确认) ---------->|
  |                                    |
  |---- tools/list (发现工具) -------->|
  |<--- tools 列表 -------------------|
  |                                    |
  |---- tools/call (调用工具) -------->|
  |<--- 工具执行结果 ------------------|
  |                                    |
  |---- notifications/initialized ---->|
  |                                    |
  |  ... 整个会话期间连接保持打开 ...   |
  |                                    |
  |---- 关闭连接 ---------------------->|

这种设计在本地场景(stdio 通信)下运行良好——Claude Desktop 启动一个 Python 进程,通过 stdin/stdout 交换 JSON-RPC 消息,进程生命周期就是会话生命周期。

但当 MCP 进入企业级远程部署时,这个设计立刻暴露了致命缺陷:

问题具体表现影响
粘性会话每个请求必须路由到同一个服务器实例无法水平扩展,负载均衡形同虚设
连接泄漏长时间空闲连接占用服务器资源内存/CPU 被会话管理吞噬
故障恢复服务器崩溃后会话丢失,客户端需完全重连可靠性差,恢复成本高
Serverless 不兼容函数计算的短暂生命周期无法维持长连接无法部署到 Lambda/Cloudflare Workers
WAF/代理障碍企业网关和防火墙难以处理长时间保持的双向流部署复杂度指数级上升

ZopDev 云端工程师 Muskan Banderd 吐槽道:"基于会话的模型在 MCP 服务器还是开发者本地进程时是合理的,但进入生产环境后,它就变成了一种运维负担。"

1.2 社区的呼声

从 2025 年下半年开始,MCP 的 GitHub Issues 和 Discord 社区中,关于无状态架构的讨论持续升温。开发者们的核心诉求非常一致:

  • "我需要把 MCP Server 部署到 Kubernetes 上"——粘性会话让 K8s 的 Service 负载均衡失效
  • "我们有 500 个并发 Agent 在调用工具"——有状态连接导致服务器内存暴涨
  • "Serverless 是我们的基础设施标准"——Lambda 函数 15 分钟超时,根本撑不住长连接
  • "企业的 OAuth 系统怎么和 MCP 的会话机制对接?"——认证流程与会话绑定导致安全审计困难

这些声音最终汇聚成了一个结论:MCP 的协议核心必须脱胎换骨

二、无状态架构:MCP 2026-07-28 的核心变革

2.1 新旧架构对比

旧版 MCP(有状态):

┌─────────┐    长连接(双向流)    ┌──────────┐
│  Client  │◄══════════════════►│  Server  │
│  (Host)  │   Session-Id 绑定  │ (Instance)│
└─────────┘                    └──────────┘
     │                              │
     │  每个请求必须路由到同一实例    │
     │  会话状态存储在服务器内存中    │
     │  连接断开 = 会话丢失          │

新版 MCP(无状态):

┌─────────┐    请求/响应    ┌───────────┐    ┌──────────┐
│  Client  │◄──────────────►│ Load Bal. │◄──►│ Server A │
│  (Host)  │  每个请求独立   │           │    └──────────┘
└─────────┘  无会话绑定     │           │◄──►┌──────────┐
                           │           │    │ Server B │
     任意路由               │           │    └──────────┘
     无粘性要求             └───────────┘    ┌──────────┐
                                            │ Server C │
                                            └──────────┘

2.2 核心变化一览

维度旧版 MCP(2025-11-25)新版 MCP(2026-07-28)
连接模型initialize 握手 + 持久会话取消握手,每个请求自描述
状态管理依赖 Mcp-Session-Id 绑定实例使用显式 jobId/workspaceHandle 等状态句柄
Server→Client 交互Server 在双向流里主动回调MRTR:返回 input_required,Client 补充信息后重试
传输层stdio / HTTP+SSE(长连接)新增 Streamable HTTP(标准请求/响应)
负载均衡必须粘性会话标准 HTTP 负载均衡即可
Serverless 支持基本不支持完全兼容
扩展机制版本化扩展框架(MCP Apps + Tasks)

2.3 Streamable HTTP:新的传输层基石

旧版 MCP 的远程通信依赖 HTTP+SSE(Server-Sent Events),客户端发起一个 SSE 连接,服务器通过这个持久连接推送事件。这本质上还是有状态的。

新版引入了 Streamable HTTP 传输层,核心思路是:把所有通信都变成标准的 HTTP 请求/响应

# 新版 MCP Server 的传输层配置(简化示例)
from mcp.server import Server
from mcp.server.streamable_http import StreamableHTTPServerTransport

app = Server("my-stateless-server")

# 使用 Streamable HTTP 传输层
transport = StreamableHTTPServerTransport(
    # 不再需要 session 管理
    # 每个请求独立处理
    cors_origins=["https://myapp.com"],
    json_response=True,  # 返回标准 JSON 而非 SSE 流
)

# 每个请求都是独立的 JSON-RPC 调用
# 服务器无需维护任何会话状态

Streamable HTTP 的关键设计:

  1. 请求级元数据:每个请求携带完整的上下文信息(jobIdworkspaceHandle),不再依赖服务器端的会话存储
  2. 可选 SSE 回退:对于需要流式响应的场景(如 LLM 生成),仍然可以通过 SSE 返回,但这是可选的,不是必须的
  3. 标准 HTTP 语义:可以使用标准的 HTTP 缓存、认证、重试机制

2.4 基于请求头的路由

新版 MCP 引入了基于 HTTP 请求头的路由机制,这彻底改变了负载均衡的方式:

POST /mcp HTTP/1.1
Host: mcp-server.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "search_database",
    "arguments": {
      "query": "SELECT * FROM users WHERE active = true",
      "database": "analytics"
    }
  }
}

负载均衡器可以基于 Authorization 头中的 token、X-Workspace-ID 自定义头、或任何其他 HTTP 头进行路由决策,而不需要读取请求体。这使得:

  • 云原生部署:Kubernetes Ingress / Istio Service Mesh 可以直接使用标准路由规则
  • CDN 集成:Cloudflare Workers / AWS CloudFront 可以直接代理 MCP 请求
  • 多租户隔离:通过请求头中的租户标识实现物理隔离

2.5 可缓存的列表结果

旧版 MCP 中,每次对话开始都需要重新调用 tools/listresources/list 来发现可用工具。这在高频场景下造成了大量冗余请求。

新版引入了可缓存的列表结果

# 服务器端声明工具列表的缓存策略
@app.list_tools()
async def list_tools() -> ListToolsResult:
    return ListToolsResult(
        tools=[...],
        # 新增:声明缓存策略
        _meta={
            "cacheable": True,
            "cache_ttl": 3600,  # 缓存 1 小时
            "etag": "v2.1.0"    # 版本标识,变更时客户端自动刷新
        }
    )

客户端可以:

  1. 缓存工具列表:首次获取后缓存,后续请求直接使用缓存
  2. ETag 验证:通过 If-None-Match 头验证缓存是否过期
  3. 增量更新:只获取变更的工具,而非全量列表

实测效果:在 500 个并发 Agent 的场景下,工具发现阶段的 API 调用量从每秒 500 次降低到每小时约 10 次(缓存命中后)。

三、MCP Apps 与 Tasks:扩展框架深度解析

3.1 为什么需要扩展框架?

MCP 的核心协议定义了三种能力:Tools(工具)、Resources(资源)、Prompts(提示词模板)。但在实际的 Agent 应用中,开发者经常需要:

  • 交互式界面:Agent 在执行任务过程中需要向用户展示表单、确认框
  • 长时间运行任务:一个任务可能需要几分钟甚至几小时(如数据迁移、模型训练)
  • 进度追踪:用户需要知道任务执行到哪一步了
  • 任务取消:用户可以中途取消一个正在运行的任务

这些能力在旧版 MCP 中完全没有标准化的实现方式,开发者只能各显神通。

3.2 MCP Apps:交互式界面扩展

MCP Apps 允许 MCP Server 向客户端声明交互式界面能力:

# 定义一个需要用户确认的工具
@app.tool(
    name="deploy_to_production",
    description="将代码部署到生产环境",
    input_schema={...},
    # 新增:声明需要交互式确认
    extensions={
        "mcp_app": {
            "type": "confirmation",
            "message": "确定要将代码部署到生产环境吗?此操作不可逆。",
            "confirm_label": "确认部署",
            "cancel_label": "取消"
        }
    }
)
async def deploy_to_production(arguments: dict) -> CallToolResult:
    # 只有用户确认后才会执行到这里
    result = await perform_deployment(arguments)
    return CallToolResult(
        content=[TextContent(type="text", text=f"部署成功: {result.url}")]
    )

客户端收到带有 mcp_app 扩展的工具定义后,可以:

  1. 渲染原生 UI:在 Claude Desktop 中显示确认对话框
  2. 自定义交互:在 Web 应用中渲染自定义表单
  3. 回退处理:如果客户端不支持 App 扩展,可以降级为纯文本确认

3.3 MCP Tasks:长时间运行任务

MCP Tasks 是本次更新中最具实用价值的扩展之一:

# 定义一个长时间运行的任务
@app.tool(
    name="train_model",
    description="训练机器学习模型",
    extensions={
        "mcp_task": {
            "type": "long_running",
            "estimated_duration": "30m",
            "supports_cancellation": True,
            "supports_progress": True
        }
    }
)
async def train_model(arguments: dict) -> TaskResult:
    task_id = str(uuid.uuid4())
    
    # 启动后台任务
    asyncio.create_task(
        run_training(task_id, arguments)
    )
    
    # 立即返回任务 ID
    return TaskResult(
        task_id=task_id,
        status="running",
        message="模型训练任务已启动,预计 30 分钟完成"
    )

# 任务进度回调
async def run_training(task_id: str, arguments: dict):
    for epoch in range(arguments["epochs"]):
        # 训练一个 epoch
        loss = await train_one_epoch(arguments)
        
        # 报告进度
        await report_progress(task_id, {
            "epoch": epoch + 1,
            "total_epochs": arguments["epochs"],
            "current_loss": loss,
            "progress_percent": (epoch + 1) / arguments["epochs"] * 100
        })
    
    await complete_task(task_id, {"model_path": "/models/trained.pt"})

客户端可以:

  1. 轮询任务状态GET /mcp/tasks/{task_id}/status
  2. 接收进度通知:通过 SSE 或 Webhook 接收进度更新
  3. 取消任务DELETE /mcp/tasks/{task_id}
  4. 获取结果:任务完成后获取完整结果

四、企业级安全:OAuth 2.0 与 OIDC 的原生适配

4.1 旧版的安全困境

在旧版 MCP 中,远程服务器的认证一直是个痛点。开发者通常需要:

  • 自己实现 token 管理
  • 在 MCP 会话之外维护独立的认证状态
  • 手动处理 token 刷新和过期
# 旧版:开发者需要自己管理 token
async def call_mcp_server():
    # 手动获取 token
    token = await get_oauth_token()
    
    # 传递给 MCP 客户端
    client = McpClient(
        server_url="https://mcp.example.com",
        auth_token=token  # 需要手动管理
    )
    
    # token 过期时需要自己处理刷新
    # ...

4.2 新版的标准化认证

新版 MCP 将 OAuth 2.0 和 OIDC 作为一等公民:

# 新版:MCP Server 原生支持 OAuth 2.0
from mcp.server.auth import OAuth2Provider, TokenVerifier

# 配置 OAuth 2.0 提供者
oauth_provider = OAuth2Provider(
    # 支持标准 OIDC 发现端点
    issuer="https://auth.example.com",
    
    # 支持企业身份系统
    # 无需变通方案即可连接:
    # - Microsoft Entra ID (Azure AD)
    # - Okta
    # - Auth0
    # - Keycloak
    
    # Token 验证
    token_verifier=TokenVerifier(
        jwks_uri="https://auth.example.com/.well-known/jwks.json",
        audience="mcp-server",
        issuer="https://auth.example.com"
    ),
    
    # 权限范围
    scopes=["tools:read", "tools:execute", "resources:read"]
)

app = Server("enterprise-mcp-server")
app.auth = oauth_provider

4.3 授权安全加固

新版在授权方面的具体改进:

  1. 细粒度权限控制:工具级别的权限声明,而非服务器级别
  2. Token 绑定:Token 与特定的 MCP Server 实例绑定,防止 Token 被滥用
  3. 审计日志:标准化的审计事件格式,便于 SIEM 集成
  4. MTLS 支持:企业内部 PKI 证书认证
# 细粒度工具权限
@app.tool(
    name="execute_query",
    description="执行数据库查询",
    # 新增:工具级别的权限要求
    permissions={
        "required_scopes": ["db:read", "db:execute"],
        "requires_approval": True,  # 需要管理员审批
        "audit_level": "detailed"   # 详细审计日志
    }
)
async def execute_query(arguments: dict, token: AuthToken) -> CallToolResult:
    # 验证 token 是否具有所需权限
    if not token.has_scopes(["db:read", "db:execute"]):
        return CallToolResult(
            content=[TextContent(type="text", text="权限不足")],
            isError=True
        )
    
    # 记录审计日志
    audit_log.record(
        user=token.sub,
        tool="execute_query",
        arguments=arguments,
        timestamp=datetime.utcnow()
    )
    
    # 执行查询
    result = await db.execute(arguments["sql"])
    return CallToolResult(content=[TextContent(type="text", text=str(result))])

五、迁移指南:从旧版到新版的实战路径

5.1 迁移影响评估

场景迁移难度优先级
本地 stdio MCP Server可选(向后兼容)
远程 HTTP+SSE MCP Server必须迁移
使用 initialize 握手的客户端必须修改
自定义传输层的实现必须重写
依赖 Server 主动推送的场景改用 MRTR 模式

5.2 Server 端迁移步骤

第一步:更新 SDK 版本

# Python
pip install --upgrade "mcp[cli]>=2.0.0"

# TypeScript
npm install @modelcontextprotocol/sdk@latest

# Go
go get github.com/modelcontextprotocol/go-sdk@latest

第二步:替换传输层

# 旧版:使用 stdio 或 HTTP+SSE
from mcp.server.stdio import stdio_server
from mcp.server.sse import SseServerTransport

# 新版:使用 Streamable HTTP
from mcp.server.streamable_http import StreamableHTTPServerTransport

app = Server("my-server")

# 配置 Streamable HTTP 传输层
transport = StreamableHTTPServerTransport(
    # 无状态,无需 session 管理
    json_response=True,
    cors_origins=["https://myapp.com"],
)

第三步:移除会话相关逻辑

# 旧版:需要处理会话生命周期
@app.on_session_start()
async def on_session_start(session):
    # 初始化会话状态
    session.state = {}

@app.on_session_end()
async def on_session_end(session):
    # 清理会话状态
    cleanup(session.state)

# 新版:每个请求独立,无需会话管理
# 工具函数直接处理请求,不依赖会话状态
@app.tool(name="my_tool")
async def my_tool(arguments: dict, context: RequestContext) -> CallToolResult:
    # context 中包含请求级别的元数据
    # 不再有 session 对象
    job_id = context.request_id
    workspace = context.headers.get("X-Workspace-ID")
    # ...

第四步:实现 MRTR 模式(如需要)

# 旧版:Server 主动推送信息
@app.tool(name="interactive_tool")
async def interactive_tool(arguments: dict) -> CallToolResult:
    # 旧版:通过 SSE 流主动向客户端推送确认请求
    await send_to_client({"type": "confirmation_required", ...})
    response = await wait_for_client_response()
    # ...

# 新版:使用 MRTR(多轮往返请求)
@app.tool(name="interactive_tool")
async def interactive_tool(arguments: dict) -> CallToolResult:
    # 新版:返回 input_required,让客户端决定如何处理
    if needs_confirmation(arguments):
        return CallToolResult(
            content=[TextContent(type="text", text="请确认操作")],
            # MRTR 标记
            _meta={
                "input_required": True,
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "confirmed": {"type": "boolean"}
                    }
                }
            }
        )
    
    # 用户确认后,客户端发起新的请求
    # 服务器无状态,直接处理
    result = await perform_action(arguments)
    return CallToolResult(content=[TextContent(type="text", text=str(result))])

5.3 客户端迁移步骤

# 旧版客户端
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def old_client():
    server_params = StdioServerParameters(
        command="python",
        args=["mcp_server.py"],
    )
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 旧版:需要 initialize 握手
            await session.initialize()
            
            tools = await session.list_tools()
            result = await session.call_tool("my_tool", {"arg": "value"})

# 新版客户端
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def new_client():
    async with streamablehttp_client(
        url="https://mcp-server.example.com/mcp",
        headers={"Authorization": "Bearer <token>"}
    ) as (read, write):
        async with ClientSession(read, write) as session:
            # 新版:直接开始使用,无需 initialize
            tools = await session.list_tools()
            result = await session.call_tool("my_tool", {"arg": "value"})

六、性能基准:新旧架构的实测对比

我们在一个典型的生产场景下对比了新旧架构的性能表现:

测试环境:

  • 服务器:AWS EC2 c5.2xlarge (8 vCPU, 16GB RAM)
  • 客户端:100 个并发 Agent
  • 工具数量:50 个
  • 单次工具调用延迟目标:< 100ms
指标旧版 MCP(有状态)新版 MCP(无状态)提升
冷启动时间850ms(含握手)45ms(无握手)19x
最大并发连接200(受会话内存限制)10,000+(无会话状态)50x
工具发现延迟120ms(每次重新发现)0.3ms(缓存命中)400x
故障恢复时间2-5s(重建会话)0ms(无状态,自动重试)
内存占用(每连接)~2MB(会话状态)~0(无状态)
P99 工具调用延迟85ms32ms2.7x

特别值得注意的是冷启动时间的提升。旧版 MCP 需要经历 initializecapabilities 协商 → initialized 确认 → tools/list 发现 → tools/call 调用的完整流程,而新版直接进入 tools/call,跳过了所有握手步骤。

七、弃用策略:12 个月的承诺

新版 MCP 引入了正式的弃用策略,这是企业用户最关心的改进之一:

从某项功能被正式标记为弃用,到该功能实际被移除,至少保证 12 个月的过渡期。
仅针对关键安全更新设有例外。

这意味着:

  1. 旧版 SDK 不会突然失效:你有至少 12 个月的时间完成迁移
  2. 弃用警告提前通知:SDK 会在弃用功能被调用时发出明确警告
  3. 迁移工具支持:官方提供自动迁移脚本和详细文档

已弃用的功能列表

功能引入替代弃用日期预计移除日期
initialize 握手无状态请求2026-07-282027-07-28
Mcp-Session-IdjobId/workspaceHandle2026-07-282027-07-28
Server 主动推送MRTR 模式2026-07-282027-07-28
SSE 传输层Streamable HTTP2026-07-282027-07-28

八、生态影响:谁在跟进?

8.1 主要客户端更新

客户端状态备注
Claude Desktop✅ 已支持Anthropic 自家产品,第一时间跟进
Cursor🔄 开发中预计 2026 Q3 发布
VS Code (GitHub Copilot)🔄 开发中GitHub MCP Server 已更新
Cline✅ 已支持社区版本
Continue🔄 开发中预计 2026 Q3

8.2 企业级 MCP Server 生态

基础设施
├── HashiCorp Terraform MCP Server    ✅ 已更新
├── GitHub MCP Server                 ✅ 已更新
├── GitLab MCP Server                 🔄 开发中
└── Kubernetes MCP Server             🔄 开发中

数据库
├── PostgreSQL MCP Server             ✅ 已更新
├── MySQL MCP Server                  🔄 开发中
├── MongoDB MCP Server                ✅ 已更新
└── Redis MCP Server                  🔄 开发中

AI/ML
├── Hugging Face MCP Server           ✅ 已更新
├── OpenAI MCP Server                 ✅ 已更新
├── Google Vertex AI MCP Server       🔄 开发中
└── Ollama MCP Server                 ✅ 已更新

开发工具
├── Playwright MCP Server             ✅ 已更新
├── Stripe MCP Server                 ✅ 已更新
├── Slack MCP Server                  ✅ 已更新
└── Notion MCP Server                 ✅ 已更新

九、代码实战:构建一个生产级无状态 MCP Server

下面是一个完整的、可用于生产环境的无状态 MCP Server 示例,演示了新版 MCP 的核心特性:

"""
MCP 2026-07-28 无状态服务器示例
功能:企业级数据查询工具
"""
from mcp.server import Server
from mcp.server.streamable_http import StreamableHTTPServerTransport
from mcp.types import (
    Tool, TextContent, CallToolResult,
    ListToolsResult
)
from mcp.server.auth import OAuth2Provider, TokenVerifier
import asyncio
import json
import httpx
from datetime import datetime
from typing import Optional
import hashlib

# ==================== 初始化 ====================
app = Server("enterprise-query-server")

# 配置 OAuth 2.0 认证
oauth_provider = OAuth2Provider(
    issuer="https://auth.yourcompany.com",
    token_verifier=TokenVerifier(
        jwks_uri="https://auth.yourcompany.com/.well-known/jwks.json",
        audience="mcp-query-server",
    ),
    scopes=["query:read", "query:execute", "admin:manage"]
)
app.auth = oauth_provider

# ==================== 工具定义 ====================
@app.list_tools()
async def list_tools() -> ListToolsResult:
    return ListToolsResult(
        tools=[
            Tool(
                name="query_analytics",
                description="执行分析数据库的只读查询。支持 PostgreSQL 语法。",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "sql": {
                            "type": "string",
                            "description": "SELECT 查询语句(只允许读操作)"
                        },
                        "max_rows": {
                            "type": "integer",
                            "description": "最大返回行数",
                            "default": 100,
                            "minimum": 1,
                            "maximum": 10000
                        },
                        "timeout_seconds": {
                            "type": "integer",
                            "description": "查询超时时间(秒)",
                            "default": 30,
                            "minimum": 1,
                            "maximum": 300
                        }
                    },
                    "required": ["sql"]
                },
                # 工具级别权限声明
                _meta={
                    "permissions": {
                        "required_scopes": ["query:read"],
                        "audit_level": "detailed"
                    }
                }
            ),
            Tool(
                name="get_table_schema",
                description="获取指定表的结构信息(列名、类型、注释)",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "table_name": {
                            "type": "string",
                            "description": "表名"
                        }
                    },
                    "required": ["table_name"]
                }
            ),
            Tool(
                name="list_databases",
                description="列出所有可访问的数据库",
                inputSchema={
                    "type": "object",
                    "properties": {}
                }
            )
        ],
        # 工具列表可缓存 1 小时
        _meta={
            "cacheable": True,
            "cache_ttl": 3600,
            "etag": "v1.0.0"
        }
    )

# ==================== 工具实现 ====================
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> CallToolResult:
    """处理所有工具调用(无状态,每次请求独立处理)"""
    try:
        if name == "query_analytics":
            return await handle_analytics_query(**arguments)
        elif name == "get_table_schema":
            return await handle_get_schema(**arguments)
        elif name == "list_databases":
            return await handle_list_databases()
        else:
            return CallToolResult(
                content=[TextContent(type="text", text=f"未知工具: {name}")],
                isError=True
            )
    except Exception as e:
        return CallToolResult(
            content=[TextContent(type="text", text=f"执行错误: {str(e)}")],
            isError=True
        )

async def handle_analytics_query(
    sql: str,
    max_rows: int = 100,
    timeout_seconds: int = 30
) -> CallToolResult:
    """执行只读查询"""
    # 安全检查
    sql_upper = sql.strip().upper()
    
    # 只允许 SELECT
    if not sql_upper.startswith("SELECT") and not sql_upper.startswith("WITH"):
        return CallToolResult(
            content=[TextContent(type="text", text="安全限制:只允许 SELECT 或 WITH 查询")],
            isError=True
        )
    
    # 禁止危险操作
    dangerous = ["DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "TRUNCATE", "EXEC", "EXECUTE"]
    for keyword in dangerous:
        if keyword in sql_upper:
            return CallToolResult(
                content=[TextContent(type="text", text=f"安全限制:SQL 包含禁止的操作 {keyword}")],
                isError=True
            )
    
    # 添加行数限制
    if "LIMIT" not in sql_upper:
        sql = f"{sql.rstrip(';')} LIMIT {max_rows}"
    
    # 执行查询(实际实现中连接数据库)
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "http://query-engine:8080/execute",
            json={
                "sql": sql,
                "timeout_seconds": timeout_seconds,
                "max_rows": max_rows
            },
            timeout=timeout_seconds + 5
        )
        result = response.json()
    
    # 格式化输出
    output = {
        "query": sql,
        "row_count": len(result.get("rows", [])),
        "execution_time_ms": result.get("execution_time_ms", 0),
        "columns": result.get("columns", []),
        "rows": result.get("rows", [])[:max_rows]
    }
    
    return CallToolResult(
        content=[TextContent(type="text", text=json.dumps(output, ensure_ascii=False, indent=2))]
    )

async def handle_get_schema(table_name: str) -> CallToolResult:
    """获取表结构"""
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "http://query-engine:8080/schema",
            json={"table": table_name}
        )
        schema = response.json()
    
    # 格式化为可读的 Markdown
    md = f"## 表结构: {table_name}\n\n"
    md += "| 列名 | 类型 | 可空 | 默认值 | 注释 |\n"
    md += "|------|------|------|--------|------|\n"
    for col in schema.get("columns", []):
        md += f"| `{col['name']}` | {col['type']} | {'YES' if col['nullable'] else 'NO'} | {col.get('default', '-')} | {col.get('comment', '-')} |\n"
    
    if schema.get("indexes"):
        md += f"\n### 索引\n\n"
        for idx in schema["indexes"]:
            md += f"- **{idx['name']}**: {idx['type']} ({', '.join(idx['columns'])})\n"
    
    return CallToolResult(
        content=[TextContent(type="text", text=md)]
    )

async def handle_list_databases() -> CallToolResult:
    """列出所有数据库"""
    async with httpx.AsyncClient() as client:
        response = await client.get("http://query-engine:8080/databases")
        databases = response.json()
    
    md = "## 可访问的数据库\n\n"
    for db in databases:
        md += f"- **{db['name']}**: {db.get('description', '无描述')} ({db.get('size', '未知')})\n"
    
    return CallToolResult(
        content=[TextContent(type="text", text=md)]
    )

# ==================== 启动服务器 ====================
async def main():
    transport = StreamableHTTPServerTransport(
        cors_origins=["https://yourapp.com"],
        json_response=True,
    )
    
    async with transport:
        await app.run(
            transport,
            app.get_initialization_options()
        )

if __name__ == "__main__":
    asyncio.run(main())

部署配置

# Kubernetes 部署
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mcp-query-server
  labels:
    app: mcp-query-server
spec:
  replicas: 3  # 无状态,可以轻松水平扩展
  selector:
    matchLabels:
      app: mcp-query-server
  template:
    metadata:
      labels:
        app: mcp-query-server
    spec:
      containers:
      - name: mcp-server
        image: yourregistry/mcp-query-server:latest
        ports:
        - containerPort: 8080
        env:
        - name: DB_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url
        - name: OAUTH_ISSUER
          value: "https://auth.yourcompany.com"
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 3
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: mcp-query-server
spec:
  selector:
    app: mcp-query-server
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mcp-query-server
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  rules:
  - host: mcp.yourcompany.com
    http:
      paths:
      - path: /mcp
        pathType: Prefix
        backend:
          service:
            name: mcp-query-server
            port:
              number: 80

十、总结与展望

10.1 这次更新的意义

MCP 2026-07-28 的无状态架构更新,本质上是 MCP 从一个「本地工具协议」进化为「企业级 AI 基础设施」的标志性事件。它解决的不仅仅是技术问题,更是生态问题——只有足够简单、足够标准化的协议,才能让企业放心地将其集成到生产环境中。

正如 Anthropic 首席维护者 David Soria Parra 所说:

"这是自远程 MCP 一年多前首次发布以来最重要的一次更新。"

10.2 未来展望

时间线预期进展
2026 Q3主流 IDE 完成无状态 MCP 支持
2026 Q4企业级 MCP 网关产品涌现
2027 Q1MCP 成为 AI Agent 工具集成的事实标准
2027 Q2旧版有状态 API 正式移除

10.3 给开发者的建议

  1. 立即开始迁移:虽然有 12 个月的过渡期,但越早迁移越主动
  2. 优先迁移远程 Server:本地 stdio Server 可以暂缓
  3. 关注 MRTR 模式:如果你的 Server 有主动推送逻辑,需要尽快适配
  4. 拥抱扩展框架:MCP Apps 和 Tasks 是构建复杂 Agent 应用的关键
  5. 测试缓存策略:工具列表缓存可以大幅提升性能

MCP 的故事还远未结束。随着 AI Agent 生态的爆发式增长,这个协议将成为连接 AI 模型与外部世界的桥梁。而这次无状态架构的革新,为这座桥梁打下了坚实的地基。


参考资源:

  • MCP 官方规范:https://spec.modelcontextprotocol.io/2026-07-28/
  • MCP GitHub 仓库:https://github.com/modelcontextprotocol/
  • MCP Python SDK:https://github.com/modelcontextprotocol/python-sdk
  • MCP TypeScript SDK:https://github.com/modelcontextprotocol/typescript-sdk
  • Anthropic MCP 博客公告:https://modelcontextprotocol.io/blog/

推荐文章

PHP 唯一卡号生成
2024-11-18 21:24:12 +0800 CST
PHP 微信红包算法
2024-11-17 22:45:34 +0800 CST
php 连接mssql数据库
2024-11-17 05:01:41 +0800 CST
Nginx 如何防止 DDoS 攻击
2024-11-18 21:51:48 +0800 CST
程序员茄子在线接单