Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/http-proxy-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@moonshot-ai/agent-core": minor
"@moonshot-ai/kimi-code-sdk": minor
"@moonshot-ai/kimi-code": minor
---

Honor the standard `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY` environment variables, including SOCKS proxies, for all outbound traffic.
5 changes: 5 additions & 0 deletions apps/kimi-code/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import {
createKimiHarness,
flushDiagnosticLogs,
installGlobalProxyDispatcher,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

log,
resolveGlobalLogPath,
resolveKimiHome,
Expand Down Expand Up @@ -117,6 +118,10 @@ const MIGRATE_CLI_OPTIONS: CLIOptions = {
export function main(): void {
process.title = PROCESS_NAME;
installCrashHandlers();
// Route all outbound fetch through HTTP_PROXY/HTTPS_PROXY (honoring NO_PROXY)
// before any client is constructed. No-op when no proxy variable is set; an
// invalid proxy URL is reported and ignored rather than aborting startup.
installGlobalProxyDispatcher();
installNativeModuleHook();
if (runNativeAssetSmokeIfRequested()) return;

Expand Down
15 changes: 15 additions & 0 deletions docs/en/configuration/env-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,21 @@ The CLI also reads several standard system variables to detect the runtime envir
- `WSL_DISTRO_NAME`, `WSLENV`: detect WSL for the clipboard PowerShell bridge
- `LOCALAPPDATA`: used on Windows when probing for the Git Bash installation path

## HTTP proxy

Kimi Code honors the standard proxy environment variables for all outbound traffic — model API calls, MCP servers, web tools, telemetry, sign-in, and update checks:

- `HTTP_PROXY` / `http_proxy`: proxy for `http://` requests
- `HTTPS_PROXY` / `https_proxy`: proxy for `https://` requests
- `ALL_PROXY` / `all_proxy`: fallback proxy used when the scheme-specific variable is unset; this is where a SOCKS proxy is usually set
- `NO_PROXY` / `no_proxy`: comma-separated hosts that bypass the proxy

Both HTTP(S) and SOCKS proxies are supported. A SOCKS proxy is recognized by its scheme — `socks5://`, `socks5h://`, `socks4://`, or `socks://` (an alias for `socks5://`) — and is typically set via `ALL_PROXY` (the form used by tools like Clash and V2RayN). An HTTP(S) proxy takes precedence over `ALL_PROXY` for HTTP/HTTPS traffic.

The proxy is applied only when one of these variables is set; otherwise connections are made directly. Loopback hosts (`localhost`, `127.0.0.1`, `::1`) always bypass the proxy, so a local server such as a localhost MCP server keeps working when a proxy is configured — add your own internal hosts to `NO_PROXY` to exempt them too.

Stdio MCP servers that run as Node child processes honor `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` automatically when the child's Node version supports `NODE_USE_ENV_PROXY` (Node ≥ 22.21 or ≥ 24.5); SOCKS proxying applies to Kimi Code's own traffic only.

## Next steps

- [Config overrides](./overrides.md) — how environment variables, CLI options, and the config file interact by priority
Expand Down
15 changes: 15 additions & 0 deletions docs/zh/configuration/env-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,21 @@ CLI 还会读取一些标准系统变量来检测运行环境,不会修改它
- `WSL_DISTRO_NAME`、`WSLENV`:检测 WSL,用于剪贴板 PowerShell 桥接
- `LOCALAPPDATA`:Windows 上探测 Git Bash 安装路径

## HTTP 代理

Kimi Code 会遵循标准代理环境变量,让所有出网流量——模型 API 调用、MCP 服务、网络工具、遥测、登录、更新检查——都走代理:

- `HTTP_PROXY` / `http_proxy`:用于 `http://` 请求的代理
- `HTTPS_PROXY` / `https_proxy`:用于 `https://` 请求的代理
- `ALL_PROXY` / `all_proxy`:当对应 scheme 的变量未设置时使用的兜底代理;SOCKS 代理通常设在这里
- `NO_PROXY` / `no_proxy`:以逗号分隔的、绕过代理的主机列表

同时支持 HTTP(S) 代理和 SOCKS 代理。SOCKS 代理通过 scheme 识别——`socks5://`、`socks5h://`、`socks4://` 或 `socks://`(`socks5://` 的别名)——通常设在 `ALL_PROXY`(Clash、V2RayN 等工具使用的形式)。对 HTTP/HTTPS 流量,HTTP(S) 代理优先于 `ALL_PROXY`。

仅当设置了其中任一变量时才启用代理,否则直连。回环地址(`localhost`、`127.0.0.1`、`::1`)始终绕过代理,因此配置了代理后,本地服务(例如 localhost 上的 MCP 服务)仍能正常工作——你也可以把自己的内网主机加入 `NO_PROXY` 一并放行。

以 Node 子进程运行的 stdio MCP 服务,在其 Node 版本支持 `NODE_USE_ENV_PROXY` 时(Node ≥ 22.21 或 ≥ 24.5)会自动遵循 `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`;SOCKS 代理仅作用于 Kimi Code 自身的流量。

## 下一步

- [配置覆盖](./overrides.md) — 环境变量、CLI 选项、配置文件的优先级关系
Expand Down
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
inherit (finalAttrs) pname version src pnpmWorkspaces;
inherit pnpm;
fetcherVersion = 3;
hash = "sha256-/Kgq76JAgi1NygbnYkBNACUl+U9TO5zwF1MaCzk3n9o=";
hash = "sha256-x5O/+bDRoEW3juoxe3XyvTJxHitQ0hZ2nVXBItkBA5E=";
};

nativeBuildInputs = [
Expand Down
2 changes: 2 additions & 0 deletions packages/agent-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@
"regexp.escape": "^2.0.1",
"retry": "0.13.1",
"smol-toml": "^1.6.1",
"socks": "^2.8.9",
"tar": "^7.5.13",
"undici": "^7.27.1",
"yauzl": "^3.3.0",
"zod": "catalog:"
},
Expand Down
1 change: 1 addition & 0 deletions packages/agent-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export {
} from './logging/logger';
export { resolveLoggingConfig } from './logging/resolve-config';
export type { ResolveLoggingInput } from './logging/resolve-config';
export { installGlobalProxyDispatcher } from './utils/proxy';
export type {
LogContext,
LogEntry,
Expand Down
20 changes: 16 additions & 4 deletions packages/agent-core/src/mcp/client-stdio.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ErrorCodes, KimiError } from '#/errors';
import type { McpServerStdioConfig } from '#/config/schema';
import { proxyEnvForChild, reconcileChildNoProxy } from '#/utils/proxy';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';

Expand Down Expand Up @@ -216,13 +217,24 @@ class BoundedTail {
}

// Inherit the parent's env so PATH/HOME/etc. survive — otherwise `npx`/`uvx`
// style stdio servers fail to launch even with a valid config. Explicit
// `config.env` entries still override on conflict.
function mergeStdioEnv(configEnv?: Record<string, string>): Record<string, string> {
// style stdio servers fail to launch even with a valid config. `config.env`
// overrides on conflict. A node child does not inherit our in-process undici
// dispatcher, so `proxyEnvForChild` adds `NODE_USE_ENV_PROXY` (and a
// loopback-protected `NO_PROXY`) to make it honor the proxy natively (on a Node
// version that supports the flag — ≥22.21 or ≥24.5). It is computed from the
// MERGED env so a proxy declared only in `config.env` is honored too.
// `reconcileChildNoProxy` then mirrors a single-casing `NO_PROXY` override onto
// both casings so it isn't shadowed by the injected value.
export function mergeStdioEnv(
configEnv?: Record<string, string>,
parentEnv: Readonly<Record<string, string | undefined>> = process.env,
): Record<string, string> {
const merged: Record<string, string> = {};
for (const [key, value] of Object.entries(process.env)) {
for (const [key, value] of Object.entries(parentEnv)) {
if (value !== undefined) merged[key] = value;
}
if (configEnv !== undefined) Object.assign(merged, configEnv);
Object.assign(merged, proxyEnvForChild(merged));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

reconcileChildNoProxy(merged, configEnv);
return merged;
}
Loading
Loading