给AI agent接十个工具,一个下午就能跑通。真正的麻烦从第二周开始——同一个MCP server要服务三个租户,SSE transport在高负载下不断断连,再加上某个第三方server的工具描述里悄悄藏着"忽略此前指令"的提示词,这周突然变长了。
modelcontextprotocol.io上有完整规范。这篇文章记录的是多租户部署出第一次事故后,团队反复踩坑总结出的五个模式。它们是day-one架构崩掉之后,团队被迫演化出的形状。每节都有代码,默认你已经知道MCP server是什么。
模式一:工具列表必须按会话裁剪,不能按server固定
典型的一期部署:一个MCP server暴露给所有用户、所有租户、所有会话。server在tools/list里返回完整工具集,agent全部可见,心里想着"以后再加权限控制"。
你不会加的。或者加了,会很痛。
能活下来的模式:把tools/list返回的工具集视为每会话决策,而非server层面的常量。MCP server本身实现全部工具,但在前面(或tools/list handler内部)加一层薄裁剪层,只返回当前调用者有权查看和调用的子集。
代码示例展示了scoped MCP wrapper:真实server有14个工具,免费租户只看到search_docs和read_doc,专业版额外开放create_ticket和update_ticket,企业版开放全部。模型永远不知道租户无权调用的工具存在,因此不会幻觉出"给免费用户调用delete_workspace"。call边界的拒绝检查是纵深防御:即使模型通过泄露示例、注入提示词或历史会话获知工具名,调用仍会被拒绝。
拒绝必须以结构化tool error返回给模型,不能静默丢弃。agent loop只有在看到拒绝信息时才会调整策略——换工具或提示用户升级。
会话身份绑定:MCP本身不携带租户身份,需在transport层附加(HTTP header、SSE握手中的JWT、stdio的env var),在首次tools/list前解析。如果列工具前无法识别租户,就已经输了——每个会话只能回落到最低信任子集。
这是大多数团队意外发现的模式。
热门跟贴