MCP(Model Context Protocol)协议自 2024 年 11 月由 Anthropic 首次发布以来,已经历多次重大迭代。2025-03-26 版本是迄今为止变动最大的一次更新,它从认证安全、传输架构、工具元数据三个维度对协议进行了全面升级。如果你正在构建或维护 MCP Server,这次更新值得你认真对待。

本文将深入解读这些变更的技术细节和设计动机,并提供可运行的代码示例帮助你快速落地。

核心要点

  • OAuth 2.1 强制升级:废弃隐式授权流,强制 PKCE + HTTPS,支持动态客户端注册
  • Streamable HTTP 取代 SSE:单端点设计替代双通道架构,支持双向通信和断线恢复
  • Tool Annotations:为每个工具声明行为元数据(只读/破坏性/幂等/开放域),驱动智能权限控制
  • JSON-RPC 批处理:协议级强制支持,大幅降低网络开销
  • 会话管理Mcp-Session-Id 头实现有状态连接和断点续传
  • 生态爆发:Claude、ChatGPT、VS Code、Cursor 等主流平台全面支持

想要发现更多 MCP Server?访问 MCP 导航页面 快速浏览生态全貌。

MCP 规范演进时间线

MCP 协议自诞生以来经历了四个重要版本:

版本 发布日期 核心变更
2024-11-05 2024年11月 初始版本,定义 Tools/Resources/Prompts 三大原语,HTTP+SSE 传输
2025-03-26 2025年3月 OAuth 2.1、Streamable HTTP、Tool Annotations、JSON-RPC 批处理
2025-06-18 2025年6月 Elicitation(服务端请求用户输入)、安全最佳实践文档、结构化输出
2025-11-25 2025年11月 进一步安全增强、协议稳定性改进

其中 2025-03-26 版本是最具里程碑意义的一次迭代,它将 MCP 从"本地开发工具协议"推向了"企业级生产就绪协议"的新阶段。

graph LR V1["2024-11-05 初始版本"] -->|"4个月"| V2["2025-03-26 OAuth + Streamable HTTP"] V2 -->|"3个月"| V3["2025-06-18 Elicitation + 安全增强"] V3 -->|"5个月"| V4["2025-11-25 协议稳定化"] style V2 fill:#e6f3ff,stroke:#0066cc,stroke-width:3px

如果你还不熟悉 MCP 的基础概念,建议先阅读 MCP 协议深度解析 了解核心架构。

OAuth 2.1 认证授权框架

为什么 MCP 需要认证?

在 MCP 的初始版本中,Server 大多通过 stdio 在本地运行,本地进程天然拥有当前用户的权限,无需额外认证。然而,当 MCP Server 走向远程部署,认证就成了不可回避的问题:

  • 权限隔离:不同用户访问不同数据,远程 Server 必须识别调用者身份
  • 工具安全:具有破坏性的工具(如数据库删除)不能被未授权的 AI Agent 随意调用
  • 合规要求:企业环境中对 API 访问的审计追溯是基本要求

OAuth 2.0 到 2.1 的关键升级

MCP 2025-03-26 版本直接采用了 OAuth 2.1 标准,相比旧版 OAuth 2.0 有三个核心变化:

变更项 OAuth 2.0 OAuth 2.1
隐式授权流 支持 完全废弃
PKCE 可选 强制要求
HTTPS 推荐 强制要求

**PKCE(Proof Key for Code Exchange)**是这次升级的核心。它通过密码学挑战-应答机制,彻底消除了授权码在传输过程中被截获的中间人攻击风险。

OAuth 认证流程

以下是 MCP 中 OAuth 2.1 + PKCE 的完整授权流程:

sequenceDiagram participant C as "MCP Client" participant S as "MCP Server" participant A as "Authorization Server" participant U as "User" C->>S: GET /.well-known/oauth-authorization-server S-->>C: 返回授权服务器元数据 C->>A: POST /register (动态客户端注册) A-->>C: 返回 client_id C->>C: 生成 code_verifier + code_challenge C->>U: 打开浏览器授权页 U->>A: 授权确认 A-->>C: 返回 authorization_code C->>A: POST /token (code + code_verifier) A-->>C: 返回 access_token + refresh_token C->>S: MCP请求 + Authorization: Bearer token S-->>C: 返回工具调用结果

动态客户端注册

MCP 规范要求支持 RFC 7591 动态客户端注册,这意味着 MCP Client 不需要预先在 Server 端手动配置——连接时自动注册即可获得凭证。这对 AI 工具生态尤为重要:

json
POST /register HTTP/1.1
Content-Type: application/json

{
  "client_name": "My AI Assistant",
  "redirect_uris": ["http://localhost:3000/callback"],
  "grant_types": ["authorization_code"],
  "token_endpoint_auth_method": "none"
}

响应中返回 client_id,后续授权流程使用该 ID 进行身份标识。

Node.js 实现:带 OAuth 认证的 MCP Server

下面演示如何用 Node.js 构建一个符合 2025-03-26 规范的 MCP Server,集成 OAuth 2.1 认证。如果你在调试过程中需要检查 JWT Token 的结构,可以使用 JWT 生成与解析工具

javascript
import express from 'express';
import crypto from 'crypto';
import jwt from 'jsonwebtoken';

const app = express();
app.use(express.json());

// --- OAuth 2.1 端点 ---

// 授权服务器元数据发现
app.get('/.well-known/oauth-authorization-server', (req, res) => {
  res.json({
    issuer: 'https://mcp.example.com',
    authorization_endpoint: 'https://mcp.example.com/authorize',
    token_endpoint: 'https://mcp.example.com/token',
    registration_endpoint: 'https://mcp.example.com/register',
    response_types_supported: ['code'],
    code_challenge_methods_supported: ['S256'],
    grant_types_supported: ['authorization_code', 'refresh_token']
  });
});

// 动态客户端注册 (RFC 7591)
const clients = new Map();
app.post('/register', (req, res) => {
  const clientId = crypto.randomUUID();
  clients.set(clientId, {
    client_name: req.body.client_name,
    redirect_uris: req.body.redirect_uris,
    created_at: Date.now()
  });
  res.status(201).json({ client_id: clientId });
});

// Token 端点 (验证 PKCE)
app.post('/token', (req, res) => {
  const { code, code_verifier, client_id } = req.body;
  const stored = authCodes.get(code);
  if (!stored) return res.status(400).json({ error: 'invalid_grant' });

  // 验证 PKCE: SHA256(code_verifier) === code_challenge
  const challenge = crypto
    .createHash('sha256')
    .update(code_verifier)
    .digest('base64url');
  if (challenge !== stored.code_challenge) {
    return res.status(400).json({ error: 'invalid_grant' });
  }

  const accessToken = jwt.sign(
    { sub: stored.user_id, client_id, scope: stored.scope },
    process.env.JWT_SECRET,
    { expiresIn: '15m' }
  );
  const refreshToken = crypto.randomUUID();

  res.json({
    access_token: accessToken,
    token_type: 'Bearer',
    expires_in: 900,
    refresh_token: refreshToken
  });
});

// --- MCP Streamable HTTP 端点 ---
app.post('/mcp', authenticateToken, (req, res) => {
  // 处理 JSON-RPC 请求(含批处理)
  const body = req.body;
  if (Array.isArray(body)) {
    // 批处理模式
    const results = body.map(msg => handleJsonRpc(msg, req.user));
    res.json(results.filter(r => r !== null));
  } else {
    const result = handleJsonRpc(body, req.user);
    result ? res.json(result) : res.status(202).end();
  }
});

function authenticateToken(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'unauthorized' });
  try {
    req.user = jwt.verify(token, process.env.JWT_SECRET);
    next();
  } catch {
    res.status(403).json({ error: 'invalid_token' });
  }
}

Python 实现:OAuth PKCE 客户端

python
import hashlib
import base64
import os
import httpx

class MCPOAuthClient:
    def __init__(self, server_url: str):
        self.server_url = server_url
        self.client_id = None
        self.access_token = None

    async def discover(self):
        """发现授权服务器元数据"""
        async with httpx.AsyncClient() as client:
            resp = await client.get(
                f"{self.server_url}/.well-known/oauth-authorization-server"
            )
            return resp.json()

    async def register(self):
        """动态客户端注册"""
        async with httpx.AsyncClient() as client:
            resp = await client.post(
                f"{self.server_url}/register",
                json={
                    "client_name": "Python MCP Client",
                    "redirect_uris": ["http://localhost:8080/callback"],
                    "grant_types": ["authorization_code"]
                }
            )
            self.client_id = resp.json()["client_id"]

    def generate_pkce(self) -> tuple[str, str]:
        """生成 PKCE code_verifier 和 code_challenge"""
        code_verifier = base64.urlsafe_b64encode(
            os.urandom(32)
        ).decode('utf-8').rstrip('=')
        code_challenge = base64.urlsafe_b64encode(
            hashlib.sha256(code_verifier.encode()).digest()
        ).decode('utf-8').rstrip('=')
        return code_verifier, code_challenge

    async def exchange_token(self, code: str, code_verifier: str):
        """使用授权码 + code_verifier 换取 Access Token"""
        async with httpx.AsyncClient() as client:
            resp = await client.post(
                f"{self.server_url}/token",
                data={
                    "grant_type": "authorization_code",
                    "code": code,
                    "code_verifier": code_verifier,
                    "client_id": self.client_id
                }
            )
            tokens = resp.json()
            self.access_token = tokens["access_token"]
            return tokens

    async def call_tool(self, tool_name: str, arguments: dict):
        """调用 MCP 工具"""
        async with httpx.AsyncClient() as client:
            resp = await client.post(
                f"{self.server_url}/mcp",
                headers={"Authorization": f"Bearer {self.access_token}"},
                json={
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/call",
                    "params": {
                        "name": tool_name,
                        "arguments": arguments
                    }
                }
            )
            return resp.json()

Streamable HTTP:传输层架构革新

从双端点到单端点

旧版 MCP(2024-11-05)采用 HTTP+SSE 双通道方案:GET /sse 建立 Server→Client 的推送通道,POST /message 接收 Client→Server 的 JSON-RPC 请求。这种设计存在明显的工程痛点——如果你曾经实现过这套方案(参考 用 Go 从零实现 SSE 传输层),你一定对双连接管理的复杂性深有体会。

2025-03-26 版本引入 Streamable HTTP 彻底重构了传输层:

对比维度 旧版 HTTP+SSE Streamable HTTP
端点数量 2个(GET /sse + POST /message) 1个(POST /mcp)
通信方向 单向推送(SSE)+ 请求-响应(POST) 双向通信
断线恢复 需重建完整会话 支持 Last-Event-ID 断点续传
简单请求 被强制走流式传输 可直接返回 JSON 响应
连接建立时间 约 320ms 约 180ms(降低 44%)

核心工作机制

Streamable HTTP 的智能之处在于协议协商——Client 通过 Accept 头声明自己的能力,Server 据此选择响应方式:

http
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
Mcp-Session-Id: sess_abc123

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "long_running_task", "arguments": {} }
}

Server 可以选择两种响应策略:

策略 1:简单 JSON 响应(短任务)

http
HTTP/1.1 200 OK
Content-Type: application/json

{"jsonrpc": "2.0", "id": 1, "result": {"content": [{"type": "text", "text": "done"}]}}

策略 2:SSE 流式响应(长任务)

http
HTTP/1.1 200 OK
Content-Type: text/event-stream

event: message
data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progress":50,"message":"处理中..."}}

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"完成"}]}}

会话管理与断线恢复

新增的 Mcp-Session-Id HTTP 头为长任务场景带来了关键的可靠性保障。当网络断开后重新连接时,Client 携带之前的 Session ID 和 Last-Event-ID,Server 可以从断点恢复而无需重新初始化:

javascript
// 客户端断线重连逻辑
async function reconnect(sessionId, lastEventId) {
  const response = await fetch('/mcp', {
    method: 'GET',
    headers: {
      'Accept': 'text/event-stream',
      'Mcp-Session-Id': sessionId,
      'Last-Event-ID': lastEventId
    }
  });
  // Server 从 lastEventId 之后继续推送事件
  const reader = response.body.getReader();
  // ... 处理 SSE 事件流
}

关于高并发场景下的连接管理策略,可以参考 高并发 MCP Gateway 架构设计

Tool Annotations:工具行为元数据

为什么需要 Tool Annotations?

在 MCP 初始版本中,工具只有 namedescriptioninputSchema 三个字段。Client(或 LLM)只能通过文本描述来"猜测"一个工具会做什么——这在涉及破坏性操作时非常危险。

2025-03-26 版本引入 Tool Annotations 机制,允许 Server 为每个工具声明结构化的行为元数据:

typescript
interface ToolAnnotations {
  title?: string;              // 语义化标题(UI展示用)
  readOnlyHint?: boolean;      // 是否只读(默认 false)
  destructiveHint?: boolean;   // 是否有破坏性(默认 true)
  idempotentHint?: boolean;    // 是否幂等(默认 false)
  openWorldHint?: boolean;     // 是否与外部交互(默认 true)
}

四个 Hint 字段详解

Hint 字段 默认值 含义 典型场景
readOnlyHint false 工具不会修改任何外部状态 数据库查询、文件读取
destructiveHint true 工具可能造成不可逆的破坏 删除记录、重置配置
idempotentHint false 相同参数多次调用效果一致 更新操作(PUT语义)
openWorldHint true 工具会与外部世界交互 发送邮件、调用第三方API

重要提示:所有 Hint 字段都是"建议性"的,Client 不应将其作为安全控制的唯一依据。实际的权限校验必须由 Server 端的 RBAC 引擎执行。

实际应用示例

json
{
  "tools": [
    {
      "name": "query_database",
      "description": "Execute a read-only SQL query",
      "inputSchema": {
        "type": "object",
        "properties": {
          "sql": { "type": "string" }
        }
      },
      "annotations": {
        "title": "Database Query",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "delete_user",
      "description": "Permanently delete a user account",
      "inputSchema": {
        "type": "object",
        "properties": {
          "user_id": { "type": "string" }
        }
      },
      "annotations": {
        "title": "Delete User Account",
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "send_email",
      "description": "Send an email to the specified recipient",
      "inputSchema": {
        "type": "object",
        "properties": {
          "to": { "type": "string" },
          "subject": { "type": "string" },
          "body": { "type": "string" }
        }
      },
      "annotations": {
        "title": "Send Email",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": true
      }
    }
  ]
}

Client 可以基于这些 Annotations 实现智能化的 UI 交互——例如,对 destructiveHint: true 的工具自动弹出二次确认对话框,对 readOnlyHint: true 的工具跳过确认直接执行。这与 Function CallingTool Use 的安全理念一脉相承。

JSON-RPC 批处理与进度通知

协议级批处理支持

2025-03-26 版本明确规定:所有 MCP 实现必须支持 JSON-RPC 2.0 批处理规范。这意味着 Client 可以在单个 HTTP 请求中发送多个 JSON-RPC 调用,大幅减少网络开销。

json
[
  {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "analyze_text", "arguments": {"text": "hello"}}},
  {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "translate", "arguments": {"text": "hello", "target": "zh"}}},
  {"jsonrpc": "2.0", "method": "notifications/initialized"}
]

批处理的性能优势非常显著:

指标 单请求模式 批处理模式 优化幅度
TCP 握手次数(100个请求) 100 1 99%
Header 总开销 ~150KB ~2KB 98.7%
3G 网络总耗时 12.3s 1.8s 85.4%

增强的进度通知

新版进度通知增加了 message 字段,支持语义化的状态描述:

json
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "task_123",
    "progress": 65,
    "total": 100,
    "message": "数据清洗中:已处理 12000/20000 条记录"
  }
}

这让用户界面可以展示更有意义的进度信息,而不仅仅是一个百分比数字。

生态支持与 MCP Registry

主流平台支持现状

截至 2026 年,MCP 协议已获得全面的生态支持:

平台 支持状态 协议版本 备注
Claude Desktop ✅ 原生支持 2025-03-26+ Anthropic 官方客户端,支持最全面
ChatGPT ✅ 已支持 2025-03-26+ 通过 Plugin/Actions 集成
VS Code / Copilot ✅ 已支持 2025-03-26+ GitHub Copilot Agent 模式
Cursor ✅ 已支持 2025-03-26+ 深度集成,支持自定义 MCP Server
Windsurf ✅ 已支持 2025-03-26+ Codeium 的 AI IDE

你可以在 AI 工具导航 中发现更多支持 MCP 的 AI 客户端和应用。

GitHub MCP Registry

2025 年 9 月,GitHub 上线了官方 MCP Registry 预览版——这是 MCP Server 的"应用商店",为生态系统提供了一个中心化的发现与分发平台:

  • 权威索引:公开 MCP Server 的单一可信数据源
  • 社区驱动:由 Anthropic、GitHub、Microsoft 等核心贡献者维护
  • 客户端集成:VS Code 等工具可以直接从 Registry 安装 MCP Server
  • 质量保证:提供 Server 的信誉评分和安全审计信息

对于 Server 开发者而言,将你的 MCP Server 发布到 Registry 可以显著提升可发现性。你可以通过 JSON 格式化工具 来检查和美化你的 Registry 配置文件。

从旧版迁移到新规范

迁移检查清单

如果你正在维护基于 2024-11-05 版本的 MCP Server,以下是迁移到 2025-03-26 版本的完整路线:

第 1 步:传输层升级(优先级最高)

javascript
// ❌ 旧版:双端点 HTTP+SSE
app.get('/sse', handleSSE);
app.post('/message', handleMessage);

// ✅ 新版:单端点 Streamable HTTP
app.post('/mcp', handleStreamableHTTP);
app.get('/mcp', handleSSEStream); // 可选:用于服务端主动推送

第 2 步:实现 OAuth 2.1 认证

javascript
// 新增元数据发现端点
app.get('/.well-known/oauth-authorization-server', (req, res) => {
  res.json({ /* 授权服务器配置 */ });
});

// 新增动态客户端注册
app.post('/register', handleDynamicRegistration);

// 所有 MCP 端点添加认证中间件
app.post('/mcp', authenticateOAuth, handleMCP);

第 3 步:添加 Tool Annotations

javascript
// ❌ 旧版:仅有基础描述
{ name: 'delete_file', description: 'Delete a file' }

// ✅ 新版:包含行为元数据
{
  name: 'delete_file',
  description: 'Delete a file',
  annotations: {
    title: 'Delete File',
    readOnlyHint: false,
    destructiveHint: true,
    idempotentHint: false,
    openWorldHint: false
  }
}

第 4 步:支持 JSON-RPC 批处理和会话管理

javascript
app.post('/mcp', (req, res) => {
  // 处理 Mcp-Session-Id
  const sessionId = req.headers['mcp-session-id'] || generateSessionId();
  res.setHeader('Mcp-Session-Id', sessionId);

  // 支持批处理
  if (Array.isArray(req.body)) {
    const results = req.body.map(msg => processMessage(msg, sessionId));
    return res.json(results.filter(Boolean));
  }
  // 单消息处理
  const result = processMessage(req.body, sessionId);
  result ? res.json(result) : res.status(202).end();
});

向后兼容性建议

MCP 规范建议 Server 在过渡期间同时支持新旧传输方式,通过请求头进行版本协商。旧版 Client 发送的请求会自动被路由到兼容模式。关于 MCP 协议的高阶实战技巧,可以参考 MCP 协议高阶实战指南

总结

MCP 2025-03-26 版本标志着协议从"可用"迈向"可靠"的关键一步。OAuth 2.1 解决了远程部署的认证难题,Streamable HTTP 简化了传输架构,Tool Annotations 让工具调用更加安全可控。对于正在构建 AI 应用的开发者而言,及时跟进这些规范变更,将帮助你构建更安全、更高效、更易维护的 MCP 集成方案。

如果你正在寻找可以直接使用的 MCP Server,或者想要发布自己的 Server,建议访问 MCP Server 导航 浏览完整的生态。


本文所属专栏MCP 协议精通之路 — 从入门到企业级实战的完整学习路径。