Claude Code 源码解析
返回目录
启动与基础02

配置系统

七层配置优先级

核心洞察

7 层配置合并确保企业策略始终优先,同时保持开发者灵活性

学习

概念讲解与核心设计分析

配置系统概述

Claude Code 拥有业界罕见的 7 层配置优先级体系,从 CLI 标志到全局默认值,每一层都有明确的覆盖语义。配置系统不仅支持个人开发者的灵活定制,还通过 MDM 企业策略实现了大规模部署管控。

7 层配置优先级

配置的合并遵循高优先级覆盖低优先级的原则,从高到低依次为:

  1. CLI flags — 命令行参数(如 --model opus),拥有最高优先级
  2. Policy / MDM — 企业策略配置,通过 macOS plutil 或 Windows 注册表读取,不可被用户覆盖
  3. Remote managed — 远程托管配置,来自服务器下发的策略
  4. Flag file — 功能标志文件,用于 A/B 测试和灰度发布
  5. Project — 项目级配置 .claude/settings.json,存储在仓库根目录
  6. User — 用户级配置 ~/.claude/settings.json,跨项目生效
  7. Global — 全局默认配置 ~/.claude.json,最低优先级

getInitialSettings() 合并逻辑

getInitialSettings() 是配置系统的核心入口函数。它按优先级顺序读取所有配置源,使用深度合并(deep merge)策略将它们组合成最终的配置对象。关键设计:

  • 数组类型的配置项(如 allowedTools)使用合并而非覆盖
  • 对象类型的配置项(如 mcpServers)使用深度合并
  • 标量值(如 model)使用高优先级覆盖
  • MDM 策略中的某些字段具有强制性,即使 CLI 也无法覆盖

MDM 企业策略

MDM(Mobile Device Management)是 Claude Code 支持企业部署的关键特性。在启动阶段,startMdmRawRead() 会异步启动一个子进程来读取企业策略:

  • macOS:通过 plutil -extract ... -o - /Library/Managed Preferences/com.anthropic.claude-code.plist 读取托管首选项
  • Windows:通过注册表查询 HKLM\SOFTWARE\Policies\Anthropic\ClaudeCode
  • MDM 策略可以强制锁定模型选择、禁用特定工具、配置代理服务器等

Zod v4 Schema 验证

所有配置文件在加载时都会通过 Zod v4 schema 进行严格验证。SettingsJson 类型定义了配置的完整结构:

  • permissions — 权限模式设置
  • allowedTools — 允许使用的工具白名单
  • denyTools — 禁止使用的工具黑名单
  • mcpServers — MCP 服务器配置映射
  • hooks — 生命周期钩子配置
  • model — 默认模型设置
  • customApiKeyResponses — 自定义 API 密钥行为

验证失败时会给出详细的错误信息并回退到默认值,避免因配置错误导致应用崩溃。

配置变更检测

changeDetector.ts 实现了文件系统监控,使用 fs.watch 监听配置文件变更。当用户在编辑器中修改 .claude/settings.json 时,系统能在毫秒级感知变更并自动重新加载配置,无需重启 Claude Code。

环境变量处理

applySafeConfigEnvironmentVariables() 在信任链建立之前执行,仅应用被标记为"安全"的环境变量。这个设计避免了恶意项目通过 .env 文件注入危险的环境变量。

settingsCache 性能优化

由于配置读取在热路径上频繁调用(每次工具调用都需要检查权限),系统使用 settingsCache 缓存解析后的配置对象。缓存在检测到文件变更时自动失效,确保一致性与性能的平衡。

架构

模块关系与设计决策

配置系统架构

配置合并流程

优先级配置源读取方式合并策略
1 (最高)CLI flagsCommander.js 解析直接覆盖
2MDM Policyplutil / reg query 子进程强制覆盖(部分字段)
3Remote managedHTTP API 请求深度合并
4Flag file文件系统读取深度合并
5Project settings.claude/settings.json深度合并
6User settings~/.claude/settings.json深度合并
7 (最低)Global defaults~/.claude.json基础值

核心模块关系

  • utils/settings/ — 配置系统主目录
    • settings.ts — getInitialSettings(), getCurrentSettings()
    • config.ts — enableConfigs(), 配置文件路径解析
    • changeDetector.ts — 文件监控与缓存失效
    • settingsCache.ts — 解析结果缓存
  • schemas/ — Zod v4 验证 schema
    • SettingsJsonSchema — 主配置 schema
    • McpServerSchema — MCP 服务器配置 schema
    • HooksSchema — 钩子配置 schema
  • mdm.ts — MDM 企业策略读取与解析

设计决策

为什么用 7 层而不是简单的覆盖?

Claude Code 需要同时满足个人开发者和企业 IT 管理员的需求。个人开发者需要项目级和用户级的灵活配置;企业 IT 需要通过 MDM 策略强制执行安全策略(如禁止使用某些工具、锁定模型选择)。7 层体系让这两种需求和谐共存,MDM 策略可以覆盖用户设置,但 CLI 标志仍然在开发/调试时拥有最高优先级。

源码

3 个关键代码示例

01
7 层配置合并 — getInitialSettings()
TypeScript
// utils/settings/settings.ts — 简化版
export function getInitialSettings(cliFlags?: Partial<SettingsJson>): SettingsJson {
  // 第 7 层:全局默认值
  const globalDefaults = loadJsonSafe<SettingsJson>(
    path.join(os.homedir(), '.claude.json'),
    SettingsJsonSchema
  )

  // 第 6 层:用户级配置
  const userSettings = loadJsonSafe<SettingsJson>(
    path.join(os.homedir(), '.claude', 'settings.json'),
    SettingsJsonSchema
  )

  // 第 5 层:项目级配置
  const projectSettings = loadJsonSafe<SettingsJson>(
    path.join(findProjectRoot(), '.claude', 'settings.json'),
    SettingsJsonSchema
  )

  // 第 4 层:Flag file
  const flagFileSettings = loadFlagFileSettings()

  // 第 3 层:远程托管配置
  const remoteSettings = getRemoteManagedSettings()

  // 第 2 层:MDM 企业策略
  const mdmPolicy = getMdmSettings()

  // 深度合并,高优先级覆盖低优先级
  let merged = deepMerge(
    globalDefaults,
    userSettings,
    projectSettings,
    flagFileSettings,
    remoteSettings
  )

  // MDM 策略的强制字段不可覆盖
  merged = applyMdmEnforcement(merged, mdmPolicy)

  // 第 1 层:CLI 标志(最高优先级)
  if (cliFlags) {
    merged = deepMerge(merged, cliFlags)
  }

  return merged
}
02
MDM 企业策略读取
TypeScript
// mdm.ts — macOS 通过 plutil 读取托管首选项
let mdmRawReadPromise: Promise<string> | null = null

export function startMdmRawRead(): void {
  if (process.platform === 'darwin') {
    // 异步启动 plutil 子进程(与模块导入并行)
    mdmRawReadPromise = execAsync(
      'plutil -extract . json -o - ' +
      '/Library/Managed\ Preferences/com.anthropic.claude-code.plist'
    ).then(({ stdout }) => stdout)
     .catch(() => '{}')  // 无 MDM 策略时返回空对象
  } else if (process.platform === 'win32') {
    // Windows 通过注册表读取
    mdmRawReadPromise = execAsync(
      'reg query "HKLM\\SOFTWARE\\Policies\\Anthropic\\ClaudeCode" /s'
    ).then(({ stdout }) => parseRegistryOutput(stdout))
     .catch(() => '{}')
  }
}

export async function getMdmSettings(): Promise<Partial<SettingsJson>> {
  if (!mdmRawReadPromise) return {}
  const raw = await mdmRawReadPromise
  const parsed = JSON.parse(raw)
  // 通过 Zod 验证 MDM 策略格式
  return MdmPolicySchema.safeParse(parsed).data ?? {}
}
03
Zod v4 Schema 验证与 SettingsJson 类型
TypeScript
// schemas/settingsSchema.ts
import { z } from 'zod/v4'

const McpServerSchema = z.object({
  command: z.string(),
  args: z.array(z.string()).optional(),
  env: z.record(z.string()).optional(),
  type: z.enum(['stdio', 'sse']).default('stdio'),
})

export const SettingsJsonSchema = z.object({
  // 权限控制
  permissions: z.object({
    allow: z.array(z.string()).default([]),
    deny: z.array(z.string()).default([]),
  }).optional(),

  // 工具白名单 / 黑名单
  allowedTools: z.array(z.string()).optional(),
  denyTools: z.array(z.string()).optional(),

  // MCP 服务器配置
  mcpServers: z.record(McpServerSchema).optional(),

  // 生命周期钩子
  hooks: z.record(z.object({
    command: z.string(),
    timeout: z.number().default(30000),
  })).optional(),

  // 模型设置
  model: z.string().optional(),
  maxThinkingTokens: z.number().optional(),

  // 自定义 API 密钥
  customApiKeyResponses: z.record(z.string()).optional(),
}).passthrough()  // 允许未知字段,向前兼容

export type SettingsJson = z.infer<typeof SettingsJsonSchema>

// 安全加载配置文件
function loadJsonSafe<T>(filePath: string, schema: z.ZodType<T>): T {
  try {
    const raw = fs.readFileSync(filePath, 'utf-8')
    const parsed = JSON.parse(raw)
    const result = schema.safeParse(parsed)
    if (result.success) return result.data
    // 验证失败:记录警告并返回默认值
    logWarning('Config validation failed:', result.error.issues)
    return schema.parse({})  // 返回默认值
  } catch {
    return schema.parse({})  // 文件不存在或格式错误
  }
}

互动

步进式流程演示

互动演示

配置系统互动解析

第 1 步:理解优先级

想象一个场景:你在公司使用 Claude Code。IT 部门通过 MDM 策略禁止了 Bash 工具的使用。你在项目的 .claude/settings.json 中配置了 "allowedTools": ["Bash"],然后用 --allowedTools Bash 启动。最终 Bash 能用吗?

答案:不能。MDM 策略的 denyTools 是强制性的,即使 CLI 标志也无法覆盖它。这就是第 2 层(MDM)对某些字段具有"强制覆盖"语义的体现。

第 2 步:深度合并如何工作

假设用户级配置定义了 mcpServers: { "github": { command: "gh-mcp" } },项目级配置定义了 mcpServers: { "jira": { command: "jira-mcp" } }。合并结果是什么?

答案:两个 MCP 服务器都会存在,因为对象类型使用深度合并。最终的 mcpServers 会同时包含 githubjira

第 3 步:Schema 验证的安全网

如果你不小心在 settings.json 中写了 "model": 123(应该是字符串),会发生什么?

答案:Zod schema 验证会捕获这个类型错误,记录一条警告日志,然后回退到默认值。应用不会崩溃,而是静默降级。

第 4 步:变更检测的实时性

changeDetector.ts 使用 fs.watch 监听所有配置文件。当你在 VS Code 中修改并保存 .claude/settings.json 时,系统会:

  1. 收到文件系统事件通知
  2. 使 settingsCache 中对应的缓存条目失效
  3. 下次调用 getCurrentSettings() 时重新读取并解析文件
  4. 新配置立即生效,无需重启

关键设计洞察

  • 安全优先:MDM 策略不可被用户覆盖,确保企业合规
  • 向前兼容:Schema 使用 .passthrough() 允许未知字段
  • 性能意识:settingsCache 避免重复的文件 I/O 和 JSON 解析
  • 优雅降级:配置错误不会导致崩溃,而是回退到默认值

相关源文件

utils/settings/utils/config.tsschemas/