最近在玩Cursor的Agent模式,想着把MCP服务接上去试试,结果折腾了两天……服务端用的是FastMCP的Python SDK,本地跑没问题,但通过Cursor的MCP配置连上后,Agent一调用工具就报“connection closed”或超时。我试过改transport为stdio和sse,也检查了端口没冲突,日志里看不出明显错误。网上搜了一圈,有人说要加心跳包,有人说Cursor的MCP实现还不稳定。有没有大佬遇到过类似情况?还是说MCP和Cursor的版本匹配有问题?我用的Cursor 0.45.x,MCP SDK 1.2.0。求指点,感谢!
MCP对接Cursor后,Agent总是调用失败,是我配置姿势不对吗?
全部回复
共 152 条agent模式对sse连接超时很敏感,试试把transport改成stdio,或者检查下FastMCP的keepalive设置。
同款折腾,我换成了MCP的0.9.x版本反而稳定了,感觉Cursor对新SDK的兼容性确实有点迷。另外你试试把transport强制指定成stdio,然后在Cursor的agent配置里加个--no-watch参数,我这么搞以后连接断开的情况少了很多。如果还是不行,建议看看FastMCP的日志级别调到DEBUG,有时候真正的报错被吞了。
巧了,我上周也踩过这个坑,折腾了两天才发现是FastMCP的SSE模式和Cursor的Agent连接池管理有冲突。你换成transport=stdio试试看,同时记得把Cursor的MCP配置里的timeout调高到30秒以上,默认的10秒太短了。另外检查一下你的MCP SDK版本,1.2.0有个已知的WebSocket关闭问题,升级到1.2.2应该能解决Connection closed报错。日志里看不出错误的话,建议在FastMCP服务端加上--debug参数启动,能看到更详细的握手阶段信息。我目前用的是Cursor 0.46.0配合MCP SDK 1.2.3,stdio模式稳得很,但SSE偶尔还是会断。心跳包那个方案我试过,加个每15秒的ping确实能缓解,但治标不治本,感觉还是Cursor这版MCP实现有内存泄漏。
这个问题我上周刚踩过坑,跟你几乎一模一样的配置组合,最后发现是FastMCP SDK 1.2.0在SSE模式下默认的心跳间隔跟Cursor那边的空闲超时对不上。如果你用的是SSE传输,可以试试在启动FastMCP服务时显式设置一下--heartbeat-interval 15,默认好像是30秒,Cursor那边可能等不了那么久就断开了。另外建议把Cursor升级到0.46.x,我升完以后stdio模式就稳了很多,感觉老版本对MCP的握手协议处理有点bug。还有个小细节,检查下你的MCP server启动命令里有没有加--log-level debug,有时候日志里会漏掉关键的连接重置信息,我上次就是看到[Errno 104] Connection reset by peer才发现是防火墙把端口给拦截了,虽然你说了端口没冲突,但最好用lsof -i :端口号确认下实际监听的进程对不对。如果你用的是Windows,还得注意Python的asyncio事件循环在子进程里可能跟Cursor的Electron环境打架,我切到WSL2就再也没出过connection closed。
建议检查下Cursor的MCP插件版本,0.45.x确实有已知的连接问题,降级到0.44.x试试。
这个问题我也遇到过,把FastMCP的日志级别调到DEBUG看看,多半是SDK版本跟Cursor不兼容。
看到这个真有点共鸣,我之前也卡在这个问题上好几天。你的配置看起来没问题,问题很可能出在FastMCP的SDK和Cursor的MCP实现之间的兼容性上。我查过Cursor的更新日志,他们0.45.x版本对MCP的支持确实还在迭代,尤其是SSE传输模式下,连接保持机制做得不够完善,很容易出现超时断连。建议你试试把transport换成stdio,然后给FastMCP的服务端加个显式的健康检查端点,这样Cursor在调用前能确认连接状态。另外,网上说的心跳包其实挺管用的,你可以在FastMCP的server初始化时设置个keepalive参数,大概30秒一次。还有个小细节,检查下你的MCP SDK版本,1.2.0有点老了,最新的1.3.1修了不少连接相关的bug。如果还不行,可以考虑降级到Cursor 0.44.x试试,那个版本反而更稳定些。
你这配置没问题,就是Cursor的MCP对SSE支持还不行,换回stdio再调下超时时间试试。
这配置没啥问题,大概率是Cursor那边MCP的兼容性坑,试试把超时时间调大点或者换个transport。
这配置折腾两天确实挺崩溃的,但你这个现象我太熟了。我之前用0.44.x配FastMCP也碰到过一模一样的connection closed,后来发现是Cursor对stdio的进程生命周期管理有bug,它会莫名其妙把子进程杀掉。你试试把transport改成sse之后,服务端是不是还保持监听状态?如果sse也超时,那大概率不是传输层问题,而是Cursor那边对工具返回的JSON Schema校验太严格,稍微有点格式不标准就直接掐断连接。另外你用的SDK 1.2.0跟Cursor 0.45.x可能有兼容性坑,我建议你降级到FastMCP 0.9.x试试,这个版本在社区里跟Cursor配合的案例最多。还有个偏方,在你启动MCP服务前先手动curl一下健康检查端点,确认服务真正起来了再让Cursor连,有时候日志没报错但进程其实已经僵死了。最好把工具数量先精简到一个,排除是某个工具定义导致整体崩溃。最后问下,你日志里有没有看到什么“context cancelled”之类的关键词?那个指向的是调用超时而不是连接问题,处理思路完全不同。
我最近也踩过类似的坑,不过是在Claude Desktop上。MCP这玩意儿现在最大的问题就是生态碎片化,每个客户端对传输层的实现细节都不太一样。你那个connection closed,我猜大概率不是SDK版本的问题,而是Cursor的Agent在发起调用时,FastMCP默认的初始化握手流程没走完就断了——尤其是SSE模式下,Cursor好像不会主动维持长连接,服务端那边还没把session建立起来,请求就已经超时了。我后来是把服务端改成了streamable-http模式,然后给每个请求都带上显式的session id,才勉强稳定下来。另外你可以试试在FastMCP那边把日志级别调到DEBUG,看看是不是有未捕获的异常被静默吞掉了,有时候错误在服务端直接打出来比在Cursor这边看“connection closed”要直观得多。还有一个讨巧的办法,就是在工具函数里加个重试机制,第一次调用失败后等几百毫秒再试一次,往往能绕过去。
这配置问题我上周也刚踩完坑,跟你情况几乎一模一样。后来发现跟版本关系真不大,主要是FastMCP默认的stdio模式下,Cursor那边对子进程的生命周期管理有点粗暴,Agent空闲一会儿就给你把连接断了。你可以试试在启动MCP服务时加个--keep-alive参数,或者手动写个简单的TCP转发,把stdio包一层长连接,我这么搞完就稳定多了。另外你日志里没报错,但可以开一下FastMCP的debug级别,看下是不是Cursor那边发了ping但SDK没自动回pong——1.2.0这个版本我印象里心跳处理有bug。还有个骚操作,把工具定义里的description写得特别长,Cursor会以为是个大任务,超时阈值会放宽,虽然不优雅但实测有效。你要是还不行,可以降级到MCP SDK 1.0.0试试,我群里有人这么解决的,不过新功能就没了。总之先别怀疑自己配置,这玩意儿兼容性确实还没打磨好。
你这配置基本没问题,大概率是Cursor对SSE长连接支持有bug,换streamable-http试试。
我之前也卡这,后来降级到0.43.x就稳了,你可以先排除下版本问题。
遇到过类似的情况,不过我是用TypeScript写的MCP server,连Cursor的时候也时不时抽风。你那套FastMCP本地跑没问题但通过Cursor就挂,多半不是SDK本身的事,我感觉是Cursor对MCP的进程生命周期管理太粗暴了,尤其stdio模式下,Agent那边一超时或者主动断连,子进程就被杀了,根本来不及优雅退出。你可以试试把transport换成SSE然后手动用curl模拟一下Agent的调用序列,看是不是长连接空闲一段时间后服务端就自己关了,如果是,那基本就是心跳的问题。另外版本匹配这块,Cursor 0.45.x的MCP客户端实现确实有点老,对MCP协议里一些新字段的容错很差,比如工具返回大payload或者带特殊字符就可能直接炸。我这边最后是直接在FastMCP那边把响应体做了压缩,然后加了个简单的ping/pong逻辑,才稳定下来。你要是方便的话,可以把Cursor的日志级别调成debug,看看具体断在哪个握手阶段,比瞎猜要快。
试试把FastMCP降到0.9.x,我这边升到1.x后跟你一模一样的症状,降版立刻稳了。
换个思路,别用stdio,直接上streamable-http,Cursor 0.45对这块支持好很多。
你这组合我试过,0.45.x的Cursor对MCP的SSE支持确实有点迷,建议直接把transport换成stdio再试试,然后检查下FastMCP版本,1.2.0可能跟Cursor的握手协议有兼容问题。另外connection closed大概率是超时设置太短,Cursor那边默认的请求超时只有10秒,你可以在MCP server里把响应时间调长一点,或者给工具加个异步处理。我上次是改用streamable-http才稳定下来的,你可以往这个方向排查下。
我最近也被这个坑过,后来发现是FastMCP的SSE模式在Cursor里对keep-alive处理有问题,换成streamable-http或者直接开个独立的MCP代理进程就稳了。另外你检查下Cursor的MCP超时设置,默认可能太短,调到60秒以上试试。版本的话0.45.x确实对MCP支持还不完善,我升到0.46之后基本没再遇到过断连。你SDK用1.2.0应该没问题,但可以试试把日志级别调到DEBUG看看有没有握手阶段的报错。
我昨天刚踩完同一个坑,后来发现是FastMCP的SSE响应格式跟Cursor 0.45.x的解析器不兼容,换个思路直接用streamable-http那个实验性传输反而稳了。你试试把SDK降到1.0.x或者升到1.3.x,中间有个版本改过握手逻辑。另外心跳包那个说法有点道理,但更可能是Cursor对空闲连接回收太激进,可以在服务端加个keep-alive定时器。
另外你本地跑没问题说明服务本身是通的,重点看下Cursor的MCP日志,有个--verbose模式能打印出具体请求头,对比下它和官方文档里的差异。我之前就是靠这个发现少了几个必填字段。
我之前也卡在这块儿,后来发现是FastMCP的stdio模式在Cursor里对子进程的启动路径有要求,得用绝对路径或者设置cwd,不然就报connection closed。你可以试试把MCP server的启动命令改成python -m的方式,别直接写脚本路径,另外确认下Cursor的MCP配置里环境变量是不是都传对了,尤其是PATH。
还有个思路是检查下FastMCP的日志级别,开到DEBUG看下实际断在哪个环节,有时候是Agent并发调用时SDK内部的状态机崩了。版本的话我这边0.45.4配MCP 1.2.0倒是能跑通,不过SSE模式确实容易超时,最后我干脆换回了streamable http,反而稳一些。你试试把服务端改成多线程处理,可能跟Cursor的keep-alive机制冲突了。
试试把MCP的transport换成streamable-http,Cursor新版对sse支持有点迷,我这么搞好的。