Hmbown/CodeWhale · error
reviewed plugin MCP endpoint has an unsafe origin
Error message
reviewed plugin MCP endpoint has an unsafe origin
What it means
For remote (HTTP/SSE) MCP endpoints from reviewed plugins, the endpoint URL is sanitized: no credentials (userinfo), no query string or fragment, and finally reviewed_remote_origin must yield a safe origin (scheme/host/port without embedded userinfo). If the URL has a scheme/host combination the origin extractor cannot consider safe, this error is returned instead of trusting the endpoint.
Solutions
- Use a plain https:// (or http:// where allowed) URL with an explicit host and no credentials, query, or fragment: https://mcp.example.com/mcp.
- Move authentication out of the URL into headers/token config instead of user:pass@ userinfo.
- Fix the scheme — file://, ws://, and custom schemes are rejected.
- Validate the endpoint URL at plugin-review time so this never surfaces at connect time.
Example fix
// before (plugin manifest) endpoint = "https://user:secret@mcp.example.com/rpc?env=prod" // after endpoint = "https://mcp.example.com/rpc" # auth via headers
Defensive patterns
Strategy: validation
Validate before calling
// reject unsafe endpoints at config load
fn endpoint_ok(raw: &str) -> bool {
reqwest::Url::parse(raw).map(|u| {
matches!(u.scheme(), "http" | "https")
&& u.host_str().map_or(false, |h| !h.is_empty())
&& u.username().is_empty()
&& u.password().is_none()
&& u.query().is_none()
&& u.fragment().is_none()
}).unwrap_or(false)
} Type guard
fn is_safe_origin(u: &reqwest::Url) -> bool {
crate::mcp::reviewed_remote_origin(u).is_some()
} Try / catch
let (endpoint, origin) = sanitize_endpoint(raw).map_err(|e| {
ConfigError::new(format!("plugin remote endpoint rejected: {e}; use a bare https://host/path URL"))
})?; Prevention
- Always use plain https://host/path endpoints for remote MCP servers
- Keep credentials in headers or env, never in the URL
- Strip query strings and fragments from endpoint config at review time
- Validate endpoint URLs when the plugin is reviewed, not at connect time
When it happens
Trigger: Configuring a reviewed plugin's remote MCP endpoint with a URL whose origin cannot be safely derived — non-http(s) scheme, missing host, or (per reviewed_remote_origin) embedded username/password; also reached after the earlier checks for query/fragment pass.
Common situations: Typo'd scheme (ftp://, file://, ws://) in plugin config, a URL like https://user:token@host/mcp carrying credentials, empty/relative URL after template substitution, or an unresolvable/odd host form the URL parser produces no clean origin for.
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
- Unsupported MCP URL
- Invalid MCP command
- Invalid MCP URL
- MCP HTTP destination blocked by network policy
- MCP HTTP destination is a restricted IP address
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/d1d3606739a85c00.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp.rs:1117
.context("open reviewed launch file without links, hard links, or write/delete sharing")
}
#[cfg(all(not(unix), not(windows)))]
fn open_reviewed_launch_file(path: &Path) -> Result<fs::File> {
fs::File::open(path).context("open reviewed launch file")
}
fn reviewed_remote_endpoint_identity(endpoint: &str) -> Result<(String, String)> {
let endpoint =
reqwest::Url::parse(endpoint).context("reviewed plugin MCP endpoint is invalid")?;
if !endpoint.username().is_empty() || endpoint.password().is_some() {
anyhow::bail!("reviewed plugin MCP endpoint must not contain user information");
}
if endpoint.query().is_some() || endpoint.fragment().is_some() {
anyhow::bail!("reviewed plugin MCP endpoint must not contain a query or fragment");
}
let origin = reviewed_remote_origin(&endpoint)
.ok_or_else(|| anyhow::anyhow!("reviewed plugin MCP endpoint has an unsafe origin"))?;
Ok((endpoint.to_string(), origin))
}
fn reviewed_remote_origin(endpoint: &reqwest::Url) -> Option<String> {
if !endpoint.username().is_empty() || endpoint.password().is_some() {
return None;
}
let host = endpoint.host_str()?;
let allowed_scheme = endpoint.scheme() == "https"
|| (endpoint.scheme() == "http"
&& (host.eq_ignore_ascii_case("localhost")
|| host
.trim_matches(['[', ']'])
.parse::<std::net::IpAddr>()
.is_ok_and(|address| address.is_loopback())));
allowed_scheme.then(|| endpoint.origin().ascii_serialization())
}
View on GitHub (pinned to 73e0f67d83)