Hmbown/CodeWhale · error
MCP SSE server sent a message before declaring its endpoint
Error message
MCP SSE server sent a message before declaring its endpoint
What it means
Per the SSE transport protocol, the server must send its endpoint event before any JSON-RPC messages. If a `Message` arrives on the inbound channel before an `Endpoint`, the client rejects it as a protocol violation rather than buffering or guessing the endpoint.
Solutions
- Fix the server to send the `endpoint` event as the first SSE event after connection
- If testing, update the mock to emit `event: endpoint` before any message events
- Check whether a stale session/stream replay is occurring and open a fresh connection
- Verify server MCP SDK version supports the SSE transport handshake correctly
Example fix
// server, before send(data_event); send(endpoint_event); // after send(endpoint_event); // endpoint MUST come first send(data_event);
Defensive patterns
Strategy: try-catch
Try / catch
match wait_for_endpoint(...).await {
Err(e) if e.to_string().contains("before declaring its endpoint") => {
error!("server violates SSE handshake order; update server or SDK");
}
other => other?,
} Prevention
- Ensure servers send the endpoint event first, before any messages
- Test mocks against the real handshake order
- Open fresh sessions instead of resuming replaying streams
When it happens
Trigger: Server emits JSON-RPC messages before the endpoint event — a non-conformant SSE MCP server implementation or a stream resumed mid-session replaying old messages.
Common situations: Custom/in-house MCP SSE servers that send data immediately; resuming a cached stream that replays messages; testing against a mock that skips the endpoint event.
Related errors
- MCP server ' ' initialize result omitted protocolVersion
- MCP server ' ' negotiated unsupported protocol version ' '…
- MCP server ' ': unsupported protocol version ' '…
- MCP SSE frame exceeded
- SSE endpoint not received within
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/2a8edb1034650d34.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp/sse.rs:231
let msg = tokio::select! {
_ = cancel_token.cancelled() => {
anyhow::bail!("SSE transport cancelled before endpoint was discovered");
}
_ = &mut timeout => {
anyhow::bail!(
"SSE endpoint not received within {}ms",
endpoint_timeout.as_millis()
);
}
msg = self.receiver.recv() => {
msg.context("SSE transport closed before endpoint was discovered")?
}
};
match msg {
SseInbound::Endpoint(endpoint) => self.store_endpoint(&endpoint),
SseInbound::Message(_) => {
anyhow::bail!("MCP SSE server sent a message before declaring its endpoint");
}
}
}
fn store_endpoint(&mut self, endpoint: &str) -> Result<()> {
self.endpoint_url = Some(Self::resolve_endpoint_url(&self.base_url, endpoint)?);
Ok(())
}
fn resolve_endpoint_url(base_url: &str, endpoint_url: &str) -> Result<String> {
let base = reqwest::Url::parse(base_url)?;
let resolved =
if endpoint_url.starts_with("http://") || endpoint_url.starts_with("https://") {
reqwest::Url::parse(endpoint_url)?
} else {
base.join(endpoint_url)?
};
// Security: the server-supplied `endpoint` event must stay same-originView on GitHub (pinned to 73e0f67d83)