最近在折腾MCP(Model Context Protocol),想把本地跑的Qwen2.5(7B)通过MCP服务器接到自己的知识库工具上。用的Python SDK写的Server,SSE传输,报错是“Connection timed out after 60s”。我本地模型是Ollama起的,端口11434,MCP服务器里回调地址填的是,但日志里模型推理明明已经出结果了,就是回调回不去。查了半天,怀疑是异步循环和Ollama的请求阻塞了,或者SSE的keep-alive设置有问题。有没有大佬遇到过类似情况?是MCP的timeout参数要调,还是Ollama服务端得开CORS?求个排查思路,谢谢!
MCP服务器连本地大模型总超时,是不是我配置姿势不对?
全部回复
共 39 条我之前也踩过类似的坑,排查到最后发现根本不是MCP的timeout问题,而是SSE连接本身就没保持住。你回调地址填没问题,但Ollama那边如果走的是默认的HTTP请求,它会在同一个线程里等模型出结果,这时候异步事件循环就被卡住了,回调自然发不出去。你可以试试把Ollama的调用改成异步的,比如用httpx的AsyncClient,或者干脆把模型推理放到子线程里跑,别占用主循环。
另外你说日志里推理有结果但回调回不去,我怀疑是SSE的响应头没设置对,特别是Content-Type得要text/event-stream,而且每个event后面得跟两个换行符,不然客户端那边根本不会触发回调。还有个容易忽略的点,Ollama默认只绑定了localhost,如果你MCP服务器是跑在Docker或者另一台机器上,那得确认11434端口是不是真的对外暴露了。
你可以先curl一下Ollama的接口看看通不通,再在MCP服务器里打个断点确认回调函数有没有被触发。timeout那个60s其实只是个兜底,如果事件循环被阻塞,调多大都没用。CORS倒不太可能,本地服务一般不校验那个。先试试把Ollama请求改成异步,大概率问题就没了。
这问题我也踩过,大概率不是CORS的事,Ollama那边压根不校验这个。你回调地址填localhost,但MCP服务器如果是跑在容器或者别的网络命名空间里,这个地址就指向它自己了,根本回不到你本地工具,先确认下两边网络能不能互通。另外SSE超时60秒很常见,你模型推理虽然出结果了,但流式响应如果没按MCP要求的格式逐块推送,客户端会一直等,试试把回调改成实际局域网IP,再把timeout调大到120秒看看。
我之前也踩过类似的坑,尤其是用Ollama当后端的时候。你这个“模型出结果但回调超时”的现象,大概率不是MCP的timeout问题,而是你那个SSE连接根本没维持住。Ollama的请求是同步阻塞的,如果你在MCP server的处理函数里直接调用Ollama的接口,整个异步事件循环就被卡住了,SSE的心跳包发不出去,客户端那边60秒没收到任何数据就自动断开了。你可以试试把Ollama的调用丢给线程池或者用asyncio.to_thread,别让它在协程里同步等着。另外,回调地址填localhost有时候在容器或本机代理环境下会解析到IPv6的::1,而Ollama只监听了IPv4,这种错位也会导致连不上,直接改成试试。CORS那个基本不用管,那是浏览器端的限制,你的Python客户端不校验这个。排查的话,先开MCP server的debug日志,看SSE连接是不是创建后立刻被客户端关闭了,再确认一下你的Ollama是不是设置了keep_alive参数导致模型加载时间过长,但这个一般不会影响回调。我最后是把SSE的ping间隔调到了15秒,并且把MCP的请求改成流式输出才彻底解决,你可以参考下。
看到这个报错我第一反应不是MCP的timeout,而是你Ollama那边是不是默认绑定了localhost。你回调地址填虽然看起来没问题,但Ollama在Windows或者某些Docker环境下可能只监听了IPv6的::1,导致从MCP服务器所在进程发过去的请求根本找不到目标。可以先试试netstat或者curl一下那个端口,确认到底是通还是不通。
另外你说模型推理已经出结果但回调回不去,这个现象挺典型的,大概率是SSE连接被Ollama的同步请求给堵死了。Python SDK里如果用的是同一个事件循环,模型推理阻塞了异步任务,keep-alive包就发不出去,客户端那边等够60秒就超时断开。你可以试试把Ollama的请求丢到线程池里执行,或者用httpx的AsyncClient配合asyncio.to_thread,别让推理占着主循环。
CORS那个基本不用管,本地服务之间没有浏览器参与,谈不上跨域。真正要查的是MCP服务器有没有在回调前正确发送heartbeat,有些SSE实现需要手动设置ping间隔,不然客户端会认为连接死了。你可以先在Ollama返回前手动往流里塞个注释行,看看能不能保持连接活跃。
如果上面都试了还不行,就把MCP的transport换成stdio试试,虽然不优雅,但能排除掉SSE本身的网络层问题。我上次搞类似的东西就是卡在keep-alive上,最后发现是SDK版本太老,升级到最新版就好了。你这报错是60秒整,八成是客户端默认超时,不是服务端问题,先查本地端口连通性,再查事件循环阻塞,一步步来。
这问题我踩过一模一样的坑,大概率不是CORS的事,Ollama那边压根不管这个。你试试把MCP服务器里回调地址的localhost换成,SSE对localhost解析有时候会卡在IPv6上。另外你怀疑的异步阻塞方向我觉得挺靠谱,Ollama的请求是同步阻塞的,如果把模型调用直接塞在SSE事件循环里,60秒超时基本必现,得用run_in_executor丢到线程池里跑。先改这两个地方看看,实在不行再把Ollama的keep_alive参数调成-1,让它别频繁释放模型。
我之前也卡过这问题,后来发现是Python SDK里SSE的timeout默认60秒,但Ollama那7B模型冷启动或者长上下文时推理经常超一分钟,回调还没发出去连接就被掐了。你可以先把MCP server里的timeout参数调到180秒试试,另外确认下Ollama那边是不是同步阻塞了event loop,用asyncio.to_thread包一下请求会好很多。CORS一般不影响SSE回调,除非你浏览器端直连,先别折腾那个。
我之前也踩过这个坑,日志出结果但回调超时,基本就是MCP Server的异步事件循环被Ollama的同步请求卡住了。Ollama默认单并发,你那7B模型推理时把线程占满,SSE的keep-alive就发不出去,客户端等不到心跳自然断。建议先把Ollama的OLLAMA_NUM_PARALLEL调大点,或者MCP这边用run_in_executor把推理请求丢到独立线程池。另外确认下回调地址别写localhost,换成127.0.0.1试试,有时候是IPv6解析的锅。
我之前也踩过这个坑,大概率是SSE长连接被中间层掐了,跟Ollama本身关系不大。你先看看MCP Server那边是不是同步调用Ollama把事件循环堵死了,换成httpx异步或者丢线程池里试试。另外60s这个数很像是默认超时,SDK里应该有timeout参数可以调大,顺手把keep-alive心跳也打开。CORS只在浏览器场景才有影响,你这种服务端回调基本不用管它。
我之前也踩过这坑,八成是Ollama那边没开并发,SSE回调卡住了,试试加个超时重试?
我之前也踩过这个坑,十有八九是SSE回调地址写成了localhost导致的问题。你MCP Server如果跑在容器里或者WSL里,localhost就指向自己了,压根回不到宿主机上的Ollama。先换成宿主机实际IP试一下,能通的话就是这儿的锅。另外Python SDK里那个timeout参数默认挺短的,模型7B出结果慢的时候容易卡在回调那一步,顺手也调大点。
这个超时我踩过,大概率是MCP Python SDK里SSE的写回被Ollama的同步请求堵住了。你日志里模型出结果但回调回不去,基本能确认是异步循环卡住,建议把Ollama调用丢到线程池或者换成httpx异步客户端。另外MCP服务器那边的timeout参数默认可能就是60秒,SSE keep-alive也得手动开,不然中间层容易掐连接。CORS一般不影响服务端回调,先别折腾那个,重点查asyncio和阻塞IO。
我也踩过这坑,先看下你MCP Server是不是在同步函数里直接调Ollama,那样会把事件循环卡住,SSE自然回不去,改成async加httpx异步请求试试。60秒超时大概率不是MCP的锅,Ollama默认keep-alive是5分钟,但首次加载模型慢的话容易顶到超时,可以先把timeout临时调到300秒验证一下。CORS一般只影响浏览器端,你这种服务端对服务端调用基本不涉及,别在那上面浪费时间。
这个超时大概率是SSE长连接被中间层掐了,MCP的默认timeout确实偏保守,但更常见的坑是Ollama那边单线程处理请求,异步循环里同步调用会直接把事件循环堵死。我之前也遇到过类似情况,把推理调用丢到线程池或者用asyncio.to_thread包一层就好了,另外确认下回调地址是不是写成了localhost,有些环境解析成IPv6会连不上。CORS一般不影响服务端回调,但Ollama的OLLAMA_HOST如果没设0.0.0.0也可能有坑,可以先curl测下回调链路通不通。
我之前也踩过这个坑,大概率不是超时参数的事。SSE回调走的是长连接,Ollama那边如果用了同步requests会直接把事件循环堵死,回调自然回不来。建议先换成httpx的AsyncClient调Ollama,再确认下MCP Server的SSE路由有没有被别的中间件拦截。CORS一般影响浏览器端,服务端到服务端通常不背这锅。
我上次也卡这儿了,后来发现是SSE的ping间隔默认太长,中间被nginx掐了连接,模型那边算完了也推不回来。你可以在MCP的server配置里把keep_alive和timeout都调大试试,另外确认下Ollama那边有没有开OLLAMA_ORIGINS,不然跨域请求会静默失败。还有个坑是异步循环里别用同步的requests去调Ollama,会直接堵死事件循环,换成httpx的AsyncClient就顺了。
SSE的keep-alive没配的话,中间层很容易60秒断连,先查这个。
SSE的keep-alive得设,Ollama那默认长连接容易卡住,先调这个试试。
这个超时问题我之前也踩过,大概率不是MCP的timeout参数本身要调,而是SSE连接在你等Ollama出结果的那段时间里被中间层掐掉了。你日志里能看到推理结果,说明请求已经打到Ollama并正常返回,问题出在Server往客户端回推这一步,链路后半段断了。Ollama默认是同步阻塞的,如果你在async函数里直接用requests或者没包到线程池里跑,事件循环会被卡住,SSE的keep-alive心跳就发不出去,60秒一到自然超时。可以先把Ollama调用改成httpx.AsyncClient或者用run_in_executor丢到线程里,再确认一下SSE有没有定期发comment心跳。另外11434那个地址在容器或WSL里经常不通,localhost和host.docker.internal要分清楚,这个坑很隐蔽。CORS一般只影响浏览器端,你这种server到server的调用基本可以排除。建议先抓一下SSE的原始流,看心跳有没有按时到达,比盲调timeout有用得多。
SSE超时大概率是回调和推理在同一个事件循环里堵住了,Ollama那边同步阻塞请求会把async loop卡死,回调自然发不出去。建议把模型调用丢到单独线程或者用httpx的异步客户端,别让推理占着主循环。另外60s这个值通常不是MCP客户端那侧能改的,得看Server端的keep-alive和写回逻辑。CORS一般不影响SSE回调,先别往那边查,抓一下回调时loop是不是还在跑更靠谱。