{"record":{"id":"2a8edb1034650d34","repo":"Hmbown/CodeWhale","slug":"mcp-sse-server-sent-a-message-before-declaring-its","errorCode":null,"errorMessage":"MCP SSE server sent a message before declaring its endpoint","messagePattern":"MCP SSE server sent a message before declaring its endpoint","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp/sse.rs","lineNumber":231,"sourceCode":"        let msg = tokio::select! {\n            _ = cancel_token.cancelled() => {\n                anyhow::bail!(\"SSE transport cancelled before endpoint was discovered\");\n            }\n            _ = &mut timeout => {\n                anyhow::bail!(\n                    \"SSE endpoint not received within {}ms\",\n                    endpoint_timeout.as_millis()\n                );\n            }\n            msg = self.receiver.recv() => {\n                msg.context(\"SSE transport closed before endpoint was discovered\")?\n            }\n        };\n\n        match msg {\n            SseInbound::Endpoint(endpoint) => self.store_endpoint(&endpoint),\n            SseInbound::Message(_) => {\n                anyhow::bail!(\"MCP SSE server sent a message before declaring its endpoint\");\n            }\n        }\n    }\n\n    fn store_endpoint(&mut self, endpoint: &str) -> Result<()> {\n        self.endpoint_url = Some(Self::resolve_endpoint_url(&self.base_url, endpoint)?);\n        Ok(())\n    }\n\n    fn resolve_endpoint_url(base_url: &str, endpoint_url: &str) -> Result<String> {\n        let base = reqwest::Url::parse(base_url)?;\n        let resolved =\n            if endpoint_url.starts_with(\"http://\") || endpoint_url.starts_with(\"https://\") {\n                reqwest::Url::parse(endpoint_url)?\n            } else {\n                base.join(endpoint_url)?\n            };\n        // Security: the server-supplied `endpoint` event must stay same-origin","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp/sse.rs#L213-L249","documentation":"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.","triggerScenarios":"The server pushes a JSON-RPC message or notification immediately on connect without first sending `event: endpoint` with the POST URL.","commonSituations":"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.","solutions":["Fix the server to send `event: endpoint` (POST URL in data) as its first event after connect","Update the server to a spec-compliant MCP SDK version","If the server only speaks streamable HTTP, switch the client to transport=http instead of sse"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"```rust\n// Reuse the endpoint probe and additionally assert the FIRST event is `endpoint`:\nasync fn first_event_is_endpoint(client: &reqwest::Client, url: &str) -> bool {\n    // read the first complete event block; return true only if it starts with `event: endpoint`\n    // (same shape as announces_endpoint, but return false on any `event: message` seen first)\n    announces_endpoint_no_messages_first(client, url).await\n}\n```","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["Only attach transport=sse to MCP SDK servers that implement the endpoint-first handshake","Smoke-test new or upgraded servers with curl -N: the first event must be `endpoint`"],"tags":["mcp","sse","protocol","server-bug"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}