-
Notifications
You must be signed in to change notification settings - Fork 489
Server fatally rejects any request before initialized notification — breaks Streamable HTTP clients #783
Description
Describe the bug
After sending the InitializeResult, the server enters a loop waiting for notifications/initialized. This loop only tolerates logging/setLevel and ping — any other message (e.g. tools/list, resources/list) causes a fatal ServerInitializeError::ExpectedInitializedNotification error that kills the entire session.
This is the problematic code in crates/rmcp/src/service/server.rs lines 276-280:
other => {
return Err(ServerInitializeError::ExpectedInitializedNotification(
Some(other),
));
}PR #730 (fixing #704) added tolerance for SetLevelRequest and PingRequest, but any other request type still fatally terminates the session.
To Reproduce
- Configured OpenClaw to run with mcp-remote.
- Client sends
initialize, receivesInitializeResult, then sends bothnotifications/initializedandtools/listas separate HTTP POSTs - If
tools/listPOST arrives beforenotifications/initializedPOST (which is common — there's no ordering guarantee), the server crashes
Error:
Failed to serve session: expect initialized notification, but received:
Some(Request(JsonRpcRequest { ... request: ListToolsRequest(...) }))
The session worker exits, the transport drops, and all subsequent requests return HTTP 500.
Expected behavior
The server should process requests arriving before notifications/initialized normally, rather than fatally rejecting them. The initialized notification should not gate request handling.
Why this should be fixed
1. MCP spec uses SHOULD NOT, not MUST NOT
From the MCP Specification (2025-11-25), Lifecycle:
"The client SHOULD NOT send requests other than pings before the server has responded to the
initializerequest.""The server SHOULD NOT send requests other than pings and logging before receiving the
initializednotification."
Per RFC 2119, SHOULD NOT means "there may exist valid reasons in particular circumstances when the behavior is acceptable." The spec constrains what each side sends — it says nothing about rejecting messages received during this phase.
2. Streamable HTTP has no ordering guarantee
From the MCP Specification (2025-11-25), Transports:
"Every JSON-RPC message sent from the client MUST be a new HTTP POST request to the MCP endpoint."
Each POST is independent. Even if a well-behaved client sends initialized before tools/list, the HTTP layer may deliver them in any order.
3. The TypeScript SDK (reference implementation) does not gate on initialized
The official TypeScript SDK handles this differently — it has no initialization gate and processes all requests immediately after initialize:
-
Protocol layer (
protocol.tsline 542):_onrequest()dispatches immediately to the registered handler — no_initializedcheck exists anywhere in this file. -
Server class (
server.tsline 136): Thenotifications/initializedhandler merely fires an optionaloninitialized?.()callback. There is no logic that rejects or queues requests arriving beforeinitialized. -
StreamableHTTP transport (
streamableHttp.tsline 678):_initializedis set totrueon theinitializerequest (not theinitializednotification).validateSession()(line 852) only rejects if_initializedisfalse— i.e., before theinitializerequest, not beforenotifications/initialized.
The sequence initialize → tools/list → notifications/initialized works perfectly in the TypeScript SDK. This was confirmed in typescript-sdk#578, which was closed with the clarification that responding to client requests before initialized does not violate the spec.
Proposed fix
Remove the initialized notification wait loop entirely. After sending InitializeResult, enter serve_inner immediately. The initialized notification is then handled as a regular notification by the service's normal dispatch — matching the TypeScript SDK behavior.
This is a ~40-line deletion with no new code needed, since serve_inner already handles all message types including notifications.
I have a working implementation and can open a PR if preferred.