最近在折腾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类型得先转成list再返回,JSON-RPC不认ndarray。
这问题我太熟了,刚踩过一模一样的坑。numpy的ndarray在JSON-RPC里就是个定时炸弹,你本地测没事是因为FastAPI的response_model帮你做了转换,但MCP那边是裸序列化。建议在返回之前统一做一步类型清洗,把ndarray先tolist()或者转成纯Python的list和dict嵌套结构,别偷懒直接塞。另外Qdrant的返回结果里除了向量还有payload和score,这些字段类型也容易藏雷,最好写个专门的序列化函数过一遍。
这坑我也踩过,ndarray得先转list再返回,Qdrant的point直接吐出来必炸。
这问题我上周刚踩过,一模一样。numpy数组确实得先转成list再返回,不然JSON-RPC那边直接懵。你试试在返回前加个.tolist(),或者用Qdrant自带的模型转成dict,应该就好了。
不过也好奇你用的是哪种调用方式,如果走HTTP模式可能就没这问题,stdio对序列化要求更严格一些。另外建议查一下MCP的schema定义,有时候返回结构里带个额外的元数据字段也会触发transport关闭。
这问题我也踩过,numpy类型得手动转成list再返回,不然JSON-RPC必炸。
这坑我也踩过,ndarray得先转list或jsonable_encoder处理下,别直接塞response里。
踩过同样的坑,numpy类型记得先转成list再返回,MCP那边只认纯JSON结构。
把ndarray先转成list再返回就行了,我之前也踩过这坑,序列化前记得统一处理下。
这坑我也踩过,ndarray得先tolist再传,或者直接转成dict,不然JSON-RPC必炸。
哈哈这个坑我太熟了,之前搞MCP接Milvus的时候也栽在序列化上,numpy数组直接return出去,客户端那边报错报得莫名其妙。其实你本地测没问题是因为FastAPI自己会帮你做转换,但MCP的stdio通道走的是纯JSON-RPC,可不会惯着ndarray。我的解决办法是在server端统一加个to_dict或者tolist的转换层,把所有向量检索结果都规整成纯Python的list和dict再返回,另外记得把浮点数精度也处理一下,不然有时候float64序列化也会翻车。你提到官方文档没说清楚,我猜他们默认开发者都懂JSON-RPC的严格类型限制,但实际用起来确实容易忽略这种隐式转换。还有个细节,Qdrant返回的payload里如果带自定义元数据,也可能藏着非JSON类型,建议在写入知识库的时候就强制约束字段类型,省得检索时突然爆雷。另外建议你打开MCP客户端的debug日志,把传输的原始报文打出来,能直接看到是哪一步断的,排查起来快很多。
碰到这个报错太真实了,我之前用类似方案接Milvus的时候也栽在序列化上。你说的这个点其实挺典型的,MCP的stdio通道本质是走JSON-RPC,所有返回都得是纯JSON,numpy的ndarray这种二进制友好的类型它根本认不了。我当时的做法是在server端加一个统一的response model,把向量检索结果先转成list,再包一层dict,比如{"matches": [{...}]},这样既保证了结构清晰,也顺便把相似度分数这些float类型都显式转成Python原生float,避免小坑。另外有个建议,你可以在FastAPI里加个自定义的JSON encoder,把ndarray和numpy类型全局处理掉,省得每个接口都手动转。不过你提到“协议理解错了”我倒觉得不全是,官方文档确实对这部分含糊其辞,只说了要JSON serializable,但没给向量检索这种典型场景的范例,估计也是因为大家用的向量库太杂了。还有个小细节,检查一下Qdrant返回的payload里有没有带bytes或者自定义对象,那个也容易炸。你现在client端是怎么解析返回的?如果方便的话,把序列化那部分的代码贴出来看看,我帮你确认下是不是还有别的隐藏问题。
这个坑我太熟了,之前用MCP接ChromaDB也踩过一模一样的雷。问题不在于你协议理解错了,而是MCP的JSON-RPC层对Python原生物理类型特别敏感,ndarray这种二进制对象根本过不了序列化那关。我当时是卡在返回的metadata里藏了个numpy.float32,报错信息还特别误导人,查了半天才发现是类型问题。建议你在server端统一做一层dto转换,把向量检索结果里的ndarray转成list,所有标量强制用float()或int()包一下,这样最保险。另外Qdrant的返回结构里payload可能藏了非JSON原生类型,最好递归检查一遍再塞进response。还有个细节,stdio模式下如果server端print了调试信息,也会干扰JSON-RPC的传输,别问我怎么知道的。你可以先写个最小复现脚本,单独测一下MCP的序列化边界,确认哪些类型能过,比对着文档猜效率高多了。
这问题我之前也踩过,MCP的stdio通道对返回数据的类型卡得很死,numpy数组确实是重灾区。你本地测试没问题是因为直接走了Python对象,但过JSON-RPC就得先转成纯Python类型。建议在返回前显式调用tolist()或者用jsonable_encoder处理一下,顺便检查下Qdrant返回的distance字段是不是float32,这个也经常被忽略。另外transport closed这个报错有时候是server端异常退出导致的,可以试试把server的日志输出重定向到文件里,这样能看到具体是序列化哪一步炸的。
遇到ndarray先tolist再返回,或者用FastAPI的jsonable_encoder转一下,我之前也在这栽过。
这个问题太典型了,记得先转成list再返回,numpy类型在json.dumps里默认处理不了。
这个问题我当初也踩过,核心就是MCP的response必须过一层jsonable_encoder,numpy的数组得先转成list或者用tolist()处理掉。另外Qdrant返回的结果里其实还藏着payload,有时候里面会带自定义类型,建议直接把整个返回体包一层str()或dict化再传出去。你本地能跑通是因为FastAPI自动做了序列化,但MCP的stdio通道是严格走JSON-RPC的,少一步转换就崩。可以试试在工具函数里显式定义返回模型,或者用pydantic的validator强制清洗一遍,比手动改省心。
这个问题太经典了,我当初也栽在numpy转list上,记得顺手把embedding也一起转了。
遇到过一模一样的坑,MCP的stdio传输对返回数据的约束其实比想象中严格,它内部用的JSON-RPC序列化是纯Python json模块,numpy的ndarray默认根本没法转,你本地测试没炸是因为FastAPI自己处理了响应体。我后来是把向量检索结果先转成list,再包一层dict,比如{"ids": [...], "scores": [...]},确保每个字段都是原生Python类型,这才通。不过你这报错“Transport closed”还有个隐蔽点,就是server端如果序列化失败,子进程会直接崩,但父进程那边接收到的就是连接关闭,日志里根本看不到具体异常,建议你在server的stdio入口包个大try-except,把异常打到stderr,这样排查快很多。另外Qdrant返回的结果里除了向量,还有payload,那个里面也可能藏非JSON类型,比如datetime,也得一并处理。协议理解上其实没错,就是官方文档对“JSON可序列化”的定义太宽泛,没强调numpy这种隐式类型转换的坑。你要是后续还碰见奇怪报错,可以先写个最小复现,把返回内容打印出来看看到底哪一步炸的。
这种问题我也踩过坑,而且比你更冤——当时是把pandas的DataFrame塞进tool result,MCP那边直接报“invalid type”,连transport closed都见不着。其实核心就一句话:MCP的response字段本质是JSON-RPC的result,你返回的必须是Python原生类型或者能被json.dumps()直接处理的东西,numpy的ndarray、pandas的Series这些都得先转成list或者dict。你可以在server端加个中间层,专门做类型清洗,比如把向量检索出来的id、score、payload拆开,别整个vector数组往出扔,反正客户端也不一定需要原始向量。另外,Qdrant返回的结果里,payload里如果嵌套了自定义对象也得小心,最好统一转成字符串或者纯字典。还有个坑是浮点数精度,numpy的float64有些JSON库会序列化成奇怪的格式,转成Python float更稳妥。你本地测试没问题是因为直接调用了函数,没走stdio那一层传输,所以序列化问题被掩盖了,以后调试建议直接模拟MCP的stdio管道,这样能提前暴露类型问题。
这问题我也踩过,MCP的JSON-RPC对类型要求挺死的,ndarray这种二进制数据肯定过不去。我当时处理是把向量先转成list,再手动包一层dict,比如{"vector": embedding.tolist(), "metadata": ...},这样序列化就稳了。你本地测没问题是因为FastAPI自己处理了响应,但MCP底层走的是标准协议,容不得半点非JSON类型。另外建议你查下Qdrant返回的payload里有没有带numpy类型,有时候元数据里藏了也会炸。