核心摘要
MCP 是协议边界,不是授权系统,也不是自主 Agent 框架。Host 使用一个或多个 Client 连接 Server;Server 声明能力、接收 JSON-RPC 消息并返回结果。应用仍然负责:
- 认证调用者;
- 针对精确的 Tool、Resource、Tenant 和对象执行授权;
- 校验参数和结果大小;
- 限制成本、并发和副作用;
- 审计并删除敏感数据。
本文解释基础词汇和生命周期。生产安全可阅读 MCP 生产实践,远程 OAuth 边界可阅读 企业 OAuth 集成指南。
为什么需要协议边界
没有共享协议时,每个 Host 都要用不同 Adapter 接入每个外部能力。MCP 不能消除应用工作,但提供了共同的生命周期和消息模型:
| 关注点 | MCP 提供 | 应用仍负责 |
|---|---|---|
| Connection | 生命周期与 Transport Profile | TLS、Proxy、进程和网络策略 |
| Message | JSON-RPC framing 与 Method 形状 | 校验、限制和错误映射 |
| Discovery | Capability 和列表方法 | 信任、审批和清单治理 |
| Tool | 名称、描述、输入 Schema | 身份、授权和副作用 |
| Resource | URI 形式的数据引用 | 所有权、新鲜度、分类和删除 |
| Prompt | 可复用消息模板 | 内容审查、注入控制和策略 |
互操作性不等于权限可迁移。一个 Server 可以被多个 Host 使用,但每个部署仍需要自己的授权模型。
四种角色
Host
Host 是面向用户的应用或 Agent Runtime,通常负责:
- 接收用户请求;
- 选择或路由模型;
- 管理一个或多个 Client;
- 把发现到的能力呈现给模型;
- 执行 Host 侧审批和展示策略。
Host 不应把 Server 的描述当成可信指令。描述、Resource 和 Result 都是跨信任边界的数据。
Client
MCP Client 是由 Host 管理的协议连接。常见拓扑是一个 Client 对应一个 Server,以隔离 Session、Capability 和故障状态。Host 也可以使用 Router 或 Gateway,但这会增加新的状态和策略边界。
Server
Server 暴露能力并在自己的策略下执行。它可以访问文件系统、数据库、API 或进程内服务。“Server”描述的是协议角色,不保证一定是独立 OS 进程、沙箱或安全边界。
External System
数据库、文件存储、SaaS API 或本地操作系统才是业务数据的权威。MCP Server 是 Adapter 和策略执行点,不能替代外部系统的访问控制。
生命周期与 Capability 协商
具体 Method 取决于固定的规范版本,但连接通常包含:
Capability 协商不是权限授予,只说明对端支持哪些协议功能。Server 仍需在执行时检查已认证 Principal 和具体操作。
可靠 Client 还应处理:
- Protocol Version 不匹配;
- 初始化超时;
- 畸形或超大消息;
- 取消和断连;
- Capability 变化;
- Server 关闭和重连;
- 重复请求和幂等。
JSON-RPC 边界
MCP 使用 JSON-RPC 的请求、响应、通知和错误概念。Request ID 用于关联响应,不负责认证请求。
type RequestId = string | number;
type JsonRpcRequest = {
jsonrpc: "2.0";
id: RequestId;
method: string;
params?: Record<string, unknown> | 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。Method Schema、请求大小、授权、取消和业务规则仍由 Server 负责。JSON-RPC 错误中不要返回 Stack Trace 或凭证。
三类核心能力
Tools
Tools 是 Client 可以请求 Server 执行的操作。Tool 定义通常包含名称、描述、输入 Schema 和 Annotation。模型发出的调用应视为提议:
模型提出 Tool Call
-> Host/Client 执行审批和预算策略
-> Server 认证并授权
-> Server 校验参数
-> Server 执行操作
-> Server 返回有界 Result
好的 Tool 应明确:
- 做什么以及不做什么;
- 输入限制和默认值;
- 读写行为;
- Resource 与 Tenant 范围;
- 超时、重试、幂等和输出限制。
除非整个能力已被有意沙箱化并授权,否则不要暴露通用 run_command、任意 SQL、无限制 HTTP Client 或“管理一切”的 Tool。严格 Schema 不能让危险能力自动安全。
Resources
Resources 提供 Client 可以读取的数据引用。URI 是标识符,不是读取权限证明。Server 必须通过允许的 Scheme、Tenant 策略、对象授权、新鲜度规则和大小限制解析它。
Resource Link 可以避免把大 Artifact 嵌入 Tool Result,但后续读取必须重新鉴权。不要假定可信 Server 返回的 Resource 适合直接放入 Prompt,它可能包含指令或敏感数据。
Prompts
Prompts 是 Server 暴露的可复用消息模板,可以提升一致性,但不是安全角色,不能覆盖系统策略。Prompt 内容、参数和插入的 Resource 都应视为不可信数据。
Transport 选择
stdio
Host 启动本地进程时可使用 stdio。仍需审查继承的环境变量、文件系统、网络外发、Shell、OS 用户和进程生命周期。本地进程并不天然沙箱化。
Streamable HTTP
新的远程部署应评估固定规范和 SDK 支持的当前 Streamable HTTP Profile。要审查 TLS、认证、请求限制、Session、重连、取消、Proxy Buffering、Origin 策略和优雅关闭。
旧式 SSE 兼容
部分旧 Client 和 SDK 使用 SSE 事件流加独立 POST 消息端点。如果必须支持:
- 隔离并标记兼容路由;
- 生成并绑定 Server 侧 Session;
- 对 SSE 和每个消息请求执行认证;
- 限制事件队列和结果大小;
- 明确顺序、取消、重连和重复行为;
- 通过真实 Proxy 路径测试。
不要把旧式 SSE 称为“MCP 的流式协议”,也不要推断所有远程 Server 都使用同样的端点名称。
安全模型:MCP 不保证什么
MCP 标准化通信,但不保证:
- 用户认证;
- 租户隔离;
- 对象所有权;
- Tool 行为安全;
- Prompt Injection 抵抗能力;
- Transport 或 Telemetry 机密性;
- 缓存和日志中复制数据的删除。
远程 Server 至少需要:
- 可信 Token 或 Workload 认证;
- Issuer、Audience/Resource、Expiry、Algorithm 和 Key Rotation 校验;
- 逐调用 Scope 和对象级授权;
- 请求、结果、并发、超时和成本限制;
- 副作用的幂等与取消;
- 脱敏审计事件和 Trace Context;
- 跨租户、Tool Result Injection 和重放测试。
Read-only 或 Destructive 等 Annotation 可以帮助 Host 展示风险,但 Server 必须独立验证真实行为并执行策略。
最小 Server 设计
把协议适配、Policy 和业务逻辑分开:
Transport Adapter
-> JSON-RPC 与 Method 校验
-> Principal 与 Tenant Context
-> Tool/Resource Policy
-> Domain Service
-> 有界 Result 与 Audit Event
Domain Service 应接收可信 Context,而不是从模型参数推断身份:
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 | initialize、版本不匹配、畸形 JSON-RPC、Capability 变化 |
| Policy | 缺少 Scope、错误 Tenant、对象所有权、过期 Token |
| Reliability | 超时、取消、重连、队列溢出、重复请求 |
| Abuse | Result 注入、超大 Payload、SSRF、路径穿越、数据外泄 |
观测低基数事件:
- Protocol 和 SDK 版本;
- Run、Request 和 Trace 标识;
- Method/Tool 名称和版本;
- 参数摘要、结果类别和字节数;
- Policy 结果和错误类别;
- 延迟、重试、取消和预算。
默认不要保存原始 Token、私有 Prompt、完整 Tool Result 或隐藏推理。
如何选择 SDK
选择覆盖目标版本、Transport、取消、Capability 协商、授权 Hook 和安全更新的维护中 SDK。手写 Adapter 可以用于学习或狭窄的兼容边界,但会产生长期一致性和事故响应责任。
评估 SDK 时检查:
- Protocol 一致性测试;
- 依赖和发布历史;
- Transport 与 Proxy 行为;
- 错误和取消语义;
- 授权和脱敏扩展点;
- 观测和删除支持;
- 升级和回滚策略。
生产检查清单
- [ ] 固定 MCP 版本和 SDK。
- [ ] 记录 Host、Client、Server、外部系统和信任边界。
- [ ] 选择 Transport Profile,并标记旧式 SSE 为兼容行为。
- [ ] 校验 JSON-RPC framing 和 Method Schema。
- [ ] 对每个操作执行认证和授权。
- [ ] 将 Resource 和 Tool 绑定到 Tenant 与对象策略。
- [ ] 限制请求、结果、队列、并发、超时和成本。
- [ ] 定义取消、重连、重复和关闭行为。
- [ ] 将描述、Prompt、Resource 和 Result 视为不可信数据。
- [ ] 脱敏 Telemetry,并测试删除传播。
常见问题
MCP 是什么?
它是 Host 连接面向模型的 Client 与暴露能力的 Server 的开放协议。它标准化生命周期和消息,但不标准化业务授权。
Host、Client、Server 有什么区别?
Host 协调用户和模型,Client 管理协议连接,Server 在自己的策略下声明并执行能力。
MCP Tool 是 Function Calling 吗?
两者在结构化调用边界上有交集,但 MCP 还定义跨进程发现、生命周期、Transport 和结果交换。Server 仍要校验和授权每次调用。
新的远程 Server 应该使用什么 Transport?
固定规范并使用其当前支持的 Profile。只有存在明确兼容需求时才使用旧式 SSE。
Schema 能让 Tool 安全吗?
不能。Schema 只校验形状,身份、Tenant、对象、Purpose、副作用和输出策略必须由 Server 执行。
总结
MCP 的价值在于保持边界诚实:它标准化 Host 与 Server 如何发现能力、交换消息,却把身份、业务权威、数据治理和运行安全留给应用。先理解生命周期,固定 Transport Profile,保持 Tool 窄化,并把所有描述、Resource 和 Result 当作不可信输入,才能正确进入 OAuth、Gateway、Tool 设计和评估等生产主题。