很多人第一次看 MCP,会以为它就是「换了个名字的 HTTP 接口」,于是照着调 REST 的习惯去写超时、重试和并发,结果连接莫名其妙断掉、日志里什么都看不到。这篇不重复讲协议兼容层和客户端怎么配,只讲工程上怎么把两者放对位置:什么该走 HTTP,什么该走 MCP,以及在超时、并发、降级和排错上分别要盯哪些点。
很多人第一次看到 MCP,第一反应就是「这不就是把 REST 接口换了个名字嘛」,于是照着调 HTTP 的老习惯去写超时、重试和并发,结果跑一会儿连接就莫名其妙断了,翻日志还什么都看不出来。问题往往不在代码写错,而在于把两个定位完全不同的东西当成了同一种东西来用。
这篇不重复讲协议兼容层怎么配、客户端怎么接,只讲工程上怎么把两者放对位置:什么场景该走 HTTP,什么场景才值得上 MCP,以及超时、并发、降级、排错这几个环节分别要盯哪些点。判断标准给到,动作给到,剩下的数字你自己去站内页面核对。
定位不同:一个是取数据,一个是给模型装手
HTTP 描述的是「谁来请求、请求哪个资源」,服务端无状态,一次请求一次响应,用完即走,请求之间互不依赖。MCP 描述的是「模型能调用哪些能力」,它把工具、资源和提示模板按能力清单暴露出来,由客户端会话统一管理,调用顺序本身也是模型决策的一部分。
- HTTP 的抽象单位
- 资源与端点,重点是这次请求要拿到什么
- MCP 的抽象单位
- 能力清单与会话,重点是模型这次能选哪些动作
- 谁来决定调用顺序
- HTTP 由你的代码决定,MCP 由模型在会话里决定
所以判断标准很简单:你要的是拉一份数据、提交一次任务,走 HTTP;你要的是让模型在对话里自己判断该调哪个能力、按什么顺序调,才考虑 MCP。反过来,如果只是一条固定几步、参数完全确定的调用链,HTTP 加一段编排代码更省事,硬套 MCP 只会多出一层会话管理成本。
传输和连接生命周期:长会话 vs 短请求
HTTP 每次请求自带完整语义,服务端可以随便水平扩容,挂了一台换一台就行。MCP 则是在连接层之上维护一次会话,双方要先交换初始化参数、对齐能力清单,会话存续期间这条连接要保持可用。这意味着同样叫「断了」,两者要处理的事情完全不是一回事。
| 对比维度 | HTTP | MCP |
|---|---|---|
| 生命周期 | 单次请求,无状态 | 一次会话,有状态 |
| 初始化 | 不需要,直接发请求 | 要先握手、对齐能力清单 |
| 失败重试 | 重发一次通常就好 | 先分清是会话没建起来还是中途断的 |
| 扩容方式 | 服务端水平扩,客户端无感 | 会话和某一端绑定,迁移要重连 |
| 典型误用 | 把长连接业务硬拆成轮询 | 把单次查询塞进会话,白背保活成本 |
排查顺序建议固定成四步,别跳:先确认传输方式选对没有,再确认进程或地址能连通,接着看初始化握手有没有过,最后才去看具体某个工具的调用参数。顺序跳着查,最常见的后果就是在一个参数上反复改,其实问题在第一层。
不适用的情况也说清楚:本地同机运行的服务,握手和传输开销都很低,如果照远端服务的保守参数去设保活和重连,反而会让它显得很慢很迟钝。纯无状态的单次查询更不要为了统一而塞进 MCP 会话。
超时与并发:两套参数不要混用
HTTP 的超时一般分连接、读取、整体三档,按接口的预期响应时间去设,好理解也好调。MCP 除了单次调用的超时,还要额外考虑初始化握手和会话空闲存活这两个时间。这两个设短了,表现往往不是「立刻报错」,而是「一开始好好的,跑了一会儿就连不上」,很难一眼看出是参数问题。
- 第一步:先量基线到 /tools/speed-test 把接口延迟实测一遍,把实测值记下来,超时都要在这个基础上留余量,别照抄别人的秒数。
- 第二步:分档设超时HTTP 按连接、读取、整体三档设;MCP 再加初始化握手和会话空闲两项,后两项要明显大于单次调用。
- 第三步:控制并发MCP 客户端对同一会话的并行调用更敏感,能串行就串行;必须并发时先确认服务端对同一会话的并发承受度,再放开。
- 第四步:压一轮再看跑一段持续调用,观察是调用级报错先出现还是会话级断连先出现,哪种先出现就调哪一档参数。
并发上两者也不一样。HTTP 靠服务端限流加客户端连接池就能控制住;MCP 更多是看同一会话能不能承受并行。调试阶段的建议是:先串行跑通,再逐步放并发,每次只动一个变量。不适用的情况是本地短脚本验证,这时按远端保守参数去调纯属自找麻烦。
降级、日志与验证:出问题时先看哪一层
降级设计上,HTTP 侧的选择比较直接:换源、换模型、走缓存。MCP 侧更实际的做法是让单个能力「先不可用」,而不是整个会话崩掉,也就是把工具级失败和会话级失败分开处理。混在一起处理,一次工具报错就会把整条链路拖下水。
- 会话级失败:重连、重建会话,别在原会话上原地重试
- 调用级失败:换参数、换工具或标记该能力暂不可用
- 日志字段至少留:会话 ID、调用 ID、失败层级、最后一次成功的动作
- 重试要标清发生在连接层还是业务层,否则排查时会把两次失败当成一次
- 原始报错一律保留,别在上报前做「友好化」裁剪,裁剪掉的那行往往是关键
日志的核心目标是能回答三个问题:这次是会话级失败还是调用级失败、失败前最后一个成功的动作是什么、重试发生在连接层还是业务层。字段设计可以抄 /tools/verify#probes 里探针的记录思路。验证也别只测「通不通」,要按顺序测三步:能不能建会话、能不能看到能力清单、能不能真的调一次并拿到结构化结果,三步都过才算接好。
最后提醒一句,不同中转站和不同客户端对 MCP 会话的保活策略、并发上限差别不小,具体数字别照抄某一份文档,去 /sites 看各家当时的口径和检测结果更靠谱;价格和模型条目这类会变的东西,统一到 /price-watch 核对当时的数字。
下一步把本文的四步排查顺序和两套超时参数对照着过一遍,再拿你手上的实际链路压一轮,看看断连先出现在哪一层,比反复读协议文档有用得多。→常见问题
MCP 是不是就是 HTTP 的封装,学会 HTTP 就够了?
不能说等价。底层传输可能确实跑在 HTTP 之上,但 MCP 多出来的是能力发现和会话生命周期这两层:调用前要先知道对方有哪些能力,调用时要维持会话状态。只懂 HTTP 你调得通单个接口,但不知道为什么连接会中途掉、为什么同一个服务在不同客户端里表现不一样。
接入时该先做 HTTP 还是先做 MCP?
看你有没有「让模型自己选能力」的需求。如果调用路径在代码里是写死的,先把 HTTP 接口调稳,把超时、重试和日志补齐;只有当需要模型在运行时决定调用顺序、或者同一套能力要给多个客户端复用时,再引入 MCP。反过来做,往往是多花时间调试一个还没被需要的抽象层。
为什么我的 MCP 连接刚开始正常,跑一段时间就失效?
多数是会话保活相关的问题,而不是协议本身。常见原因有三类:空闲时间超过服务端允许的会话存活期、客户端没有做重连、以及单次调用超时设得比实际响应还短。建议先看日志里最后一次成功调用到断开的间隔是固定值还是随机的,固定值基本指向存活期设置,随机值更可能是超时或网络抖动。