最近在折腾MCP(Model Context Protocol),想把公司的知识库通过MCP Server暴露给Claude用。我用的FastAPI写了个简单的server,里面调用Qdrant的Python客户端做向量检索。本地测试没问题,但一通过MCP的stdio方式连上客户端,就报“Tool execution failed: Transport closed”。查了半天日志,发现是server端在返回结果时,把numpy的ndarray直接塞进了response里,MCP的JSON-RPC序列化直接炸了。我懵的是,按官方文档说返回类型得是JSON可序列化的,但也没说清楚向量查询结果该怎么包装。是我理解错了MCP的tool输出格式,还是Qdrant返回的数据结构需要额外处理?求大佬指个路,最好能说下你们生产环境是怎么处理的。
MCP服务器接入向量数据库报错,不知道是配置问题还是协议理解错了?
全部回复
共 63 条遇到过,numpy类型记得先tolist()再返回,官方文档这块确实写得不够细。
我前几天也踩过类似的坑,不过用的是LanceDB。MCP的stdio传输对数据格式要求特别严格,它内部走的是JSON-RPC 2.0,任何非标准类型都得提前转换好。你直接把ndarray塞回去肯定不行,numpy的数组连json.dumps都过不去,更别说MCP那层封装了。我当时的解决办法是在返回前统一做一步处理,把向量结果转成list或者直接用字典包一层,比如{"data": embedding.tolist(), "metadata": ...},这样序列化就稳了。另外你提到官方文档没说清楚,其实这块规范里确实只提了“可序列化的JSON值”,但没给具体例子,导致很多人栽跟头。建议你在server端加个自定义的encoder,或者干脆在工具函数返回前强制类型转换,别依赖隐式行为。还有个小细节,Qdrant返回的record里可能带payload,有些字段也是非JSON原生的,比如datetime或者UUID,也得一起处理掉。你那个“Transport closed”报错,本质上就是客户端收到非法响应后直接断开连接,不是网络问题。最好在本地用MCP的debug模式跑一下,它会打印出实际发送的payload,一眼就能看出是哪个字段炸了。我后来是把所有返回对象都过了一遍pydantic模型,强制序列化,再也没出过这问题。
这个问题我前两天刚踩过,numpy数组得先转成list或者用json.dumps处理一下,不然MCP的序列化器根本不认。你可以在返回前对向量结果做个自定义的encode,把ndarray转成base64或者直接转成普通列表,但要注意大向量会很占带宽。另外建议看下MCP的调试模式,把请求响应日志都打出来,能很直观看到哪一步数据格式出了问题。Qdrant返回的payload里可能还藏着其他非JSON类型,最好是统一做一层序列化清洗再返回。
这问题我上个月也踩过一模一样的坑,当时还以为是Qdrant客户端版本的问题,排查了半天才发现就是numpy类型在作祟。其实MCP的JSON-RPC序列化比想象中严格得多,不光ndarray,连Python的datetime、Decimal这类对象都会炸,官方文档确实写得太模糊了。我的处理办法是在server端加一个统一的响应封装层,所有查询结果先过一遍自定义的to_jsonable函数,把ndarray转成list,顺便把浮点数精度也处理好,不然就算不报错,后续Claude解析出来的数据也可能有精度丢失。另外你提到transport closed,这个报错有时候也是因为超时或者数据量太大,如果知识库的向量结果动辄几百条,建议在MCP的配置里把超时时间调大,或者干脆改成streamable HTTP模式,stdio对长连接的处理确实不太友好。还有个小细节,Qdrant返回的payload里如果有嵌套的dict结构,里面也可能藏了numpy类型,最好递归处理一遍。你要是后面调试还遇到类似问题,可以试试在server端捕获所有异常后把原始错误信息打印到stderr,MCP的日志有时候会把真正的异常原因吞掉。
这问题太典型了,我当初接ChromaDB也踩过一模一样的坑,numpy类型在JSON-RPC里就是个隐形炸弹。你试试在返回前统一做一次类型转换,比如把向量和metadata拆开,所有float32先转成float,list再包一层dict结构,别直接丢原始查询结果。另外确认下Qdrant返回的payload里有没有嵌套特殊类型,有时候Python客户端会自动带上bytes或UUID,这些也得手动处理。反正记住一个原则:MCP的response边界上只留纯Python基础类型,其余全在server里消化干净。
这问题我也踩过坑,MCP的序列化比想象中严格,numpy数组肯定过不了JSON-RPC那关。我当时是在返回前统一调了tolist(),再包一层dict结构,就没再出过transport closed。不过你用的Qdrant,建议直接查它官方有没有现成的MCP adapter,别自己硬撸协议,省得后面还得处理嵌套类型和metadata的兼容性。另外,stdio模式下日志要记得打到stderr,不然客户端那边啥也看不见,排查起来特别被动。
这问题我也踩过坑,Qdrant返回的向量和payload里经常混着numpy类型,MCP的序列化层走JSON-RPC确实不认。我当时是写了个转换函数,把所有ndarray强制tolist,顺便检查了numpy标量类型,全转成Python原生类型才通。你本地能跑是因为FastAPI的jsonable_encoder帮你兜底了,但MCP那边是直接硬序列化,不惯着。另外建议把返回结构包一层,比如统一成{content: [...]},避免顶层就是裸数组,协议解析有时候对结构敏感。
这问题太典型了,MCP的JSON-RPC传输层对numpy类型零容忍,我之前也踩过一模一样的坑。建议你在server端统一做一层序列化转换,把ndarray先tolist()再返回,或者干脆用Qdrant自带的返回格式,别直接裸传向量数据。另外可以试试在返回前用json.dumps先验证一下能不能过,能省好多排查时间。顺便问下,你用的MCP SDK版本是哪个?有些老版本对类型校验更严格,升级到最新可能也会好很多。
这坑我也踩过,MCP的JSON-RPC对numpy类型零容忍,记得用tolist()或者直接转成纯Python的float列表。另外Qdrant返回的结果里还藏着UUID和payload,得自己写个序列化器,把向量和元数据拆开处理。建议你在server端加个类型检查中间件,提前把所有非JSON原生类型都打回原形,不然排查起来真的头大。
这事儿我上周刚踩过一模一样的坑,也是Qdrant返回的向量带着numpy类型,直接把MCP的序列化层干懵了。说白了MCP的JSON-RPC约束比普通HTTP接口严格得多,它要求所有进出数据都得是纯Python原生类型,ndarray这种二进制友好的东西根本没戏。我当时处理的办法是封装一层response模型,把所有向量和相似度分数都转成list和float,再手动把metadata里可能藏着的numpy标量也一并清干净,反正就是递归检查一遍。另外你提到的“Transport closed”其实是个很模糊的报错,我后来在server端加了全局异常捕获,把序列化错误单独打印出来才定位到问题,不然光看日志真的会以为是管道断了。还有个建议,别直接用Qdrant的原始返回结构做响应,最好定义自己的输出schema,这样以后换向量库或者加字段都方便。不过话说回来,官方文档确实对这类边缘情况写得太简略,我翻了半天也没看到关于numpy类型转换的明确说明,估计得靠社区自己填坑了。
遇到过,numpy数组得先tolist()再塞回去,json.dumps默认搞不定这玩意儿。
之前踩坑后写了个序列化helper,专门处理ndarray和datetime,省心不少。
这种坑太典型了,建议返回前统一转成list或jsonable_encoder处理,顺手把Qdrant的payload也过一遍。
查了下官方issue,确实有类似案例,要手动把ndarray转成list再返回,或者用jsonable_encoder处理一下。
这问题我上周刚踩过一模一样的坑,numpy数组必须显式转成list或者jsonable_encoder处理一下,不然MCP的pydantic校验直接就不认了。你可以在返回前统一做一次类型转换,或者干脆把Qdrant的返回结果包个自定义响应模型。另外建议看看MCP的debug日志,它会明确告诉你哪一层的序列化挂了,比看server端日志省事很多。还有个小细节,如果用的是Qdrant的filter结果,记得把向量本身也去掉,那玩意一般不需要回传。
这问题我熟,之前搞MCP接Milvus也踩过类似的坑,numpy类型在JSON-RPC里基本必炸,官方文档确实没细说。我当时是先把向量结果转成list,再包一层自定义的response model,让pydantic帮你序列化,就没再出过这错。另外你检查下stdio的超时设置没,有时候不是序列化问题,是返回数据太大把传输通道堵死了。
这确实是序列化老坑,numpy类型得先转成list再返回,文档里没写透。
这问题我也踩过,MCP的JSON-RPC对类型要求挺死的,numpy数组确实得先转成list或者直接返回字符串,不然transport必炸。你可以在工具函数出口统一加个序列化处理,把ndarray转成嵌套列表,另外Qdrant返回的payload里可能也藏着非JSON类型,建议递归清理一遍。我猜你本地测试没走stdio所以侥幸过了,协议本身没问题,就是数据格式没对齐。
顺便问下,你server端有没有用pydantic做response model?如果定义了response_model,序列化顺序可能会在MCP封装之前就报错,这个坑我调了两天才发现。
这问题我也踩过坑,MCP的序列化比想象中严格,numpy类型直接返肯定不行。建议你在server端统一转成list或者用jsonable_encoder处理一下,别偷懒直接塞原始类型。另外Qdrant返回的payload里可能也藏了非JSON字段,最好在返回前递归清洗一遍,调试时先打印一下实际类型。
这题我熟,numpy数组得先tolist()再返回,光json.dumps也救不了你。
这问题我也踩过,numpy类型得先转成list再返回,别直接塞进response里。