Hmbown/CodeWhale · error
MCP HTTP destination is a restricted IP address
Error message
MCP HTTP destination is a restricted IP address
What it means
This error is thrown by McpHttpClient::public_dns_pin as an SSRF guard. When the MCP server URL host is a literal IP address, the client checks it against is_restricted_ip (the same guard used by the web-fetch tooling) and refuses to connect if the address is private, loopback, link-local, or otherwise restricted. It exists to stop a configured MCP endpoint from reaching internal network services.
Solutions
- Expose the MCP server on a publicly resolvable DNS name (or a routable address that is not in a restricted range) and use that in the server config
- If the server must stay local, run it through a tunnel (e.g. a public HTTPS endpoint) the guard permits
- Check the URL in the MCP server config for a typo that turned a hostname into an IP literal
- If the restriction is genuinely wrong for your deployment, change the config environment rather than bypassing the guard
Example fix
// before
"mcpServers": { "local": { "url": "http://127.0.0.1:8080/mcp" } }
// after
"mcpServers": { "local": { "url": "https://mcp.example.com/mcp" } } Defensive patterns
Strategy: validation
Validate before calling
fn is_mcp_url_allowed(url: &url::Url) -> bool {
url.host_str()
.map(|h| h.parse::<std::net::IpAddr>().map(|ip| !is_restricted_ip(&ip)).unwrap_or(true))
.unwrap_or(false)
} Type guard
fn is_literal_restricted_ip(host: &str) -> bool {
host.trim_start_matches('[').trim_end_matches(']')
.parse::<std::net::IpAddr>()
.map(|ip| is_restricted_ip(&ip))
.unwrap_or(false)
} Prevention
- Use DNS hostnames, not raw IP literals, in MCP server configs
- Never configure loopback, RFC1918, or link-local addresses as MCP destinations
- Validate server URLs at config-load time with the same is_restricted_ip check
When it happens
Trigger: Calling client_for_target / public_dns_pin with an MCP server URL whose host is a literal restricted IP (e.g. http://127.0.0.1:8080, http://10.0.0.5, http://192.168.1.10, http://169.254.169.254).
Common situations: Developers pointing an MCP server config at a local dev server (localhost by IP), Docker-internal addresses, or a staging box on a private subnet; CI environments where the MCP server runs on 127.0.0.1.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- MCP HTTP destination blocked by network policy
- MCP HTTP DNS resolved to a restricted IP address
- MCP SSE endpoint is not same-origin as — refusing to send…
- reviewed plugin MCP endpoint has an unsafe origin
- bundle redirects may not change URL scheme
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/0ea1548e111af89c.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp/http_client.rs:250
fn validate_url(url: &Url) -> Result<()> {
if !matches!(url.scheme(), "http" | "https") || url.host_str().is_none() {
bail!("MCP HTTP requires an http:// or https:// URL with a host");
}
Ok(())
}
fn url_has_credentials(url: &Url) -> bool {
!url.username().is_empty() || url.password().is_some()
}
impl McpHttpClient {
async fn public_dns_pin(&self, url: &Url) -> Result<Option<(String, SocketAddr)>> {
let host = url.host_str().context("MCP URL has no host")?;
let literal = host.trim_start_matches('[').trim_end_matches(']');
if let Ok(ip) = literal.parse::<IpAddr>() {
if is_restricted_ip(&ip) {
bail!("MCP HTTP destination is a restricted IP address");
}
return Ok(None);
}
let port = url.port_or_known_default().context("MCP URL has no port")?;
#[cfg(test)]
let injected = self
.dns_answers
.lock()
.unwrap()
.as_mut()
.map(|answers| answers.pop_front().expect("DNS fixture answer available"));
#[cfg(not(test))]
let injected: Option<Vec<SocketAddr>> = None;
let addresses: Vec<_> = if let Some(addresses) = injected {
addresses
} else {
tokio::time::timeout(self.connect_timeout, tokio::net::lookup_host((host, port)))
.awaitView on GitHub (pinned to 73e0f67d83)