Hmbown/CodeWhale · error · anyhow::Error
Unsupported MCP URL
Error message
Unsupported MCP URL
What it means
The URL parses but fails the safety constraints: the scheme must be http or https, a host must be present, and no userinfo (username/password) may be embedded. This blocks non-HTTP schemes (file:, ws:) and credential-bearing URLs; the error intentionally does not say which predicate failed.
Solutions
- Switch to `http://` or `https://` with a real host, e.g. `http://127.0.0.1:8080`
- Remove any `user:pass@` userinfo from the URL; supply credentials out-of-band (env var/bearer token)
- If the server only speaks a non-HTTP protocol, run it as a local command entry instead of a URL entry
Example fix
// before
{"mcpServers":{"api":{"url":"https://user:secret@mcp.example.com"}}}
// after
{"mcpServers":{"api":{"url":"https://mcp.example.com","bearer_token_env_var":"MCP_TOKEN"}}} Defensive patterns
Strategy: validation
Validate before calling
function urlPolicyOk(u) {
const p = new URL(u);
return ["http:","https:"].includes(p.protocol) && !!p.hostname && p.username === "" && p.password === "";
} Type guard
function isSafeHttpUrl(u) { try { return urlPolicyOk(u); } catch { return false; } } Try / catch
catch, then check scheme, host presence, and userinfo separately to identify which constraint failed
Prevention
- Never embed user:password in URLs; use bearer_token_env_var instead
- Restrict entries to http/https targets
- Use command entries for non-HTTP transports
When it happens
Trigger: A server entry with url "file:///opt/mcp/server"; "ftp://host/x"; "https://user:pass@host"; or a scheme-less but parseable URL like "localhost:3000" (parsed with scheme `localhost`).
Common situations: Using a browser-style URL with embedded basic-auth credentials; pointing at a unix socket or local file path; using websocket URLs from another client's config.
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
- reviewed plugin MCP endpoint has an unsafe origin
- Invalid MCP command
- Invalid MCP URL
- MCP HTTP requires an http:// or https:// URL with a host
- MCP HTTP URL must not contain credentials; use configured…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/5193e27faada5510.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp/external_import.rs:209
"Source contains unsupported OAuth fields"
);
}
let server: McpServerConfig = serde_json::from_value(config)
.map_err(|_| anyhow::anyhow!("Invalid MCP entry; contents omitted"))?;
anyhow::ensure!(
server.command.is_some() != server.url.is_some(),
"MCP entry must have one target"
);
if let Some(command) = &server.command {
anyhow::ensure!(
!command.trim().is_empty() && !command.chars().any(char::is_control),
"Invalid MCP command"
);
}
if let Some(url) = &server.url {
let parsed =
reqwest::Url::parse(url).map_err(|_| anyhow::anyhow!("Invalid MCP URL"))?;
anyhow::ensure!(
matches!(parsed.scheme(), "http" | "https")
&& parsed.host_str().is_some()
&& parsed.username().is_empty()
&& parsed.password().is_none(),
"Unsupported MCP URL"
);
}
super::validate_mcp_transport(server.transport.as_deref())
.map_err(|_| anyhow::anyhow!("Unsupported MCP transport"))?;
let hard_blocked = !server.is_enabled();
out.push(ImportCandidate {
summary: server_summary(&name, &server),
name,
source_kind: kind.clone(),
source_path: path.to_path_buf(),
content_hash: hash.clone(),
hard_blocked,
block_reason: hard_blocked.then(|| "Disabled at its source; cannot import".into()),View on GitHub (pinned to 73e0f67d83)