1. MCP 在 AI 工具调用链里到底扮演什么角色
先说个我最近的真实感受。
前阵子给某内容分析平台接外部工具链,一开始用的还是最土的办法:把工具说明写进 system prompt,让模型自己按约定拼 JSON,然后我们在业务代码里写一堆 if-else 去路由、解析、重试。工具数量少的时候还好,一旦超过十几个,你会发现三个问题同时冒出来:prompt 被工具描述塞得越来越长、模型开始选错工具、每次新增工具都要动一遍调度代码。整个调用链完全是“人肉维护”的。
后来我换成了 MCP(Model Context Protocol)这套方式,相当于给“模型 ↔ 外部工具”之间铺了一条标准化的调用管道。你可以把 MCP 理解成一套专门为大模型场景设计的“USB 接口”协议——工具那边只要实现 MCP server 的标准端点,模型侧通过 MCP client 就能自动发现这个工具有什么功能、参数长什么样、该怎么调用,而不是靠人写死文档和路由。
再说清楚一点:MCP 解决的核心问题不是“让模型能调用工具”这种大而空的目标,而是把工具调用的四个环节全部标准化了。哪四个环节?工具发现(discovery)、能力协商(capability negotiation)、请求调用(invocation)、结果返回(response handling)。在 MCP 之前,这四个环节各家有各家的做法,换个模型厂商、换套工具 SDK,就得重写一遍对接层;在 MCP 之后,工具侧只需要实现一套协议端点,任何兼容的 MCP client 都能直接用。
所以这篇文章我想围绕“连接机制、开销与优化”这三件事展开。适合谁看?两类人最需要。一类是正在做 AI 应用开发的工程师,想搞清楚 MCP 工具调用这条链路从握手到返回到底经历了什么、瓶颈卡在哪;另一类是已经在用 MCP 但发现工具一多就变慢、上下文不够用的人,想找一套成体系的优化思路。我尽量把协议层原理、真实延迟分布、可落地的调优策略都讲透,不绕弯子。
2. 连接机制拆解:从握手到一次工具调用的完整生命周期
MCP 底层走的是 JSON-RPC 2.0。这个协议非常轻,每个请求就是一个包含jsonrpc、method、params、id四个字段的 JSON 对象。但真正让 MCP 区别于“自己定一套 JSON 格式”的,是它在 JSON-RPC 之上定义了一套完整的会话状态机和端点语义。我拆成五个关键阶段来讲。
2.1 传输层选型:stdio 与 HTTP/SSE 各适合什么场景
MCP 支持两类传输方式,选型直接决定你的连接开销和部署形态。
第一类是stdio 传输。MCP client 启动一个子进程,通过标准输入输出和子进程里的 MCP server 通信。这种方式最适合本地开发场景:server 是本地装的一个 Python 脚本或者 Node 程序,client 拉起进程,两边不需要走网络,延迟极低,也没有鉴权、没暴露端口。我早期做内部工具链就是用 stdio 方式把一堆本地的脚本工具接进来的,启动一次连接,后面所有 tools/call 都在进程内完成,稳定得很。
第二类是HTTP/SSE(以及新的 Streamable HTTP)传输。server 跑在远端,client 通过 HTTP 请求发起会话。这类方式适合部署成共享服务的场景,比如公司内部搭建一个工具网关,多个客户端连同一个 MCP server。但代价是多了一次网络往返,而且 HTTP 模式下通常还需要带鉴权令牌、处理会话保持,连接开销明显高于 stdio。
选型上我的个人经验是:能本地跑的尽量 stdio,需要远程共享的才上 HTTP。不过有个坑要先提醒,stdio 方式的子进程生命周期管理是个麻烦事,我后面专门讲。
2.2 会话初始化与能力协商:initialize 阶段发生了什么
无论用哪种传输方式,client 和 server 建立连接后的第一件事就是initialize握手。这一步不是随便发个 hello 就完事,而是双方交换能力清单和协议版本。
client 发送 initialize 请求,里面带的是客户端自身的信息:协议版本号、client 能力声明(比如是否支持采样、是否支持附件等)。server 收到后返回自己的信息:server 名称、server 版本、协议版本、以及 server 支持的能力列表。
这里最值得关注的是协议版本协商。MCP 的协议版本不是“永远兼容旧版”,client 声明自己支持的版本,server 如果认就正常响应;如果不认,后面所有调用都可能出现字段不匹配的问题。我在生产里碰到过一次:某 SDK 升级后默认发的 initialize 带了一个 server 端不认识的 capability 字段,server 直接返回了空响应,整整排查了半天。所以协议版本匹配检查必须放在连完线后的第一优先级,不要想当然。
initialize 成功后,client 还要发一个notifications/initialized通知,告诉 server“握手完毕,可以开始干正事了”。注意这一步是通知(notification),不需要 server 返回响应。
2.3 工具发现:tools/list 的触发时机与副作用
握手完成后,最关键的阶段是获知“这个 server 上有哪些工具可用”。client 调用tools/list端点,server 返回一个工具数组。每个工具包含三段信息:
name:工具名,调用时要用description:对工具功能的自然语言描述,模型会拿这段文字来判断“该不该用这个工具”inputSchema:一个 JSON Schema,声明工具参数的结构和类型
这个列表返回后,client 通常会把工具定义转换成模型能理解的格式,注入到后续生成请求的tools参数里。这是整条链路里最容易被忽略的开销点——tools/list 返回的原始 JSON 可能并不大,但转成模型上下文里的工具描述后,token 消耗会明显放大,尤其当 description 写得很啰嗦时。后面优化章节我再细算这笔账。
tools/list 的触发时机也值得留意。有些实现是每次会话开始时拉一次,有些是首次调用前拉一次,还有人偷懒在每次请求时重新拉。前两种都可接受,第三种基本属于给自己制造延迟——工具列表一般不会频繁变动,完全没必要每次请求都重新发现。更好的做法是建立工具缓存,只有 server 主动发出列表变更通知时才重拉。
2.4 一次 tools/call 的完整请求链
工具发现搞定之后,真正调用一个工具走的是tools/call端点。请求参数里带name(工具名)和arguments(按 inputSchema 结构传入的实参)。server 执行具体逻辑后返回结果,结果有两种形态:
content数组:工具返回给模型的文本、图片等内容,模型会把这些内容当成工具执行结果来推理structuredContent:可选的纯结构化结果(比如原始 JSON),适合程序化处理
这块有两点实际经验。第一,工具名要用“域_动作”这种命名法,比如weather_query、db_read、email_send,避免模型在工具选择阶段把相似功能搞混;第二,arguments 真正传参时,能传可解析的结构化数据就别传自然语言,模型在参数构造阶段经常犯“多写一个空格、多带一句废话”的毛病,schema 里把类型卡死、枚举值给全,能少很多无效调用。
2.5 连接保持与断开:被忽略的资源管理
最后说连接侧。stdio 子进程不会自己退出,需要在不再使用时显式关闭;HTTP 模式下则要正确发送notifications/quit或关闭会话。很多线上问题都出在连接泄漏上——client 反复重连却从不清理旧连接,最终把 server 端的文件描述符或连接数打满。
我把连接生命周期归纳成一张表,方便对照:
| 阶段 | 关键动作 | 常见问题 |
|---|---|---|
| 建立 | initialize + initialized 通知 | 协议版本不匹配、能力字段不兼容 |
| 发现 | tools/list(带缓存) | 每次请求重拉,浪费往返 |
| 调用 | tools/call(name + arguments) | 参数格式不符、工具名拼写错误 |
| 保持 | 长连接复用 | 子进程/连接泄漏、超时未处理 |
| 断开 | quit / 显式关闭 | 半开连接、僵尸进程 |
连接机制这部分最大的收获是:MCP 不是“调一个 API”那么简单,它是一条有状态的会话链路。吃到协议红利的前提是你把每个阶段都管好,尤其是 initialize 的版本协商和 tools/list 的缓存策略,这两个点几乎是所有性能问题的前置入口。
3. 工具调用的开销到底花在哪:四个被忽视的成本
很多人在聊 MCP 优化时,一开始就冲进“并发、缓存、网关”这些大词里,结果效果却一般。我在实际调优中反复验证过,工具调用链路的开销分布和大多数人想的不一样。真正吃性能的往往不是调用本身,而是下面四个看起来不起眼的成本。
3.1 工具列表膨胀导致的上下文窗口吞噬
这是最大的隐性成本,没有之一。
假设你的工具网关后面挂了 30 个工具,每个工具的description平均 200 个 token,加上 name、inputSchema 和参数说明,一次请求注入模型上下文的工具描述可能高达 8000~12000 token。这是什么概念?很多模型的上下文窗口虽然标称 32K、128K,但留给工具描述的额度经不起这么吃。更麻烦的是,工具数量是持续增长的——接 10 个工具还能忍,接 30 个就开始捉襟见肘,接 60 个直接爆掉。
关键的是,这笔开销即使一次请求只用到其中 1 个工具,也照样全额支付。模型侧需要“看到”所有工具的完整描述才能做工具选择,所以它不会因为“这次只调一个”就把工具描述从上下文里拿掉。这是 MCP 架构层面的一个结构性成本,必须靠策略层去缓解,不能靠工具本身优化。
我的实测数据:某内部知识库工具链,30 个工具全量注入时,单次请求上下文里工具描述占了 11500 token 左右;而动态裁剪后只注入命中的 4~6 个工具,这部分降到 1800 token 以内。上下文消耗直接下降了 80% 以上,整个请求的首 token 延迟也跟着降了。
3.2 往返延迟与连接建立成本
第二个成本在网络上。远程 MCP server 每处理一次tools/call,至少要经过一次完整的 HTTP 往返。跨地域调用时一次 RTT 可能在 50~200ms 之间,有些内部网关还要叠加鉴权、审计,单次往返能到 300ms 以上。
如果一次业务请求里模型要顺序调用 3 个工具,那就是 3 次串行往返,光网络延迟就到 1 秒左右,还没算模型生成和工具执行的时间。很多“MCP 好慢”的体感,问题不是出在模型,而是出在工具调用链路本身的网络开销上。
连接建立成本更隐蔽。有些 client 实现是“每次调用都新建连接、用完即断”,这在 stdio 场景下表现为频繁启停子进程,在 HTTP 场景下表现为反复 TLS 握手。连接建立一次动辄几百毫秒,如果叠加在每次工具调用前,性能直接被打没。
值得注意的是:HTTP/SSE 传输在长连接模式下会持续占用一个连接,多个并发调用通常需要多个连接来避免队头阻塞。连接池的配置、Keep-Alive 时长这些参数,在工具调用频次上来之后一定要显式调过,用默认值往往不够。
3.3 鉴权与安全校验的隐藏开销
第三个成本是安全链路的叠加。远程 MCP server 如果需要鉴权,每次请求都要先验证令牌,甚至可能产生额外的身份服务调用;如果开启了审计日志,每次 tools/call 还要记录完整入参和出参;还有频率限制、数据脱敏、权限点校验……这些安全工作本身没问题,但它们加在一起,会把工具调用的 P95 延迟拉高不少。
我在某个公司内部网关场景里看到过这类开销:光鉴权+审计+脱敏三层过滤,一个原本 10ms 就能执行完的工具,被拖到了 120ms。安全是必须的,但不该让每次调用都重复做全量校验。合理的思路是把高频校验结果做短期缓存,比如令牌有效性缓存、权限判定缓存,而不是每次都穿透到后端的身份服务。
3.4 工具返回体过大引发的二次放大
第四个成本最容易爆雷:工具返回的content或structuredContent过大,直接塞进上下文。
想象一个场景:某个查询工具返回了 2 万行结构化数据,模型收到后还没来得及总结,上下文窗口先告警了。工具返回体进入上下文之后,会随请求轮次不断累积——如果第一轮返回了一大坨 JSON,第二轮又要基于它继续推理,这部分内容就一直躺在上下文里占位置。多轮下来,价格和延迟都指数级上涨。
我踩过一个具体案例:某工具直接返回了一个完整的配置树(接近 30KB JSON),模型其实只关心其中两个字段,但因为这坨数据进入了上下文,后面每一轮对话都带着它,一个本来很轻量的会话硬生生被撑成了“重量级”。后来我把返回改成只抽取关键字段的摘要结构,token 直接降到原来的 8%。
综合上面四个成本,你会发现 MCP 工具调用的开销不是线性的,而是“结构性放大”的。上下文膨胀、网络往返、安全链、返回体累积,每一项都不是独立的小问题,它们互相叠加,构成了工具调用链路性能劣化的完整链条。下面就该讲怎么对症下药。
4. 优化实践:从协议层到架构层的完整调优路线
优化 MCP 工具调用,不能只盯一个点。我按“由近及远、由软到硬”的顺序,分别从上下文、延迟、架构、缓存四个维度给出一套可落地的调优方案。
4.1 动态工具裁剪与描述压缩:把上下文从 10000 token 降到 1700 token
动态工具裁剪的核心思路是:别把所有工具一次性塞给模型,只让模型“看得到”它可能用得到的工具。这个策略说起来简单,做起来有几个细节。
第一步是做工具分类。给每个工具打标签,比如“数据库类”“搜索类”“计算类”“通知类”。在请求进入模型之前,根据用户 query 的语义做一次预处理:检测 query 里提到的主体词、意图词,映射到对应类别的工具子集。这一步可以做得粗糙一点,目标不是精确定位工具,而是把明显无关的类目过滤掉。比如用户问“今天北京的天气”,你根本不需要让模型看到数据库写入工具。
第二步是工具子集的再排序。第一步过滤完,可能还剩 10 个工具,还是偏多。这时可以结合用户的具体表述,通过简单的关键词匹配或者一个轻量 embedding 检索,把这 10 个工具再按相关度排序,只保留前 4~6 个进入上下文。这一步的效果立竿见影——工具描述 token 数从全量注入的上万级别,直接降到两三千以内。我这边实际工程里常用“query 预分类 + Top-K 工具选择”,效果稳定,也没有增加额外的大模型调用。
第三步是描述压缩。很多工具描述写得像产品说明书,动辄几百字。其实模型选择工具主要靠“这个工具是干嘛的、参数怎么传”,其他背景信息全是噪音。可以对 description 做一轮精简,保留“功能一句话 + 关键参数 + 典型使用示例”,每个工具控制在 60~100 token 以内。实测压缩后工具选择的准确率基本持平,但上下文占用降了一半以上。
提示:动态裁剪有一个需要注意的副作用——如果裁剪策略误杀了用户真正需要的工具,模型再聪明也调不到。所以裁剪逻辑必须保留一个“逃生通道”:当预筛选结果为空或相关度都不高时,回退到全量工具列表,避免因优化策略导致功能不可用。
4.2 连接复用、预取与并行调用:把延迟一层层压下去
工具调用链路里的网络延迟,是可以用工程手段消化的。核心三招:连接复用、预取、并行化。
连接复用方面,HTTP 传输下要显式开启 Keep-Alive,并用连接池管理 MCP server 的连接。不要每次都新建 client、用完就关,这会导致 TLS 握手和协议初始化反复发生。正确做法是启动时建立好连接,后续请求复用同一批连接,按需扩容。stdio 场景下则是保持子进程常驻,只在空闲超时后才回收。
预取方面,针对“多轮会话中固定会用到”的一批工具,可以在会话建立后的空闲期提前发起初始化。比如你的应用每次都要先查用户身份,那就提前把这个工具拉起来,等真正需要时直接调用,省掉连接建立和工具发现的时间。
并行化方面,当一次业务请求里需要调用多个独立工具时,尽量改成并发调用而不是串行。我碰到过不少同学写的链路是“先调工具 A,结果回来再调工具 B”,但 A 和 B 明明没有依赖关系。改成并行同时发出,延迟从两个 RTT 压缩到一个 RTT。这里要做的是在业务编排层把工具依赖图画清楚:有依赖的串行,无依赖的合并到同一个阶段并行执行。
结合实测:一个典型的“用户提问 → 检索知识库 → 查询业务数据 → 汇总答案”流程,在串行调用 3 个工具时耗时约 2.8 秒;压成“2 个独立工具并行 + 1 个串行依赖”后,降到了 1.6 秒;加上连接预热,最终稳定在 1.2 秒左右。延迟优化不是玄学,就是把每一层可省的时间都抠出来。
4.3 MCP 网关与本地代理:把远端调用变成内网调用
第三层优化在架构层面。如果你有多个业务系统需要共用一个远程 MCP server,我强烈建议在中间加一层MCP 网关。
网关的作用有三个。第一是收敛连接——所有 MCP client 只需要连网关,网关统一管理到下游工具 server 的连接,连接数从 N×M 变成 N+M。第二是协议转换与归一化——下游不兼容 MCP 的老系统,由网关做适配,对外暴露统一的 MCP 端点。第三是实现安全策略的集中化——鉴权、审计、限流统一放在网关层,而不是每个 client 各做一套。
更实用的玩法是本地代理。把网关部署在和业务服务相同的内网环境,client 通过 localhost 或者内网地址访问网关,网关再通过内网专线访问真正的远端 server。这一步把原本跨公网的高延迟往返,变成了内网低延迟往返。有一次我把某个远端 MCP server 通过本地代理接入,工具调用 RTT 从平均 180ms 降到了 25ms,效果非常明显。
网关层的配置要点是做好超时控制和连接池管理。建议给不同优先级的工具设置不同的超时阈值:读类工具短超时(3~5 秒)、写类工具长超时(10~30 秒),避免单个慢工具拖死整个网关。
4.4 缓存层:工具结果与应用语义的双层缓存
最后是缓存。工具调用场景里缓存分两层,很多人只做了第一层,忽略了第二层。
第一层是结果级缓存:相同参数的工具调用直接返回缓存结果。MCP 的tools/call天然适合做结果缓存,因为入参有明确的 Schema,参数值序列化后可以直接当缓存 key。但要注意:不是所有工具都该缓存。只读、无副作用、结果时效性要求低的工具适合缓存(比如节假日查询);写入、有副作用、强实时性的工具严禁缓存(比如发通知、更新数据库)。
第二层是语义级缓存,这层更值钱但也更高级。它不是缓存“某个具体参数的结果”,而是缓存“某类问题的答案”。比如用户频繁搜“新员工入职流程”,第一次走完工具调用链得到完整答案后,把答案按语义指纹存入缓存;后续相同或相似的 query 直接命中,完全不用再走 MCP 链路。这层缓存可以放在应用层的问答服务里,也可以放在网关侧做一个带向量检索的缓存模块。
我实际工程中的缓存命中率在 30% 左右,别小看这个数字——命中一次就省了一次完整的模型推理加工具调用链,成本不是省一点半点。
注意:加上缓存后,一定要考虑失效机制。最稳妥的做法是对工具结果做 TTL 缓存,对语义答案做版本化缓存(语义指纹 + 过期时间),两者都必须在更新工具侧数据时能主动失效。否则缓存带来的“快”会变成数据陈旧风险的来源。
5. 实测对比:一个内部项目的优化前后数据与复现方法
前面讲的策略听上去都有道理,但落到实处到底能省多少?我拿我们内部某内容分析平台的真实优化过程来拆解,附上可复现的数据和方法。
5.1 测试场景与指标定义
被测场景是一个典型的“多工具问答”任务:用户提问“帮我查一下某项目近 30 天的调用量趋势,并对比上月数据,顺便把异常波动的日期列出来”。这个 query 会触发三个工具:数据库查询工具、指标对比工具、异常检测工具。
指标定义:
- Tools_Tokens:单次请求注入模型上下文的工具描述 token 数
- TTFT(Time To First Token):从客户端发起请求到收到首个生成 token 的耗时
- Total_Latency:从发起到完整回答生成的耗时
- Return_Size:工具返回体进入上下文的累计大小(token 数)
测试环境:远程 MCP server,HTTP 传输,30 个可用工具,并发 20 压测 10 轮取中位数。
5.2 优化前后对比数据
优化前(全量工具注入 + 串行调用 + 无缓存):
| 指标 | 优化前 | 优化后 | 变化 |
|---|---|---|---|
| Tools_Tokens | 11200 | 1750 | -84.4% |
| TTFT | 2.1 s | 0.9 s | -57.1% |
| Total_Latency | 5.4 s | 2.2 s | -59.3% |
| Return_Size | 8200 | 1300 | -84.1% |
| 成本(单会话模拟 5 轮) | 基准 | 约 60% | -40% |
优化动作拆开看是这样分配的:
- 动态工具裁剪(query 预分类 + Top-K 选择):Tools_Tokens 从 11200 降到 3200,贡献最大
- 描述压缩(精简 description 到保留核心字段):Tools_Tokens 从 3200 降到 1750
- 连接预热 + 并行调用:TTFT 从 2.1s 降到 1.4s,Total_Latency 从 3.6s 降到 2.4s(此处为去掉上下文收益后的单独贡献)
- 网关本地代理 + 结果缓存:剩余延迟从 2.4s 降到 2.2s,同时带来源源不断的成本节省
最让我意外的是 Return_Size 的变化。原来那个数据库查询工具直接返回全量表数据,优化后改成先做一次聚合摘要(只返回趋势、均值、异常点),返回体 token 数直接砍到原来的 1/6。上下文里的垃圾少了,模型回答时也更精准,没有出现因为信息不全而瞎编的情况。
5.3 可复现的实验要点
如果你也想在自己的项目里复现这套对比数据,我建议按这几个步骤来:
- 先做基线测量:在改动任何东西之前,记录当前的工具数量、Tools_Tokens、TTFT、Total_Latency。不要凭感觉判断快慢,先拿到数字。
- 一次只改一个变量:先只做动态裁剪,跑一遍;再叠加描述压缩,跑一遍;最后加连接复用和缓存。分开改才能知道每个策略的独立收益,否则混在一起你根本不知道是谁起了作用。
- 用固定 Prompt 集测:准备 10~20 个覆盖不同工具类别的测试 query,每次都跑同样一组,比较中位数和 P95,而不是拿一两条口碑当结论。
- 盯着返回体大小看:优化上下文字数时,别只盯着 tools 定义,工具返回体往往才是上下文膨胀的元凶。建议在工具层统一加一个返回字段白名单机制,控制每条返回的大小。
这套实验方法最大的价值是帮你建立“量化优化”的思维——先把开销数字化,再谈怎么降。不然很容易在“优化了个寂寞”的状态里打转。
6. 我在生产环境踩过的坑和应对方式
最后一部分不谈框架,只讲我在实际接入 MCP、把工具调用链路推向生产环境的过程中,遇到的四个典型问题。这些问题很琐碎,但只要碰到一次就能浪费你半天到一天。
6.1 stdio 子进程的生命周期管理
用 stdio 方式跑本地 MCP server 时,最大的坑是子进程“没人管”。如果你的业务进程异常退出,子进程不一定会跟着退出,会变成僵尸进程;反过来,如果业务进程启动了 MCP server 却忘了在结束时关闭,也会造成进程泄漏。
我的做法是给 MCP client 封装一个生命周期管理类:启动时记录子进程 PID,结束时先发quit通知,等 1~2 秒后检查进程是否退出,没退出再强制 kill。另外设置空闲超时,超过 10 分钟没有工具调用就主动回收子进程,等下次需要时再重新拉起。宁可多一次冷启动,也不要让一堆僵尸进程占着系统资源。
6.2 超时阈值的设定:Too Many 坑
MCP 工具调用默认的超时阈值往往很乐观。我刚接入时试过默认 60 秒超时,表面上没问题,实际上有个工具偶尔会卡在慢查询上,每次都要等满 60 秒才报错,整个体验被拖垮。
不同工具应该用不同超时策略。我在网关层按工具类型分了三级:快速查询类 5 秒超时,中等计算类 10 秒,大数据导出类 30 秒。另外针对流式响应做了专门处置——有些工具会边算边返回,不能等到全量返回才算超时,要按“首字节时间 + 整体时长”双阈值来卡。
6.3 工具返回的“大 JSON”问题
前面提过一次返回体过大,这里我再展开说。工具返回大 JSON 不只是占上下文的问题,还会引发模型侧的“幻觉式总结”——模型看到一大坨看不懂的数据时,有时会自作聪明地补全缺失信息,导致结果根本不可信。
我现在的红线是:工具返回进入上下文的内容,必须经过摘要化处理。具体做法是给工具 server 加一个统一的返回包装层,内部做两件事。第一,字段白名单:只保留业务真正需要的字段,把置信度、原始日志、调试信息全挡在外面。第二,长度截断:文本类型字段超长就截断,列表类型字段超长就做聚合(比如只返回 Top-N 和总量统计)。这样工具返回体天然是“模型友好”的,不需要在模型侧做二次清理。
6.4 避免“为了 MCP 而 MCP”的过度设计
最后这条算原则性提醒。不是所有工具都适合接成 MCP server,也不是每次调用都必须走完整 MCP 链路。
我在项目里见过两种过度设计。一种是把一个其实只需要几行代码的“查配置”操作也包装成 MCP 工具,然后为了它配网关、配鉴权、配监控,管理和维护成本远超收益。另一种是让模型去调用“本来应该应用层直连”的服务,比如每次提问都让模型调一个查询今天日期的工具——这不是 MCP 该干的活,直接在系统 prompt 里给模型注入当前日期就够了。
我的个人体会是:MCP 工具调用的边界应该画在“模型需要动态决策、动态触发”的场景上。稳定的逻辑用代码写死,动态的工具选择才交给 MCP。至于要不要加缓存、要不要上本地代理,都取决于工具的访问频率和对延迟的敏感度,先量化再决策,别拍脑袋。
在这套边界之下,MCP 确实让工具接入从一个“人肉维护文档和路由”的脏活,变成了“定义工具、注册端点、结束”的轻量操作。希望这篇内容能帮你把 MCP 的链路看得更清楚,也少走几段我走过的弯路。