Skip to content

MCP SSE Server: Received request before initialization was complete #423

Description

@folkvir

Describe the bug

The MCP server receives Received request before initialization was complete after a second deployment when a client is trying to list the different tools or even when it tries to run a tool on the server.

To Reproduce

SImple main.py which is one of your examples

import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("My App")


@mcp.tool()
def calculate_bmi(weight_kg: float, height_m: float) -> float:
    """Calculate BMI given weight in kg and height in meters"""
    return weight_kg / (height_m**2)


@mcp.tool()
async def fetch_weather(city: str) -> str:
    """Fetch current weather for a city"""
    async with httpx.AsyncClient() as client:
        response = await client.get(f"https://api.weather.com/{city}")
        return response.text

app = mcp.sse_app()
  • Start using uvicorn FASTMCP_LOG_LEVEL=DEBUG uv run uvicorn main:app --port=8001 --host=0.0.0.0 --timeout-graceful-shutdown 5

  • Run the inspector in SSE mode and listen on http://localhost:8001/see. You will be able to list tools.

  • But now, reload you server to simulate a new deployment using the same command: FASTMCP_LOG_LEVEL=DEBUG uv run uvicorn main:app --port=8001 --host=0.0.0.0 --timeout-graceful-shutdown 5

  • Go to the inspector and try to list the tools, it will timeout and you'll get the following server logs

FASTMCP_LOG_LEVEL=DEBUG uv run uvicorn main:app --port=8001 --host=0.0.0.0 --timeout-graceful-shutdown 5                                       
[04/03/25 23:48:40] DEBUG    SseServerTransport initialized with endpoint: /messages/                                                                                                 sse.py:79
INFO:     Started server process [89042]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit)
[04/03/25 23:48:41] DEBUG    Setting up SSE connection                                                                                                                                    sse.py:87
                    DEBUG    Created new session with ID: f55c6f64-be08-464c-bbab-203223adb44c                                                                                           sse.py:100
                    DEBUG    Starting SSE response task                                                                                                                                  sse.py:127
                    DEBUG    Yielding read and write streams                                                                                                                             sse.py:130
INFO:     127.0.0.1:62930 - "GET /sse HTTP/1.1" 200 OK
                    DEBUG    Starting SSE writer                                                                                                                                         sse.py:107
                    DEBUG    Sent endpoint event: /messages/?session_id=f55c6f64be08464cbbab203223adb44c                                                                                 sse.py:110
                    DEBUG    chunk: b'event: endpoint\r\ndata: /messages/?session_id=f55c6f64be08464cbbab203223adb44c\r\n\r\n'                                                           sse.py:156
[04/03/25 23:48:45] DEBUG    Handling POST message                                                                                                                                       sse.py:136
                    DEBUG    Parsed session ID: f55c6f64-be08-464c-bbab-203223adb44c                                                                                                     sse.py:147
writer:  MemoryObjectSendStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=1, open_receive_channels=1, waiting_receivers=OrderedDict({<anyio._backends._asyncio.Event object at 0x103ff6f00>: MemoryObjectItemReceiver(task_info=AsyncIOTaskInfo(id=4386289792, name='mcp.shared.session.BaseSession._receive_loop'), item=None)}), waiting_senders=OrderedDict()), _closed=False)
                    DEBUG    Received JSON: b'{"jsonrpc":"2.0","id":3,"method":"tools/list","params":{}}'                                                                                sse.py:161
                    DEBUG    Validated client message: root=JSONRPCRequest(method='tools/list', params={}, jsonrpc='2.0', id=3)                                                          sse.py:165
                    DEBUG    Sending message to writer: root=JSONRPCRequest(method='tools/list', params={}, jsonrpc='2.0', id=3)                                                         sse.py:173
INFO:     127.0.0.1:62936 - "POST /messages/?session_id=f55c6f64be08464cbbab203223adb44c HTTP/1.1" 202 Accepted
                    DEBUG    Got event: http.disconnect. Stop streaming.                                                                                                                 sse.py:177
ERROR:    Exception in ASGI application
  + Exception Group Traceback (most recent call last):
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/uvicorn/protocols/http/h11_impl.py", line 403, in run_asgi
  |     result = await app(  # type: ignore[func-returns-value]
  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
  |     return await self.app(scope, receive, send)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/applications.py", line 112, in __call__
  |     await self.middleware_stack(scope, receive, send)
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 187, in __call__
  |     raise exc
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 165, in __call__
  |     await self.app(scope, receive, _send)
  |   File "/Users/xxxxx/.venv/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 "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
  |     raise exc
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
  |     await app(scope, receive, sender)
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/routing.py", line 714, in __call__
  |     await self.middleware_stack(scope, receive, send)
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/routing.py", line 734, in app
  |     await route.handle(scope, receive, send)
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/routing.py", line 288, in handle
  |     await self.app(scope, receive, send)
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/routing.py", line 76, in app
  |     await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
  |     raise exc
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
  |     await app(scope, receive, sender)
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/routing.py", line 73, in app
  |     response = await f(request)
  |                ^^^^^^^^^^^^^^^^
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/server/fastmcp/server.py", line 485, in handle_sse
  |     async with sse.connect_sse(
  |   File "/Users/xxxxx/.pyenv/versions/3.12.2/lib/python3.12/contextlib.py", line 231, in __aexit__
  |     await self.gen.athrow(value)
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/server/sse.py", line 123, in connect_sse
  |     async with anyio.create_task_group() as tg:
  |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 772, in __aexit__
  |     raise BaseExceptionGroup(
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Exception Group Traceback (most recent call last):
    |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/server/sse.py", line 131, in connect_sse
    |     yield (read_stream, write_stream)
    |   File "/Users/xxxxx.venv/lib/python3.12/site-packages/mcp/server/fastmcp/server.py", line 490, in handle_sse
    |     await self._mcp_server.run(
    |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 483, in run
    |     async with AsyncExitStack() as stack:
    |   File "/Users/xxxxx/.pyenv/versions/3.12.2/lib/python3.12/contextlib.py", line 754, in __aexit__
    |     raise exc_details[1]
    |   File "/Users/xxxxx/.pyenv/versions/3.12.2/lib/python3.12/contextlib.py", line 737, in __aexit__
    |     cb_suppress = await cb(*exc_details)
    |                   ^^^^^^^^^^^^^^^^^^^^^^
    |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/shared/session.py", line 210, in __aexit__
    |     return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 772, in __aexit__
    |     raise BaseExceptionGroup(
    | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
    +-+---------------- 1 ----------------
      | Traceback (most recent call last):
      |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/shared/session.py", line 324, in _receive_loop
      |     await self._received_request(responder)
      |   File "/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/server/session.py", line 163, in _received_request
      |     raise RuntimeError(
      | RuntimeError: Received request before initialization was complete
      +------------------------------------
[04/03/25 23:48:48] DEBUG    Setting up SSE connection                                                                                                                                    sse.py:87
                    DEBUG    Created new session with ID: 1541ccf4-7bf9-407b-b2a2-94552715a527                                                                                           sse.py:100
                    DEBUG    Starting SSE response task                                                                                                                                  sse.py:127
                    DEBUG    Yielding read and write streams                                                                                                                             sse.py:130
INFO:     127.0.0.1:62938 - "GET /sse HTTP/1.1" 200 OK
                    DEBUG    Starting SSE writer                                                                                                                                         sse.py:107
                    DEBUG    Sent endpoint event: /messages/?session_id=1541ccf47bf9407bb2a294552715a527                                                                                 sse.py:110
                    DEBUG    chunk: b'event: endpoint\r\ndata: /messages/?session_id=1541ccf47bf9407bb2a294552715a527\r\n\r\n'                                                           sse.py:156
[04/03/25 23:48:55] DEBUG    Handling POST message                                                                                                                                       sse.py:136
                    DEBUG    Parsed session ID: 1541ccf4-7bf9-407b-b2a2-94552715a527                                                                                                     sse.py:147
writer:  MemoryObjectSendStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=1, open_receive_channels=1, waiting_receivers=OrderedDict({<anyio._backends._asyncio.Event object at 0x105916510>: MemoryObjectItemReceiver(task_info=AsyncIOTaskInfo(id=4386290368, name='mcp.shared.session.BaseSession._receive_loop'), item=None)}), waiting_senders=OrderedDict()), _closed=False)
                    DEBUG    Received JSON: b'{"jsonrpc":"2.0","method":"notifications/cancelled","params":{"requestId":3,"reason":"Request timed out"}}'                                sse.py:161
                    DEBUG    Validated client message: root=JSONRPCNotification(method='notifications/cancelled', params={'requestId': 3, 'reason': 'Request timed out'}, jsonrpc='2.0') sse.py:165
                    DEBUG    Sending message to writer: root=JSONRPCNotification(method='notifications/cancelled', params={'requestId': 3, 'reason': 'Request timed out'},               sse.py:173
                             jsonrpc='2.0')                                                                                                                                                        
INFO:     127.0.0.1:62945 - "POST /messages/?session_id=1541ccf47bf9407bb2a294552715a527 HTTP/1.1" 202 Accepted
[04/03/25 23:49:03] DEBUG    ping: b': ping - 2025-04-03 21:49:03.579244+00:00\r\n\r\n'

Expected behavior

Somehow, a POST message is accepted while it should not. Is it correct to accept such request?
Shouldn't we have a 404 here instead of a 200 after the second reload? 🤔

writer = self._read_stream_writers.get(session_id)
if not writer:
logger.warning(f"Could not find session for ID: {session_id}")
response = Response("Could not find session", status_code=404)
return await response(scope, receive, send)

EDIT: I actually tested to comment this line and it works correctly now!

if self._initialization_state != InitializationState.Initialized:
raise RuntimeError(
"Received request before initialization was complete"
)

Tested with fast-agent, LibreChat and Inspector. Even after a server reload, call tools are still working.
If I understand correctly the global mechanism It should not be possible to have, at this point (after a server reload), a session-id corresponding to a SSE transport that should not have been initialized. So this comment actually is a patch on something that should not happen 😬

Desktop:

  • OS: MacOS 15.3
  • Browser chrome latest
  • Python 3.12
"mcp[cli]>=1.6.0",
"starlette>=0.46.1",
"uvicorn>=0.34.0",

Activity

  1. mheubi commented on Apr 8, 2025

    @mheubi

    It's a bug in supergateway, which sends it's own initialize message upon launch - against protocol spec - then forwards the (actual) intitialization message from the client, once it is received.

    So the server receives initialize twice.
    It's not a bug in the example, it's using the example outside the spec.
    ...although the example could handle this type of protocol violation more gracefully.

    PROOF: If you use mcp-proxy rather than supergateway, the example will work flawlessly

  2. yubigfish commented on Apr 8, 2025

    @yubigfish

    一个mysql查询功能的mcp,打包到docker中提供sse服务,有如下报错:

    ERROR: Exception in ASGI application

    • Exception Group Traceback (most recent call last):
      | File "/usr/local/lib/python3.12/site-packages/uvicorn/protocols/http/h11_impl.py", line 403, in run_asgi
      | result = await app( # type: ignore[func-returns-value]
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      | File "/usr/local/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in call
      | return await self.app(scope, receive, send)
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      | File "/usr/local/lib/python3.12/site-packages/starlette/applications.py", line 112, in call
      | await self.middleware_stack(scope, receive, send)
      | File "/usr/local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 187, in call
      | raise exc
      | File "/usr/local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 165, in call
      | await self.app(scope, receive, _send)
      | File "/usr/local/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 "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
      | raise exc
      | File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
      | await app(scope, receive, sender)
      | File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 714, in call
      | await self.middleware_stack(scope, receive, send)
      | File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 734, in app
      | await route.handle(scope, receive, send)
      | File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 288, in handle
      | await self.app(scope, receive, send)
      | File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 76, in app
      | await wrap_app_handling_exceptions(app, request)(scope, receive, send)
      | File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
      | raise exc
      | File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
      | await app(scope, receive, sender)
      | File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 73, in app
      | response = await f(request)
      | ^^^^^^^^^^^^^^^^
      | File "/usr/local/lib/python3.12/site-packages/mcp/server/fastmcp/server.py", line 485, in handle_sse
      | async with sse.connect_sse(
      | File "/usr/local/lib/python3.12/contextlib.py", line 231, in aexit
      | await self.gen.athrow(value)
      | File "/usr/local/lib/python3.12/site-packages/mcp/server/sse.py", line 123, in connect_sse
      | async with anyio.create_task_group() as tg:
      | File "/usr/local/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 772, in aexit
      | raise BaseExceptionGroup(
      | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
      +-+---------------- 1 ----------------
      | Exception Group Traceback (most recent call last):
      | File "/usr/local/lib/python3.12/site-packages/mcp/server/sse.py", line 131, in connect_sse
      | yield (read_stream, write_stream)
      | File "/usr/local/lib/python3.12/site-packages/mcp/server/fastmcp/server.py", line 490, in handle_sse
      | await self._mcp_server.run(
      | File "/usr/local/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 483, in run
      | async with AsyncExitStack() as stack:
      | File "/usr/local/lib/python3.12/contextlib.py", line 754, in aexit
      | raise exc_details[1]
      | File "/usr/local/lib/python3.12/contextlib.py", line 737, in aexit
      | cb_suppress = await cb(*exc_details)
      | ^^^^^^^^^^^^^^^^^^^^^^
      | File "/usr/local/lib/python3.12/site-packages/mcp/shared/session.py", line 210, in aexit
      | return await self._task_group.aexit(exc_type, exc_val, exc_tb)
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      | File "/usr/local/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 772, in aexit
      | raise BaseExceptionGroup(
      | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
      +-+---------------- 1 ----------------
      | Traceback (most recent call last):
      | File "/usr/local/lib/python3.12/site-packages/mcp/shared/session.py", line 324, in _receive_loop
      | await self._received_request(responder)
      | File "/usr/local/lib/python3.12/site-packages/mcp/server/session.py", line 163, in _received_request
      | raise RuntimeError(
      | RuntimeError: Received request before initialization was complete
  3. altmind commented on Apr 9, 2025

    @altmind

    I'm having the same issue with Cursor AI SSE MCP. Getting "RuntimeError: Received request before initialization was complete" upon calling a tool from cursor. Not using FastMCP - plain Starlette app with

    sse.connect_sse(...) as streams:
                await app.run(
                    streams[0], streams[1], app.create_initialization_options()
                )
    log
    DEBUG:root:Starting main application.
    DEBUG:mcp.server.lowlevel.server:Initializing server 'va2'
    DEBUG:mcp.server.lowlevel.server:Registering handler for ListToolsRequest
    DEBUG:mcp.server.lowlevel.server:Registering handler for CallToolRequest
    DEBUG:mcp.server.lowlevel.server:Registering handler for ListResourcesRequest
    DEBUG:mcp.server.lowlevel.server:Registering handler for ReadResourceRequest
    DEBUG:mcp.server.lowlevel.server:Registering handler for PromptListRequest
    DEBUG:mcp.server.sse:SseServerTransport initialized with endpoint: /messages/
    INFO:     Started server process [31792]
    INFO:     Waiting for application startup.
    INFO:     Application startup complete.
    INFO:     Uvicorn running on http://0.0.0.0:9000 (Press CTRL+C to quit)
    DEBUG:root:Starting SSE connection handling.
    DEBUG:mcp.server.sse:Setting up SSE connection
    DEBUG:mcp.server.sse:Created new session with ID: 711eea47-2418-421e-8b6f-395c0ee33985
    DEBUG:mcp.server.sse:Starting SSE response task
    DEBUG:mcp.server.sse:Yielding read and write streams
    DEBUG:root:SSE streams connected.
    DEBUG:mcp.server.sse:Starting SSE writer
    DEBUG:mcp.server.sse:Sent endpoint event: /messages/?session_id=711eea472418421e8b6f395c0ee33985
    DEBUG:sse_starlette.sse:chunk: b'event: endpoint\r\ndata: /messages/?session_id=711eea472418421e8b6f395c0ee33985\r\n\r\n'
    INFO:     127.0.0.1:34873 - "GET /sse HTTP/1.1" 200 OK
    DEBUG:sse_starlette.sse:ping: b': ping - 2025-04-09 18:22:36.517325+00:00\r\n\r\n'
    DEBUG:sse_starlette.sse:ping: b': ping - 2025-04-09 18:22:51.519323+00:00\r\n\r\n'
    DEBUG:sse_starlette.sse:ping: b': ping - 2025-04-09 18:23:06.533324+00:00\r\n\r\n'
    DEBUG:sse_starlette.sse:ping: b': ping - 2025-04-09 18:23:21.548325+00:00\r\n\r\n'
    DEBUG:sse_starlette.sse:ping: b': ping - 2025-04-09 18:23:36.548324+00:00\r\n\r\n'
    DEBUG:mcp.server.sse:Handling POST message
    DEBUG:mcp.server.sse:Parsed session ID: 711eea47-2418-421e-8b6f-395c0ee33985
    DEBUG:mcp.server.sse:Received JSON: {'method': 'tools/call', 'params': {'name': 'mytool', 'arguments': { 'typeId': 5, 'instanceId': 109}}, 'jsonrpc': '2.0', 'id': 6}
    DEBUG:mcp.server.sse:Validated client message: root=JSONRPCRequest(method='tools/call', params={'name': 'mytool', 'arguments': {'typeId': 5, 'instanceId': 109}}, jsonrpc='2.0', id=6)
    DEBUG:mcp.server.sse:Sending message to writer: root=JSONRPCRequest(method='tools/call', params={'name': 'mytool', 'arguments': {'typeId': 5, 'instanceId': 109}}, jsonrpc='2.0', id=6)
    INFO:     127.0.0.1:35059 - "POST /messages/?session_id=711eea472418421e8b6f395c0ee33985 HTTP/1.1" 202 Accepted
    ERROR:    Exception in ASGI application
      + Exception Group Traceback (most recent call last):
      |   File "dir\va2\.venv\Lib\site-packages\mcp\server\sse.py", line 131, in connect_sse
      |     yield (read_stream, write_stream)
      |   File "dir\va2\main.py", line 238, in handle_sse
      |     await app.run(
      |   File "dir\va2\.venv\Lib\site-packages\mcp\server\lowlevel\server.py", line 484, in run
      |     async with AsyncExitStack() as stack:
      |   File "C:\Python311\Lib\contextlib.py", line 730, in __aexit__
      |     raise exc_details[1]
      |   File "C:\Python311\Lib\contextlib.py", line 222, in __aexit__
      |     await self.gen.athrow(typ, value, traceback)
      |   File "dir\va2\.venv\Lib\site-packages\mcp\server\lowlevel\server.py", line 119, in lifespan
      |     yield {}
      |   File "C:\Python311\Lib\contextlib.py", line 713, in __aexit__
      |     cb_suppress = await cb(*exc_details)
      |                   ^^^^^^^^^^^^^^^^^^^^^^
      |   File "dir\va2\.venv\Lib\site-packages\mcp\shared\session.py", line 197, in __aexit__
      |     return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
      |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      |   File "dir\va2\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 767, in __aexit__
      |     raise BaseExceptionGroup(
      | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
      +-+---------------- 1 ----------------
        | Traceback (most recent call last):
        |   File "C:\Python311\Lib\asyncio\tasks.py", line 267, in __step
        |     result = coro.send(None)
        |              ^^^^^^^^^^^^^^^
        |   File "dir\va2\.venv\Lib\site-packages\mcp\shared\session.py", line 309, in _receive_loop
        |     await self._received_request(responder)
        |   File "dir\va2\.venv\Lib\site-packages\mcp\server\session.py", line 145, in _received_request
        |     raise RuntimeError(
        | RuntimeError: Received request before initialization was complete
        +------------------------------------
    During handling of the above exception, another exception occurred:
      + Exception Group Traceback (most recent call last):
      |   File "dir\va2\.venv\Lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 403, in run_asgi
      |     result = await app(  # type: ignore[func-returns-value]
      |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      |   File "dir\va2\.venv\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 60, in __call__
      |     return await self.app(scope, receive, send)
      |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      |   File "dir\va2\.venv\Lib\site-packages\starlette\applications.py", line 112, in __call__
      |     await self.middleware_stack(scope, receive, send)
      |   File "dir\va2\.venv\Lib\site-packages\starlette\middleware\errors.py", line 187, in __call__
      |     raise exc
      |   File "dir\va2\.venv\Lib\site-packages\starlette\middleware\errors.py", line 165, in __call__
      |     await self.app(scope, receive, _send)
      |   File "dir\va2\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 62, in __call__
      |     await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
      |   File "dir\va2\.venv\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
      |     raise exc
      |   File "dir\va2\.venv\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app
      |     await app(scope, receive, sender)
      |   File "dir\va2\.venv\Lib\site-packages\starlette\routing.py", line 714, in __call__
      |     await self.middleware_stack(scope, receive, send)
      |   File "dir\va2\.venv\Lib\site-packages\starlette\routing.py", line 734, in app
      |     await route.handle(scope, receive, send)
      |   File "dir\va2\.venv\Lib\site-packages\starlette\routing.py", line 288, in handle
      |     await self.app(scope, receive, send)
      |   File "dir\va2\.venv\Lib\site-packages\starlette\routing.py", line 76, in app
      |     await wrap_app_handling_exceptions(app, request)(scope, receive, send)
      |   File "dir\va2\.venv\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
      |     raise exc
      |   File "dir\va2\.venv\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app
      |     await app(scope, receive, sender)
      |   File "dir\va2\.venv\Lib\site-packages\starlette\routing.py", line 73, in app
      |     response = await f(request)
      |                ^^^^^^^^^^^^^^^^
      |   File "dir\va2\main.py", line 234, in handle_sse
      |     async with sse.connect_sse(
      |   File "C:\Python311\Lib\contextlib.py", line 222, in __aexit__
      |     await self.gen.athrow(typ, value, traceback)
      |   File "dir\va2\.venv\Lib\site-packages\mcp\server\sse.py", line 123, in connect_sse
      |     async with anyio.create_task_group() as tg:
      |   File "dir\va2\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 767, in __aexit__
      |     raise BaseExceptionGroup(
      | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
      +-+---------------- 1 ----------------
        | Exception Group Traceback (most recent call last):
        |   File "dir\va2\.venv\Lib\site-packages\mcp\server\sse.py", line 131, in connect_sse
        |     yield (read_stream, write_stream)
        |   File "dir\va2\main.py", line 238, in handle_sse
        |     await app.run(
        |   File "dir\va2\.venv\Lib\site-packages\mcp\server\lowlevel\server.py", line 484, in run
        |     async with AsyncExitStack() as stack:
        |   File "C:\Python311\Lib\contextlib.py", line 730, in __aexit__
        |     raise exc_details[1]
        |   File "C:\Python311\Lib\contextlib.py", line 222, in __aexit__
        |     await self.gen.athrow(typ, value, traceback)
        |   File "dir\va2\.venv\Lib\site-packages\mcp\server\lowlevel\server.py", line 119, in lifespan
        |     yield {}
        |   File "C:\Python311\Lib\contextlib.py", line 713, in __aexit__
        |     cb_suppress = await cb(*exc_details)
        |                   ^^^^^^^^^^^^^^^^^^^^^^
        |   File "dir\va2\.venv\Lib\site-packages\mcp\shared\session.py", line 197, in __aexit__
        |     return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
        |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        |   File "dir\va2\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 767, in __aexit__
        |     raise BaseExceptionGroup(
        | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
        +-+---------------- 1 ----------------
          | Traceback (most recent call last):
          |   File "C:\Python311\Lib\asyncio\tasks.py", line 267, in __step
          |     result = coro.send(None)
          |              ^^^^^^^^^^^^^^^
          |   File "dir\va2\.venv\Lib\site-packages\mcp\shared\session.py", line 309, in _receive_loop
          |     await self._received_request(responder)
          |   File "dir\va2\.venv\Lib\site-packages\mcp\server\session.py", line 145, in _received_request
          |     raise RuntimeError(
          | RuntimeError: Received request before initialization was complete
          +------------------------------------
    

    Works fine with a custom python mcp client:

        async with sse_client(url="URL/sse") as (read, write):
            async with ClientSession(read, write) as session:
                await session.initialize()
                await session.call_tool("mytool", {})

    It seems that cursor does not send initialize on each sse connection - only once when starting the IDE/editing MCP server. And it becomes a problem if the sse connection got dropped or MCP server is restarted. Seems like a cursor error, nvm :)

  4. hungchuntse commented on Apr 12, 2025

    @hungchuntse

    Hi, I’m currently facing an issue and was wondering if you might have any suggestions on how to solve it?

  5. owenliang commented on Apr 13, 2025

    @owenliang

    same error

  6. folkvir commented on Apr 13, 2025

    @folkvir
    Author

    You can use this patch, but this is not a long-term solution as it will obfuscate any other RuntimeError(s) and maybe create other errors that should not happen.

    from mcp.server.fastmcp import FastMCP
    from mcp.server.session import ServerSession
    
    ####################################################################################
    # Temporary monkeypatch which avoids crashing when a POST message is received
    # before a connection has been initialized, e.g: after a deployment.
    # pylint: disable-next=protected-access
    old__received_request = ServerSession._received_request
    
    
    async def _received_request(self, *args, **kwargs):
        try:
            return await old__received_request(self, *args, **kwargs)
        except RuntimeError:
            pass
    
    
    # pylint: disable-next=protected-access
    ServerSession._received_request = _received_request
    ####################################################################################
    
    server = FastMCP("my-project")

    @mheubi Suggested the root cause was the different clients that are not using the specification as they should. Hence 2 initialization messages are sent, or a wrong re-connection workflow.

  7. hungchuntse commented on Apr 15, 2025

    @hungchuntse

    @folkvir Thank you very much for your reply. The error no longer appears.

  8. wilson-urdaneta commented on Apr 15, 2025

    @wilson-urdaneta

    Just wanted to confirm that I was also encountering the exact same RuntimeError: Received request before initialization was complete error on the server side when using a Python client based on mcp.client.sse.sse_client and mcp.client.session.ClientSession to connect to a FastMCP server over SSE.

    Similar to others, the error occurred when the client attempted its first session.call_tool() request after establishing the connection.

    Debugging attempts like adding asyncio.sleep() delays (even up to 10 seconds) after creating the ClientSession did not resolve the issue. For reasons that now are obvious (we need to smash the supergateway bug)

    Following the suggestion from @altmind 's experience, adding an explicit await session.initialize() call immediately after the ClientSession is created solved the problem reliably.  

    Here's the structure that worked for my client script:

    Python
    
    import asyncio
    from mcp.client.session import ClientSession
    from mcp.client.sse import sse_client
    # ... other imports
    
    async def run_client(sse_url):
        try:
            async with sse_client(sse_url) as streams:
                print("SSE connection established.")
                read_stream, write_stream = streams
                async with ClientSession(read_stream, write_stream) as session:
                    print("ClientSession created.")
                    try:
                        print("--> Calling session.initialize()...")
                        await session.initialize() # <----- Explicit call added here!!!
                        print("<-- session.initialize() completed.")
    
                        # Now safe to proceed with tool calls
                        print("Session initialized, safe to call tools.")
                        # Example tool call:
                        # response = await session.call_tool("some_tool", {"arg": "value"})
                        # print(f"Tool response: {response}")
    
                    except Exception as init_error:
                        print(f"Error during session.initialize(): {init_error}")
    
        except Exception as conn_error:
            print(f"Connection error: {conn_error}")
    
    # Example usage:
    # asyncio.run(run_client("http://your-server-url/sse"))
    

    As of today, it seems explicit initialization via await session.initialize() is crucial for proper synchronization when using the Python client SDK over SSE.

    Hope this confirmation helps others debugging this issue!

  9. Nedomas commented on Apr 16, 2025

    @Nedomas

    @mheubi supergateway bug with double initialization was patched 2 days ago in 2.7.0 if still relevant (refs)

  10. rohitjangid commented on Apr 22, 2025

    @rohitjangid

    I am too facing this error when using cursor as mcp client

  11. folkvir commented on Apr 23, 2025

    @folkvir
    Author

    @Nedomas Without my patch and with the supergateway 2.7.0 I still get the error when I reload my server.
    At this point I think supergateway is working correctly and it is only the different MCP clients that do not regenerate the session correctly when the server connection has been lost that are the cause of the error. In my case this is the MCP clients (anything-llm and fastagent) that are sending a post message for an invalid session. anything-llm is built with the typescript-sdk client so I guess the workflow is correct between the 2 SDKs. As @altmind said, the main problem is for the initialization workflow. The different clients are intializing the session only when starting the server and there is no reconnection workflow. This should be part of the MCP client implementation to handle such disconnection. But it may be possible that a client is not aware of the server disconnection/reload and so if the client sends its POST message the server will crash with our famous Received request before initialization was complete. Instead we should receive a kind of reconnection notification that is a cancel notification (for stopping the current action) + a Initialization Phase request to regenerate the current session.

    I don't know how it can be done easily with the current implementation and specification but what I know is that we should never do await session.initialize() followed by await session.<action> everytime. In the worst case we will reinitialize the session everytime we want to call the MCP server. A simple REST HTTP POST with the MCP server answer would have been better at this point.

  12. CaoChensy commented on May 7, 2025

    @CaoChensy

    Mark same error

  13. goshut commented on May 16, 2025

    @goshut

    When the server restarts quickly, clients maintaining SSE connections from previous sessions receive RuntimeError: RuntimeError: Received request before initialization was complete.

    The root cause is:

    Session State Persistence
    Clients retain old session IDs after server restart and continue using them for non-initialization requests.

    Missing Session Validation
    The current _received_request handler in server/session.py doesn't detect stale sessions when processing non-initialization requests (the case _ branch).

    Affected Component:
    mcp/server/session.py in _received_request method

    Server should detect stale sessions after restart

    Clients should be notified to reinitialize when using obsolete sessions
    Or for performance, provide quick reconnection function

  14. folkvir commented on May 18, 2025

    @folkvir
    Author

    Unrelated to the issue but, for those interested to completely fix your problem, then use the recommended streamable http protocol implementation in stateless mode with mcp-remote/supergateway as proxy. It should solve your problem.

  15. nafan2008 commented on May 19, 2025

    @nafan2008

    Unrelated to the issue but, for those interested to completely fix your problem, then use the recommended streamable http protocol implementation in stateless mode with mcp-remote as proxy. It should solve your problem.

    @folkvir could you please give more specific advice how to implement this?
    Can it be resolved on server side only - as in my case I try to connect vscode to my mcp?

  16. 11 remaining items

  17. added
    bugSomething isn't working
    ready for workEnough information for someone to start working on
    P1Significant bug affecting many users, highly requested feature
    P2Moderate issues affecting some users, edge cases, potentially valuable feature
    and removed
    P1Significant bug affecting many users, highly requested feature
    on Oct 3, 2025
  18. added 2 commits that reference this issue on Mar 2, 2026
    1fce2fd
    c152ca5
  19. MukundaKatta commented on May 10, 2026

    @MukundaKatta

    Saw this when our MCP server pod restarted while clients still held the previous SSE connection. The server lost the in-memory session record but the client kept sending against the old session-id and hit "before initialization was complete" on every request.

    Two practical fixes: client should re-handshake on any 4xx/5xx instead of retrying the same session-id, and server should emit a clear close frame on shutdown so clients drop the session promptly.

    Pluggable session storage upstream (#880) would also fix the root cause but the client-side resilience pattern works today.

  20. laozhangzzz commented on Jun 25, 2026

    @laozhangzzz
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

    P2Moderate issues affecting some users, edge cases, potentially valuable featurebugSomething isn't workingready for workEnough information for someone to start working on

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions