Title: MCP SSE Server: Received request before initialization was complete · Issue #423 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: MCP SSE Server: Received request before initialization was complete · Issue #423 · modelcontextprotocol/python-sdk
X Title: MCP SSE Server: Received request before initialization was complete · Issue #423 · modelcontextprotocol/python-sdk
Description: 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 SI...
Open Graph Description: 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 r...
X Description: 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 r...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/issues/423
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"MCP SSE Server: Received request before initialization was complete","articleBody":"**Describe the bug**\n\nThe 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.\n\n**To Reproduce**\n\nSImple main.py which is one of your examples\n```python\nimport httpx\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP(\"My App\")\n\n\n@mcp.tool()\ndef calculate_bmi(weight_kg: float, height_m: float) -\u003e float:\n \"\"\"Calculate BMI given weight in kg and height in meters\"\"\"\n return weight_kg / (height_m**2)\n\n\n@mcp.tool()\nasync def fetch_weather(city: str) -\u003e str:\n \"\"\"Fetch current weather for a city\"\"\"\n async with httpx.AsyncClient() as client:\n response = await client.get(f\"https://api.weather.com/{city}\")\n return response.text\n\napp = mcp.sse_app()\n```\n\n* Start using uvicorn `FASTMCP_LOG_LEVEL=DEBUG uv run uvicorn main:app --port=8001 --host=0.0.0.0 --timeout-graceful-shutdown 5`\n\n* Run the inspector in SSE mode and listen on `http://localhost:8001/see`. You will be able to list tools.\n* 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`\n* Go to the inspector and try to list the tools, it will timeout and you'll get the following server logs\n\n```bash\nFASTMCP_LOG_LEVEL=DEBUG uv run uvicorn main:app --port=8001 --host=0.0.0.0 --timeout-graceful-shutdown 5 \n[04/03/25 23:48:40] DEBUG SseServerTransport initialized with endpoint: /messages/ sse.py:79\nINFO: Started server process [89042]\nINFO: Waiting for application startup.\nINFO: Application startup complete.\nINFO: Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit)\n[04/03/25 23:48:41] DEBUG Setting up SSE connection sse.py:87\n DEBUG Created new session with ID: f55c6f64-be08-464c-bbab-203223adb44c sse.py:100\n DEBUG Starting SSE response task sse.py:127\n DEBUG Yielding read and write streams sse.py:130\nINFO: 127.0.0.1:62930 - \"GET /sse HTTP/1.1\" 200 OK\n DEBUG Starting SSE writer sse.py:107\n DEBUG Sent endpoint event: /messages/?session_id=f55c6f64be08464cbbab203223adb44c sse.py:110\n DEBUG chunk: b'event: endpoint\\r\\ndata: /messages/?session_id=f55c6f64be08464cbbab203223adb44c\\r\\n\\r\\n' sse.py:156\n[04/03/25 23:48:45] DEBUG Handling POST message sse.py:136\n DEBUG Parsed session ID: f55c6f64-be08-464c-bbab-203223adb44c sse.py:147\nwriter: MemoryObjectSendStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=1, open_receive_channels=1, waiting_receivers=OrderedDict({\u003canyio._backends._asyncio.Event object at 0x103ff6f00\u003e: MemoryObjectItemReceiver(task_info=AsyncIOTaskInfo(id=4386289792, name='mcp.shared.session.BaseSession._receive_loop'), item=None)}), waiting_senders=OrderedDict()), _closed=False)\n DEBUG Received JSON: b'{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/list\",\"params\":{}}' sse.py:161\n DEBUG Validated client message: root=JSONRPCRequest(method='tools/list', params={}, jsonrpc='2.0', id=3) sse.py:165\n DEBUG Sending message to writer: root=JSONRPCRequest(method='tools/list', params={}, jsonrpc='2.0', id=3) sse.py:173\nINFO: 127.0.0.1:62936 - \"POST /messages/?session_id=f55c6f64be08464cbbab203223adb44c HTTP/1.1\" 202 Accepted\n DEBUG Got event: http.disconnect. Stop streaming. sse.py:177\nERROR: Exception in ASGI application\n + Exception Group Traceback (most recent call last):\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/uvicorn/protocols/http/h11_impl.py\", line 403, in run_asgi\n | result = await app( # type: ignore[func-returns-value]\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py\", line 60, in __call__\n | return await self.app(scope, receive, send)\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/applications.py\", line 112, in __call__\n | await self.middleware_stack(scope, receive, send)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py\", line 187, in __call__\n | raise exc\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py\", line 165, in __call__\n | await self.app(scope, receive, _send)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py\", line 62, in __call__\n | await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n | raise exc\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n | await app(scope, receive, sender)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/routing.py\", line 714, in __call__\n | await self.middleware_stack(scope, receive, send)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/routing.py\", line 734, in app\n | await route.handle(scope, receive, send)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/routing.py\", line 288, in handle\n | await self.app(scope, receive, send)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/routing.py\", line 76, in app\n | await wrap_app_handling_exceptions(app, request)(scope, receive, send)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n | raise exc\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n | await app(scope, receive, sender)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/starlette/routing.py\", line 73, in app\n | response = await f(request)\n | ^^^^^^^^^^^^^^^^\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/server/fastmcp/server.py\", line 485, in handle_sse\n | async with sse.connect_sse(\n | File \"/Users/xxxxx/.pyenv/versions/3.12.2/lib/python3.12/contextlib.py\", line 231, in __aexit__\n | await self.gen.athrow(value)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/server/sse.py\", line 123, in connect_sse\n | async with anyio.create_task_group() as tg:\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py\", line 772, in __aexit__\n | raise BaseExceptionGroup(\n | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)\n +-+---------------- 1 ----------------\n | Exception Group Traceback (most recent call last):\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/server/sse.py\", line 131, in connect_sse\n | yield (read_stream, write_stream)\n | File \"/Users/xxxxx.venv/lib/python3.12/site-packages/mcp/server/fastmcp/server.py\", line 490, in handle_sse\n | await self._mcp_server.run(\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py\", line 483, in run\n | async with AsyncExitStack() as stack:\n | File \"/Users/xxxxx/.pyenv/versions/3.12.2/lib/python3.12/contextlib.py\", line 754, in __aexit__\n | raise exc_details[1]\n | File \"/Users/xxxxx/.pyenv/versions/3.12.2/lib/python3.12/contextlib.py\", line 737, in __aexit__\n | cb_suppress = await cb(*exc_details)\n | ^^^^^^^^^^^^^^^^^^^^^^\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/shared/session.py\", line 210, in __aexit__\n | return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py\", line 772, in __aexit__\n | raise BaseExceptionGroup(\n | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)\n +-+---------------- 1 ----------------\n | Traceback (most recent call last):\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/shared/session.py\", line 324, in _receive_loop\n | await self._received_request(responder)\n | File \"/Users/xxxxx/.venv/lib/python3.12/site-packages/mcp/server/session.py\", line 163, in _received_request\n | raise RuntimeError(\n | RuntimeError: Received request before initialization was complete\n +------------------------------------\n[04/03/25 23:48:48] DEBUG Setting up SSE connection sse.py:87\n DEBUG Created new session with ID: 1541ccf4-7bf9-407b-b2a2-94552715a527 sse.py:100\n DEBUG Starting SSE response task sse.py:127\n DEBUG Yielding read and write streams sse.py:130\nINFO: 127.0.0.1:62938 - \"GET /sse HTTP/1.1\" 200 OK\n DEBUG Starting SSE writer sse.py:107\n DEBUG Sent endpoint event: /messages/?session_id=1541ccf47bf9407bb2a294552715a527 sse.py:110\n DEBUG chunk: b'event: endpoint\\r\\ndata: /messages/?session_id=1541ccf47bf9407bb2a294552715a527\\r\\n\\r\\n' sse.py:156\n[04/03/25 23:48:55] DEBUG Handling POST message sse.py:136\n DEBUG Parsed session ID: 1541ccf4-7bf9-407b-b2a2-94552715a527 sse.py:147\nwriter: MemoryObjectSendStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=1, open_receive_channels=1, waiting_receivers=OrderedDict({\u003canyio._backends._asyncio.Event object at 0x105916510\u003e: MemoryObjectItemReceiver(task_info=AsyncIOTaskInfo(id=4386290368, name='mcp.shared.session.BaseSession._receive_loop'), item=None)}), waiting_senders=OrderedDict()), _closed=False)\n DEBUG Received JSON: b'{\"jsonrpc\":\"2.0\",\"method\":\"notifications/cancelled\",\"params\":{\"requestId\":3,\"reason\":\"Request timed out\"}}' sse.py:161\n DEBUG Validated client message: root=JSONRPCNotification(method='notifications/cancelled', params={'requestId': 3, 'reason': 'Request timed out'}, jsonrpc='2.0') sse.py:165\n DEBUG Sending message to writer: root=JSONRPCNotification(method='notifications/cancelled', params={'requestId': 3, 'reason': 'Request timed out'}, sse.py:173\n jsonrpc='2.0') \nINFO: 127.0.0.1:62945 - \"POST /messages/?session_id=1541ccf47bf9407bb2a294552715a527 HTTP/1.1\" 202 Accepted\n[04/03/25 23:49:03] DEBUG ping: b': ping - 2025-04-03 21:49:03.579244+00:00\\r\\n\\r\\n'\n\n```\n\n**Expected behavior**\n\nSomehow, a POST message is accepted while it should not. Is it correct to accept such request?\nShouldn'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\n\nEDIT: I actually tested to comment this line and it works correctly now! \n\nhttps://github.com/modelcontextprotocol/python-sdk/blob/c2ca8e03e046908935d089a2ceed4e80b0c29a24/src/mcp/server/session.py#L162-L165\n\nTested with fast-agent, LibreChat and Inspector. Even after a server reload, call tools are still working.\nIf I understand correctly the [global mechanism ](https://spec.modelcontextprotocol.io/specification/2024-11-05/architecture/#capability-negotiation) 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 😬 \n\n\n**Desktop:**\n - OS: MacOS 15.3\n - Browser chrome latest\n - Python 3.12\n ```\n\"mcp[cli]\u003e=1.6.0\",\n\"starlette\u003e=0.46.1\",\n\"uvicorn\u003e=0.34.0\",\n```","author":{"url":"https://github.com/folkvir","@type":"Person","name":"folkvir"},"datePublished":"2025-04-03T22:11:23.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":25},"url":"https://github.com/423/python-sdk/issues/423"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:07fccf9f-e51f-72f8-f73d-f0bc8db792e1 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | BBD8:35FD83:7BD1EF:A69A13:6A5EE442 |
| html-safe-nonce | 642d99d6b2ce6f3568402e6295c4ae91338d5cebdd6359e491ea0972d718c52d |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCQkQ4OjM1RkQ4Mzo3QkQxRUY6QTY5QTEzOjZBNUVFNDQyIiwidmlzaXRvcl9pZCI6IjQwOTE1NTIwMTU5NDM5MTg2NTgiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 86962ddd58bb39997138d8d35af8039c20071a761a138b0bf8c8bddc4a924ca0 |
| hovercard-subject-tag | issue:2970806952 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/modelcontextprotocol/python-sdk/423/issue_layout |
| twitter:image | https://opengraph.githubassets.com/7e69e014119211df6706c1ba90e7ee40bb492af598bc5ec788652e83f13ec7d1/modelcontextprotocol/python-sdk/issues/423 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/7e69e014119211df6706c1ba90e7ee40bb492af598bc5ec788652e83f13ec7d1/modelcontextprotocol/python-sdk/issues/423 |
| og:image:alt | 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 r... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | folkvir |
| hostname | github.com |
| expected-hostname | github.com |
| None | 82d0004a35927bdb00c652a57f456c55aa0eda4ade1bc7956d7510fdea6e454b |
| turbo-cache-control | no-preview |
| go-import | github.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git |
| octolytics-dimension-user_id | 182288589 |
| octolytics-dimension-user_login | modelcontextprotocol |
| octolytics-dimension-repository_id | 862584018 |
| octolytics-dimension-repository_nwo | modelcontextprotocol/python-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 862584018 |
| octolytics-dimension-repository_network_root_nwo | modelcontextprotocol/python-sdk |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | d2d913dd727d2d7ac189fb4d05b10bc34ecd03ee |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width