Skip to content

Force closing a client during a tool call bricks the server #508

Description

@claudiu7t

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working. Reports of errors, unexpected behavior, or broken functionality.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions