最近在折腾MCP(Model Context Protocol)部署本地大模型,按照官方文档一步步来,但启动服务后客户端连接时总报“handshake failed”错误。我试了调整context_window大小和修改tools配置,还是不行。环境是Ubuntu 22.04,用vllm部署的Qwen2.5-7B,MCP server跑在Docker里。怀疑是不是模型版本和MCP协议版本不兼容?或者需要额外设置allow_origin?看GitHub issue里有人说是端口冲突,但我检查过了。求大佬指点一下,这玩意儿是不是有隐藏前提条件?先谢过!
MCP部署大模型时总是报错,是配置问题还是我方法不对?
全部回复
共 186 条握手失败这个问题我上周刚踩过坑,折腾了三天最后发现是Docker网络模式的问题。你如果用的是bridge模式,MCP server在容器里监听的端口和宿主机映射对不上,客户端握手时拿到的服务端地址是容器内网IP,自然就失败了。建议直接用host网络模式跑容器,或者检查一下MCP server返回的endpoint是不是你宿主机能访问的地址。
另外vllm和MCP的兼容性确实有个坑,vllm默认的API响应格式和MCP期望的tool calling schema不完全一致,尤其是Qwen2.5这种需要指定tool_choice参数的模型。你试试在vllm启动参数里加--enable-auto-tool-choice和--tool-call-parser,不然模型生成的tool调用可能直接被MCP端判定为无效响应,握手阶段就断了。
还有个小细节,MCP的handshake阶段会发一个initialize请求,里面带protocolVersion字段。如果你装的MCP SDK版本太新,而server端用的协议版本旧,也可能报这个错。检查一下两边版本号,强制统一到同一个大版本试试。
端口冲突你检查过就排除这个可能了,不过allow_origin一般不是握手失败的原因,那是浏览器跨域才需要的。真要再排查的话,把server端的日志级别调到debug,看握手时到底卡在哪个request上,比猜配置快多了。
我之前也卡在这过,后来发现是vllm的--enable-auto-tool-choice没开,MCP那边tools定义全被拒了。你先试试在启动命令里加上这个参数,或者看下docker日志里有没有tool相关警告。另外,Qwen2.5-7B对MCP的streaming模式支持有点怪,如果还不行就强制关掉streaming,用普通response模式跑一下。端口冲突这个确实容易误判,但docker网络模式是host还是bridge也会有影响,你检查下映射对不对。
我也踩过这个坑,最后发现是Docker网络模式的问题,bridge模式下容器里vllm的host得写宿主IP而不是localhost,你可以先curl一下容器里的服务通不通再排查握手。另外Qwen2.5-7B的模板和MCP默认的tool calling格式确实有出入,建议看看vllm日志里有没有报tool parse的warning,这个比版本兼容更常见。allow_origin一般不用动,除非你前端跨域了。
我之前也卡在这问题上一整天,最后发现是vllm的api key没传给MCP server。你试试在Docker环境变量里加VLLM_API_KEY,或者检查下是不是用了旧版MCP SDK,Qwen2.5对工具调用的格式要求比较严格,新老版本协议对tool schema的解析差挺多的。另外allow_origin那个一般只在浏览器跨域场景才需要,纯客户端直连基本不用管。如果还不行,把服务端日志打出来看下具体是哪一步断的,比盲调配置有效。
我之前也踩过这个坑,handshake failed大概率不是版本兼容问题,而是MCP server和vllm之间的传输层没对齐。你试试把Docker里的MCP服务改成host网络模式跑,别用默认bridge,端口映射有时会悄悄改掉协议头。另外Qwen2.5-7B用vllm的话,记得在启动参数里加--enable-auto-tool-choice,不然工具调用格式会跟MCP的JSON-RPC对不上。allow_origin倒是次要的,除非你前端跨域,否则默认localhost就够了。要是还不行,把vllm的日志开到debug级别,看握手时是不是卡在初始化tools那一步。
我之前也卡在handshake failed上,后来发现是vllm的API地址写成了localhost,但MCP server在Docker里得用宿主机IP才行。你试试把base_url改成172.17.0.1这种网关地址,有时候比改协议版本更管用。另外Qwen2.5-7B的tool calling格式跟MCP默认的有点差异,可以看看日志里是不是卡在tools定义解析那一步。如果还不行,把Docker的网络模式改成host再跑一遍,能省掉好多端口映射的幺蛾子。
这报错我之前也卡过一阵子,后来发现是vllm的API和MCP默认的传输格式对不上,尤其是Qwen2.5系列在tool calling的response schema上跟官方示例有差异。你试试在MCP server端把transport改成streamable-http,然后显式指定requested_model_name跟vllm里注册的模型别名完全一致,别省略版本号。另外Docker里跑的话,检查一下容器映射的端口是不是绑到了127.0.0.1,客户端从宿主机访问会连不上,得用0.0.0.0或加个network_mode=host。allow_origin那个一般是给浏览器端用的,纯命令行客户端大概率不是这个原因。
大概率是Docker网络模式问题,host和bridge模式下MCP握手行为不一样,试试加--network host。
vllm的API路径默认是/v1,MCP里base_url得带全,少了这个也容易握手失败。
碰到过类似的,当时卡了我一整天,最后发现是Docker网络模式的问题,bridge模式下容器内vllm的host和端口映射对不上,MCP握手直接超时。你可以先用host模式跑一下排除网络因素。另外Qwen2.5-7B对MCP的tool calling支持确实有点怪,建议把tools配置里的strict_schema关掉试试,有些模型对严格JSON schema支持不好。还有个小坑,如果vllm开了--enable-auto-tool-choice,MCP那边得手动配tool_choice,不然容易握手后立刻断连。
我之前也卡在handshake failed上,后来发现是Docker网络模式的问题,vllm和MCP server得在同一个bridge网络里,不然握手包根本传不过去。你可以先docker network ls看看,然后跑的时候加--network参数试试。另外Qwen2.5-7B对MCP的tool calling支持其实还行,但记得在启动vllm时加上--enable-auto-tool-choice,不然模型返回的格式MCP解析不了。allow_origin那个倒不是必须的,除非你前端有跨域需求。如果还不行,建议抓包看下具体是TLS层挂的还是应用层挂的,两种情况排查方向完全不同。
试试把MCP server的host从127.0.0.1改成0.0.0.0,Docker里默认绑localhost很容易握手失败。
这问题我上周刚踩过类似的坑,vllm的OpenAI兼容接口和MCP握手时对header的校验挺严格的。你试试在MCP server的Docker启动命令里加个--host 0.0.0.0,然后确认下客户端连的是不是容器映射出来的端口,有时候docker网络模式是bridge的话会默认走代理导致握手失败。另外Qwen2.5-7B用vllm的话记得开--enable-auto-tool-choice,不然tools配置会被静默忽略,报错日志又不会明说。allow_origin那个一般只在浏览器场景才需要,本地客户端不用管。要是还不行就抓包看下TCP层,多半是MTU问题在作怪。
我之前也卡在handshake failed上,后来发现是Docker里MCP server的host绑定了127.0.0.1,而客户端从宿主机访问时根本连不上,改成0.0.0.0就好了。你vllm和MCP server是不是都跑在容器里?如果是,得确认两个容器网络是通的,别光看端口占用。另外Qwen2.5-7B用vllm的话,MCP协议的传输格式得匹配,试试把server端的transport类型从stdio改成sse或http,有些版本默认不兼容。我那时候还特意看了下MCP的Python SDK版本,太老的话也会出这种问题,升级到最新版再跑一遍,说不定就通了。
我之前也被这个handshake failed卡了好久,后来发现问题根本不在模型配置上,而是vllm和MCP server之间的HTTP/2协商问题。你试试在启动vllm的时候显式加上--api-key参数,哪怕随便设一个,有时候MCP的TLS握手会因为这个莫名其妙地失败。另外,如果你MCP server跑在Docker里,检查一下容器内的proxy环境变量是不是被继承了,我之前就是公司网络代理污染了localhost的请求,搞得我一度怀疑人生。关于allow_origin,这个通常只在浏览器端调用时才需要,你用客户端直连的话可以先排除掉。不过你提到Qwen2.5-7B,我印象里这个模型对工具调用的响应格式有点特殊,MCP在解析某些function call时会因为模型返回的JSON结构不标准而握手中断,你可以把日志级别调到DEBUG看看是不是卡在“capabilities negotiation”这一步。如果方便的话,试试换一个更老实的模型比如Llama-3.1-8B做交叉验证,能快速定位是不是模型侧的问题。端口冲突那个issue我也看过,但更多时候是docker的network_mode设成了host导致vllm和MCP抢同一个端口,你检查下docker-compose里有没有显式映射端口。最后实在不行,直接在宿主机上裸跑MCP server,排除掉容器网络这一层,我赌五毛是这里的问题。
我之前也卡在handshake failed上,后来发现是Docker网络模式的问题,默认bridge模式下MCP客户端访问不到宿主机映射的端口,改成host模式或者用docker-compose里extra_hosts指定一下就好了。另外vllm的API返回格式跟MCP期待的有些差异,特别是tools的schema,建议先用curl直接调vllm的接口看看返回结构,再对比MCP文档里的要求。Qwen2.5-7B本身没问题,但MCP版本最好用最新的0.2.x,老版本对openai兼容接口支持不完整。allow_origin那个一般不用设,除非你是从浏览器直接连。
这报错我当初也踩过,vllm和MCP的握手机制确实容易对不上,尤其是Qwen2.5系列有时会带额外的chat template头,导致MCP解析失败。你先试试把MCP server的host改成0.0.0.0而不是默认的localhost,Docker里经常是网络模式的问题。另外allow_origin大概率要设成*或者你的客户端域名,不然浏览器端CORS直接卡住握手。如果还不行,建议抓一下TCP包看是TLS层挂的还是HTTP层挂的,这俩排查方向完全不一样。
我之前也踩过这个坑,后来发现是vllm的api server和MCP的握手协议里对response格式要求不一致,尤其是Qwen这种带tool_calls字段的模型,vllm默认返回结构会少个id。你可以试试在MCP server那边把transport改成sse模式,或者给vllm加个--enable-auto-tool-choice参数,之前有人这么解决过。另外Docker里跑的话,检查下容器网络是不是host模式,有时候端口映射会导致握手包被截断。
我之前也卡在handshake failed上,后来发现是Docker里MCP server的host绑定了127.0.0.1,容器外根本访问不到,改成0.0.0.0就好了。你vllm和MCP server是不是也在不同网络命名空间?先确认下两边能不能直接curl通,别急着调协议版本。另外Qwen2.5-7B本身对MCP支持没问题,我跑过,大概率还是网络或health check路径的问题。allow_origin那个一般只有浏览器场景才需要,本地客户端不用管。
我之前也遇到过一模一样的handshake failed,折腾了两天才发现是Docker网络模式的问题,vllm和MCP server不在同一个network里,握手包根本传不出去,你试试用--network host跑容器。另外Qwen2.5-7B的tokenizer对MCP的JSON-RPC格式有点挑,建议把max_model_len设成和context_window一样,别用默认值。还有个小坑,如果你在Ubuntu上开了防火墙,记得放行vllm的端口,不然客户端连上去瞬间就被reset了。要不你先把Docker日志拉出来看看,是不是在STDIO阶段就断了,那个报错信息会比客户端那边明确很多。
vllm的api server默认不开streaming的话,MCP那边拿response格式会解析失败,报handshake不一定是握手问题,你抓包看看是不是返回了非SSE的json。之前我也卡这,后来在vllm启动参数里加--enable-auto-tool-choice --tool-call-parser hermes才通,Qwen2.5的tool calling格式跟MCP默认的不太一样。
这报错我之前也遇到过,docker网络模式是bridge的话,宿主机访问容器里的MCP服务要用映射端口,别直接用容器内IP。另外检查下MCP client端连的是不是http而不是ws,vllm的openai兼容接口走的是前者的协议。实在不行把vllm的日志打到debug级别,看它到底卡在哪一步。
allow_origin那个倒不是必须的,除非你前端跨域。我怀疑是MCP的stdio和streamable http两种传输模式搞混了,你docker里暴露的端口对应的是哪种?vllm那边如果没开--api-key,MCP默认会带个空token过去,有些版本直接拒了。你试试在MCP server的配置里显式加个假token,哪怕随便填几个字符。
握手失败大概率是版本问题,Qwen2.5-7B的tokenizer对MCP的function calling支持需要特定模板,你检查下vllm