Repository navigation
MCP SSE Server: Received request before initialization was complete #423
Description
Activity
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
Reacted by A.G and earonesty一个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
- Exception Group Traceback (most recent call last):
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 :)
Reacted by Daniel Lahyani and MistyTealHi, 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.
Reacted by Nick Hung, Quang Nguyễn, Heiko Braun, Minseok Jung, Sakthi Narayanan R, xuanran, Karthic Hariharan, Tingli Tan, tusiDeng, Austin Kelley and 11 moreReacted by Nick Hung, Roman Elizarov, Quang Nguyễn, Omar McIver, xuanran, Tingli Tan, Laura Dang, Austin Kelley, Arvid Mildner, bdigismat and 4 more@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 completeerror 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!
Reacted by Jesse, Saman, Betula-L, Andrew G, huangsun and Eugene WuI 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 famousReceived request before initialization was complete. Instead we should receive a kind ofreconnection notificationthat is acancel notification(for stopping the current action) + aInitialization Phaserequest 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 byawait 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.Reacted by goshut and aviotjMark 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 methodServer should detect stale sessions after restart
Clients should be notified to reinitialize when using obsolete sessions
Or for performance, provide quick reconnection functionUnrelated 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?11 remaining items
- addedbugSomething isn't workingSomething isn't workingready for workEnough information for someone to start working onEnough information for someone to start working onP1Significant bug affecting many users, highly requested featureSignificant bug affecting many users, highly requested featureP2Moderate issues affecting some users, edge cases, potentially valuable featureModerate issues affecting some users, edge cases, potentially valuable featureand removedP1Significant bug affecting many users, highly requested featureSignificant bug affecting many users, highly requested feature
on Oct 3, 2025 - added 2 commits that reference this issue
on Dec 2, 2025 - added a commit that references this issue
on Feb 2, 2026 - added 2 commits that reference this issue
on Mar 2, 2026 - added a commit that references this issue
on Apr 9, 2026 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.
- added a commit that references this issue
on Aug 14, 2026
Describe the bug
The MCP server receives
Received request before initialization was completeafter 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
Start using uvicorn
FASTMCP_LOG_LEVEL=DEBUG uv run uvicorn main:app --port=8001 --host=0.0.0.0 --timeout-graceful-shutdown 5Run 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 5Go to the inspector and try to list the tools, it will timeout and you'll get the following server logs
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? 🤔
python-sdk/src/mcp/server/sse.py
Lines 153 to 157 in c2ca8e0
EDIT: I actually tested to comment this line and it works correctly now!
python-sdk/src/mcp/server/session.py
Lines 162 to 165 in c2ca8e0
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: