最近在折腾MCP(Model Context Protocol)框架,想给LLM加个文件系统工具。我用Python写了个简单的MCP服务器,挂载到本地目录,结果客户端一直报“Transport not ready”或者“通道未就绪”。我确认了stdio传输和JSON-RPC格式都没问题,也试了用官方的mcp-cli工具启动,但一调用工具就卡住。是不是我异步处理写得不对?还是说MCP服务器必须用asyncio的run()来启动?网上资料好少,求各位大佬指点一下,感谢!
MCP服务器调用报错“通道未就绪”,有大佬遇到过吗?
全部回复
共 139 条我也在折腾MCP,这个“通道未就绪”的报错确实挺常见的,尤其是在刚开始用stdio传输的时候。我猜你大概率是asyncio的启动方式有问题——MCP服务器确实必须用asyncio.run()来启动主循环,不然事件循环没跑起来,stdio的读写管道就处于未初始化状态,客户端一调用工具自然就卡住了。我之前就是直接同步调用了serve()函数,结果跟你一模一样的报错,改成async def main() + asyncio.run(main())之后就通了。另外检查一下你的JSON-RPC消息里有没有漏掉id字段,MCP的请求和响应必须严格配对,不然客户端也会认为通道没准备好。还有个坑:如果你用的是mcp-cli的stdio模式,记得在启动命令里加上--transport stdio,默认可能是sse模式。你把你服务器启动部分的代码贴出来看看?大概率就是事件循环没正确初始化,或者stdin/stdout被其他库占用了。
大概率是asyncio事件循环没跑起来,试试用asyncio.run(main())启动服务器,我之前也卡这步。
检查下asyncio.run()里有没有创建独立的事件循环,MCP服务端确实需要异步上下文才能正常初始化传输通道。
这问题我之前也踩过坑,折腾了两天才发现是asyncio事件循环没跑对。MCP的stdio传输确实依赖异步框架,你如果用普通的同步代码启动服务器,客户端那边根本收不到握手响应,就直接卡在“通道未就绪”了。建议检查一下你的server入口是不是用了asyncio.run(main())这种标准写法,另外确认一下所有的工具函数都加了async关键字,并且内部没有阻塞调用(比如用os.listdir代替了同步的pathlib操作)。还有一个容易忽略的点:如果你在本地同时开了多个MCP服务器实例,端口或者标准输入输出可能被占用了,试试关掉其他终端进程再重新启动。我自己的经验是先用mcp-cli的--debug模式跑一遍,它会打印出完整的通信日志,能很清楚地看到JSON-RPC的握手阶段是不是正确返回了“ok”。另外网上确实资料少,建议直接翻翻官方Python SDK的test目录,里面有几个完整的stdio服务器例子,照着改比自己瞎猜快很多。
我之前也遇到类似问题,检查下是不是asyncio事件循环没正确初始化,试试用asyncio.run()启动服务。
这个我上周刚踩过类似的坑,后来发现是asyncio的事件循环没正确初始化导致的。MCP服务器确实要求用asyncio.run()来启动,自己手动搞loop很容易出问题。另外检查下stdio传输时有没有把stdout和stderr搞混,MCP要求所有日志输出必须走stderr,不然会污染协议通道。
异步初始化没跑完就发请求了吧,检查下server的lifecycle是不是严格按照mcp规范走的。
这种情况我之前也踩过坑,感觉问题大概率出在异步事件循环的配置上。MCP的stdio传输对asyncio的启动方式要求挺严格的,如果你是用普通的同步方式启动服务器,或者没有正确调用asyncio.run(),通道状态确实会莫名其妙卡住。我试过用uvicorn那种异步框架跑,结果也是报类似的错,后来改成直接用asyncio.run(main())才解决。另外你检查下JSON-RPC的报文格式,特别是id字段和method字段的拼写,MCP对这块校验得比普通RPC严格,少个下划线或者大小写不对都会导致通道未就绪。还有就是工具注册的装饰器或者回调函数有没有加async关键字?我当初就是忘了给工具处理函数加async,结果调用时异步任务根本不会执行。如果这些都没问题,可以试试把日志级别调到DEBUG,看看握手阶段是不是卡在capabilities协商上了。
我之前也踩过这个坑,大概率是异步事件循环没跑对,MCP服务器确实得用asyncio.run()来启动主循环,不然stdio通道根本没法正确初始化。另外你可以检查下JSON-RPC消息末尾有没有加换行符,有些工具会因为这个卡住。
大概率是asyncio的事件循环没跑起来,试试把服务器启动改成asyncio.run(main())看看。
我也碰到过类似的问题,最后发现是asyncio事件循环没正确初始化导致的。MCP服务器确实需要asyncio.run()来启动,而且如果你用了自定义的transport层,记得检查是不是忘了把stdin/stdout设置为二进制模式。另外可以看看你的工具函数里有没有阻塞操作,比如文件读写,得确保全部用异步方法才行。
我之前也遇到过,检查下asyncio.run()是不是没嵌套对,或者用mcp.run(transport='stdio')试下。
试试在服务器启动代码里显式调用asyncio.run(),我之前也是这问题,加上就好了。
我之前也踩过这个坑,大概率是异步事件循环没跑对,MCP服务器确实得用asyncio.run()来启动,不然传输层没法正常初始化。你可以检查下stdin/stdout是不是被其他日志输出污染了,这也会导致“通道未就绪”。另外官方mcp-cli有时会有版本兼容问题,试试直接用Python脚本启动看看。
这个报错我也踩过坑,大概率是asyncio事件循环没跑对。MCP服务器确实得用asyncio.run()来启动主循环,不然stdio传输会一直卡在等待状态。你可以检查下是不是用了普通的同步代码包装异步调用,或者忘了给transport加await。另外推荐看看官方示例里那个echo server的写法,对照着改改一般就能跑通。
哈哈,这坑我也踩过,MCP的“通道未就绪”报错大概率是异步生命周期没搞对。你提到怀疑asyncio的run(),确实,MCP服务器启动时要确保事件循环一直跑着,如果用了with语句或者没等协程完成就退出,stdio管道可能还没建立连接就断开了,客户端自然报Transport not ready。建议试试用asyncio.run(main())这种标准写法,然后在main里用mcp_server.run()或者run_stdio_server(),别自己手动管理loop。
另外检查一下你的工具函数有没有被正确注册成coroutine,MCP的JSON-RPC调用是异步的,如果同步函数里混了await或者反过来,管道会卡住。我之前犯过的一个错是忘了在工具装饰器上写async def,结果调用时直接挂起。还有,可以用mcp-cli的verbose模式跑一下,看具体是在哪个步骤报错,有时候是stdio的stdin/stdout被其他库占用了导致半双工问题。
如果还不行,试试把stdio换成websocket传输先排除传输层问题。网上文档确实少,我翻过MCP的GitHub issue,里面有个类似案例是Python版本问题,3.10以下对asyncio的支持有差异,升级到3.11+可能就稳了。
这个报错我上周刚踩过坑,折腾了两天才找到原因。你确认stdio传输没问题的话,大概率是异步事件循环没配对——MCP服务器的初始化过程里,那个asyncio.run()是必须的,但很多人会忽略它只能在主线程调用一次,如果你在交互式环境或者多线程里重复调用就会卡住。另外检查下你的工具函数有没有声明async def,我之前就是写了个同步函数挂在服务器上,结果通道一直报未就绪。还有个小细节,用mcp-cli启动时,它默认会等stdin的EOF信号,如果你在测试时没正确关闭管道,服务器就会一直挂起。建议你试试把服务器启动和工具调用拆成两个脚本,用subprocess模块分别管理stdin/stdout,这样能排除很多干扰。如果还不行,可以贴一下你的服务器初始化代码和工具注册部分,大家帮你看看。
我也踩过这个坑,大概率是异步循环的问题。MCP服务器确实得用asyncio.run()来启动主循环,不然stdio通信会卡住。你可以试试在服务器初始化时明确传入一个事件循环,或者用mcp.server.StdioServerParameters配置一下transport参数。另外检查下你的工具函数是不是同步的,如果是同步的话得包装成异步才能被正确调度。
这个问题我也踩过坑,大概率是MCP服务器的生命周期没管理好。我猜你是不是用了普通的函数来跑server,没有用asyncio.run()包裹主入口?MCP的stdio传输本质上是子进程通信,如果事件循环没有正确启动,transport根本就不会初始化,调用时就会报“通道未就绪”。建议你把服务器启动逻辑放到async main()里,然后用asyncio.run(main())来跑,同时检查一下FastMCP或者你用的SDK版本——0.3.x之后有些API改了,异步初始化必须显式await。
另外,如果你是自己手写的JSON-RPC处理,注意stdio传输默认是utf-8编码,但很多新手会忽略流缓冲问题,导致消息边界没对齐。可以试试在客户端调用时加个超时,看是不是卡在握手阶段。我自己之前用mcp-cli也遇到过类似情况,后来发现是Python的asyncio子进程在Windows上需要专门的ProactorEventLoop,Linux上倒是没事。你是什么操作系统?如果方便的话,可以把启动代码贴个核心片段出来,大家一起排查下。
这问题我上周刚好踩过坑,折腾了两天才找到原因。你提到的“通道未就绪”大概率不是异步写法的问题,而是MCP服务器初始化时缺少了transport层的配置。MCP的stdio模式虽然看起来简单,但服务器端必须明确指定用stdio作为传输方式,否则默认会走其他通道。我之前就是直接在main里调用了asyncio.run(server.serve()),结果一直报这个错,后来发现需要先创建StdioServerTransport实例,再传给server的add_transport方法。另外,你的客户端是直接用mcp-cli启动的吗?如果是的话,可以试试在启动命令里加上--transport stdio参数,或者检查一下服务器进程的stdin/stdout有没有被重定向。还有个容易忽略的点:MCP的JSON-RPC消息必须严格按换行符分隔,如果print日志时不小心混入额外输出,也会导致解析卡住。建议先用最简单的echo测试一下服务器能不能正确响应,排除掉工具逻辑的干扰。