Hmbown/CodeWhale · error
MCP HTTP discovered URL must not contain credentials
Error message
MCP HTTP discovered URL must not contain credentials
What it means
During redirect handling, discovered (non-operator, non-same-origin) targets are checked for URL-embedded credentials. Only the operator-configured same-origin path is exempt; any other redirect target carrying user:password@ is rejected to avoid sending credentials to an endpoint that was never explicitly configured.
Solutions
- Authenticate at the discovered target with headers (via configured proxy/headers), not URL userinfo
- Make the redirect target same-origin and operator-configured if credentials-in-URL is unavoidable
- Remove credentials from the redirecting server's Location header
Defensive patterns
Strategy: validation
Validate before calling
let next = base.join(location)?;
let same_origin = next.origin().ascii_serialization() == client_origin;
if !same_origin && (!next.username().is_empty() || next.password().is_some()) {
// discovered target carries credentials -> will be rejected
} Try / catch
if let Err(e) = client.execute(req).await {
if e.to_string().contains("discovered URL must not contain credentials") {
// switch auth to headers or restrict redirect to operator-configured origin
}
} Prevention
- Send credentials via configured headers on every hop, never via URL
- Limit reviewed plugins to single-origin redirect paths
- Inventory plugin endpoints for embedded credentials before review
When it happens
Trigger: client_for_target computes operator_origin = operator_configured && same_origin; when operator_origin is false and url_has_credentials(url) is true (redirect target on another origin, or runtime-added client, with userinfo in the URL).
Common situations: Redirect chain lands on a different-origin host that includes basic-auth in the URL; a plugin bounces to an auth gateway with embedded credentials; legacy staging URL with credentials reached via redirect.
Related errors
- MCP HTTP redirect must not contain credentials
- MCP HTTP redirect would downgrade HTTPS
- MCP HTTP URL must not contain credentials; use configured…
- MCP redirect leaves the reviewed plugin origin
- bundle redirects may not change URL scheme
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/d2b958e666709df2.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp/http_client.rs:171
}
}
*request.headers_mut() = headers;
}
*request.url_mut() = next_url;
}
unreachable!("redirect loop is bounded")
}
async fn client_for_target(&self, url: &Url) -> Result<reqwest::Client> {
validate_url(url)?;
let same_origin = url.origin().ascii_serialization() == self.origin;
if self.reviewed_plugin && !super::reviewed_redirect_matches_origin(url, &self.origin) {
bail!("MCP redirect leaves the reviewed plugin origin");
}
validate_network_policy(url, self.network_policy.as_ref())?;
let operator_origin = self.operator_configured && same_origin;
if !operator_origin && url_has_credentials(url) {
bail!("MCP HTTP discovered URL must not contain credentials");
}
let proxy =
super::configured_mcp_proxy(url, !operator_origin || self.reviewed_plugin, |key| {
std::env::var(key)
})?;
// A selected operator proxy resolves its own destinations. This is
// delegated proxy authority, never evidence of a local DNS pin.
let pin = if (self.private_origin_allowed && same_origin) || proxy.is_some() {
None
} else {
self.public_dns_pin(url).await?
};
// Validate DNS before reusing a client too: a new private answer revokes
// this request. Each cached client itself remains pinned to its old public
// address, including reconnects after a keep-alive socket expires.
let key = format!("{}:{pin:?}", url.origin().ascii_serialization());
if proxy.is_none()
&& let Some(client) = selfView on GitHub (pinned to 73e0f67d83)