Description
I might be using something incorrectly, but it seems that one can make a server unusable by closing the client in the middle of a tool call.
To reproduce, start the server below, then start the client code and let it run for a bit. Then force close it (Ctrl + C). The server will display the following error:
File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py", line 111, in run
yield # Let the application run
^^^^^
File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/fastmcp/server/http.py", line 322, in lifespan
yield
File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/starlette/routing.py", line 699, in lifespan
await receive()
File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/uvicorn/lifespan/on.py", line 137, in receive
return await self.receive_queue.get()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/condaenv/lib/python3.12/asyncio/queues.py", line 158, in get
await getter
asyncio.exceptions.CancelledError: Cancelled by cancel scope 7f9be28dd250
During handling of the above exception, another exception occurred:
+ Exception Group Traceback (most recent call last):
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/starlette/routing.py", line 692, in lifespan
| async with self.lifespan_context(app) as maybe_state:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/contextlib.py", line 231, in __aexit__
| await self.gen.athrow(value)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/fastmcp/server/http.py", line 321, in lifespan
| async with session_manager.run():
| ^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/contextlib.py", line 231, in __aexit__
| await self.gen.athrow(value)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py", line 106, in run
| async with anyio.create_task_group() as tg:
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 815, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/streamable_http.py", line 916, in connect
| yield read_stream, write_stream
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py", line 238, in run_server
| await self.app.run(
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 495, in run
| async with AsyncExitStack() as stack:
| ^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/contextlib.py", line 754, in __aexit__
| raise exc_details[1]
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/contextlib.py", line 737, in __aexit__
| cb_suppress = await cb(*exc_details)
| ^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/shared/session.py", line 220, in __aexit__
| return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 815, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 506, in run
| async with anyio.create_task_group() as tg:
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 815, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 534, in _handle_message
| await self._handle_request(
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 580, in _handle_request
| await message.respond(response)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/shared/session.py", line 131, in respond
| await self._session._send_response( # type: ignore[reportPrivateUsage]
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/shared/session.py", line 343, in _send_response
| await self._write_stream.send(session_message)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/streams/memory.py", line 242, in send
| self.send_nowait(item)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/streams/memory.py", line 211, in send_nowait
| raise ClosedResourceError
| anyio.ClosedResourceError
+------------------------------------
|
| During handling of the above exception, another exception occurred:
|
| Exception Group Traceback (most recent call last):
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py", line 235, in run_server
| async with http_transport.connect() as streams:
| ^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/contextlib.py", line 231, in __aexit__
| await self.gen.athrow(value)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/streamable_http.py", line 843, in connect
| async with anyio.create_task_group() as tg:
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 815, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 506, in run
| async with anyio.create_task_group() as tg:
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 815, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 534, in _handle_message
| await self._handle_request(
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 580, in _handle_request
| await message.respond(response)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/shared/session.py", line 131, in respond
| await self._session._send_response( # type: ignore[reportPrivateUsage]
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/shared/session.py", line 343, in _send_response
| await self._write_stream.send(session_message)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/streams/memory.py", line 242, in send
| self.send_nowait(item)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/streams/memory.py", line 211, in send_nowait
| raise ClosedResourceError
| anyio.ClosedResourceError
+------------------------------------
|
| During handling of the above exception, another exception occurred:
|
| Exception Group Traceback (most recent call last):
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/streamable_http.py", line 916, in connect
| yield read_stream, write_stream
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py", line 238, in run_server
| await self.app.run(
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 495, in run
| async with AsyncExitStack() as stack:
| ^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/contextlib.py", line 754, in __aexit__
| raise exc_details[1]
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/contextlib.py", line 737, in __aexit__
| cb_suppress = await cb(*exc_details)
| ^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/shared/session.py", line 220, in __aexit__
| return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 815, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 506, in run
| async with anyio.create_task_group() as tg:
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 815, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 534, in _handle_message
| await self._handle_request(
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 580, in _handle_request
| await message.respond(response)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/shared/session.py", line 131, in respond
| await self._session._send_response( # type: ignore[reportPrivateUsage]
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/mcp/shared/session.py", line 343, in _send_response
| await self._write_stream.send(session_message)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/streams/memory.py", line 242, in send
| self.send_nowait(item)
| File "/home/user/miniconda3/envs/condaenv/lib/python3.12/site-packages/anyio/streams/memory.py", line 211, in send_nowait
| raise ClosedResourceError
| anyio.ClosedResourceError
+------------------------------------
If I now try to run the client script again, I get the following error:
Traceback (most recent call last):
File "/mnt/d/redacted/redacted-server/mmcp.py", line 11, in <module>
asyncio.run(main())
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/asyncio/runners.py", line 195, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/asyncio/base_events.py", line 691, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/mnt/d/redacted/redacted-server/mmcp.py", line 5, in main
async with Client("http://0.0.0.0:8000/mcp/") as client:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/fastmcp/client/client.py", line 136, in __aenter__
with catch(get_catch_handlers()):
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/exceptiongroup/_catch.py", line 39, in __exit__
raise unhandled from exc.__cause__
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/exceptiongroup/_catch.py", line 65, in handle_exception
result = handler(matched)
^^^^^^^^^^^^^^^^
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/fastmcp/utilities/exceptions.py", line 29, in _exception_handler
raise leaf
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/mcp/client/streamable_http.py", line 388, in handle_request_async
await self._handle_post_request(ctx)
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/mcp/client/streamable_http.py", line 266, in _handle_post_request
response.raise_for_status()
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/httpx/_models.py", line 829, in raise_for_status
raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: Server error '500 Internal Server Error' for url 'http://0.0.0.0:8000/mcp/'
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500
The server now displays a new error as well:
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 409, in run_asgi
result = await app( # type: ignore[func-returns-value]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/starlette/applications.py", line 112, in __call__
await self.middleware_stack(scope, receive, send)
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/starlette/middleware/errors.py", line 187, in __call__
raise exc
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/starlette/middleware/errors.py", line 165, in __call__
await self.app(scope, receive, _send)
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/fastmcp/server/http.py", line 59, in __call__
await self.app(scope, receive, send)
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
raise exc
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/starlette/routing.py", line 714, in __call__
await self.middleware_stack(scope, receive, send)
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/starlette/routing.py", line 734, in app
await route.handle(scope, receive, send)
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/starlette/routing.py", line 460, in handle
await self.app(scope, receive, send)
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/fastmcp/server/http.py", line 282, in handle_streamable_http
await session_manager.handle_request(scope, receive, send)
File "/home/redacted/miniconda3/envs/redacted12/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py", line 137, in handle_request
raise RuntimeError("Task group is not initialized. Make sure to use run().")
RuntimeError: Task group is not initialized. Make sure to use run().
The server appears to be bricked and won't work until I restart it. In my actual code the part that uses mcp clients can receive some sort of kill signals from the outside, leading to errors like above. I can work around it by waiting for the requests to finish before killing stuff, but I guess I still shouldn't be able to brick a server like this.
Example Code
# server.py - ran with python server.py
from fastmcp import FastMCP
import asyncio
mcp = FastMCP("test")
@mcp.tool()
async def tool(
) -> str:
await asyncio.sleep(0.5)
return "yay"
if __name__ == "__main__":
asyncio.run(mcp.run_async(transport="streamable-http"))
# client code - ran with python client.py
from fastmcp import Client
import asyncio
import time
async def main():
async with Client("http://0.0.0.0:8000/mcp/") as client:
while True:
await client.call_tool("tool")
print(1)
asyncio.run(main())
Version Information
FastMCP version: 2.3.4
MCP version: 1.9.0
Python version: 3.12.9
Platform: Linux-5.15.153.1-microsoft-standard-WSL2-x86_64-with-glibc2.35
FastMCP root path: /home/redacted/miniconda3/envs/redacted/lib/python3.12/site-packages
Additional Context
No response
Description
I might be using something incorrectly, but it seems that one can make a server unusable by closing the client in the middle of a tool call.
To reproduce, start the server below, then start the client code and let it run for a bit. Then force close it (Ctrl + C). The server will display the following error:
If I now try to run the client script again, I get the following error:
The server now displays a new error as well:
The server appears to be bricked and won't work until I restart it. In my actual code the part that uses mcp clients can receive some sort of kill signals from the outside, leading to errors like above. I can work around it by waiting for the requests to finish before killing stuff, but I guess I still shouldn't be able to brick a server like this.
Example Code
Version Information
Additional Context
No response