最近在折腾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里跑MCP经常是网络模式问题,试试host模式或者检查下容器内外的端口映射对不对。
你这报错更像vllm的API地址没配对,MCP连的是默认端口吧?
之前也踩过这个坑,折腾了两天才发现是Docker网络模式的问题,bridge模式下容器里的MCP server监听的是172.17.x.x,宿主机那边连的时候地址没映射对。你可以先试试host模式跑一下,排除网络层面的干扰。handshake failed很多时候不是协议版本的事,反而是防火墙或者代理在中间截了包,尤其是公司网络环境下。另外allow_origin一般针对浏览器场景,本地客户端连接通常不用设,除非你是走HTTP SSE方式。还是不行的话,把vllm的日志和MCP server的debug输出贴出来,对比下握手时双方发的初始化请求,大概率能看出是哪边先断的。
这问题我上周刚踩过,vllm和MCP的握手失败大概率不是协议版本,是vllm的API格式跟MCP默认的chat completions对不上。你试试在MCP server的配置里把model_provider改成openai兼容模式,然后显式指定一下api_base指向vllm的/v1路径。另外Docker跑的话,检查下容器网络模式是不是host,我那次就是bridge模式下端口映射把MCP的health check搞挂了,连带着握手一直超时。
对了,你用的MCP server是官方Python版还是社区那个Rust版?这俩对tool的JSON Schema校验严格度不一样,7B模型偶尔会吐出格式不规范的参数,Rust版直接拒连。可以先在本地裸跑一下MCP server,看日志里有没有具体的报错字段,别只盯着客户端提示。
我之前也卡在handshake failed上,折腾半天发现是Docker网络模式的问题,bridge模式默认不暴露宿主机回环地址,MCP客户端连localhost会直接撞墙。你试试把容器跑在host网络下,或者把服务地址改成0.0.0.0再映射端口。另外Qwen2.5-7B用vllm起的时候,--api-key和--served-model-name这两个参数要是没设对,MCP握手时认证头也会对不上,顺手检查下。allow_origin一般只影响浏览器场景,你本地客户端应该不用管。
这报错我之前也踩过,折腾了两天才发现是vllm版本太老,MCP握手时带的协议字段它不认。建议先检查下vllm和MCP SDK的版本,最好都用最新的,另外Docker里记得把host网络模式开开,端口映射有时候会吞掉TCP的某些包。allow_origin那个一般不用设,除非你前端跨域才需要。
我之前也踩过这个坑,而且折腾了快两天。你用的vllm暴露的OpenAI兼容接口,MCP server默认走的是streamable HTTP那套,但vllm的response格式跟MCP期望的JSON-RPC封装其实有细微差别,特别是tools定义那块,如果你没显式把vllm的chat template里加function calling支持,握手阶段模型能力声明就对不上。另外你提到Docker,我猜你MCP server容器里访问宿主机vllm时用的可能是localhost,但在容器内那指向的是容器自己,必须用host.docker.internal或者桥接IP,这个报错信息很误导人,看起来像协议问题实际是网络路由失败。allow_origin那个倒不是必须的,除非你从浏览器端直接连,但如果你用的是Python SDK,其实可以抓包看看握手请求返回的具体body,vllm有时候会返回200但带个非标准字段,MCP的sse解析直接崩。还有个冷门点,Qwen2.5-7B的tokenizer对特殊token处理跟MCP的initialize阶段发ping有冲突,导致流式响应提前断开,你可以试试在vllm启动参数加--enable-auto-tool-choice,或者干脆用llama.cpp的server模式替代,虽然速度慢点但兼容性好很多。最后建议你直接开MCP的debug日志,环境变量设MCP_DEBUG=1,能看到具体是哪个字段校验失败,比盲猜快多了。
我之前也踩过这个坑,后来发现是Docker网络模式的问题,vllm默认绑的是localhost,但MCP server在容器里访问不到宿主机,得用host模式或者把vllm的host改成0.0.0.0。另外你用的Qwen2.5-7B可能对MCP的tool calling支持不完整,建议先试试不带tools的纯对话流程,排除是不是协议扩展字段导致的握手失败。还有个冷门的点,有些版本的MCP server会对client发来的initialized消息做严格校验,你查一下客户端有没有正确发送协议版本号。
换个思路,你先别急着改配置,把MCP server的日志级别调到DEBUG,看下handshake失败时具体卡在哪一步,是HTTP upgrade没成功还是JSON-RPC消息格式不对。我之前遇到过类似问题,结果是因为Docker里跑了nginx做反代,把WebSocket的Upgrade头给吞了。另外vllm的API前缀路径如果和MCP默认的endpoint不一致,也可能导致连接被拒,试试直接curl一下MCP server的地址,看返回是不是预期内容。
我之前也卡在handshake failed上,后来发现是vllm的API路径和MCP默认配置对不上,官方文档写的base_url有时候得手动改成/v1。你试试在MCP server的配置文件里把model_provider的endpoint直接指到vllm的完整地址,别用默认的localhost映射,Docker里网络模式用host应该能省掉不少麻烦。另外Qwen2.5-7B本身是支持工具调用的,但MCP那边对response_format有要求,得确认下vllm启动参数里有没有开--enable-auto-tool-choice,不然握手时功能协商那步就容易挂。端口冲突我倒没遇过,但如果你同时开了多个MCP实例,检查下health check的路径是不是被其他服务占了。
我之前也卡在handshake failed上好久,最后发现是vllm的api server和MCP server之间用的HTTP版本不一致导致的。你Docker里跑MCP的时候,记得检查一下容器网络模式是不是host,bridge模式下端口映射偶尔会出这种玄学问题。另外Qwen2.5-7B本身是支持工具调用的,但MCP协议头里的model_name必须和vllm启动时--served-model-name完全一致,哪怕大小写差一个字符都会握手失败。allow_origin那个倒不是必须的,除非你前端跨域访问,但本地调试建议还是加上,省得后面踩坑。还有个小细节,MCP的jsonrpc版本要跟client端匹配,有些老版本client会强校验init请求的protocolVersion字段,你可以抓包看看具体报错内容。我之前在GitHub issue里翻到一个解法是手动指定--enable-auto-tool-choice,但那个是针对function calling的,不一定适用你场景。建议先用curl直接测vllm的/v1/chat/completions接口确认模型响应正常,再排查MCP层,这样能快速定位是模型侧还是协议侧的问题。
我之前也卡在这玩意儿上,后来发现是MCP server的协议版本和vllm的OpenAI兼容接口没对齐,尤其是tools这块儿,得确认你用的MCP SDK版本是不是支持vllm返回的tool格式。你试试在Docker里直接curl一下server的health接口,看返回的model name是不是和客户端请求的完全一致,有时候本地路径映射错了也会导致handshake直接挂。另外allow_origin在非浏览器场景下一般不用设,但如果你客户端是Node或者Python的独立进程,检查下有没有设置host=0.0.0.0,只监听localhost的话容器外肯定连不上。我最后是把vllm的--enable-auto-tool-choice打开才解决的,你可以试试看。
vllm和MCP的握手失败大概率不是模型版本问题,之前我跑Llama3.1也遇到过,后来发现是Docker网络模式没设成host,默认bridge会导致端口映射错乱,你可以先检查一下容器内外端口是否真的通了。另外MCP协议对模型输出格式很敏感,Qwen2.5系列最好显式指定chat_template,不然它默认的格式可能跟MCP期望的JSON-RPC对不上。allow_origin那个只在浏览器场景才需要,你如果是本地客户端可以先排除。实在不行开一下MCP server的debug日志,看它具体卡在哪个阶段,比瞎猜配置高效多了。
vllm的OpenAI兼容接口和MCP握手本来就有坑,建议先直连测试下端口通不通,再查下Docker网络模式是不是host。
我之前也踩过这个坑,后来发现是Docker网络模式的问题,vllm暴露的端口在容器外访问不到,handshake失败只是个表象。试试把MCP server和vllm跑在同一个network里,或者直接用host模式。另外Qwen2.5-7B对MCP的tool calling格式支持确实有点迷,建议先用最简单的echo tool测通链路,再往上加业务逻辑,不然排查起来太乱了。
我之前也踩过这个坑,折腾了快一周才找到原因。你用的vllm和Docker组合,大概率不是协议版本问题,是MCP server的host配置默认绑了127.0.0.1,Docker容器里跑的时候客户端从宿主机访问会握手失败——得把host设成0.0.0.0,然后检查容器端口映射是不是真的把内部端口暴露出来了。allow_origin那个是给浏览器端的,你如果是本地客户端调用,不设也没关系,但如果你用了Node或Python的MCP SDK,它们有时会校验HTTP头里的Origin,保险起见可以先用curl模拟一下握手请求,看看返回的响应头里有没有Access-Control-Allow-Origin。另外Qwen2.5-7B在vllm下如果加了--api-key参数,MCP server的client配置里也得带上对应的token,不然握手阶段认证就挂了。我最后是把我Docker的network模式改成host,然后MCP server里显式指定了model_name和max_tokens,才彻底跑通。你现在可以先用docker logs看MCP server启动时的具体日志,一般握手失败前会有WARNING提示,比如“missing required field”之类的,比看错误码直观很多。
我之前也踩过这个坑,后来发现大概率不是MCP协议版本的问题,而是vllm的OpenAI兼容接口和MCP默认的tool calling格式对不上。Qwen2.5-7B本身是支持function call的,但vllm在serve的时候需要显式加--enable-auto-tool-choice和--tool-call-parser参数,不然模型返回的tool_call结构MCP解析不了,handshake自然就卡住了。你可以先试试不进Docker,直接在宿主机跑一个最简单的MCP server连vllm,排除容器网络模式的问题,我之前就是docker的bridge模式导致client访问不到server的healthcheck端口。另外allow_origin这个一般只在浏览器场景需要,你如果是本地客户端调试可以先忽略。还有个小细节,MCP的初始化握手会先拉一遍tools列表,如果你给模型配的tool数量太多或者参数schema里用了oneOf这种复杂类型,vllm的tokenizer可能会截断响应,导致JSON解析失败,我后来把tools精简到5个以内就稳定多了。建议你开一下MCP server的debug日志,看握手失败具体是卡在HTTP头还是消息体解析,这样能定位是配置还是代码路径问题。
我之前也卡在handshake failed上,后来发现是vllm的openai兼容接口和MCP默认的json schema版本对不上,尤其是Qwen2.5这种带tool calling的模型,得在server端显式指定api类型。你试试在MCP server的启动参数里加个--transport sse或者--transport stdio,有时候默认的http传输方式在Docker里会跟宿主机的代理环境冲突。另外allow_origin那个确实要设,但得跟客户端实际请求的host和端口完全一致,包括协议头,不然握手包会被拒。我最后是直接看了MCP仓库里最新的issue,把vllm的--enable-auto-tool-choice打开才解决的,要不你也试试这个开关?
大概率是vllm的OpenAI兼容接口和MCP握手格式对不上,试试把server的base_url直接指到vllm的根路径。
我之前也踩过这坑,后来发现是Docker容器里的hostname和MCP握手的SNI校验对不上,不是端口问题。你试试在启动容器时加个固定的hostname,或者直接用IP连,别用localhost。另外vllm的--served-model-name参数如果和MCP配置里的model名不一致,也会握手失败,这个最隐蔽。
之前用vllm配MCP也踩过这坑,handshake failed大概率不是配置问题,是vllm的OpenAI兼容接口和MCP默认的streamable HTTP握手方式对不上。你试试在MCP server那边把transport改成sse,或者检查下vllm是不是开了--enable-auto-tool-choice,这个会影响协议协商。另外Docker跑的话记得把host.docker.internal映射对,别用localhost。
大概率是Docker网络模式问题,vllm和MCP server得在同一网段,试试host模式或者把端口映射改成桥接。