跳到主要内容

关于在生产环境运行 MCP,没人告诉你的那些事

· 阅读需 12 分钟
Tian Pan
Software Engineer

Model Context Protocol (MCP) 将自己定位为 AI 的 USB-C 接口 —— 将任何工具接入任何模型,然后看着它们自如交流。在实践中,第一天确实感觉如此。第二天你会遇到扩展性漏洞。到了第三天,你就在阅读关于那些你甚至都不知道存在的工具投毒攻击(tool poisoning attacks)的 CVE 了。

MCP 是一个非常有用的标准。它于 2024 年底推出,并迅速被整个行业采用,它解决了大语言模型(LLM)与外部系统之间真实的集成摩擦。但在“完成演示原型”与“在真实用户负载下可靠运行”之间,存在着比大多数团队预想的更大的鸿沟。以下是这个鸿沟的真实样貌。

你定会后悔选错的传输决策

在任何 MCP 部署中,第一个架构决策 —— 传输机制 —— 也是团队最常反复修改的决策。MCP 支持三种传输方式:STDIO、HTTP+SSE 和 Streamable HTTP。

STDIO 适用于本地工具。如果你正在构建一个在开发者的笔记本电脑上作为子进程运行的东西,那没问题。一旦你想要多用户远程访问,你就需要 HTTP。

HTTP+SSE 是最初的远程传输方式。现在它已被弃用。问题在于 SSE 固有的局限性:它每个会话都需要一个持久的长连接。这使得无服务器(serverless)部署在经济上非常痛苦 —— 像 Lambda 或 Cloud Run 这样缩减至零的平台会为了保持长连接而维持活跃状态,从而累积成本。更糟糕的是,当你把多个服务器实例放在负载均衡器后面时,有状态的 SSE 连接会中断,除非你实现粘性路由(sticky routing)或外部会话存储。

Streamable HTTP 是生产环境的正确选择。它允许跨服务器实例的无状态操作,能在标准负载均衡器后正常工作,并消除了每个会话一个连接的限制。如果你今天开始,请完全跳过 SSE,从第一天起就为 Streamable HTTP 进行设计。稍后迁移意味着要更新连接到你服务器的每一个客户端 —— 而客户端的更新并不总是那么迅速。

在确定传输层之前的实战测试:你的服务器能否在会话中途重启而不破坏活跃的智能体(agent)工作流?如果答案是否定的,说明你的基础设施工作还没有完成。

工具设计本质上是一个提示词工程问题

这是一个大多数团队需要迭代几次才能内化的见解:你的 MCP 工具实现的质量只有一半取决于工具代码。另一半则取决于你向模型暴露的 Schema。

LLM 决定调用哪个工具 —— 以及如何调用 —— 几乎完全取决于你的工具名称和描述。当两个工具名称相似或职责重叠时,模型会感到困惑并选错工具。当参数描述模糊时,模型会传递无效值并触发错误。当工具返回一个 3,000 token 的 JSON 块时,智能体必须在能够执行任何有用操作之前,消耗上下文预算来解析它。

在实践中行之有效的具体建议:

少而精的工具优于多而散的工具。 一个支持过滤、排序和分页的 search_documents 工具,其表现优于三个分别名为 search_documentsfilter_documentspaginate_results 的工具。当模型需要思考该串联哪种工具序列时,模型层面的组合效果往往很差。

像模型从未见过你的系统一样编写描述。 描述是给 LLM 看的文档,而不是给人类开发者看的。包含工具的用途、适用于什么、适用的约束条件以及调用者在响应中应期望得到什么。“搜索文档”不是合格的描述。“通过关键字或语义查询搜索索引文档。返回按相关性排序的最多 20 个结果。当用户要求查找、检索或定位信息时使用此工具。”则更接近要求。

保持响应精简且结构化。 如果你的工具可能返回大型负载,请添加分页,并让智能体再次调用以获取下一页。在返回之前使用第二个 LLM 调用进行总结,虽然增加了延迟,但相比在单个工具响应中耗尽上下文限制,这是值得的。

针对你实际使用的模型验证 Schema。 供应商通常声称支持 JSON Schema 约束 —— enumminimumpattern —— 但并不强制执行。使用你的实际模型测试 Schema,并验证无效输入是否会产生预期的拒绝,而不是静默通过。

安全性:你不经意间构建的攻击面

MCP 的安全威胁模型与传统 API 不同。攻击面不仅仅是你的服务器代码 —— 还有 LLM 在处理工具元数据时的行为。

工具投毒(Tool poisoning) 是当其公开时让大多数团队措手不及的一类攻击。机制如下:攻击者在工具的描述字段中嵌入恶意指令。查看工具列表的用户看不到这些指令,但语言模型会逐字处理它们。被投毒的工具描述可以指示模型窃取数据、执行额外调用或绕过应用层安全检查 —— 而用户看不到任何异常。

在针对各大 LLM 智能体的基准测试中,工具投毒攻击的成功率惊人。越强大的模型往往容易受到攻击,因为这种攻击直接利用了模型的指令遵循能力。

地毯式抽离攻击(Rug pull attacks) 进一步扩展了这一点。MCP 工具在用户批准后可以更新自己的定义。控制第三方 MCP 服务器的攻击者可以部署一个看似无害的工具,等待安装量积累,然后静默更新描述以包含恶意指令。用户批准的是原始版本;他们无法看到该工具目前对自己的描述。

CVE-2025-6514 展示了完全沦陷的样子:一个恶意的 MCP 服务器利用命令注入漏洞,在连接的客户端上执行任意代码。研究人员还分别演示了通过将投毒工具与合法的消息集成相结合,窃取用户整个聊天记录的过程。

实际有效的缓解措施:

  • 锁定第三方 MCP 服务器版本,并将任何更新视为需要重新审查。不要自动更新。
  • 在所需的最小权限上下文中运行每个 MCP 服务器。数据库服务器默认应为只读。文件系统服务器应限定在特定目录。在没有明确、经过审计的沙箱的情况下,切勿暴露原始 Shell 执行。
  • 将凭据与智能体分离。API 密钥和令牌应存储在你的网关层,并在执行时从服务器端注入,而不是放在智能体的上下文中,否则可能会被泄露。
  • 记录每一次工具调用,包括调用者身份、参数和响应。当出现问题时,你需要审计线索。

团队最常遇到的失败模式是在没有阅读代码的情况下部署一堆社区 MCP 服务器。任何 MCP 服务器都以你应用程序的凭据运行。对待安装第三方 MCP 服务器,应像对待添加包含你数据库密码的任意 npm 包一样谨慎。

网关模式及其在规模化下的重要性

早期的 MCP 部署往往倾向于将工具服务器直接连接到 LLM。这在单用户或低访问量的场景下行之有效,但在需要针对每个用户的访问控制、频率限制和连贯审计日志的多租户应用中,这种方式就会失效。

能够实现规模化的生产模式是采用中心化的 MCP 网关——这是一个代理所有工具访问的单一服务。网关负责处理身份验证、授权、频率限制、工具注册与发现以及集中式日志记录。各个工具服务器与网关通信;LLM 与网关通信;用户永远不会直接与工具服务器交互。

这种架构具有几个具体的优势。首先,它消除了多租户数据泄露问题:网关强制要求用户 A 只能使用用户 A 的凭据调用工具,并且只能访问用户 A 的数据。其次,它集中了可观测性问题:你无需分别为每个工具服务器配置监测,而是可以从一个地方获取调用图、延迟直方图和错误率。第三,它允许你在不更改 LLM 配置的情况下添加或删除工具服务器。

如果你在网关后面放置了无服务器(Serverless)函数,冷启动延迟是一个现实存在的问题。Lambda 的冷启动时间大约在 5 秒左右,这足以破坏 Agent 的响应感知。对于交互式用例,请至少保持网关本身处于热启动状态。

会话状态比看起来更复杂

MCP 拥有一种会话模型,但大多数教程并没有解释当会话需要跨越服务器重启或在多个实例之间路由时会发生什么。

在 STDIO 模式下,会话在子进程的存续期间内存在。这既干净又简单。但在远程 HTTP 模式下,会话需要存储在服务器进程内内存之外的地方——这意味着如果你运行多个实例,就需要使用 Redis 或数据库。

以下具体场景会打破你的无状态假设:负载均衡器将对话的第二轮路由到了与第一轮不同的服务器实例;部署在长时运行的 Agent 工作流中途重启;用户的浏览器在多步操作期间关闭并重新打开。

MCP 的会话管理需求并不新颖——它们与任何有状态 Web 应用的需求相同。错误在于,因为 MCP 看起来像是一个简单的 RPC 协议,就假设你不需要处理这些问题。

在会话层面缓存高开销操作——授权检查、昂贵的查询、分页游标——但要确保每个缓存项的范围都限定在单个用户。没有用户范围限定的全局缓存是等待触发的多租户安全漏洞。

运营就绪的真实面貌

MCP 运营就绪的基准不是通过健康检查,而是能否回答以下问题:

可观测性:对于过去 30 天内的任何工具调用,你是否能说出是谁调用的、使用了什么参数、返回了什么以及耗时多久?如果不能,你就是在盲目运营。

事件响应:当 Agent 通过工具采取了意外操作时——例如创建了不该创建的记录、向错误的收件人发送了消息——你是否有足够的审计数据来精确还原发生了什么?如果操作是可逆的,你能回滚吗?

版本控制:你是否跟踪部署的每个第三方 MCP 服务器的版本?在应用更新之前,你是否有审核变更的流程?

故障模式:当工具服务器宕机时,Agent 会做什么?它是无限期重试、优雅地超时,还是以一种可能导致意外行为的方式“故障开启”(Fail open)?

能够对这些问题回答“是”的团队才赢得了在生产环境中运行 MCP 的权利。无法回答这些问题的团队只是构建了一个恰好在为真实用户提供服务的演示 Demo。

标准尚年轻;为变化而构建

MCP 仍在演进中。传输层在一年内从 SSE 转向了可流式传输的 HTTP。身份验证正在进行重大重新设计,以支持企业级 SSO 流程。水平扩展模式也尚未定型。

对于一个从初始发布到广泛采用的速度超过大多数开放标准的协议来说,这是正常现象。在实践中的意义是:不要与可能发生变化的实现细节建立深度耦合。保持工具服务器轻量化并专注于业务逻辑。将基础设施关注点——认证、日志、路由、频率限制——放在网关层,这样可以在不触及单个工具实现的情况下进行更新。

在 MCP 领域蓬勃发展的团队,是那些将其视为任何面向外部的 API 一样对待的团队:为失败而设计、严格限定权限范围,并在需要之前就建立可观测性。而挣扎的团队则是那些在集成上动作很快,但在运营上动作缓慢的团队。这并不是一个新故事,但当初始原型运行如此顺畅时,MCP 让人异乎寻常地容易跳过基础设施工作。

构建网关。锁定你的依赖版本。记录一切。协议会持续改进;而在此期间,你的生产系统需要保持可靠。

References:Let's stay in touch and Follow me for more thoughts and updates