问题先暴露在 RecursionError 上。一个 Python Agent 在查询库存、更新 CRM、发送邮件之间来回调用。主控最初只接了 get_weather、check_sales、send_email 等六个 HTTP 函数,后来扩到十几个插件,参数名、返回值和错误格式也跟着分叉。
报错还算容易发现。更麻烦的是,工具说明没有写清副作用:模型分不清查询和更新,调用失败后还可能重复执行写操作。为了维持上下文,旧架构还会把对话历史序列化,再塞进每个请求的 payload。插件一多,工具定义和中间结果持续占用上下文,排查一次错误要翻好几层日志。
钛媒体在回溯 MCP 的发展时提到,2024 年 7 月,Anthropic 工程师 David Soria Parra 曾在旧金山办公室面对满屏错误。另一篇 MCP 技术解析则指出,LangChain Tools、LlamaIndex 和 Vercel AI SDK 已经提供了 Agent 开发能力,但面对不同 API,定制代码仍然很难完全避免。
MCP 常被称作 AI 应用的“USB-C 接口”,这个比喻说清了它最实用的部分:先统一连接和调用规则。接口统一之后,工具仍可能设计得很差,业务规则也不会凭空出现。
迁移的第一刀,砍在接口上
旧架构里,check_sales 接收 start_date 和 end_date,用户却常说“上个月”。Prompt 既要判断意图,又要转换日期,还要记住每个插件不同的参数格式。任何一步含糊,都可能触发重复纠正。
迁移时没有急着重写 Prompt,而是先把工具包装成 MCP Server。下面是 check_sales 的简化版本:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP('SalesAgent')
@mcp.tool()
async def check_sales(target_month: str):
'''查询指定月份的销售额数据。target_month 格式为 YYYY-MM。'''
data = await db.query_sales(target_month)
return {'status': 'success', 'data': data}
MCP 使用 JSON-RPC 2.0 传递消息。FastMCP 可以根据函数签名和说明生成工具定义,客户端拿到的是统一的工具名、参数和描述,不必再为每个插件手写一套调用格式。
不过,注释里写了 YYYY-MM,不等于服务端已经完成格式校验。用户输入“上个月”时,应用仍要把它转换成明确月份;参数是否合法,也要由工具内部检查。MCP 规范了交接方式,没有替开发者补上业务逻辑。
读写边界同样要落进工具定义。查询库存可以直接执行,更新 CRM、发送邮件这类操作则应写明会修改状态,并在应用层增加确认和权限检查。只换协议、不整理工具说明,模型照样会选错。
MCP 不会自动补上记忆
迁移中最容易被夸大的,是 MCP 对上下文的作用。它不会自动保存长期记忆,也无法保证 Agent 在多轮调用后仍能准确复述最初意图。
这次改造中更有用的做法,是让工具返回结构化结果。状态、数据和错误信息各自归位,主模型不再接收一大段临时拼出的文本。日志也更容易定位:究竟是参数错误、认证失败,还是服务端没有响应。
MCP 的 Resources 可以向客户端暴露公司合规政策、产品手册等资料,减少重复拼接静态内容。但这些资料如何存储、更新和授权,仍由服务端负责。上下文窗口怎么裁剪、跨会话状态怎么保存,也属于宿主应用的工作。
认证和失败处理,才是迁移里的硬活
Security PRIDE 团队对 MCP 的安全威胁做过系统梳理。统一入口方便集中管理权限,也会把风险集中到 MCP Server:一旦服务端配置过宽,原本分散的工具可能同时暴露。
旧插件各自管理 API Key,Token 过期后,Agent 只知道调用失败,很难判断原因。迁移后,认证逻辑被放到 MCP Server,由服务端统一注入 Header。模型上下文只接触必要结果,不直接接触凭据,异常也能在同一处记录。
超时需要单独设计。check_sales 在这套系统里设置了 5 秒硬超时;这是该接口的工程参数,并非 MCP 默认规则。超过时间后,客户端应收到明确的超时信号,而不是一直等待,或者把空结果交给模型猜测。
写操作还要防止盲目重试。MCP 本身不保证多个工具调用的事务原子性,更新 CRM 成功、邮件发送失败时,系统仍要处理重复调用、补偿和人工确认。客户端与服务端的 SDK 版本也应固定,并针对工具发现、参数校验、认证过期和超时路径做契约测试。
协议统一了,坏工具还是坏工具
接入 MCP 后,下面这些问题不会自行消失:
- 工具名和描述含糊,模型仍会调用错误的函数。
- “上个月”“最近”“尽快”等业务表述,仍需明确的解析规则。
- 模型连续选择同一个失败工具时,宿主应用仍要设置调用上限和熔断。
- 跨工具的数据一致性、权限审批和审计,需要业务系统兜底。
- 返回值塞入过多无关字段,照样会挤占上下文。
MCP 更适合解决工具层的重复劳动。LangChain Tools、LlamaIndex 或其他编排框架仍可继续使用;MCP 负责工具暴露和调用约定,任务规划、状态管理与失败恢复留在原有架构中。
如果系统只连接少量固定 API,直接函数调用可能更省事。当插件已经扩到十几个,同一能力还要被不同客户端复用时,统一协议的收益才会明显。
实际迁移可以按这个顺序推进:
- 盘点现有工具,标明只读、写入和外部副作用。
- 统一参数命名、返回结构和错误格式,并在服务端完成校验。
- 先迁移高频只读工具,再处理更新 CRM、发送邮件等写操作。
- 把认证、超时、日志和权限控制收进 MCP Server。
- 覆盖成功、超时、认证失效和重复调用等路径,再逐步替换旧插件。
迁移后,模型没有突然变聪明。变化发生在工具层:Schema 更清楚,错误可以识别,凭据不再散落,写操作也有了边界。MCP 把这些工程规则放进一套通用接口里。规则没整理好,换成 MCP 也只是给混乱换了一个新包装。