Kuberwastaken/claurst · error
legacy SSE endpoint event did not include a POST endpoint
Error message
legacy SSE endpoint event did not include a POST endpoint
What it means
Legacy SSE MCP servers first send an 'endpoint' event telling the client where to POST JSON-RPC messages. If that event's payload is empty or whitespace-only after trimming, the client cannot construct the POST endpoint and bails. This protects against malformed servers that open an SSE stream but never advertise a message endpoint.
Solutions
- Verify the server actually implements the legacy SSE MCP handshake and emits an 'endpoint' event with a non-empty value
- If the server supports the streamable HTTP transport, switch the config type from 'sse' to 'http'
- Check intermediate proxies/gateways for rewriting or dropping the endpoint event payload
- Confirm the base_url is correct so relative endpoints can be resolved
Defensive patterns
Strategy: try-catch
Try / catch
match client.connect().await {
Err(e) if e.to_string().contains("legacy SSE endpoint event") => {
// fall back to streamable HTTP transport or surface server-compat error
}
other => other?,
} Prevention
- Prefer the streamable HTTP transport when the server supports it
- Test MCP servers behind proxies for intact endpoint events
- Pin to server versions known to implement the legacy SSE handshake correctly
When it happens
Trigger: resolve_legacy_endpoint is called with an empty or whitespace-only endpoint string, i.e. the server's SSE 'endpoint' event carried no endpoint data.
Common situations: Server behind a proxy that strips the endpoint event body; a non-MCP SSE endpoint mistakenly registered as an MCP server; old/broken SSE server implementations; URL path not URL-encoded when parsed upstream.
Related errors
- MCP server ' ': failed to parse legacy SSE JSON payload
- MCP server ' ': legacy SSE stream returned HTTP
- MCP server ' ': HTTP from legacy SSE transport
- invalid legacy SSE base URL
- failed to resolve legacy SSE endpoint
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/830d1bfb22e2f7b1.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/mcp/src/lib.rs:417
pub(crate) fn bearer_header_value(token: &str) -> anyhow::Result<HeaderValue> {
HeaderValue::from_str(&format!("Bearer {}", token))
.map_err(|e| anyhow::anyhow!("invalid bearer token header: {}", e))
}
pub(crate) fn is_event_stream_response(response: &reqwest::Response) -> bool {
response
.headers()
.get(CONTENT_TYPE)
.and_then(|value| value.to_str().ok())
.map(|value| value.contains("text/event-stream"))
.unwrap_or(false)
}
pub(crate) fn resolve_legacy_endpoint(base_url: &str, endpoint: &str) -> anyhow::Result<String> {
let endpoint = endpoint.trim();
if endpoint.is_empty() {
anyhow::bail!("legacy SSE endpoint event did not include a POST endpoint");
}
if let Ok(url) = url::Url::parse(endpoint) {
return Ok(url.to_string());
}
let base = url::Url::parse(base_url)
.map_err(|e| anyhow::anyhow!("invalid legacy SSE base URL '{}': {}", base_url, e))?;
base.join(endpoint)
.map(|url| url.to_string())
.map_err(|e| anyhow::anyhow!("failed to resolve legacy SSE endpoint '{}': {}", endpoint, e))
}
#[cfg(test)]
pub(super) fn route_incoming_value(
server_name: &str,
value: serde_json::Value,
response_tx: &mpsc::UnboundedSender<JsonRpcResponse>,
notification_tx: &mpsc::UnboundedSender<serde_json::Value>,
) -> anyhow::Result<()> {
View on GitHub (pinned to b0637c97ec)