MCP SSE Server: Received request before initialization was complete
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? 🤔 https://github.com/modelcontextprotocol/python-sdk/blob/c2ca8e03e046908935d089a2ceed4e80b0c29a24/src/mcp/server/sse.py#L153-L157
EDIT: I actually tested to comment this line and it works correctly now!
https://github.com/modelcontextprotocol/python-sdk/blob/c2ca8e03e046908935d089a2ceed4e80b0c29a24/src/mcp/server/session.py#L162-L165
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",
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
一个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
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 :)
Hi, I’m currently facing an issue and was wondering if you might have any suggestions on how to solve it?
same error
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.
@folkvir Thank you very much for your reply. The error no longer appears.
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!
@mheubi supergateway bug with double initialization was patched 2 days ago in 2.7.0 if still relevant (refs)
I am too facing this error when using cursor as mcp client
@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.
Mark same error
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
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.
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?
@nafan2008 Just follow their examples: https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/servers/simple-streamablehttp/mcp_simple_streamablehttp/server.py And use mcp-remote like that (adapt to your needs):
{
"mcpServers": {
"myserver": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"http://localhost:8000/mcp",
"--header",
"Authorization: Bearer <my-access-token-if-needed>"
]
}
}
}
Notes:
- mcp-remote is for now one of the few proxies that is working with the new specification.
- supergateway is on its way too: https://github.com/supercorp-ai/supergateway/pull/63
mark
@folkvir It happens with mcp-remote also.
Even if I remove ~/.mcp-auth folder content, it still get RuntimeError.
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.
Hey! So I’m using mcp-remote just as you have in the example above with streamable http but I still get the same error
@bruno-oliveira We added Streamable HTTP to Stdio to Supergateway v3.0.1 and previously solved the Received request before initialization was complete bug, so you can test it out. Let me know if it solves it for you
https://github.com/supercorp-ai/supergateway
It seems like there are several things going in how people are experiencing this error. Certainly the SuperGateway bug was a factor in some, and the bugs in SSE reconnection are another set. I have a specific instance that is different from these.
In my case, I can cause this to happen (infrequently), by just sending notifications/initialized and tools/list in quick succession (which is allowed by the protocol and a pretty normal thing to do).
My assertion is that the server session code in the Python SDK is not properly sequencing the notifications/initialized message that concludes initialization and any following messages. I'm not really sure how the Python code handles this case (in terms of async and message overlap), but it looks like while it's handling the notifications/initialized message the tools/list messages comes it, gets processed, and generates the error (because it hasn't completed initialization yet). I think this might work correctly almost all of the time because the session initialization is fast and usually completes in time.
I think the correct logic is that if initialization is in process, but not complete, it should wait for completion before processing any other pending messages.
Any update?
meet the same problem, any updates for official sdk?