跳到主要内容

MCP-Server设计模式

核对日期:2026-10-27(按 MCP 2026-07-28 规范更新)。

1. 定义与边界​

MCP Server 是通过 Model Context Protocol 暴露工具、资源、提示词等能力的服务端组件。它可以是本地 stdio 进程,也可以是远程 Streamable HTTP 服务。

MCP Server 不应被设计成“把整个系统无差别暴露给模型”。好的 Server 是一个受限、可审计、语义清晰的能力边界。

2. 为什么重要​

MCP Server 的设计质量决定了 Host 和 Agent 能否安全、稳定地使用外部能力。糟糕设计会导致:

  • 工具粒度过粗,无法审批。
  • 资源范围过宽,越权读取。
  • schema 不稳定,模型选择错误。
  • 远程授权混乱,token 被误用。
  • 日志和错误污染协议通道。

3. 核心机制​

3.1 Server 能力​

能力设计重点
Tools语义清晰、输入输出 schema、错误码、幂等
ResourcesURI 设计、分页、访问控制、内容类型
Prompts参数化模板、版本、适用场景
server/discover2026-07-28 起必须实现,声明支持的协议版本、能力和 Server 身份
Logging(已废弃)新实现不要接入;stdio 写 stderr,远程服务用 OpenTelemetry
Roots(已废弃)新实现不要依赖;文件边界改由工具参数、resource URI 或 Server 配置声明

3.2 传输选择​

传输适用场景注意点
stdio本地开发工具、IDE 插件、本机文件访问stdout 只输出协议消息,日志走 stderr
Streamable HTTP远程 SaaS、企业共享服务、多用户访问HTTPS、OAuth、无状态、限流、审计
HTTP+SSE(已废弃)仅兼容存量迁移到 Streamable HTTP

MCP 2026-07-28 规范移除了协议级会话(Mcp-Session-Id)和 initialize 握手,远程 Server 可以像普通 HTTP 服务一样放在负载均衡后面横向扩展。设计上的直接影响:

  • 跨调用状态(如分页游标、草稿、长任务)用 Server 签发的 handle 作为工具参数传递,不绑定连接。
  • Streamable HTTP POST 必须带 Mcp-Method、Mcp-Name 请求头,网关可以据此按方法和工具名做路由、限流和审计,无需解析 body。
  • SSE 断点续传已移除,流断开后 Client 会用新 request ID 重发,写工具必须幂等。
  • 需要用户补充输入时,不再由 Server 主动发起请求,而是返回 resultType: "input_required",跨重试的关联信息放进 requestState。
  • 长耗时任务使用官方 Tasks 扩展(io.modelcontextprotocol/tasks),Client 通过 tasks/get 轮询。

4. 设计模式​

4.1 Thin Wrapper​

一个 MCP 工具包装一个已有 API。

适用:API 已经稳定,权限清晰。

风险:直接暴露内部 API 细节。应通过 adapter 隔离字段。

4.2 Workflow Facade​

一个工具触发受控工作流,而不是直接执行多步副作用。

适用:审批、异步任务、长耗时流程。

示例:

deploy.preview -> deploy.request_approval -> deploy.execute

4.3 Read/Write Split​

读工具和写工具分离。不要让 customer.manage 同时查询、修改、删除。

适用:几乎所有生产 Server。

4.4 Resource + Tool Pair​

Resource 用于读取上下文,Tool 用于执行动作。

示例:

resource: repo://owner/name/pull/123
tool: repo.create_review_comment

4.5 Registry Gateway​

企业内部用一个网关管理多个 MCP Server:

  • Server allowlist。
  • OAuth 和 SSO。
  • 工具元数据审查。
  • 审计与限流。
  • 租户隔离。

5. 工程实现​

5.1 Server 目录结构​

mcp-server-crm/
src/
server.ts
tools/
search-customer.ts
update-customer-status.ts
resources/
customer.ts
auth/
policy.ts
audit/
logger.ts
schemas/
crm.search_customer.json
crm.update_customer_status.json
tests/
contract.test.ts
security.test.ts

5.2 工具实现边界​

server.registerTool("crm.search_customer", {
description: "按名称或客户 ID 查询客户摘要。",
inputSchema: SearchCustomerInput,
outputSchema: SearchCustomerOutput
}, async (args, ctx) => {
await assertScope(ctx.auth, "crm:read", args.tenant_id);
const data = await crm.search(args);
return redactCustomer(data);
});

6. 生产实践​

  • 每个工具都有 owner、版本、风险等级和错误码表。
  • 远程 Server 默认 HTTPS,使用 OAuth scope 限制能力。
  • Server 不信任 Host 传来的用户声明,需验证 token 和 audience。
  • tools/list 等列表结果必须返回 ttlMs 和 cacheScope;含租户或用户特定内容时用 private,避免共享网关缓存串数据。
  • 工具列表按确定顺序返回,便于 Client 缓存,也能提高模型 prompt cache 命中率。
  • 工具变更通过 subscriptions/listen 推送 toolsListChanged,Host 据此触发重新审查。
  • 透传 _meta 中的 traceparent / tracestate,让 Server 内部 span 挂到 Agent trace 下。
  • Server 返回结果要限制大小,并提供分页。
  • 对高风险写操作支持 dry-run、preview、idempotency key。
  • 本地 stdio Server 的日志写 stderr,避免破坏协议消息。

7. 常见反模式​

反模式后果
暴露 run_sql 给通用 Agent数据泄露和破坏风险
暴露整个 home 目录为 resource文件边界失去权限意义
在内存里按连接保存会话状态无状态部署后请求落到其他实例即丢失
列表结果用 public 缓存但内容随用户变化共享缓存泄露其他用户的工具或资源
用一个工具代理所有内部 APIHost 无法审批和审计
忽视多租户隔离用户数据串租户
Server 自行决定调用外部模型且不可见Host 失去成本和数据控制

8. 评测方法​

  • 协议契约测试:tools/list、tools/call、错误格式、schema。
  • 权限测试:不同用户、租户、scope 的访问矩阵。
  • 安全测试:prompt injection、路径穿越、越权参数、恶意返回。
  • 稳定性测试:超时、并发、限流、Server 重启。
  • 兼容性测试:目标 Host、SDK、Inspector。

9. 安全与治理​

  • 工具描述和返回内容都可能成为攻击面。
  • 远程 Server 必须校验 OAuth token audience,不能接受发给其他资源服务器的 token。
  • 授权服务器应在授权响应中返回 iss(RFC 9207);客户端注册优先支持 Client ID Metadata Documents,OAuth 动态客户端注册已废弃。
  • 不把第三方 API token 原样返回 Host 或模型。
  • 对 Server 依赖做供应链扫描。
  • 重大工具新增和权限扩大需要安全评审。

10. Server 安全设计清单​

项要求失败后果
工具粒度读写分离、语义明确Host 无法审批
输入 schema禁止宽泛参数和任意命令参数注入、越权
输出 schemastructuredContent 优先下游解析不稳定
授权校验 token、audience、scopeconfused deputy
租户隔离token、缓存、日志隔离数据串租户
日志结构化日志写 stderr 或日志系统stdio 协议污染
版本server/discover 声明版本;tools/list 带 ttlMs,变化可订阅Host 使用过期 schema
状态不依赖连接会话,跨调用状态用显式 handle横向扩展后状态丢失
供应链依赖扫描、发布签名或来源验证恶意 Server 风险

11. 工程示例:工具返回结构​

{
"content": [
{
"type": "text",
"text": "找到 2 条客户记录,详细字段见 structuredContent。"
}
],
"structuredContent": {
"items": [
{"customer_id": "cus_001", "name": "Acme", "tier": "enterprise"},
{"customer_id": "cus_002", "name": "Acme China", "tier": "standard"}
],
"has_more": false
},
"isError": false
}

返回给模型的自然语言内容应只做摘要;稳定消费依赖 structuredContent。

12. Server 评测门禁​

  • server/discover、tools/list 契约测试通过,结果带 resultType、ttlMs、cacheScope。
  • 多实例部署下,同一用户的连续请求落到不同实例结果一致。
  • 所有 tool input/output schema 有 JSON Schema 校验。
  • 未授权用户、错误租户、过期 token 全部拒绝。
  • 写工具具备 dry-run 或 idempotency key。
  • prompt injection 工具返回不会被包装成 Host 指令。
  • stdio 模式 stdout 没有非 JSON-RPC 日志。
  • 远程 HTTP 模式启用 HTTPS、OAuth、限流和审计。

13. 权威资料​