在使用 Claude Agent SDK 构建 AI Agent 托管平台时,我们遇到了一个棘手的问题:CLI 在受管会话中频繁出现 403 Forbidden 错误,提示 /login。因为 Claude Agent SDK 的 API 需要 ANTHROPIC_AUTH_TOKEN 等环境变量绕过认证,而这个 token 通常存储在 ~/.claude/settings.json 中。
问题的根源在于环境隔离。当 CLI 以 Daemon 模式运行时,系统会将 CLAUDE_CONFIG_DIR 重定向到租户专属目录(如 .vs/claude-config/test),这意味着用户的 ~/.claude/settings.json 被完全绕过了。
怎么办?我们实现了一套服务端环境变量注入机制。

我是 AI灵感闪现,用AI自主开发软件表达想法和灵感,MoneyMind 省钱思维 App 开发者。正在实践和分享让 AI 自主解决健康、生活、投资和等方面的问题。我尽可能让 AI 自己完成从目标到交付以及演进的闭环,以最少的人为交互与监督,让 AI 自己跑流程。我只给 AI 想法或目标,全程不陪跑,让 AI 自主运行类似 Tesla FSD 自动驾驶。
系统架构
┌─────────────────┐
│ Server Side │
└────────┬────────┘
│
│ 1. Socket IO 下发 ENV 配置
│
↓
┌────────────────┐
│ CLI Agent │
└────────┬───────┘
│
│ 2. 启动会话传入环境变量
│
↓
┌─────────────────────┐
│ AI Agent │
│ (Claude Agent SDK) │
└─────────┬───────────┘
│
│ 3. 执行任务返回结果
│
↓
┌────────────────┐
│ CLI Agent │
└────────┬───────┘
│
│ 4. Socket IO 回传结果
│
↓
┌─────────────────┐
│ Server Side │
└─────────────────┘
数据流程说明
-
1. 环境变量下发:Server Side 通过 Socket IO 连接,将 CLIENT_ENV_*环境变量(如ANTHROPIC_AUTH_TOKEN)下发到 CLI Agent -
2. 会话启动:CLI Agent 合并服务端配置、Shell 环境变量和本地配置,按优先级生成最终环境变量,启动 Claude SDK 会话 -
3. 任务执行:AI Agent 使用注入的环境变量进行鉴权和任务执行,生成执行结果 -
4. 结果回传:CLI Agent 通过 Socket IO 将执行结果、日志等信息返回给 Server Side
核心设计
服务端通过 Session API 下发配置,CLI 按优先级合并:
-
1. 服务端配置(最高优先级) -
2. Shell 环境变量 -
3. 本地 settings.json(最低优先级)
这样,即使在隔离环境中,服务端也能确保 ANTHROPIC_AUTH_TOKEN 等关键配置生效。
服务端实现
在 cli.ts 将环境变量返回给客户端:
// server/src/web/routes/cli.ts
const clientEnv: Record<string, string> = {};
for (const [key, value] of Object.entries(process.env)) {
if (key.startsWith("CLIENT_ENV_") && value) {
const clientKey = key.replace("CLIENT_ENV_", "");
clientEnv[clientKey] = value;
}
}
// 返回给 CLI
return { ...sessionData, env: clientEnv };
服务端只需设置 CLIENT_ENV_ANTHROPIC_AUTH_TOKEN=xxx,CLI 就能收到 ANTHROPIC_AUTH_TOKEN。
CLI 侧合并逻辑
// cli/src/commands/claude.ts
const settingsEnv = loadSettingsEnv(); // 本地配置
const serverEnv = session.env ?? {}; // 服务端下发
// 优先级:serverEnv > process.env > settingsEnv
const finalEnv = {
...process.env,
...settingsEnv,
...serverEnv,
};
spawn("claude", args, { env: finalEnv });
踩过的坑
settings.json 中的数字值
settings.json 可能包含 "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": 1 这样的数字值。如果解析时跳过非字符串,配置就丢失了。
修复方法:
for (const [key, value] of Object.entries(settings.env)) {
if (value != null) {
result[key] = String(value); // 统一转成字符串
}
}
Zod Schema 类型定义
API 返回的 env 字段需要严格定义类型,否则 TypeScript 编译会报 TS2554 错误:
// cli/src/api/types.ts
export const SessionResponseSchema = z.object({
// ... 其他字段
env: z.record(z.string(), z.string()).optional(),
});
为什么不直接用本地配置?
Claude Agent SDK 受管模式下,配置目录被隔离是故意为之。这能防止不同租户的配置互相污染。
服务端注入的设计让运维更简单:只需在服务端设置一次,所有托管会话都能获得正确的鉴权信息,不用担心本地文件是否存在或正确。
小结
这个方案解决了几个问题:
-
• 受管会话的鉴权配置可控 -
• 配置优先级清晰,易于排查 -
• 服务端统一管理,减少人工介入
如果你在使用 Claude Agent SDK 构建 AI Agent 托管平台,服务端环境变量注入是个值得考虑的方向。


