MCP Python SDK: RuntimeError exiting cancel scope in a different task when closing SSE/stdio client
When closing an SSE client created with mcp.client.sse.sse_client() in mcp 1.6.0 on Python 3.10, I get: RuntimeError: Attempted to exit cancel scope in a different task than it was entered in. The same error appears during pytest teardown of a stdio_client fixture. Is there a fix or workaround?
Originally asked on GitHub: https://github.com/modelcontextprotocol/python-sdk/issues/521
5 answers
The issue is closed, but the thread does not show an SDK code fix. The cause is AnyIO's constraint that cancel scopes and task groups must be entered and exited in the same task; the MCP client helpers create these scopes internally, so calling aenter() and aexit() manually or letting cleanup run in another task triggers the error.
What people reported in the thread:
- One commenter avoided the problem by creating clients for each request in the same task context and using an async context manager pattern for lifecycle handling, rather than manually calling enter/exit. No code was shown.
- Another commenter found a solution to close the connection from another task using an asyncio.Event signal, and linked to a separate Chainlit issue with the explanation. The thread does not describe the exact waiting/closing mechanism.
- A commenter reproduced the same RuntimeError with stdio_client in a pytest_asyncio fixture (scope='class', loop_scope='class'): the tests ran, but pytest failed during cleanup. The bug therefore appears in both the SSE and stdio client paths.
- Issue #79 in the same repository was linked as related, but no details were given in this thread.
General advice, not from the thread: keep the full async context manager lifecycle inside one task, and if you use pytest fixtures, try to enter and exit the client context in the same event loop/task to avoid crossing task boundaries.
Did this work for you?What a GitHub user tried on GitHub (Didn't work)
The reporter created an SSE client with sse_client(url, headers=...), called aenter() manually, then called aexit(None, None, None) during shutdown. This produced the RuntimeError.
Source: https://github.com/modelcontextprotocol/python-sdk/issues/521 (opens external site)
Did this work for you?What @nigiva tried on GitHub (Worked)
A commenter reported finding a solution to close the connection from another task using an asyncio.Event signal, and linked a Chainlit issue with the explanation. The exact mechanism is not described in this thread.
Did this work for you?What @npkanaka tried on GitHub (Outcome unclear)
A commenter said they avoided the problem by creating clients for each request in the same task context and using an async context manager pattern for proper lifecycle handling, instead of relying on manual enter/exit calls. No code was shown.
Did this work for you?What @nickovs tried on GitHub (Didn't work)
A commenter reproduced the same RuntimeError with stdio_client and a pytest_asyncio fixture using scope='class' and loop_scope='class'. The tests ran, but pytest failed during cleanup.
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