Why does MCP Python SDK ClientSession.call_tool() hang while list_tools() works?
In the MCP Python SDK, a stdio-connected client can call session.list_tools() successfully, but await session.call_tool() blocks forever even though client-side logging shows the tool ran and produced a result. Another reporter saw the same hang on WSL Ubuntu 24.04 with Python 3.13 and mcp[cli]>=1.6.0, while the same server worked in Claude Desktop and the MCP inspector. What causes this and is there a workaround?
Originally asked on GitHub: https://github.com/modelcontextprotocol/python-sdk/issues/262
6 answers
Status: still open; no confirmed SDK fix appears in this thread. A contributor only speculated that shell=True in the stdio transport (issue 1257) might be related; nothing in the thread confirms that. Commenters reported two workarounds. For remote transport, switching from SSE to WebSocket using mcp.server.websocket.websocket_server solved the hang for one user. For an embedded client in an asyncio app, one user got responses by scheduling call_tool on the event loop that owns the session: asyncio.run_coroutine_threadsafe(self.agent.call_tool(content), self.loop).result(), while awaiting the coroutine directly in the streaming loop hung forever. Another commenter found call_tool works from the main thread but hangs from a threading.Thread. The original reporter's minimal reproducer uses stdio_client with a uvx command and a git+ssh package source; list_tools works, call_tool never returns. General advice: when embedding the MCP client in a larger asyncio application, keep the session and the call_tool await on the same event loop and do not await directly from another thread. The WebSocket workaround applies to remote setups, not local stdio connections.
Did this work for you?What @IraCyoee tried on GitHub (Worked)
For a remote server, switched from the SSE protocol to the WebSocket protocol using mcp.server.websocket.websocket_server; the commenter reported responses then arrived normally.
Did this work for you?What @loadingvx tried on GitHub (Worked)
Instead of awaiting call_tool directly inside an LLM streaming loop, scheduled it on the event loop that owns the session with asyncio.run_coroutine_threadsafe(self.agent.call_tool(content), self.loop).result(); the call then returned, with the user unsure of the root cause.
Did this work for you?What @sagardspeed2 tried on GitHub (Partly worked)
Observed that call_tool worked when called from the main thread but hung when called from a thread created with threading.Thread.
Did this work for you?What a GitHub user tried on GitHub (Didn't work)
Ran a minimal stdio client with stdio_client and ClientSession, calling initialize, list_tools (which worked), then call_tool with arguments. The await never returned even though client-side logging showed the tool call executed and produced a result; the server was launched with uvx from a git+ssh package.
Source: https://github.com/modelcontextprotocol/python-sdk/issues/262 (opens external site)
Did this work for you?What @IraCyoee tried on GitHub (Didn't work)
Used uv with Python 3.13 and mcp[cli]>=1.6.0 on WSL Ubuntu 24.04; server replies never arrived, while the same setup worked on native Windows.
Did this work for you?
Related questions
- MCP python-sdk simple-chatbot: 'Attempted to exit cancel scope in a different task than it was entered in'
- Why is my MCP resource template (greeting://{name}) not available in Claude Desktop?
- How to disable TLS certificate verification (verify=False) in MCP streamablehttp_client?
- anyio.ClosedResourceError in MCP StreamableHTTP message router on fast-failing requests?
- Python MCP SDK streamable HTTP: 'Error in message router' ClosedResourceError after 307 redirect