关于在生产环境运行 MCP,没人告诉你的那些事
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_documents、filter_documents 和 paginate_results 的工具。当模型需要思考该串联哪种工具序列时,模型层面的组合效果往往很差。
像模型从未见过你的系统一样编写描述。 描述是给 LLM 看的文档,而不是给人类开发者看的。包含工具的用途、不适用于什么、适用的约束条件以及调用者在响应中应期望得到什么。“搜索文档”不是合格的描述。“通过关键字或语义查询搜索索引文档。返回按相关性排序的最多 20 个结果。当用户要求查找、检索或定位信息时使用此工具。”则更接近要求。
保持响应精简且结构化。 如果你的工具可能返回大型负载,请添加分页,并让智能体再次调用以获取下一页。在返回之前使用第二个 LLM 调用进行总结,虽然增加了延迟,但相比在单个工具响应中耗尽上下文限制,这是值得的。
针对你实际使用的模型验证 Schema。 供应商通常声称支持 JSON Schema 约束 —— enum、minimum、pattern —— 但并不强制执行。使用你的实际模型测试 Schema,并验证无效输入是否会产生预期的拒绝,而不是静默通过。
