From 865db624874dfc89315942b8c2a454e5ada82abf Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Sun, 10 May 2026 20:58:33 -0500 Subject: [PATCH] feat(mcp): honor HTTP(S)_PROXY env vars on MCP HTTP transport (#1408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `reqwest 0.13` does not auto-detect proxy env vars by default, so MCP HTTP connections were bypassing the proxy that every other tool on the user's box (curl, npm, git, …) was using. Users behind corporate egress proxies and China-mainland setups routing through a local Clash / Shadowsocks tunnel had their MCP servers fail to connect or silently leak around the tunnel. When the `MCP HTTP transport client builder` runs, we now read `HTTPS_PROXY` / `https_proxy` / `HTTP_PROXY` / `http_proxy` (first non-empty wins) and route via `reqwest::Proxy::all(...)`. `NO_PROXY` is honored via `reqwest::NoProxy::from_env()`. Malformed proxy URLs log a `tracing::warn!` (no scroll-demon leak — see runtime_log) and the connection proceeds without a proxy rather than failing the whole MCP attach. Closes #1408. Thanks @hlx98007 for the report. Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/tui/src/mcp.rs | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/crates/tui/src/mcp.rs b/crates/tui/src/mcp.rs index 823fa934..6b27181f 100644 --- a/crates/tui/src/mcp.rs +++ b/crates/tui/src/mcp.rs @@ -934,9 +934,39 @@ impl McpConnection { } } } - let client = reqwest::Client::builder() - .timeout(Duration::from_secs(connect_timeout_secs)) - .build()?; + // Honor the standard `HTTP_PROXY` / `HTTPS_PROXY` (and their + // lowercase equivalents) plus `NO_PROXY` env vars when + // reaching MCP HTTP servers (#1408). Reqwest 0.13 does not + // auto-detect these by default, so users behind corporate + // proxies, on China-mainland connections routing through a + // local Clash / Shadowsocks tunnel, etc. previously had MCP + // HTTP traffic bypass the proxy entirely while every other + // tool on the box (curl, npm, …) used it. + let mut client_builder = + reqwest::Client::builder().timeout(Duration::from_secs(connect_timeout_secs)); + let env_proxy_url = std::env::var("HTTPS_PROXY") + .or_else(|_| std::env::var("https_proxy")) + .or_else(|_| std::env::var("HTTP_PROXY")) + .or_else(|_| std::env::var("http_proxy")) + .ok() + .filter(|s| !s.trim().is_empty()); + if let Some(proxy_url) = env_proxy_url { + match reqwest::Proxy::all(&proxy_url) { + Ok(proxy) => { + let proxy = proxy.no_proxy(reqwest::NoProxy::from_env()); + client_builder = client_builder.proxy(proxy); + } + Err(err) => { + tracing::warn!( + target: "mcp", + ?err, + proxy = %proxy_url, + "ignoring malformed HTTP(S)_PROXY env var; MCP connection will bypass proxy" + ); + } + } + } + let client = client_builder.build()?; Box::new(HttpTransport::new( client, url.clone(),