feat: honor HTTP_PROXY/HTTPS_PROXY/NO_PROXY for all outbound traffic#487
Conversation
Install a global undici dispatcher at CLI startup so every in-process fetch (LLM APIs, MCP HTTP, web tools, telemetry, sign-in, update checks) honors the standard proxy variables, and propagate NODE_USE_ENV_PROXY to spawned stdio MCP child processes. Loopback hosts always bypass the proxy; an invalid proxy URL is reported and ignored rather than aborting startup.
🦋 Changeset detectedLatest commit: 6cf1d3c The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
|
❌ Nix build failed Hash mismatch in
Please update |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bfee8b93d8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| // Process-wide HTTP proxy bootstrap — installed once at CLI startup so all | ||
| // outbound fetch honors HTTP_PROXY / HTTPS_PROXY / NO_PROXY. | ||
| export { installGlobalProxyDispatcher } from '@moonshot-ai/agent-core'; |
There was a problem hiding this comment.
Keep undici out of the public SDK surface
Re-exporting installGlobalProxyDispatcher from the public SDK also exposes its optional deps parameter type, which depends on undici's Dispatcher type from packages/agent-core/src/utils/proxy.ts. Because @moonshot-ai/kimi-code-sdk does not list undici as a dependency, consumers of the published SDK can get missing-module type resolution errors just by importing the package declarations; either hide the undici-typed injection surface from the SDK export or add the dependency where the public package exposes it.
Useful? React with 👍 / 👎.
Recognize SOCKS proxies (socks5/socks5h/socks4/socks alias) from ALL_PROXY or a socks-scheme HTTP(S)_PROXY, routing traffic through a custom undici connector backed by the socks client (reusing undici's own TLS handling for https). HTTP(S) proxies keep precedence; NO_PROXY and loopback are honored for the SOCKS path too. Child stdio MCP node processes honor HTTP(S) proxies via NODE_USE_ENV_PROXY; SOCKS applies to the main process only.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ed46887703
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…hash) - Resolve HTTP(S)_PROXY explicitly via the first non-blank casing so a blank lowercase var can no longer mask a populated uppercase one (the dispatcher installed but went direct), and coerce a SOCKS-scheme value sitting in an HTTP(S) var to '' so it is never handed to EnvHttpProxyAgent. - Reconcile a child's NO_PROXY override across both casings using the first non-blank value run through resolveNoProxy, so a per-server config override is not shadowed by the injected lowercase value, keeps the loopback bypass, and passes '*' through verbatim. - Update flake.nix pnpmDeps hash for the added socks/undici dependencies.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e30eb8919b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ): Dispatcher | undefined { | ||
| const { makeHttpAgent = defaultMakeHttpAgent, makeSocksAgent = defaultMakeSocksAgent } = factories; | ||
| try { | ||
| if (hasHttpProxy(env)) { |
There was a problem hiding this comment.
Fall back to ALL_PROXY per missing scheme
When one scheme-specific HTTP proxy is set together with a SOCKS ALL_PROXY, this branch chooses the HTTP-agent path for the whole dispatcher and never evaluates the SOCKS fallback. For example, with HTTPS_PROXY=http://corp:3128 and ALL_PROXY=socks5://127.0.0.1:1080, pickHttpProxy() passes httpProxy: '', so undici sends http:// requests directly even though ALL_PROXY is documented as the fallback when the scheme-specific variable is unset. Preserve the ALL_PROXY fallback for schemes that do not have an HTTP(S) proxy value.
Useful? React with 👍 / 👎.
… child Node version - Honor an http-scheme ALL_PROXY as the catch-all fallback for both http and https (scheme-specific HTTP(S)_PROXY still wins), so an ALL_PROXY-only setup no longer installs a no-op dispatcher and connects direct. - Make the SOCKS-path NO_PROXY matcher port-aware: a `host:port` entry now matches only that port (with IPv6-safe parsing for `::1` / `[::1]:443`). - Document that child stdio MCP proxying via NODE_USE_ENV_PROXY only applies on Node versions that support it (>= 22.21 / >= 24.5).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6b4e094712
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Strip IPv6 brackets from a SOCKS proxy host (e.g. ALL_PROXY=socks5://[::1]:1080) so the socks client connects to the bare address. - Add the bracketed [::1] to the loopback bypass: undici's EnvHttpProxyAgent only exempts IPv6 loopback when the NO_PROXY entry is bracketed (it mis-parses bare ::1). The SOCKS-path matcher normalizes brackets on both sides. - Match *.domain wildcard (and host:port) NO_PROXY entries in the SOCKS matcher. - Compute the child stdio proxy env from the MERGED env so a proxy declared only in a server's config.env also enables NODE_USE_ENV_PROXY.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 72cf98e0f9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| import { | ||
| createKimiHarness, | ||
| flushDiagnosticLogs, | ||
| installGlobalProxyDispatcher, |
There was a problem hiding this comment.
Bundle or declare the proxy runtime packages
In the packaged CLI, this new SDK import pulls in packages/agent-core/src/utils/proxy.ts, which statically imports undici and socks. I checked apps/kimi-code/tsdown.config.ts: only @moonshot-ai/* packages are forced into the bundle, so those third-party imports are left as runtime dependencies, but apps/kimi-code/package.json does not declare either package. A fresh npm install of @moonshot-ai/kimi-code would therefore fail to load dist/main.mjs before startup whenever Node cannot resolve those packages; add them to the app package dependencies or force them into the CLI bundle.
Useful? React with 👍 / 👎.
| if (value !== undefined) merged[key] = value; | ||
| } | ||
| if (configEnv !== undefined) Object.assign(merged, configEnv); | ||
| Object.assign(merged, proxyEnvForChild(merged)); |
There was a problem hiding this comment.
Preserve explicit child proxy opt-outs
When a stdio server config explicitly sets NODE_USE_ENV_PROXY: '0' to keep that child from using Node's native proxy support, this assignment runs after config.env is merged and rewrites the value back to 1 whenever any HTTP proxy variable is present. Node documents NODE_USE_ENV_PROXY=1 as the opt-in switch for env proxying, so this breaks the usual config.env override behavior for per-server opt-outs; only inject it when the merged env did not already set the variable.
Useful? React with 👍 / 👎.
| export function proxyEnvForChild(env: Env = process.env): Record<string, string> { | ||
| if (!hasHttpProxy(env)) return {}; | ||
| const noProxy = resolveNoProxy(env); | ||
| return { NODE_USE_ENV_PROXY: '1', NO_PROXY: noProxy, no_proxy: noProxy }; |
There was a problem hiding this comment.
Mirror normalized child proxy values across casings
When the merged child env has a blank lowercase proxy and a populated uppercase one (for example http_proxy='' with HTTP_PROXY='http://corp:3128'), hasHttpProxy() enables native proxying but this returned env leaves the blank lowercase value in place. Node's Built-in Proxy Support docs state that http_proxy/https_proxy take precedence when both casings are set, so supported Node stdio servers still see no usable proxy and go direct; mirror the first non-blank proxy URL into both casings or remove the blank lowercase key before setting NODE_USE_ENV_PROXY.
Useful? React with 👍 / 👎.
proxyEnvForChild now hands spawned stdio MCP children the resolved HTTP_PROXY/HTTPS_PROXY (in both casings), synthesizing them from an http-scheme ALL_PROXY when no scheme-specific variable is set. Node's --use-env-proxy reads HTTP_PROXY/HTTPS_PROXY (not ALL_PROXY), so an ALL_PROXY-only parent now proxies the child consistently with the main process. Shared resolveHttpProxyUrls helper is reused by createProxyDispatcher and proxyEnvForChild.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 54e97c083e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (value !== undefined) merged[key] = value; | ||
| } | ||
| if (configEnv !== undefined) Object.assign(merged, configEnv); | ||
| Object.assign(merged, proxyEnvForChild(merged)); |
There was a problem hiding this comment.
Preserve per-server proxy overrides across casing
When the parent env has a lowercase proxy (for example http_proxy=http://parent) and an MCP stdio server config overrides only the uppercase key (HTTP_PROXY=http://server), this post-merge proxyEnvForChild(merged) call resolves lowercase first and writes the parent value back to both casings. The spawned Node server then uses the parent proxy despite config.env being intended to override the inherited environment; normalize/mirror proxy overrides from config.env before this assignment, similar to the new NO_PROXY reconciliation.
Useful? React with 👍 / 👎.
| if (!hasHttpProxy(env)) return {}; | ||
| const noProxy = resolveNoProxy(env); | ||
| const result: Record<string, string> = { | ||
| NODE_USE_ENV_PROXY: '1', |
There was a problem hiding this comment.
Avoid claiming env proxy covers child fetch
For stdio MCP servers implemented with Node's global fetch on the repo's minimum Node 24.15.0, this env injection still does not proxy their outbound calls: fresh evidence beyond the earlier version note is that node --help documents --use-env-proxy as applying to "global HTTP/HTTPS clients", and a local Node 24.15.0 child with NODE_USE_ENV_PROXY=1 proxied http.get but global fetch went direct. Those MCP servers are common, so either avoid documenting/depending on automatic child proxying for fetch-based servers or inject a child-side undici dispatcher.
Useful? React with 👍 / 👎.
Problem
Kimi Code makes all outbound HTTP(S) requests through Node's built-in
fetch,which does not honor the standard
HTTP_PROXY/HTTPS_PROXY/NO_PROXYenvironment variables. Users behind a corporate or local proxy had no way to
route the CLI's traffic — model API calls, MCP servers, web tools, telemetry,
sign-in, and update checks — through it.
What changed
Honor the standard proxy environment variables for all outbound HTTP(S)
traffic, applied only when a proxy variable is set (the zero-config default is
unchanged):
(
EnvHttpProxyAgent) once at CLI startup, before any client is constructed.Every egress point resolves to global
fetch(LLM SDKs, MCP HTTP, web tools,telemetry, OAuth, update checks, downloads), so this single hook covers them
all. Adds
undicias anagent-coredependency (Node does not expose itsbundled undici as an importable module).
do not inherit the in-process dispatcher, so
mergeStdioEnvpropagatesNODE_USE_ENV_PROXY=1and a loopback-protectedNO_PROXY, letting them honorthe proxy natively without bundling undici.
localhost,127.0.0.1,::1are always added toNO_PROXYso a local server (e.g. a localhost MCP server) keeps working whena proxy is set. The
*wildcard (bypass everything) is preserved verbatim.directly) rather than aborting startup.
Tests: 17 unit tests for the pure proxy helpers — detection,
NO_PROXYresolution (including the
*wildcard and the blank-lowercase fallthrough),dispatcher creation, child-env propagation, and invalid-URL handling.
Docs updated in both languages (
docs/{en,zh}/configuration/env-vars.md).