MCP 生产现实
如果您在生产环境中部署 MCP 代理,第二天的痛苦可能会让您感觉正在失去控制:token 账单飙升,远程服务器出现故障,安全团队询问如何锁定这个系统。典型的痛点包括:
- 您的 MCP 服务器在开发环境中工作正常,但在生产环境中静默死亡。
- 您的 token 账单看起来像是房子的首付。
- 您"简单"的代理设置变成了一个具有 14 种故障模式的分布式系统。
这就是模型上下文协议的真实故事:它不是某种抽象规范,而是代理与它们需要接触的现实世界工具和数据之间的管道。
六个生产级代理模式
本文将这些痛点映射到六个 MCP 架构模式,您可以实际使用这些模式来部署和扩展代理而不会失控。这六个模式是一个实用的现场指南,不是官方规范——每个模式都对应一个有充分文档记录的现实世界工程模式,并有生产实现支持。
1. 直连模式 — "今晚上线"
口号: 你,一个代理,一个 MCP 服务器,没有麻烦。
直连模式是"MCP 的单体架构"——您的主机应用程序直接通过 stdio 或 HTTP 与 MCP 服务器通信,没有额外的跳转。当您只想看到某些东西工作,而暂时不关心治理幻灯片时,这是完美的选择。
最适合:
- 您正在构建 MVP 或黑客马拉松演示。
- 单一团队,单一信任边界,一切都在"您的"基础设施中运行。
- 您想要最低的延迟和最简单的调试。
避免使用:
- 您正在跨团队或租户暴露工具。
- 安全需要审计日志、访问策略,有人说"SOX"。
维度快照: 安全性 (⭐☆☆☆),可扩展性 (⭐☆☆☆),成本效率 (⭐⭐⭐☆),可调试性 (⭐⭐⭐⭐)
看看它在 FlowZap 中的样子:
Host { # 主机应用程序
n1: circle label="用户发送提示"
n2: rectangle label="代理构建 JSON-RPC 请求"
n3: rectangle label="通过 stdio 发送请求"
n4: rectangle label="接收 JSON-RPC 结果"
n5: rectangle label="代理响应用户"
n1.handle(right) -> n2.handle(left)
n2.handle(right) -> n3.handle(left)
n3.handle(bottom) -> MCPServer.n6.handle(top) [label="JSON-RPC 请求"]
n4.handle(right) -> n5.handle(left)
}
MCPServer { # MCP 服务器
n6: rectangle label="解析传入请求"
n7: rectangle label="执行工具或资源"
n8: rectangle label="构建 JSON-RPC 响应"
n6.handle(right) -> n7.handle(left)
n7.handle(right) -> n8.handle(left)
n8.handle(top) -> Host.n4.handle(bottom) [label="JSON-RPC 响应"]
}
2. 网关代理 — "让安全团队满意"
口号: 在您的工具前放一个保镖。
网关代理在您的代理和 MCP 服务器之间放置一个 API 网关,以处理身份验证、速率限制和审计。您的代理仍然认为它在正常调用工具;网关静默强制执行 OAuth 2.0、SAML、SSO、工具级速率限制和基于团队的配额执行,然后请求才会到达 MCP 服务器。这不是理论上的——MintMCP Gateway、Gravitee MCP Proxy、Kong 和 Azure APIM 等产品都实现了这个确切的模式。
这个模式的真实案例是鲜明的:没有网关级控制,单个陷入重试循环的代理可能在几小时内耗尽 API 预算。具有基于 token 的配额、突发允许和按工具细粒度控制的网关是标准预防措施。
最适合:
- 需要跨所有工具的一致身份验证(OAuth/JWT/API 密钥)。
- 合规性(SOC2、GDPR)或事件响应需要请求日志。
- 多个团队或客户共享相同的 MCP 资产。
避免使用:
- 超延迟敏感且每毫秒都很重要——网关增加了一个网络跳转。
- 流量不足以证明增加的复杂性。
维度快照: 安全性 (⭐⭐⭐⭐),可扩展性 (⭐⭐⭐⭐),成本效率 (⭐⭐☆☆),可调试性 (⭐⭐⭐☆)
FlowZap 代码
Host { # 主机应用程序
n1: circle label="用户发送提示"
n2: rectangle label="代理构建工具调用"
n3: rectangle label="向网关发送请求"
n4: rectangle label="接收网关响应"
n5: rectangle label="代理响应用户"
n1.handle(right) -> n2.handle(left)
n2.handle(right) -> n3.handle(left)
n3.handle(bottom) -> Gateway.n6.handle(top) [label="工具请求"]
n4.handle(right) -> n5.handle(left)
}
Gateway { # MCP 网关
n6: rectangle label="接收并记录请求"
n7: diamond label="已授权?"
n8: rectangle label="转发到 MCP 服务器"
n9: rectangle label="接收 MCP 响应"
n10: rectangle label="记录响应并返回"
n6.handle(right) -> n7.handle(left)
n7.handle(right) -> n8.handle(left) [label="是"]
n7.handle(top) -> Host.n4.handle(left) [label="401 未授权"]
n8.handle(bottom) -> MCPServer.n11.handle(top) [label="转发请求"]
n9.handle(right) -> n10.handle(left)
n10.handle(top) -> Host.n4.handle(bottom) [label="授权响应"]
}
MCPServer { # MCP 服务器
n11: rectangle label="执行工具"
n12: rectangle label="返回结果"
n11.handle(right) -> n12.handle(left)
n12.handle(top) -> Gateway.n9.handle(bottom) [label="工具结果"]
}
3. 工具路由器 — "别再给 LLM 一本电话簿"
口号: 50 个工具,1 个代理,合理的 token 使用。
工具路由器模式在您的工具前放置一个路由大脑,以便 LLM 只"看到"它实际需要的子集。这是一个有记录的、严重的问题:加载到上下文中的完整工具模式定义可以在用户发送第一条消息之前消耗 40% 的可用 token。Writer.com 通过构建一个语义"搜索元工具"来解决这个问题,该工具使用向量嵌入和余弦相似度将用户意图动态匹配到正确的工具。Speakeasy 使用动态工具集实现了输入 token 减少 96%,总 token 消耗减少 90%。
语义 MCP 路由器方法提供两条发现路径:一个精选的"前 20"默认工具集预加载到上下文中,以及一条用于专业工具的深层语义搜索路径。这种双轨模型保持快速路径快速,长尾可访问,而不会膨胀每个提示。
最适合:
- 超过五个工具,提示疯狂膨胀。
- 不同用例需要不同的工具切片(计费 vs. 分析 vs. 运维)。
- 需要在不降低代理能力的情况下减少上下文大小。
避免使用:
- 一个只有几个工具的小型应用程序。
- 团队还不能支持路由逻辑、指标和回退。
维度快照: 安全性 (⭐⭐⭐☆),可扩展性 (⭐⭐⭐⭐),成本效率 (⭐⭐⭐⭐),可调试性 (⭐⭐☆☆)
FlowZap 代码
Host { # 主机应用程序
n1: circle label="用户发送提示"
n2: rectangle label="代理提取意图"
n3: rectangle label="将意图发送到路由器"
n4: rectangle label="接收路由结果"
n5: rectangle label="代理响应用户"
n1.handle(right) -> n2.handle(left)
n2.handle(right) -> n3.handle(left)
n3.handle(bottom) -> Router.n6.handle(top) [label="意图 + 工具请求"]
n4.handle(right) -> n5.handle(left)
}
Router { # 工具路由器
n6: rectangle label="接收意图"
n7: rectangle label="通过嵌入进行语义匹配"
n8: diamond label="哪个 MCP 服务器?"
n9: rectangle label="转发到服务器 A"
n10: rectangle label="转发到服务器 B"
n11: rectangle label="规范化并返回结果"
n6.handle(right) -> n7.handle(left)
n7.handle(right) -> n8.handle(left)
n8.handle(bottom) -> n9.handle(top) [label="路由 A"]
n8.handle(right) -> n10.handle(left) [label="路由 B"]
n9.handle(bottom) -> ServerA.n12.handle(top) [label="调用服务器 A"]
n10.handle(bottom) -> ServerB.n14.handle(top) [label="调用服务器 B"]
n11.handle(top) -> Host.n4.handle(bottom) [label="最终结果"]
}
ServerA { # MCP 服务器 A
n12: rectangle label="执行工具 A"
n13: rectangle label="返回 A 结果"
n12.handle(right) -> n13.handle(left)
n13.handle(top) -> Router.n11.handle(bottom) [label="结果 A"]
}
ServerB { # MCP 服务器 B
n14: rectangle label="执行工具 B"
n15: rectangle label="返回 B 结果"
n14.handle(right) -> n15.handle(left)
n15.handle(top) -> Router.n11.handle(left) [label="结果 B"]
}
4. 代理网格 — "特工小队,一个大脑"
口号: 许多专家,共享上下文,受控的混乱。
代理网格是当您停止假装一个代理可以做所有事情时发生的事情。多个代理通过 MCP 支持的共享上下文代理进行通信,实现协调的工具访问和状态同步。微软的 Azure 实现使用 Cosmos DB(带有内存回退)的持久会话状态,支持动态模式交换和可追踪的多代理交互。
这里的关键架构选择是编排与编排。在编排设置中,管理器代理协调所有交互,维护任务分类账,并可以基于中间发现动态重新规划。在编排中,代理通过 MCP 的结构化 JSON-RPC 交换进行点对点通信,任何代理都可以向任何其他代理请求帮助。两种方法都依赖共享内存,因此所有代理访问相同的状态存储以保持一致上下文。
风险是真实的:代理可以在彼此之间无限期地来回 ping-pong 任务,没有适当的终止条件和可观察性。
最适合:
- 存在不同角色:规划者、编码者、审查者、操作员。
- 需要共享状态(任务、资源、工作流)而不是隔离孤岛。
- 工作负载自然分解为可并行化的子任务。
避免使用:
- 一个装备精良的单一代理就足够了。
- 可观察性和追踪尚未到位(调试将很痛苦)。
维度快照: 安全性 (⭐⭐⭐☆),可扩展性 (⭐⭐⭐⭐),成本效率 (⭐⭐☆☆),可调试性 (⭐⭐☆☆)
FlowZap 代码
Orchestrator { # 编排器代理
n1: circle label="收到复杂任务"
n2: rectangle label="分解为子任务"
n3: rectangle label="将子任务分配给代理 B"
n4: rectangle label="接收子任务结果"
n5: rectangle label="请求共享上下文"
n6: rectangle label="编译最终响应"
n1.handle(right) -> n2.handle(left)
n2.handle(right) -> n3.handle(left)
n3.handle(bottom) -> Worker.n7.handle(top) [label="子任务分配"]
n4.handle(right) -> n5.handle(left)
n5.handle(bottom) -> Broker.n11.handle(top) [label="上下文请求"]
n6.handle(left) -> n2.handle(bottom) [label="下一次迭代"]
}
Worker { # 工作代理
n7: rectangle label="接收子任务"
n8: rectangle label="获取共享上下文"
n9: rectangle label="调用 MCP 工具"
n10: rectangle label="向编排器返回结果"
n7.handle(right) -> n8.handle(left)
n8.handle(bottom) -> Broker.n11.handle(left) [label="上下文请求"]
n8.handle(right) -> n9.handle(left)
n9.handle(bottom) -> MCPServer.n13.handle(top) [label="MCP 工具调用"]
n10.handle(top) -> Orchestrator.n4.handle(bottom) [label="子任务结果"]
}
Broker { # 上下文代理
n11: rectangle label="解析上下文请求"
n12: rectangle label="返回共享状态"
n11.handle(right) -> n12.handle(left)
n12.handle(top) -> Orchestrator.n6.handle(bottom) [label="上下文到编排器"]
n12.handle(left) -> Worker.n9.handle(top) [label="上下文到工作器"]
}
MCPServer { # MCP 服务器
n13: rectangle label="执行工具"
n14: rectangle label="返回工具输出"
n13.handle(right) -> n14.handle(left)
n14.handle(top) -> Worker.n10.handle(bottom) [label="工具输出"]
}
5. 熔断器 — "不再有僵尸调用"
口号: 如果工具正在死亡,停止锤击它。
熔断器使用健康感知门包装 MCP 调用,使用三种状态:关闭(正常操作,请求通过),打开(检测到故障,请求快速失败),和半开(测试服务是否已恢复)。这是应用于 MCP 工具调用的经典分布式系统卫生。
没有熔断器,故障级联是可预测的:工具 A 失败 → 重试堆积 → 资源耗尽 → 其他工具变慢 → 系统超载 → 一切都失败。有了熔断器:工具 A 失败 → 电路打开 → 快速失败 → 其他工具不受影响 → 系统稳定 → 准备好时恢复。
这种模式有真实的 MCP 实现。IBM 的 mcp-context-forge 有一个完整的熔断器功能请求,具有半开状态恢复、故障阈值和快速故障保护。MCP Go SDK 包括一个生产就绪的错误恢复示例,实现熔断器以及指数退避重试和隔板隔离。Octopus.com 记录了使用 pybreaker 库进行 MCP 工具调用的完整 Langchain + Python 实现。
最适合:
- 依赖不可靠的第三方 API 或遗留数据库。
- 观察到代理冻结,因为单个 MCP 服务器变得无响应。
- 更喜欢优雅降级而不是全有或全无的行为。
避免使用:
- 一切都是本地、快速和坚如磐石的(例如,stdio 到本地进程)。
- 对"快速失败"时做什么没有计划(回退工具、用户消息等)。
维度快照: 安全性 (⭐⭐⭐☆),可扩展性 (⭐⭐⭐⭐),成本效率 (⭐⭐⭐☆),可调试性 (⭐⭐⭐⭐)
FlowZap 代码
Host { # 主机应用程序
n1: circle label="用户发送提示"
n2: rectangle label="代理准备 MCP 调用"
n3: rectangle label="将调用传递给熔断器"
n4: rectangle label="接收结果或错误"
n5: rectangle label="代理响应用户"
n1.handle(right) -> n2.handle(left)
n2.handle(right) -> n3.handle(left)
n3.handle(bottom) -> CB.n6.handle(top) [label="MCP 工具调用"]
n4.handle(right) -> n5.handle(left)
}
CB { # 熔断器
n6: rectangle label="检查电路状态"
n7: diamond label="电路打开?"
n8: rectangle label="转发到 MCP 服务器"
n9: rectangle label="快速失败并返回错误"
n10: diamond label="调用成功?"
n11: rectangle label="记录成功"
n12: rectangle label="记录失败并检查阈值"
n6.handle(right) -> n7.handle(left)
n7.handle(right) -> n8.handle(left) [label="关闭"]
n7.handle(bottom) -> n9.handle(top) [label="打开"]
n8.handle(bottom) -> MCPServer.n13.handle(top) [label="转发请求"]
n9.handle(top) -> Host.n4.handle(bottom) [label="电路打开错误"]
n10.handle(right) -> n11.handle(left) [label="是"]
n10.handle(bottom) -> n12.handle(top) [label="否"]
n11.handle(top) -> Host.n4.handle(left) [label="返回结果"]
n12.handle(top) -> Host.n4.handle(right) [label="返回错误"]
}
MCPServer { # MCP 服务器
n13: rectangle label="尝试工具执行"
n14: rectangle label="返回结果或错误"
n13.handle(right) -> n14.handle(left)
n14.handle(top) -> CB.n10.handle(bottom) [label="执行结果"]
}
6. 上下文代理 — "将您的 LLM 账单减半"
口号: 缓存无聊的东西,为智能的东西付费。
上下文代理是代理和 MCP 服务器之间的缓存和压缩层,在冗余上下文请求到达网络之前拦截它们。这将上下文视为具有 TTL、失效钩子和命中率监控的实际托管资源——而不是魔法般的无限 token 流。
这种模式的有力证据是强有力的。Token Optimizer MCP 服务器将 Brotli 压缩与基于 SQLite 的持久缓存相结合,实现了高达 95%+ 的 token 减少。GitHub 上的 mcp-context-proxy 项目充当透明的 MCP 代理,在使用外部 LLM 将大型工具响应传递给资源受限的本地模型之前对其进行压缩。有效策略包括提示级缓存(重用完整的提示-响应对)、部分上下文缓存(重用静态系统提示)和语义缓存(通过嵌入匹配近重复请求)。
缓存失效是困难的部分。基于时间的过期适用于缓慢变化的数据,基于事件的失效处理数据更新,混合方法平衡新鲜度和效率。根据实际应用需求定义陈旧容忍度。
最适合:
- 代理不断询问相同的文档、模式或代码库切片。
- 检索在相对静态的数据上运行(知识库、规范)。
- 账单 screaming "上下文膨胀" 多于"模型大小"。
避免使用:
- 数据是实时的,陈旧是危险的(交易、关键操作)。
- 没有明确的失效和新鲜度策略。
维度快照: 安全性 (⭐⭐⭐☆),可扩展性 (⭐⭐⭐⭐),成本效率 (⭐⭐⭐⭐),可调试性 (⭐⭐⭐☆)
FlowZap 代码
Host { # 主机应用程序
n1: circle label="用户发送提示"
n2: rectangle label="代理请求上下文"
n3: rectangle label="接收上下文"
n4: rectangle label="代理响应用户"
n1.handle(right) -> n2.handle(left)
n2.handle(bottom) -> Proxy.n5.handle(top) [label="上下文请求"]
n3.handle(right) -> n4.handle(left)
}
Proxy { # 上下文代理
n5: rectangle label="接收上下文请求"
n6: rectangle label="使用 TTL 检查缓存"
n7: diamond label="缓存命中?"
n8: rectangle label="返回缓存上下文"
n9: rectangle label="从 MCP 服务器获取新鲜数据"
n10: rectangle label="压缩并缓存响应"
n5.handle(right) -> n6.handle(left)
n6.handle(right) -> n7.handle(left)
n7.handle(right) -> n8.handle(left) [label="命中"]
n7.handle(bottom) -> n9.handle(top) [label="未命中"]
n8.handle(top) -> Host.n3.handle(bottom) [label="缓存上下文"]
n9.handle(bottom) -> MCPServer.n11.handle(top) [label="获取请求"]
n10.handle(top) -> Host.n3.handle(left) [label="新鲜上下文"]
}
MCPServer { # MCP 服务器
n11: rectangle label="获取完整上下文"
n12: rectangle label="返回新鲜数据"
n11.handle(right) -> n12.handle(left)
n12.handle(top) -> Proxy.n10.handle(bottom) [label="新鲜数据"]
}
如何实际使用这些模式
如果您在想"我应该选择哪一个?",请使用这个阶梯:
- 从直连模式开始,让某些东西工作。
- 一旦有了真实用户和安全需求,添加网关代理。
- 当您超过 5 个工具并出现 token 痛苦时,引入工具路由器。
- 一旦任何远程东西可能失败(它会),就加入熔断器。
- 财务第一次向您发送关于 LLM 成本的消息时,就引入上下文代理。
- 只有当单个代理确实无法跟上时,才考虑代理网格。
灵感来源
- https://www.codeant.ai/blogs/llm-cost-calculation-guide
- https://mobisoftinfotech.com/resources/blog/ai-development/llm-api-pricing-guide
- https://modelcontextprotocol.io/docs/learn/architecture
- https://opencv.org/blog/model-context-protocol/
- https://www.anthropic.com/news/model-context-protocol
- https://modelcontextprotocol.io
- https://dida.do/blog/a-practical-introduction-to-the-model-context-protocol-mcp
- https://www.speakeasy.com/mcp/using-mcp/ai-agents/architecture-patterns
- https://cloud.google.com/discover/what-is-model-context-protocol
- https://agent-patterns.readthedocs.io/en/stable/Agent_Tools_Design.html
- https://dev.to/cristiansifuentes/tokens-tokenization-the-science-behind-llm-costs-quality-and-output-577h
- https://ai.rundatarun.io/AI+Systems+&+Architecture/agent-architectures-with-mcp
- https://www.decodingai.com/p/getting-agent-architecture-right
- https://github.com/IBM/mcp-context-forge/issues/301
- https://www.ibm.com/think/topics/model-context-protocol
- https://en.wikipedia.org/wiki/Model_Context_Protocol
