Kuberwastaken/claurst · error · anyhow::Error

failed to resolve legacy SSE endpoint

Error message

failed to resolve legacy SSE endpoint '{}': {}

What it means

For legacy SSE MCP servers, a relative `endpoint` event value is joined onto the base URL with Url::join. This error fires when the join itself fails, meaning the endpoint string from the server cannot be resolved against the base URL (e.g. the join produced no valid URL).

Solutions

  1. Inspect the server's `endpoint` event output and fix the server/proxy so it advertises a valid endpoint path or absolute URL
  2. Update or replace the non-compliant MCP server implementation
  3. Bypass proxies that rewrite SSE handshake events
  4. Ensure the endpoint string contains only valid URL path/query characters
Defensive patterns

Strategy: try-catch

Try / catch

match connect(cfg).await {
    Err(e) if e.to_string().contains("failed to resolve legacy SSE endpoint") => {
        // log raw endpoint event, disable or upgrade that server
    }
    r => r?,
}

Prevention

When it happens

Trigger: Legacy SSE handshake returns an endpoint event with a malformed relative path that Url::join rejects when combined with the server base URL.

Common situations: Server (or an intermediate proxy) advertises a corrupted endpoint value such as `http://[bad-ipv6/endpoint`; reverse proxy rewriting the SSE endpoint event incorrectly; nonstandard MCP server implementation.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/6370bd9ae3d81cb6. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/mcp/src/lib.rs:426

            .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<()> {
        let is_response = value.get("id").map(|id| !id.is_null()).unwrap_or(false)
            || value.get("result").is_some()
            || value.get("error").is_some();

        if is_response {
            let response: JsonRpcResponse = serde_json::from_value(value).map_err(|e| {
                anyhow::anyhow!(
                    "MCP server '{}': failed to parse JSON-RPC response from HTTP transport: {}",
                    server_name,

View on GitHub (pinned to b0637c97ec)