Kuberwastaken/claurst · error · anyhow::Error
invalid legacy SSE base URL
Error message
invalid legacy SSE base URL '{}': {} What it means
When a legacy SSE MCP server advertises an `endpoint` event whose URL is not absolute, the library resolves it against the configured base URL. This error is thrown if the base URL itself fails url::Url::parse, i.e. the server's configured base URL is not a valid absolute URL.
Solutions
- Fix the server URL in the MCP config to a valid absolute URL including scheme (http:// or https://)
- Trim whitespace/control characters from the configured URL
- Validate the URL with url::Url::parse before storing it in config
- If the endpoint should be absolute, correct the server's advertised endpoint event
Example fix
// before "url": "localhost:8080/sse" // after "url": "http://localhost:8080/sse"
Defensive patterns
Strategy: validation
Validate before calling
fn valid_base_url(s: &str) -> bool {
url::Url::parse(s).map(|u| u.scheme().starts_with("http")).unwrap_or(false)
} Prevention
- Store MCP server URLs with explicit http(s):// scheme
- Trim config/env URL values before use
- Validate URLs at config parse time, not at connect time
When it happens
Trigger: Legacy SSE transport receives an `endpoint` event with a relative path, and the McpServerConfig URL used as base_url is malformed (missing scheme, spaces, control characters) so url::Url::parse fails.
Common situations: Config typo like `localhost:8080/sse` (no scheme) or `http://se rver/sse`; env-var interpolated URL with stray spaces; base URL read from an unvalidated settings field.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- failed to resolve legacy SSE endpoint
- MCP server ' ' is configured as ' ' but missing URL
- legacy SSE endpoint event did not include a POST endpoint
- MCP server ' ': legacy SSE stream returned HTTP
- MCP server ' ': HTTP from legacy SSE transport
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/ebcca7867c3eb379.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/mcp/src/lib.rs:423
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<()> {
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| {
View on GitHub (pinned to b0637c97ec)