Skip to content

fix(server): remove initialized notification gate to support Streamable HTTP - #788

Merged
DaleSeo merged 2 commits into
modelcontextprotocol:mainfrom
binahm:fix/remove-init-gate-for-streamable-http
Apr 9, 2026
Merged

fix(server): remove initialized notification gate to support Streamable HTTP#788
DaleSeo merged 2 commits into
modelcontextprotocol:mainfrom
binahm:fix/remove-init-gate-for-streamable-http

Conversation

@anara123

@anara123 anara123 commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Remove the ~40-line initialized notification wait loop that fatally rejected any request arriving before notifications/initialized
  • Enter serve_inner immediately after sending InitializeResult, letting the main service loop handle all messages including initialized
  • Remove the now-unreachable ExpectedInitializedNotification error variant from ServerInitializeError

Motivation

Streamable HTTP requires each JSON-RPC message as a separate POST request. The transport layer cannot guarantee delivery order, so tools/list can easily arrive before notifications/initialized, causing a fatal HTTP 500 error.

The MCP spec uses SHOULD NOT (RFC 2119), not MUST NOT, for pre-initialized messages — allowing exceptions in specific circumstances like HTTP's lack of ordering guarantees.

This aligns with the TypeScript SDK's behavior, which processes requests immediately after initialize without gating on initialized (validated in modelcontextprotocol/typescript-sdk#578).

Test plan

  • Updated server_init_set_level_response_is_empty_result to handle notifications from serve_inner dispatch
  • Updated server_init_succeeds_after_set_level_before_initialized similarly
  • Replaced server_init_rejects_unexpected_message_before_initialized with server_init_buffers_request_before_initialized — verifies tools/list before initialized is processed successfully
  • Added server_init_buffers_multiple_requests_before_initialized — verifies multiple pre-init messages are all processed

Closes #783

…le HTTP

The server's init handshake loop fatally rejected any request arriving
before the `notifications/initialized` message. This breaks Streamable
HTTP clients where each JSON-RPC message is a separate POST with no
ordering guarantee — `tools/list` can easily arrive before `initialized`.

Remove the ~40-line wait loop and enter `serve_inner` immediately after
sending `InitializeResult`. The `initialized` notification is now
handled as a regular notification by the main service loop, matching the
TypeScript SDK behavior (validated in typescript-sdk#578).

Also remove the now-unreachable `ExpectedInitializedNotification` error
variant from `ServerInitializeError`.

Closes modelcontextprotocol#783

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@anara123
anara123 requested a review from a team as a code owner April 3, 2026 12:56
@github-actions github-actions Bot added T-test Testing related changes T-core Core library changes T-service Service layer changes labels Apr 3, 2026
@anara123

anara123 commented Apr 9, 2026

Copy link
Copy Markdown
Contributor Author

@DaleSeo @alexhancock can anyone review this please.

@DaleSeo DaleSeo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.