核心摘要

MCP 是协议边界,不是授权系统或自主 Agent 框架。在 MCP 2026-07-28 中,每个 Request 都是 Self-describing:它携带 Protocol Version 与相关 Client Capabilities,任意兼容 Server Replica 都能独立处理,不依赖 Protocol Session。Host 使用一个或多个 Client 连接 Server,Server 则暴露 Tool、Resource 与 Prompt。应用仍然负责:

  • 认证调用者;
  • 针对精确的 Tool、Resource、Tenant 和对象执行授权;
  • 校验参数和结果大小;
  • 限制成本、并发和副作用;
  • 审计并删除敏感数据。

本文解释基础词汇和生命周期。生产安全可阅读 MCP 生产实践,远程 OAuth 边界可阅读 企业 OAuth 集成指南

为什么需要协议边界

没有共享协议时,每个 Host 都要用不同 Adapter 接入每个外部能力。MCP 不能消除应用工作,但提供了共同的生命周期和消息模型:

关注点 MCP 提供 应用仍负责
Connection stdio 与 Streamable HTTP Binding TLS、Proxy、进程和网络策略
Message JSON-RPC Framing、Per-request Metadata、Result Type 校验、限制和错误映射
Discovery server/discover 与 Capability List Method 来源、审批和清单治理
Tool 名称、描述、输入 Schema 身份、授权和副作用
Resource URI 形式的数据引用 所有权、新鲜度、分类和删除
Prompt 可复用消息模板 内容审查、注入控制和策略

互操作性不等于权限可迁移。一个 Server 可以被多个 Host 使用,但每个部署仍需要自己的授权模型。

协议角色与外部系统

Host

Host 是面向用户的应用或 Agent Runtime,通常负责:

  • 接收用户请求;
  • 选择或路由模型;
  • 管理一个或多个 Client;
  • 把发现到的能力呈现给模型;
  • 执行 Host 侧审批和展示策略。

Host 不应把 Server 的描述当成可信指令。描述、Resource 和 Result 都是跨信任边界的数据。

Client

MCP Client 是由 Host 管理的协议适配器。常见拓扑是一个 Client 对应一个 Server,以隔离 Credential、Capability Catalog、Cancellation 和故障。可以保留 Dedicated Connection,但它不是 Protocol Session、Conversation、User 或 Task。Host 也可以使用 Gateway,这会增加新的路由、身份、缓存和策略边界。

Server

Server 暴露能力并在自己的策略下执行。它可以访问文件系统、数据库、API 或进程内服务。“Server”描述的是协议角色,不保证一定是独立 OS 进程、沙箱或安全边界。

External System

数据库、文件存储、SaaS API 或本地操作系统才是业务数据的权威。MCP Server 是 Adapter 和策略执行点,不能替代外部系统的访问控制。

无状态请求与 Discovery

MCP 2026-07-28 没有 initialize / notifications/initialized 握手,也没有 Mcp-Session-Id。每个 Request 都在 _meta 中携带选定 Revision 和相关 Client Capabilities。clientInfo 可用于展示和调试,但它是 Self-reported Metadata,不能充当认证身份。

sequenceDiagram participant H as "Host" participant C as "MCP Client" participant S as "MCP Server" H->>C: 创建连接 C->>S: server/discover + Per-request Metadata S-->>C: Supported Version + Capability + Cache Hint C->>S: tools/list 或其他独立 Request S-->>C: resultType complete + 有界 Result C->>S: 使用新 Request ID 调用 tools/call S-->>C: complete、input_required 或 Error C-->>H: 有界 Observation

每个 Server 都必须实现 server/discover,但 Client 也可以先调用其他 Method,并处理 UnsupportedProtocolVersionError-32022)。Discovery 返回支持版本、Server Capabilities、Identity Metadata 与 Cache Hint,只能证明协议兼容,不能证明 Endpoint 来源、可信度或权限。

精简后的 Discovery Request 展示了 Self-describing Contract:

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "server/discover",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "acme-host",
        "version": "4.2.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {
        "elicitation": {}
      }
    }
  }
}

Capability Discovery 不是权限授予。列表可以按当前 Request 携带的 Credential 变化,但不能因其他 Request 复用了同一 Connection 而变化。可靠 Client 还应处理:

  • Protocol Version 不匹配;
  • Discovery 超时与过期 Cache Hint;
  • 畸形或超大消息;
  • 取消和断连;
  • 已订阅的 Capability 变化;
  • Server 关闭与 Stream 重连;
  • 重复请求和幂等。

JSON-RPC 边界

MCP 使用 JSON-RPC 的请求、响应、通知和错误概念。Request ID 用于关联响应,不负责认证请求。

typescript
type RequestId = string | number;

type JsonRpcRequest = {
  jsonrpc: "2.0";
  id: RequestId;
  method: string;
  params?: {
    _meta: {
      "io.modelcontextprotocol/protocolVersion": string;
      "io.modelcontextprotocol/clientCapabilities": Record<string, unknown>;
    };
    [key: string]: unknown;
  };
};

type JsonRpcError = {
  code: number;
  message: string;
  data?: unknown;
};

function requireRequest(value: unknown): JsonRpcRequest {
  if (!value || typeof value !== "object") throw new Error("invalid_request");
  const request = value as Record<string, unknown>;
  if (request.jsonrpc !== "2.0" ||
      (typeof request.id !== "string" && typeof request.id !== "number") ||
      typeof request.method !== "string") {
    throw new Error("invalid_request");
  }
  if (request.params !== undefined &&
      (!request.params || typeof request.params !== "object")) {
    throw new Error("invalid_params");
  }
  return request as unknown as JsonRpcRequest;
}

这段代码只校验部分 Framing。真实实现还要校验固定版本的 Protocol Schema、非 Null 且唯一的 Request ID、必需 _meta、Result resultType、Method-specific Data、Byte / Depth Limit 与 Extension Negotiation。JSON Schema 未声明时默认 Draft 2020-12;默认不得解析网络 $ref,复杂 Composition 也需要资源上限。

三类核心能力

Tools

Tool 是 Model-controlled Operation,Client 可以请求 Server 执行。Definition 包含 Name、Description、必需 inputSchema、可选 outputSchema 与不可信 Annotation。tools/list 支持分页与缓存;tools/call 可返回 Text、Media、Resource Link、Embedded Resource 或经过 Schema 校验的 structuredContent。模型发出的调用应视为提议:

text
模型提出 Tool Call
    -> Host/Client 执行审批和预算策略
    -> Server 认证并授权
    -> Server 校验参数
    -> Server 执行操作
    -> Server 返回有界 Result

好的 Tool 应明确:

  • 做什么以及不做什么;
  • 输入限制和默认值;
  • 读写行为;
  • Resource 与 Tenant 范围;
  • 超时、重试、幂等和输出限制。

除非整个能力已被有意沙箱化并授权,否则不要暴露通用 run_command、任意 SQL、无限制 HTTP Client 或“管理一切”的 Tool。严格 Schema 不能让危险能力自动安全。

Resources

Resource 是 Application-controlled、由 URI 标识的数据。resources/listresources/templates/listresources/read 分别负责发现或读取;RFC 6570 Template 描述参数化 URI Space。URI 是标识符,不是读取权限证明。Server 必须执行 Scheme 校验、Tenant / Object Authorization、Freshness、MIME、Byte、Decompression、Path 与 Egress Limit。

Resource Link 可以避免把大 Artifact 嵌入 Tool Result,但后续读取必须重新鉴权。不要假定可信 Server 返回的 Resource 适合直接放入 Prompt,它可能包含指令或敏感数据。

Prompts

Prompt 是由 Server 编写、由用户选择使用的模板。prompts/list 返回 Descriptor 与 String Argument Metadata,prompts/get 渲染 userassistant Message。获取 Prompt 不会调用模型,Assistant-role Message 也不会变成 System Instruction。Prompt Content、Argument 与链接或嵌入的 Resource 都是不可信数据。

Primitive 主要控制约定 核心 Method 安全边界
Tool Model 提议 tools/listtools/call 校验 Schema、领域规则、授权、副作用与输出
Resource Application 选择 resources/listresources/templates/listresources/read 授权 URI / Object,限制 Byte、MIME、Freshness、Traversal 与 SSRF
Prompt User 选择 prompts/listprompts/get 校验 Argument 与内容,保留 Provenance,防御 Prompt Injection

List 与 Resource Read Result 携带 ttlMscacheScope。只有所有调用方得到相同且可共享的内容才可使用 public;按 User、Tenant、Role 或 Token 过滤的结果必须使用 private,并让 Cache Key 绑定 Authorization Context。Notification 只能让 Cache 失效,不能授予权限。

Result、MRTR 与 Notification

每个成功的 2026-07-28 Result 都声明 resultType"complete" 表示最终结果;"input_required" 表示符合条件的 prompts/getresources/readtools/call 需要额外 Client Input 才能完成。

json
{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "resultType": "input_required",
    "inputRequests": {
      "approval": {
        "method": "elicitation/create",
        "params": {
          "mode": "form",
          "message": "是否批准发布这份报告?",
          "requestedSchema": {
            "type": "object",
            "properties": { "approved": { "type": "boolean" } },
            "required": ["approved"]
          }
        }
      }
    },
    "requestState": "opaque-integrity-protected-state"
  }
}

MRTR 不是 Server-initiated JSON-RPC Request。初始 Request 已结束;Client 只收集自己支持的输入,再使用新的 JSON-RPC ID、inputResponses 和原样不透明 requestState 重试原 Method。Server 按攻击者可控输入处理 State,把它绑定 Principal、Method、关键参数、Policy Revision 与短 Expiry;若 Replay 可能重复副作用,还要保证单次消费。

长生命周期变更事件使用 subscriptions/listen。Client 选择 toolsListChangedpromptsListChangedresourcesListChanged 或具体 Resource Subscription 等 Filter;Server 确认接受的子集,并用 Subscription ID 标记每个 Notification。断线后创建新 Subscription;Event 可以让 Cache 失效,但不能携带 Permission。

Transport 选择

stdio

Host 启动本地进程时可使用 stdio。仍需审查继承的环境变量、文件系统、网络外发、Shell、OS 用户和进程生命周期。本地进程并不天然沙箱化。

Streamable HTTP

面向 2026-07-28 的新远程部署应使用 Streamable HTTP。每条消息都是发往单一 MCP Endpoint 的独立 POST;Request 的响应是单个 JSON Object,或只承载相关 Notification 与最终 Response 的 Request-scoped SSE Stream。关闭该 Stream 表示取消,但不能证明下游副作用已回滚。

每个 POST 都包含 MCP-Protocol-VersionMcp-Methodtools/callresources/readprompts/get 还包含 Mcp-Name。JSON-RPC Body 是权威数据,Server 必须拒绝 Header / Body 不一致。还应校验 Origin、认证每个 Request、为 SSE 关闭 Proxy Buffering,并禁止把 Secret 放进 Mcp-Param-* Header。

http
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json,text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: reports.read

{"jsonrpc":"2.0","id":42,"method":"tools/call","params":{"name":"reports.read","arguments":{"reportId":"rpt_72"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}

旧式 SSE 兼容

MCP 2025-11-25 及更早版本使用 Connection-scoped Initialization,可能包含 Mcp-Session-Id,并允许 Server-initiated Request。2024-11-05 HTTP+SSE Transport 则使用独立 SSE Endpoint 与 POST Message Endpoint。若必须兼容:

  • 隔离并标记兼容路由;
  • 按目标 Revision 实施 Initialize、Session 与 Message Direction;
  • 对 SSE 和每个消息请求执行认证;
  • 限制事件队列和结果大小;
  • 明确顺序、取消、重连和重复行为;
  • 通过真实 Proxy 路径测试。

不得把 Legacy Capability State 泄漏到现代路径,也不要把旧式 SSE 称为当前 Streaming Model。

HTTP 授权边界

Authorization 在 MCP 中是可选能力,但受保护的 Streamable HTTP Server 扮演 OAuth Resource Server,Client 扮演 OAuth Client。Client 发现 Protected Resource 与 Authorization Server Metadata,通过 Client ID Metadata Document 或预注册取得 Client ID,使用 PKCE 与 State,校验 Authorization Response Issuer,并在 Authorization 与 Token Request 中携带 RFC 8707 Resource Indicator。

Server 在每个 Request 上校验 Token Issuer、Audience、Expiry、Signature 与 Challenged Scope。Token 只能通过 Authorization: Bearer Header 传输,不能进入 URI。协议禁止 Token Passthrough:Server 不得接受或转发签发给其他 Resource 的 Token。

OAuth Scope 只是粗粒度门禁。Server 仍需授权具体 Principal、Tenant、Tool / Resource、Object、Argument、Purpose 与 Side Effect。Credential 缺失或无效使用 401;已认证但缺少当前操作 Scope 时,使用带 insufficient_scope Challenge 的 403。HTTP Profile 不适用于 stdio;本地 Server 从受限执行环境获取 Credential。

安全模型:MCP 不保证什么

MCP 标准化通信,但不保证:

  • 用户认证;
  • 租户隔离;
  • 对象所有权;
  • Tool 行为安全;
  • Prompt Injection 抵抗能力;
  • Transport 或 Telemetry 机密性;
  • 缓存和日志中复制数据的删除。

远程 Server 至少需要:

  1. 可信 Token 或 Workload 认证;
  2. Issuer、Audience/Resource、Expiry、Algorithm 和 Key Rotation 校验;
  3. 逐调用 Scope 和对象级授权;
  4. 请求、结果、并发、超时和成本限制;
  5. 副作用的幂等与取消;
  6. 脱敏审计事件和 Trace Context;
  7. 跨租户、Tool Result Injection 和重放测试。

Read-only 或 Destructive 等 Annotation 可以帮助 Host 展示风险,但 Server 必须独立验证真实行为并执行策略。

最小 Server 设计

把协议适配、Policy 和业务逻辑分开:

text
Transport Adapter
  -> JSON-RPC 与 Method 校验
  -> Principal 与 Tenant Context
  -> Tool/Resource Policy
  -> Domain Service
  -> 有界 Result 与 Audit Event

Domain Service 应接收可信 Context,而不是从模型参数推断身份:

typescript
type ExecutionContext = {
  requestId: string;
  principalId: string;
  tenantId: string;
  scopes: ReadonlySet<string>;
};

type ReportRequest = {
  reportId: string;
};

async function getReport(
  context: ExecutionContext,
  request: ReportRequest,
  authorize: (context: ExecutionContext, reportId: string) => Promise<boolean>,
  repository: { read: (tenantId: string, reportId: string) => Promise<unknown> },
) {
  if (!context.scopes.has("reports.read")) throw new Error("insufficient_scope");
  if (!(await authorize(context, request.reportId))) throw new Error("access_denied");
  const report = await repository.read(context.tenantId, request.reportId);
  return { status: "ok", report };
}

这是核心片段,不是完整 SDK 接入。实际应用还要增加 Repository 级 Tenant 约束、结果脱敏、大小限制、Trace、错误映射和取消。

测试与运维

从五层测试契约:

层级 示例
Protocol server/discover、Per-request _meta、版本不匹配、Result Type、畸形 JSON-RPC
Policy 缺少 Scope、错误 Tenant、对象所有权、过期 Token
Reliability 超时、取消、SSE 中断、Subscription 重连、队列溢出、重复副作用
Abuse Tool / Prompt / Resource 注入、超大 Schema、SSRF、路径穿越、Token Passthrough
Compatibility Modern-to-modern、Modern Probe Legacy、Legacy Fallback、不支持的 Revision

观测低基数事件:

  • Protocol 和 SDK 版本;
  • Request、Subscription 和 Trace 标识;
  • Method、稳定 Server Identity 和 Capability Revision;
  • 参数摘要、结果类别和字节数;
  • Policy、Approval、Cache 和 MRTR Decision;
  • Queue / Upstream Latency、Retry、Cancellation、Budget 与最终 Effect Status。

默认不要保存原始 Token、私有 Prompt、完整 Tool Result 或隐藏推理。

如何选择 SDK

选择覆盖目标版本、Transport、取消、Capability 协商、授权 Hook 和安全更新的维护中 SDK。手写 Adapter 可以用于学习或狭窄的兼容边界,但会产生长期一致性和事故响应责任。

评估 SDK 时检查:

  • Protocol 一致性测试;
  • 依赖和发布历史;
  • Transport 与 Proxy 行为;
  • 错误和取消语义;
  • 授权和脱敏扩展点;
  • 观测和删除支持;
  • 升级和回滚策略。

生产检查清单

  • [ ] 固定 MCP 版本和 SDK。
  • [ ] 记录 Host、Client、Server、外部系统和信任边界。
  • [ ] 每个 Request 携带 Version 与 Client Capabilities,并支持或探测 server/discover
  • [ ] 使用 stdio 或当前 Streamable HTTP,隔离 Legacy Initialize、Session、GET Stream 与 HTTP+SSE。
  • [ ] 校验 JSON-RPC Framing、Result Type、Routing Header 与 Method-specific Schema。
  • [ ] 对每个操作执行认证和授权。
  • [ ] 将 Resource 和 Tool 绑定到 Tenant 与对象策略。
  • [ ] 按 Server、Revision、Authorization Context 与 cacheScope 隔离缓存。
  • [ ] 保护 MRTR requestState,把 Sampling 与 Roots 作为 Deprecated 迁移路径。
  • [ ] 限制请求、结果、队列、并发、超时和成本。
  • [ ] 定义 Cancellation、Subscription Reconnect、Duplicate Effect 与 Shutdown 行为。
  • [ ] 将描述、Prompt、Resource 和 Result 视为不可信数据。
  • [ ] 脱敏 Telemetry,并测试删除传播。

常见问题

MCP 是什么?

它是 Host 连接面向模型的 Client 与暴露能力的 Server 的开放协议。它标准化生命周期和消息,但不标准化业务授权。

Host、Client、Server 有什么区别?

Host 协调用户和模型,Client 为一个 Server 适配协议,Server 在自己的策略下声明并执行能力。Connection 是 Transport Resource,不是 Protocol Session 或认证身份。

MCP Tool 是 Function Calling 吗?

两者在结构化调用边界上有交集,但 MCP 还定义跨进程发现、生命周期、Transport 和结果交换。Server 仍要校验和授权每次调用。

新的远程 Server 应该使用什么 Transport?

所有 Peer 支持时,使用 2026-07-28 Streamable HTTP:独立 POST Request 与 JSON 或 Request-scoped SSE Response。旧 Streamable HTTP 与 HTTP+SSE 应放在明确的兼容路由。

MCP Sampling 仍适合新实现吗?

不适合。MCP Sampling 在弃用窗口内仍可运行,但新实现应直接接入 LLM Provider API。现有 2026-07-28 兼容路径使用 MRTR,不再发送未经请求的 Server JSON-RPC Request。

总结

MCP 2026-07-28 是 Stateless Context-exchange Protocol:每个 Request 声明 Revision 与 Client Capabilities,Server 提供可发现的 Tool、Resource 与 Prompt,需要多次交互的流程使用显式 MRTR 或 Subscription。无状态化简化了水平扩展,却不会自动建立信任。应固定 Revision、校验每个边界、保留 Server Authorization、隔离 Legacy 行为,并把所有 Descriptor 与 Payload 视为不可信输入。

一手来源