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 MCP SSE protocol the server's first event must be endpoint; this error fires when a message event arrives before any endpoint was declared. The transport refuses to continue because it has no URL to POST requests to — a server-side protocol violation.

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-origin

View on GitHub (pinned to 8880682c63)

Solutions

  1. Fix the server to send `event: endpoint` (POST URL in data) as its first event after connect
  2. Update the server to a spec-compliant MCP SDK version
  3. If the server only speaks streamable HTTP, switch the client to transport=http instead of sse
Defensive patterns

Strategy: fallback

Validate before calling

```rust
// Reuse the endpoint probe and additionally assert the FIRST event is `endpoint`:
async fn first_event_is_endpoint(client: &reqwest::Client, url: &str) -> bool {
    // read the first complete event block; return true only if it starts with `event: endpoint`
    // (same shape as announces_endpoint, but return false on any `event: message` seen first)
    announces_endpoint_no_messages_first(client, url).await
}
```

Try / catch

On this protocol violation, mark the server non-conforming and fall back to streamable-http transport or a compliant server version; retrying the same SSE connect cannot succeed.

Prevention

When it happens

Trigger: The server pushes a JSON-RPC message or notification immediately on connect without first sending `event: endpoint` with the POST URL.

Common situations: Home-grown or outdated MCP servers that skip the handshake; servers migrated to streamable-http that still accept SSE GETs; test stubs emitting canned messages first.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/2a8edb1034650d34. Report an issue: GitHub.