最近跟着教程折腾MCP,用的官方Python SDK写了个简单的服务器,暴露了一个查询本地SQLite的函数。在Claude Desktop里配好mcpServers,启动后日志显示“Transport closed”,连initialize握手都没完成。我确认了URL是http://localhost:8000/mcp,也试过用--transport sse和streamable-http两种模式,都不行。
MCP服务器连自家API都连不上,是我配置姿势不对还是这玩意儿本来就难搞?
全部回复
共 47 条碰到过类似问题,后来发现多半是路径或者协议没对齐。Claude Desktop对streamable-http的支持其实挺挑剔的,你试试把URL末尾的/mcp去掉,或者检查下SDK版本是不是太新,有些版本默认走的是新协议但客户端只认旧的。另外Transport closed大概率是握手时content-type没配对,可以抓包看看响应头。我之前折腾半天,最后换回stdio模式就稳了,虽然麻烦点但至少能跑通。
我也踩过类似的坑,最后发现是Claude Desktop对本地回环地址的CORS检查特别严格,你试试在服务器端手动加上Access-Control-Allow-Origin: *响应头,很多握手失败都是这个原因。另外确认下Python SDK版本,新版streamable-http的endpoint路径可能不是默认的/mcp,得在代码里显式注册一下路由。如果还不行,直接抓包看下HTTP响应体,错误信息往往比日志更明确。
我之前也卡在过这个Transport closed上,后来发现是Claude Desktop对streamable-http的支持还不太稳,换到旧版SSE反而一次通了。你试试把服务端改成--transport sse后,在配置里显式写"type": "sse"而不是只给URL,有时候它默认走的是stdio。另外确认下防火墙或者代理有没有拦localhost,我这边就是公司VPN把本地端口搞挂了,关了VPN立刻就好。
我上周也踩过这个坑,后来发现是Claude Desktop对localhost的权限校验特别严格,得在配置里加--host 0.0.0.0或者把URL改成127.0.0.1试试,有时候它默认解析IPv6就会握手失败。另外你检查下Python SDK版本,官方最近改过SSE的握手逻辑,老版本跟新客户端不兼容。如果还不行,建议直接抓包看下HTTP响应头,Transport closed多半是content-type没设对,streamable-http要求application/json,SSE则是text/event-stream,这两个很容易被忽略。
八成是Claude Desktop对本地回环地址有限制,试试把localhost换成127.0.0.1或者关掉代理。
我之前也卡在这个地方好久,后来发现是Claude Desktop对本地回环地址的权限卡得比较死,试试把localhost换成127.0.0.1,或者直接在配置里加个环境变量允许非TLS连接。另外建议先用curl手动调一下你的MCP端点,确认SSE握手响应头真的对了,很多教程都忽略了Content-Type要精确匹配,差一个分号都会Transport closed。
还有个小坑,如果你用的是streamable-http,Claude Desktop目前对它的支持其实还不算稳定,反而老版的SSE模式兼容性更好些。我最后是换成自定义传输才跑通的,感觉官方SDK的默认行为跟桌面端实现有点脱节,实在不行就抓包看看请求到底发出去没有,多半是路径或端口被本地代理劫持了。
我也踩过类似的坑,后来发现大概率不是你的问题。MCP这玩意儿现在文档和实现脱节得厉害,官方SDK的streamable-http和Claude Desktop内置的支持版本经常对不上,建议直接抓包看下握手请求的具体报错内容,比看日志有用。另外你SQLite路径是不是相对路径?服务端工作目录不同会导致初始化直接崩,这个也很隐蔽。能连上但transport closed的话,试试把响应头里的Content-Type改成application/json,有些SDK会严格校验这个。
我也遇到过类似的坑,后来发现是Claude Desktop对本地回环地址有严格限制,得把localhost改成127.0.0.1再试试。另外确认下SDK版本和Claude Desktop的MCP协议版本是否匹配,这俩经常悄悄更新导致握手失败。实在不行可以先写个简单的HTTP客户端手动发initialize请求,看服务端到底返回什么错误,别光看客户端日志。
这情况太典型了,八成不是配置问题而是协议细节。你试试把URL路径去掉直接挂到根路由,有些服务端SDK默认不挂/mcp。还有检查下header里Content-Type是不是application/json,streamable-http模式对请求格式要求很死。我之前就是漏了Accept字段卡了半天。
我上次也卡在Transport closed上,最后发现是防火墙把回环端口拦了,虽然本地请求但某些系统策略也管。你可以先curl -X POST http://localhost:8000/mcp带个空JSON看看通不通。另外Claude Desktop最近版本对SSE模式支持有点毛病,建议直接上streamable-http并且把超时时间调大点。
这问题我踩过,先查下Claude Desktop的日志路径,多半是权限或端口绑定问题,跟姿势关系不大。
八成是Claude Desktop对本地回环地址做了限制,试试把localhost换成127.0.0.1,或者关掉代理再跑一遍。
我之前也卡在这好久,后来发现多半是transport协议没对齐。Claude Desktop现在默认走的是streamable-http,但你本地起的服务要是只开了sse,它握手就直接断给你看。建议先单独用curl测一下/mcp端点的响应头,确认返回的是text/event-stream还是application/json,再对应改服务端启动参数。另外检查下有没有代理或防火墙拦了localhost的8000端口,我之前就是被系统代理坑的,加个NO_PROXY=localhost立马就好了。
我之前也卡在这过,后来发现是Claude Desktop对本地host的权限卡得比较死,试试把地址换成127.0.0.1而不是localhost,有时候DNS解析会出问题。另外如果日志里transport closed发生在握手之前,八成是Content-Type没设对,SDK默认返回的可能不是application/json,手动加个中间件强制一下试试。实在不行就开个ngrok转发到公网域名,能绕开一堆本地校验。
之前我也踩过这个坑,大概率不是姿势问题,是MCP的传输层对URL路径和握手头要求特别死。你试试把endpoint直接指到根路径http://localhost:8000/,然后确认下服务端有没有正确实现initialize的响应格式,尤其是protocolVersion字段。另外检查下Claude Desktop是不是走了代理,本地请求被拦了也会出现Transport closed。
我之前也卡在这地方过,后来发现是Claude Desktop的mcp配置里URL末尾不能带路径,得直接写http://localhost:8000,让服务端自己挂到/mcp上。另外你是不是用了uvicorn?它默认只监听127.0.0.1,但有时候代理或Docker会把它映射到别的接口,导致握手时连接被重置。建议先用curl手动调一下/mcp端点看返回什么,再排查SDK那边是不是少了@mcp.tool()装饰器(如果没暴露工具,某些客户端会直接断连)。
我之前也卡在这步好久,后来发现是Claude Desktop对localhost的权限卡得严,换成127.0.0.1立马就通了,你可以先试试这个。另外如果用的是streamable-http,记得服务端得显式支持GET和POST两个方法,只写个POST路由的话握手会直接断。还有个小坑是Python SDK版本和Claude Desktop内置的MCP客户端版本得匹配,有时候升级下库就解决了。实在不行抓个包看看握手响应具体报什么错,比猜配置快得多。
我前段时间也卡在过这个握手阶段,后来发现大概率不是协议配置的问题,而是Claude Desktop对本地回环地址的权限管得特别死。你试试把localhost换成127.0.0.1,有时候DNS解析或者IPv6优先会直接导致transport层静默断开。另外你确认过SDK版本和Claude Desktop内置的MCP客户端版本兼容吗?官方Python SDK最近更新挺频繁的,老版本连streamable-http的content-type都校验不过去。还有一个坑是如果你用了uvicorn启动,记得关掉reload模式,那个会fork子进程导致stdio管道错乱。不过说实话,我觉得MCP这玩意儿现在生态还太早期,官方文档全是理想化示例,实际跑起来各种隐式约束,光debug这个握手就够写篇血泪史了。你要是实在搞不定,可以先用npx的官方TS模板跑通一遍,再回头对比Python的实现,两边在header和路径处理上有些微妙差异。
我上周也被这个坑过,后来发现是Claude Desktop对本地地址有沙箱限制,得把localhost改成127.0.0.1或者用ngrok穿透。另外你试试在服务器代码里把CORS中间件加上,很多握手失败其实是预检请求被拦了。还有个小细节,如果用了uvicorn记得开--reload,不然代码改了不生效容易误判配置问题。
顺便说一句,官方SDK的streamable-http模式在Claude Desktop里支持得还不完善,我最后是退回用sse加/sse路径才跑通的。你那个Transport closed日志如果能在服务端看到完整traceback,可以贴出来一起看看,光看客户端日志很难定位。
我之前也卡在Transport closed上,后来发现是Claude Desktop对本地回环地址有安全限制,换成127.0.0.1或者绑到局域网IP反而好了。另外官方SDK里streamable-http的路径要跟服务端注册的路由完全一致,斜杠多了少了都会静默断开。你要是日志开了debug级别,能看到更具体的握手失败原因,别只看默认输出。SQLite那个函数本身没问题的话,八成还是配置细节,建议贴下完整的mcpServers JSON。
这问题我上周刚踩过一模一样的坑,后来发现是Claude Desktop对本地回环地址的权限卡得特别死,试试把URL里的localhost换成127.0.0.1,或者干脆用ngrok暴露个临时公网地址看看能不能通。另外你检查下SDK版本没,官方最近把streamable-http的握手逻辑改过一版,老版本跟新客户端会直接transport closed,升级到最新版说不定就解决了。
说到这个我就想起自己折腾MCP时被坑惨的经历,你那日志只有一句Transport closed其实信息量太少了,建议把日志级别调到DEBUG,看看是不是CORS头没配好——Claude Desktop的请求会带Origin头,本地服务如果没正确返回Access-Control-Allow-Origin,握手直接就被浏览器策略中断了。还有SQLite路径别用相对路径,绝对路径加读写权限,有时候初始化失败也会显示成transport问题。
我倒是觉得这玩意儿现在真没到“开箱即用”的程度,官方文档写得太理想化了。你试试直接用curl模拟一下MCP的initialize请求,看服务端到底返回啥,大概率是Content-Type没设成application/json,或者HTTP状态码不是200。另外检查下Python SDK里那个streamable-http是不是要显式调用app.run(),有时候忘了起事件循环,连接建立了但请求永远挂起,表现就是握手超时。
这个问题我也踩过坑,Claude Desktop对本地回环地址限制很严,试试把host改成0.0.0.0或者用ngrok转发。