首页 / AI工具 / MCP 2026-07-28 大版本迁移指南:Breaking Changes 全解析与代码适配

MCP 2026-07-28 大版本迁移指南:Breaking Changes 全解析与代码适配

MCP 2026-07-28 大版本迁移指南:Breaking Changes 全解析与代码适配

2026 年 7 月 28 日,Model Context Protocol 将迎来协议诞生以来最大规模的一次修订。这不是一次可以忽视的小版本升级——核心协议层从有状态转为无状态,Session ID 从传输层彻底移除,OAuth 授权链路加严,三个核心功能进入 12 个月弃用倒计时。本文将逐项拆解每一条 Breaking Change,配合可直接运行的代码示例,帮你完成从旧版到新版的全量适配。


一、为什么这次更新不能跳过?

先说结论:现有 Server 在 7 月 28 日之后仍可运行,但你会失去生态入场券。

根据官方生命周期策略(SEP-2596),任何功能从标记弃用到实际移除,至少保留 12 个月窗口。也就是说,你基于 2025-11-25 规范写的 Server 不会在 7 月 28 日突然挂掉。但现实是——Claude 连接器目录、MCP Directory、各主流客户端的兼容性矩阵,都会在新版发布后以 2026-07-28 作为准入标准。晚迁移一天,你的 Server 在用户发现链路里的曝光就少一天。

更关键的是,这次改动的性质不同于以往的功能叠加。它是协议层的结构性重构,涉及的改动相互耦合——无状态化影响 Session 管理、请求路由、长时任务、服务端推送,几乎贯穿了远程 Server 的每一个环节。跳过这次更新,下一次迁移时你会面对多版本的累积债务。


二、Breaking Changes 逐项解析与代码适配

2.1 Session ID 移除:你的状态去哪了?(SEP-2567)

影响范围:所有依赖 Mcp-Session-Id 的代码。

旧版协议中,客户端首次请求后,服务端返回一个 Mcp-Session-Id,此后所有请求必须携带该 Header。这意味着请求被"钉"在生成该 Session 的实例上——如果你想水平扩展,就需要 sticky routing 或共享 Session 存储。

新版彻底移除了这个机制。每个请求都是自包含的,可以落在任意实例上。

旧版 Server(2025-11-25):

import { McpServer } from "@modelcontextprotocol/sdk";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { randomUUID } from "node:crypto";

const server = new McpServer({
  name: "my-doc-server",
  version: "1.0.0",
});

// 旧版:transport 层生成并管理 session
const transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: () => randomUUID(),
});

// 旧版:状态挂在 session 上
const sessionStore = new Map<string, { document: unknown }>();

server.setRequestHandler(
  CallToolRequestSchema,
  async (request) => {
    const sessionId = transport.sessionId; // 依赖 session ID
    const doc = sessionStore.get(sessionId);
    // ...处理逻辑
  }
);

新版 Server(2026-07-28):

import { McpServer } from "@modelcontextprotocol/sdk";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";

const server = new McpServer({
  name: "my-doc-server",
  version: "1.0.0",
});

// 新版:无需 session 生成器
const transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: undefined,
});

// 新版:状态通过显式句柄传递,不依赖传输层
server.tool(
  "create_document",
  "Create a new document and return a handle",
  {},
  async () => {
    const documentId = crypto.randomUUID();
    // 将文档存到外部存储(数据库、Redis 等),而非内存 session
    await storeDocument(documentId, { content: "", pages: [] });

    return {
      content: [
        {
          type: "text",
          text: JSON.stringify({ document_id: documentId }),
        },
      ],
    };
  }
);

server.tool(
  "append_page",
  "Append a page to an existing document",
  {
    document_id: z.string().describe("The document handle from create_document"),
    page_content: z.string().describe("Content of the new page"),
  },
  async ({ document_id, page_content }) => {
    // document_id 作为普通参数传入,不依赖任何 session
    const doc = await loadDocument(document_id);
    doc.pages.push(page_content);
    await storeDocument(document_id, doc);

    return {
      content: [
        {
          type: "text",
          text: `Page appended. Document now has ${doc.pages.length} pages.`,
        },
      ],
    };
  }
);

实操要点: 运行 grep -rn "Mcp-Session-Id\|sessionIdGenerator\|sessionStore" src/ 找出所有读取 session 的代码,逐一改为显式参数传递。


2.2 initialize 握手移除:协议版本和能力怎么传递?(SEP-2575)

影响范围:所有在 initialize 阶段缓存能力的代码。

旧版协议要求客户端先发送 initialize 请求,双方交换协议版本和能力声明,此后才能调用工具。新版将这个信息移到了每个请求的 _meta 字段中——每次请求自带身份。

旧版请求流程:

// 第一步:握手
POST /mcp HTTP/1.1
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-11-25",
    "capabilities": {},
    "clientInfo": { "name": "my-app", "version": "1.0" }
  }
}

// 第二步:才能调用工具,且必须携带 Session ID
POST /mcp HTTP/1.1
Mcp-Session-Id: 1868a90c-3a3f-4f5b
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": { "name": "search", "arguments": { "q": "otters" } }
}

新版请求(单次、自包含):

POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "search",
    "arguments": { "q": "otters" },
    "_meta": {
      "io.modelcontextprotocol/clientInfo": {
        "name": "my-app",
        "version": "1.0"
      }
    }
  }
}

新版中获取服务端能力的方式也变了——用 server/discover 替代握手时的能力交换:

// 新版:客户端需要了解服务端能力时,主动调用 server/discover
const discoverResponse = await client.request(
  { method: "server/discover" },
  ServerDiscoverResultSchema
);
// 返回服务端支持的能力、扩展等信息
console.log(discoverResult.capabilities);

实操要点: 运行 grep -rn "onInitialize\|InitializeRequestSchema\|initialized" src/,将所有在握手阶段缓存的数据改为从请求 _meta 中按需读取。


2.3 新增必需路由头:别让网关吃掉你的请求(SEP-2243)

影响范围:所有经过 WAF、反向代理、CDN 的远程部署。

新版要求 Streamable HTTP 传输必须携带 Mcp-MethodMcp-Name 两个 Header,用于让负载均衡器在不解析请求体的情况下完成路由。如果 Header 与 Body 中的 method/name 不一致,服务端会直接拒绝请求。

这个改动本身由 SDK 处理,但你必须确认基础设施不会过滤 Mcp-* 前缀的 Header

# 检查清单:从客户端到服务端的完整链路
# 1. CDN(如 Cloudflare)—— 自定义 Header 是否放行
# 2. WAF —— 是否有 "未知 Header 拒绝" 规则
# 3. Nginx/HAProxy —— proxy_pass_config 是否透传
# 4. Ingress Controller —— 是否有 header 限制注解

Nginx 配置示例(确保透传):

location /mcp {
    proxy_pass http://upstream_mcp;

    # 必须透传 Mcp-* 前缀的 Header
    proxy_pass_request_headers on;

    # 如果使用了 header 过滤,显式放行
    proxy_set_header Mcp-Method $http_mcp_method;
    proxy_set_header Mcp-Name $http_mcp_name;
    proxy_set_header MCP-Protocol-Version $http_mcp_protocol_version;
}

2.4 错误码 -32002 变为 -32602:一个容易忽略的测试陷阱(SEP-2164)

影响范围:所有硬编码 -32002 的服务端和客户端代码。

"资源未找到"的 JSON-RPC 错误码从 MCP 自定义的 -32002 改为标准 JSON-RPC 的 -32602(Invalid Params)。

// 旧版
throw new McpError(-32002, "Resource not found");

// 新版
throw new McpError(-32602, "Resource not found");

这个改动在测试套件中的"杀伤力"远大于生产代码。 如果你写了类似 expect(error.code).toBe(-32002) 的断言,测试会全部飘红。

# 一键排查
grep -rn "32002" src/ test/

2.5 多轮往返请求替代长连接 SSE:工具中的交互式流程怎么办?(SEP-2260, SEP-2322)

影响范围:所有通过 SSE 长连接向客户端推送消息的工具。

新版规定,服务端只能在处理客户端请求的过程中向客户端发请求,禁止通过持久 SSE 通道主动推送。需要用户输入的工具改为返回 InputRequiredResult,客户端收集答案后携带 requestState 重新发起请求。

新版多轮请求模式:

// 服务端:工具需要确认时返回 InputRequiredResult
server.tool(
  "delete_files",
  "Delete files matching a pattern",
  { pattern: z.string() },
  async ({ pattern }) => {
    const files = await glob(pattern);

    // 不再通过 SSE 推送确认请求,而是返回 InputRequiredResult
    return {
      resultType: "inputRequired",
      inputRequests: {
        confirm: {
          type: "elicitation",
          message: `确定要删除这 ${files.length} 个文件吗?`,
          schema: { type: "boolean" },
        },
      },
      requestState: Buffer.from(
        JSON.stringify({ step: "confirm", files })
      ).toString("base64"),
    };
  }
);

// 客户端收到后,收集用户输入并重新发起请求
// POST /mcp
// {
//   "method": "tools/call",
//   "params": {
//     "name": "delete_files",
//     "inputResponses": { "confirm": true },
//     "requestState": "eyJzdGVwIjoiY29uZmlybSIsImZpbGVzIjpbXX0="
//   }
// }

关键点: requestState 是 Base64 编码的服务端上下文,客户端原样回传。任何实例都可以接手处理,因为所有状态都在 payload 中。


三、MCP Apps:工具终于可以有 UI 了

这是本次更新最令人期待的新特性(SEP-1865)。以前工具只能返回文本或结构化数据,现在可以声明并返回交互式 HTML 界面,由 Host 在沙箱 iframe 中渲染。

MCP Apps 配置示例:

// 声明工具的 UI 模板
server.tool(
  "visualize_data",
  "Generate an interactive chart from data",
  {
    data: z.array(z.object({
      label: z.string(),
      value: z.number(),
    })),
    chart_type: z.enum(["bar", "line", "pie"]),
  },
  async ({ data, chart_type }) => {
    return {
      content: [
        {
          type: "text",
          text: `Generated ${chart_type} chart with ${data.length} data points`,
        },
        {
          type: "resource",
          resource: {
            uri: "mcp-app://visualize/render",
            name: "Interactive Chart",
            mimeType: "text/html",
            text: generateChartHTML(data, chart_type),
          },
        },
      ],
      // 声明该工具的 UI 模板(Host 可预取和缓存)
      _meta: {
        appTemplates: {
          "mcp-app://visualize/render": {
            templateUri: "https://your-server.com/templates/chart.html",
            cacheScope: "server",
          },
        },
      },
    };
  }
);

工作流程: 1. Server 声明工具可用的 UI 模板,Host 可预取、缓存并进行安全审查 2. 工具被调用时,返回带有 mcp-app:// URI 的资源 3. Host 在沙箱 iframe 中渲染该 UI 4. UI 通过 postMessage 与 Host 通信,所有操作走同一 JSON-RPC 审计链路


四、弃用清单:12 个月倒计时(SEP-2577)

以下三个功能在新版中标记为弃用,12 个月后(约 2027 年 7 月)可能被移除:

弃用功能 替代方案 排查命令
Roots(客户端声明文件系统根目录) 通过工具参数显式传递 grep -rn "ListRootsRequest\|roots" src/
Sampling(服务端请求客户端调用 LLM) 服务端直接调用 LLM Provider API grep -rn "createMessage\|sampling" src/
Logging(MCP 协议级日志方法) stdio 传输用 stderr;结构化可观测性用 OpenTelemetry grep -rn "sendLoggingMessage\|setLoggingLevel" src/

注意: 弃用不等于移除。现有代码在 7 月 28 日后仍然可以工作。但新项目不应再使用这些功能,已有项目建议尽早迁移。


五、完整迁移实操步骤

以下是推荐的三阶段迁移路径:

阶段一:处理硬性 Breaking Changes(7 月 28 日前)

# 步骤 1:排查 Session 依赖
grep -rn "Mcp-Session-Id\|sessionIdGenerator\|sessionStore" src/

# 步骤 2:排查 initialize 握手依赖
grep -rn "onInitialize\|InitializeRequestSchema\|initialized" src/

# 步骤 3:排查硬编码错误码
grep -rn "32002" src/ test/

# 步骤 4:升级 SDK 到支持 2026-07-28 的版本
npm install @modelcontextprotocol/sdk@latest

# 步骤 5:验证基础设施 Header 透传
curl -X POST https://your-server.com/mcp \
  -H "Content-Type: application/json" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: ping" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"ping","arguments":{}}}'

阶段二:适配新特性(7 月 28 日后逐步推进)

阶段三:清理弃用功能(12 个月内)


六、常见问题 FAQ

Q1:我现有的 MCP Server 在 7 月 28 日之后还能用吗?

能用。 根据 SEP-2596 生命周期策略,已发布的功能在弃用后至少保留 12 个月。但主流客户端(Claude、Cursor 等)会优先协商 2026-07-28,旧版 Server 可能降级到兼容模式运行,部分新特性(如 MCP Apps、缓存控制)将不可用。

Q2:无状态化是否意味着我的应用也必须无状态?

不是。 协议层无状态不等于应用层无状态。你仍然可以维护状态——只不过状态需要通过工具返回的显式句柄(如 document_idsession_token)在请求间传递,而不是依赖传输层的 Mcp-Session-Id。这种"显式句柄"模式实际上比隐式 Session 更灵活,因为模型可以跨工具组合和传递这些句柄。

Q3:Tasks API 怎么办?我基于 2025-11-25 的实验性 Tasks 写了代码。

需要迁移。 Tasks 已从核心协议移入 Extension(SEP-2663),生命周期发生了变化:tools/call 返回任务句柄,通过 tasks/gettasks/updatetasks/cancel 驱动,tasks/list 已被移除。这不是简单的搬移,而是重新设计。如果你还没开始用 Tasks,建议等 Extension 正式稳定后再接入。

Q4:我的 Server 跑在 Serverless 平台上(Vercel、Cloudflare Workers),迁移难度大吗?

反而更简单。 Serverless 本身就是"每次请求一个新实例"的模型,与无状态协议天然契合。你主要需要确认:(1) 没有在内存中跨请求保存状态;(2) 平台的网关/CDN 不拦截 Mcp-* Header。

Q5:MCP Apps 对现有工具的兼容性如何?

完全向后兼容。 MCP Apps 是纯增量特性。如果你不声明 appTemplatesmcp-app:// 资源,工具的行为与之前完全一致。你可以按需为特定工具添加 UI 能力,不需要一次性改造所有工具。

Q6:授权方面有什么需要注意的?

客户端现在会按照 RFC 9207 校验 iss 参数。服务端侧,你需要:(1) 发布准确的 RFC 9728 protected-resource metadata,确保 authServerUrls 稳定;(2) 保持 issuer 不变,因为 issuer 变更会强制所有客户端重新注册;(3) 校验 audience-bound token,而非接受授权服务器曾经签发的任何 token。


七、迁移检查清单速查


距离 7 月 28 日正式发布还有不到两周。RC 已于 5 月 21 日锁定,变更集不会再变——现在迁移,目标明确、节奏可控。等正式版发布后再动手,你就是被别人的截止日期推着走了。