Hmbown/CodeWhale · error

MCP HTTP destination blocked by network policy

Error message

MCP HTTP destination blocked by network policy

What it means

When a NetworkPolicyDecider is configured, every MCP HTTP destination host is evaluated before any request (initial connect and each redirect). A Decision::Deny produces this error, meaning the host is explicitly forbidden by network policy (e.g. SSRF protection or allowlist enforcement).

Solutions

  1. Ask the operator to add the host to the network-policy allowlist
  2. Connect to an allowed public endpoint instead of the blocked host
  3. Remove/reconfigure the NetworkPolicyDecider only if you own the policy decision and accept the SSRF risk

Example fix

// before
let policy = NetworkPolicy::deny_all();
let client = McpHttpClient::new(url, ..., Some(&policy))?; // bails
// after
policy.allow_host("mcp.example.com");
let client = McpHttpClient::new(url, ..., Some(&policy))?;
Defensive patterns

Strategy: validation

Validate before calling

let host = Url::parse(endpoint)?.host_str().ok_or("no host")?.to_string();
match policy.evaluate(&host, "mcp") {
    Decision::Allow => { /* safe to connect */ }
    Decision::Deny => return Err("host denied by network policy"),
    Decision::Prompt => { /* obtain approval first */ }
}

Try / catch

match connect(...).await {
    Err(e) if e.to_string().contains("blocked by network policy") => {
        // request allowlist change from operator; do not retry blindly
    }
    other => other?,
}

Prevention

When it happens

Trigger: validate_network_policy calls policy.evaluate(host, "mcp") and gets Decision::Deny — triggered from McpHttpClient::new on the configured URL, or from client_for_target on a redirect target host.

Common situations: Connecting to localhost/metadata IPs while an SSRF-guarding policy denies private ranges; host not on the operator allowlist; DNS rebinding protection flagging the host; corporate deny-list rules.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/b68795e6819370bc. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/mcp/http_client.rs:226

            .build()
            .context("building guarded MCP HTTP client")?;
        let mut clients = self
            .clients
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner);
        if !proxied && clients.len() < 32 {
            clients.insert(key, client.clone());
        }
        Ok(client)
    }
}

fn validate_network_policy(url: &Url, network_policy: Option<&NetworkPolicyDecider>) -> Result<()> {
    let host = url.host_str().context("MCP URL has no host")?;
    if let Some(policy) = network_policy {
        match policy.evaluate(host, "mcp") {
            Decision::Allow => {}
            Decision::Deny => bail!("MCP HTTP destination blocked by network policy"),
            Decision::Prompt => bail!("MCP HTTP destination requires network approval"),
        }
    }
    Ok(())
}

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 {

View on GitHub (pinned to 73e0f67d83)