fix(server): remove initialized notification gate to support Streamable HTTP (#788)
* fix(server): remove initialized notification gate to support Streamable 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 #783 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(server): keep ExpectedInitializedNotification as deprecated Retain the variant for semver compatibility — removing it would be a breaking change caught by cargo-semver-checks. Mark it deprecated with a note that it is never constructed and will be removed in a future major release. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Anar Azadaliyev <anar.azadaliye@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a7b570062e
commit
65d2b29da5
1 changed files with 78 additions and 54 deletions
|
|
@ -53,6 +53,10 @@ pub enum ServerInitializeError {
|
|||
#[error("expect initialized request, but received: {0:?}")]
|
||||
ExpectedInitializeRequest(Option<ClientJsonRpcMessage>),
|
||||
|
||||
#[deprecated(
|
||||
since = "1.4.0",
|
||||
note = "The server no longer gates on the initialized notification. This variant is never constructed and will be removed in a future major release."
|
||||
)]
|
||||
#[error("expect initialized notification, but received: {0:?}")]
|
||||
ExpectedInitializedNotification(Option<ClientJsonRpcMessage>),
|
||||
|
||||
|
|
@ -243,49 +247,12 @@ where
|
|||
ServerInitializeError::transport::<T>(error, "sending initialize response")
|
||||
})?;
|
||||
|
||||
// Wait for initialized notification. The MCP spec permits logging/setLevel and ping
|
||||
// before initialized; VS Code sends setLevel immediately after the initialize response.
|
||||
let notification = loop {
|
||||
let msg = expect_next_message(&mut transport, "initialize notification").await?;
|
||||
match msg {
|
||||
ClientJsonRpcMessage::Notification(n)
|
||||
if matches!(
|
||||
n.notification,
|
||||
ClientNotification::InitializedNotification(_)
|
||||
) =>
|
||||
{
|
||||
break n.notification;
|
||||
}
|
||||
ClientJsonRpcMessage::Request(req)
|
||||
if matches!(
|
||||
req.request,
|
||||
ClientRequest::SetLevelRequest(_) | ClientRequest::PingRequest(_)
|
||||
) =>
|
||||
{
|
||||
transport
|
||||
.send(ServerJsonRpcMessage::response(
|
||||
ServerResult::EmptyResult(EmptyResult {}),
|
||||
req.id,
|
||||
))
|
||||
.await
|
||||
.map_err(|error| {
|
||||
ServerInitializeError::transport::<T>(error, "sending pre-init response")
|
||||
})?;
|
||||
}
|
||||
other => {
|
||||
return Err(ServerInitializeError::ExpectedInitializedNotification(
|
||||
Some(other),
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
let context = NotificationContext {
|
||||
meta: notification.get_meta().clone(),
|
||||
extensions: notification.extensions().clone(),
|
||||
peer: peer.clone(),
|
||||
};
|
||||
let _ = service.handle_notification(notification, context).await;
|
||||
// Continue processing service
|
||||
// Enter the main service loop immediately after sending InitializeResult.
|
||||
// The initialized notification will be handled as a regular notification by serve_inner.
|
||||
// This matches the TypeScript SDK behavior: no init gate, no waiting for initialized.
|
||||
// Streamable HTTP has no ordering guarantee between POSTs, and the MCP spec uses
|
||||
// SHOULD NOT (not MUST NOT) for pre-initialized messages, so any request arriving
|
||||
// before initialized is processed normally.
|
||||
Ok(serve_inner(service, transport, peer, peer_rx, ct))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue