深入 Claude Code 的 settings.json

01 它不只是偏好文件

大多数人第一次接触 settings.json,是因为想改个主题或换个模型。文件看起来也确实无害:

json 复制代码
{
  "model": "opus",
  "theme": "dark"
}

但翻开完整的 JSON Schema 会发现,这个文件有一百多个顶层键,覆盖的范围远超「偏好」:哪些命令可以不经确认直接执行、写文件前后要触发什么自动化、子进程能访问哪些域名、环境变量里的密钥要不要对沙箱隐藏、企业管理员如何强制登录方式和最低版本。

Claude Code 是一个能执行任意命令的 AI 代理。代理的行为是概率性的,而 settings.json 是把它圈进确定性护栏的主要机制。理解这个文件,本质上是在回答三个问题:我允许它做什么(permissions、sandbox),我要求它必须做什么(hooks),谁有权决定前两者(分层与企业管控)。

02 五层配置与优先级

同名的 settings.json 会出现在多个位置,按固定顺序加载与覆盖。从高到低:

层级 名称 说明
L1 企业托管 managed-settings.json,管理员通过 MDM 或系统目录下发,用户不可覆盖
L2 命令行参数 claude --settings 传入,作用于当前会话
L3 项目本地 .claude/settings.local.json,个人专用,不进 Git
L4 项目共享 .claude/settings.json,提交进仓库,对全团队生效
L5 用户全局 ~/.claude/settings.json,跟人走,对所有项目生效

企业托管文件的位置随平台不同:

平台 路径
macOS /Library/Application Support/ClaudeCode/managed-settings.json,也支持通过 Jamf 等 MDM 下发 com.anthropic.claudecode plist
Linux / WSL /etc/claude-code/managed-settings.json
Windows C:\Program Files\ClaudeCode\managed-settings.json,或组策略注册表 HKLM\SOFTWARE\Policies\ClaudeCode

各平台还支持 managed-settings.d/ 拆分目录,多个策略文件按字母序合并,方便不同团队分管不同策略片段。

合并语义:比「高层覆盖低层」更细

标量值(如 modeltheme)遵循简单覆盖:高层赢。但有三类例外,恰恰是这个体系的设计精髓:

  • 权限数组跨层合并。 permissions.allow / deny / ask 不是覆盖关系,各层规则叠加生效,且 deny 永远优先。团队在项目级放行 Bash(npm *),你仍可在个人层 deny 掉其中一部分。
  • 「任一层为 true 即生效」的开关。 例如 disableClaudeAiConnectors:项目可以为自己关掉云连接器,但项目级的 false 无法推翻用户级的 true
  • 只认托管层的键。 forceLoginMethodrequiredMinimumVersionallowManagedHooksOnly 等键写在用户或项目文件里会被直接忽略,只有 L1 有资格声明。

把这三条放在一起看,能读出一条贯穿全篇的设计原则:收紧可以来自任何一层,放宽只能来自更高层。 后文的沙箱、MCP、插件治理都在重复这个模式。

容易踩的坑 某一层的 JSON 语法写坏,不会报错弹窗,而是该文件的全部设置静默失效 。改完配置用 jq . settings.json 或编辑器校验一遍,是性价比最高的习惯。给文件加上 "$schema": "https://json.schemastore.org/claude-code-settings.json" 还能获得编辑器自动补全。

03 permissions:给代理画边界

权限系统由三个规则数组和一个默认模式组成:

json 复制代码
{
  "permissions": {
    "allow": ["Bash(git *)", "Read", "Edit(src/**)"],
    "deny":  ["Read(.env)", "Read(secrets/**)"],
    "ask":   ["Bash(git push*)"],
    "defaultMode": "default",
    "additionalDirectories": ["../shared-libs"]
  }
}

裁决顺序固定:deny → ask → allow。命中 deny 直接拒绝,且 deny 规则跨所有配置层合并,任何一层都拦得住。

规则语法

形式 示例 含义
裸工具名 Read 放行该工具的全部调用
精确匹配 Bash(npm run test) 只匹配这条命令
前缀通配 Bash(git *) 匹配以 git 开头的命令;Bash(git:*) 是等价写法
路径规则 Edit(docs/**) Read / Edit 类工具用 gitignore 风格模式;//path 为绝对路径,~/path 相对家目录,/path 相对 settings 文件所在位置
域名规则 WebFetch(domain:*.github.com) 限定网络抓取的目标域,通配符只在点分段内匹配
MCP 工具 mcp__github__get_issue 命名规则为 mcp__服务器名__工具名

几个实践细节值得单独说:

  • Windows 上 PowerShell 是独立工具 ,规则要写成 PowerShell(git status*) 而不是 Bash(...)------照抄网上的类 Unix 配置是 Windows 用户权限「不生效」的头号原因。
  • 星号前的空格有语义。 Bash(ls *) 要求词边界,匹配 ls -la 但不匹配 lsofBash(ls*) 两者都匹配。写宽了就是给自己挖洞。
  • 复合命令逐段判定。 Claude Code 识别 &&;、管道等操作符,Bash(git *) 不会连带放行 git status && rm -rf /,每一段都要各自过规则;点「不再询问」保存的也是逐段的多条规则。timeoutnice 这类进程包装器会被剥掉后再匹配。

defaultMode:没有命中任何规则时怎么办

模式 行为
default 每类首次使用弹窗确认,标准交互(新版界面标注为 Manual)
acceptEdits 自动接受文件编辑及 mkdirmv 等安全的文件系统命令,其余命令仍确认
plan 只读分析,不执行修改,适合方案评审
auto 由一个分类器模型判断动作风险,安全操作放行、危险操作拦下;可用 autoMode 键定制分类规则
dontAsk 不询问,未被规则明确允许的操作直接拒绝
bypassPermissions 跳过确认,仅保留 ask 规则和删根目录这类熔断防线。只应在容器或一次性虚拟机里用,企业可用 disableBypassPermissionsMode: "disable" 一票禁用

04 hooks:把确定性塞回去

提示词只能「建议」模型做事,hooks 则是在生命周期的固定节点无条件执行的钩子------每次写完文件必定跑 formatter,每条 bash 命令必定留审计日志。这是把不可协商的规则从「说服模型」变成「架构保证」。

结构是三层嵌套:事件 → 匹配器 → 钩子列表。

json 复制代码
{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.file_path' | { read -r f; prettier --write \"$f\"; } 2>/dev/null || true"
      }]
    }]
  }
}

事件:远不止「工具前后」

事件 触发时机 典型用途
PreToolUse 工具执行前,可拦截、可改写输入 安全门禁、参数重写
PostToolUse 工具成功后 格式化、跑测试、审计
PostToolUseFailure 工具失败后 失败告警
UserPromptSubmit 用户提交输入时 注入上下文、拦截敏感词
PermissionRequest 弹权限确认框之前 自动化审批策略
Stop / SubagentStop 主代理 / 子代理结束回合 完成度校验,不达标可打回重做
SessionStart / SessionEnd 会话启停 装载环境、清理现场
PreCompact / PostCompact 上下文压缩前后 保护关键信息
Notification 产生通知时 转发到 IM、系统通知

schema 里还有一批更新的事件------WorktreeCreateFileChangedTeammateIdleConfigChange 等,共三十余种,hooks 已经从「工具回调」长成了完整的生命周期事件总线。

五种钩子类型

多数文章只写 command 一种,实际有五种,能力差异很大:

类型 执行方式 适用场景
command 运行 shell 命令,stdin 收 JSON 格式化、日志、门禁脚本
prompt 调用一个快速 LLM 判断是/否 「这条命令安全吗」这类无法用正则表达的判断
agent 起一个带工具的子代理去验证 「确认测试真的跑过且通过」
http 把事件 JSON POST 到指定 URL 接入企业审计与合规平台
mcp_tool 调用已配置的 MCP 服务器工具 复用现有集成,支持 ${tool_input.file_path} 插值

输入输出协议

钩子从 stdin 读到完整的事件 JSON(session_idtool_nametool_input 等),通过两条通道回话:

  • 退出码。 0 表示放行;2 表示阻断,stderr 的内容会喂回给模型让它修正;其他退出码视为非阻断错误,只展示给用户。注意 1 也属于「非阻断」------想强制拦截必须显式 exit 2,这与 Unix 惯例相反。输出上限 10000 字符。
  • stdout 输出 JSON ,做精细控制。PreToolUse 钩子可以返回 permissionDecision: "allow" | "deny" | "ask" 直接裁决权限,甚至用 updatedInput 改写工具的入参 ;任何钩子可以用 additionalContext 把文本注入回模型上下文,用 systemMessage 给用户弹提示。

一个用 JSON 输出做门禁的例子------禁止任何工具读取环境文件:

json 复制代码
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Read|Bash",
      "hooks": [{
        "type": "command",
        "command": "python guard_env_files.py"
      }]
    }]
  }
}
json 复制代码
// guard_env_files.py 检测到目标含 .env 时输出:
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Env files are off-limits. Read .env.example instead."
  }
}

修饰字段同样值得关注:if 用权限规则语法先过滤(如 "if": "Bash(git *)",避免每条命令都起进程);async 后台执行不阻塞;once 执行一次即移除;timeoutstatusMessage 控制体验;args 数组则以 exec 形式直接 spawn 可执行文件,参数不经过 shell 解析,天然免疫注入。

安全提醒 hooks 以当前用户的完整权限执行任意命令,项目级 hooks 会随仓库分发给每个克隆代码的人。审阅不熟悉仓库的 .claude/settings.json 应当像审阅 CI 脚本一样认真。个人侧可用 disableAllHooks 一键停用,企业侧可用 allowManagedHooksOnly 只保留管理员下发的钩子。

05 模型与推理策略

json 复制代码
{
  "model": "claude-fable-5[1m]",
  "fallbackModel": ["opus", "default"],
  "effortLevel": "xhigh",
  "alwaysThinkingEnabled": true,
  "autoCompactWindow": 200000
}
  • model 接受别名(opussonnet)或完整 ID,后缀 [1m] 申请百万级长上下文窗口。
  • fallbackModel 是一个按序尝试的数组,主模型过载时自动降级,"default" 展开为当前默认模型。它是少数不跨层合并的数组:优先级最高的那份配置提供完整的降级链。
  • effortLevel(low / medium / high / xhigh)控制推理深度,与 alwaysThinkingEnabled 配合决定「想多久再动手」。
  • 企业侧有一组对应的管控键:availableModels 白名单限定可选模型,enforceAvailableModels 连默认模型也纳入约束,modelOverrides 把 Anthropic 模型 ID 映射到 Bedrock 推理配置 ARN 等私有部署目标。

06 sandbox:操作系统级隔离

permissions 决定「允不允许执行」,sandbox 决定「执行时能碰到什么」。它在 macOS 用 Seatbelt、Linux 用 bubblewrap 实现真正的 OS 级隔离,三个维度各自收紧:

json 复制代码
{
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "network": {
      "allowedDomains": ["github.com", "*.npmjs.org"],
      "deniedDomains": ["*.internal.corp"],
      "allowLocalBinding": true
    },
    "filesystem": {
      "denyRead": ["~/.ssh", "~/.aws"],
      "allowWrite": ["./build"]
    },
    "credentials": {
      "envVars": [
        { "name": "NPM_TOKEN", "mode": "mask",
          "injectHosts": ["registry.npmjs.org"] },
        { "name": "PROD_DB_PASSWORD", "mode": "deny" }
      ]
    }
  }
}

最精巧的是凭证掩码。mode: "mask" 让沙箱内的进程只能看到一个哨兵值,出口代理在请求发往 injectHosts 白名单域名时才把真值换进去------命令能正常用 token 发布包,却自始至终拿不到 token 本身 ,即使模型被诱导执行 echo $NPM_TOKEN 也只会打出占位符。deniedDomains 从所有配置层合并且优先于 allow,再次呼应「收紧无需授权,放宽必须升级」的原则。

autoAllowBashIfSandboxed 则揭示了 sandbox 与 permissions 的联动逻辑:既然命令已经被物理隔离,就不必每条都弹窗------用隔离换流畅,比裸奔的 bypassPermissions 安全得多。

07 MCP 与插件治理

MCP 服务器和插件都是往代理里注入第三方代码的通道,settings.json 为两者各配了一套「个人选择 + 企业裁决」的双层开关。

个人与团队层面:enabledMcpjsonServers / disabledMcpjsonServers 逐个批准或拒绝项目 .mcp.json 里声明的服务器,enableAllProjectMcpServers 整体放行;插件用 enabledPlugins插件名@市场名 为键启停,extraKnownMarketplaces 在项目里登记团队私有市场,克隆仓库即获得完整插件源。

企业层面的键则全部只在托管层生效:

  • allowedMcpServers / deniedMcpServers:按名称、启动命令或 URL 通配符做白/黑名单,两边都命中时黑名单赢;
  • strictKnownMarketplaces / blockedMarketplaces:限定插件市场来源,校验发生在下载之前,被拒来源不会落盘;
  • disableSideloadFlags:封掉 --plugin-dir--mcp-config 等 CLI 旁路,堵住绕过白名单的口子;
  • strictPluginOnlyCustomization:更进一步,把 skills、agents、hooks、mcp 四类本地自定义全部锁死,只允许来自受管插件。

精度提示 要在本机关掉项目启用的插件,应写进 .claude/settings.local.json。写在用户全局层没用------优先级是 user < project < local,全局层的 false 会被项目层的 true 覆盖。

08 企业管控专用键

一批键只有写在 managed-settings.json 里才生效,它们共同把 settings.json 变成了一个 MDM 策略载体:

作用
forceLoginMethod / forceLoginOrgUUID 强制登录方式,并要求账号属于指定组织
requiredMinimumVersion / requiredMaximumVersion 版本围栏,越界直接拒绝启动
claudeMd 以组织记忆的形式注入一段 CLAUDE.md 指令,全员生效
allowManagedPermissionRulesOnly 只认托管层的权限规则,用户/项目/CLI 的规则全部失效
allowManagedHooksOnly 只运行托管层下发的 hooks
availableModels + enforceAvailableModels 模型白名单,含默认模型兜底改写
disableAgentView / disableRemoteControl 关闭后台代理与远程控制入口
pluginTrustMessage / companyAnnouncements 插件安装警示附言、启动横幅公告

配合第 2 节的合并语义看,整个治理链条是闭环的:管理员用 strictKnownMarketplaces 圈定插件来源,用 disableSideloadFlags 封 CLI 旁路,用 allowManagedHooksOnly 保证审计钩子不被本地配置顶掉------每一个「用户侧自由度」都有对应的「管理侧闸门」。

09 体验层配置

治理之外,settings.json 也管日常手感。几个高频项:

  • statusLine:接一个命令自定义状态栏,会话上下文以 JSON 从 stdin 传入,可用 refreshInterval 定时刷新------不少人用它显示当前分支、消耗 token 数。
  • attribution:定制或清空 commit / PR 里的 Claude 署名(置空串即隐藏),取代已废弃的 includeCoAuthoredBy
  • env:为所有会话注入环境变量,适合放代理地址、遥测开关这类跟人走的变量。
  • outputStylelanguagethemeeditorMode(vim 党的 "vim")、spinnerVerbs(自定义等待动词,纯粹的乐趣项)。
  • cleanupPeriodDays:会话记录保留天数,默认 30。

这类简单标量不必手改文件,交互式的 /config 命令更稳妥;手改 JSON 的价值在 hooks、权限数组这类结构化配置上。

10 分层实践与排错清单

什么放哪一层

三个文件的分工,用「这条配置该跟着谁走」来判断:

json 复制代码
// ~/.claude/settings.json ------ 跟人走:偏好与通用只读放行
{
  "model": "claude-fable-5[1m]",
  "effortLevel": "xhigh",
  "permissions": {
    "defaultMode": "auto",
    "allow": [
      "PowerShell(git status*)", "PowerShell(git diff*)",
      "PowerShell(git log*)",    "PowerShell(Get-ChildItem *)",
      "PowerShell(Get-Content *)", "PowerShell(Test-Path *)"
    ]
  }
}
json 复制代码
// .claude/settings.json ------ 跟仓库走:团队约定,提交进 Git
{
  "permissions": {
    "allow": ["Bash(npm run lint)", "Bash(npm run test *)"],
    "deny":  ["Read(.env*)", "Read(secrets/**)"]
  },
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{ "type": "command",
        "command": "jq -r '.tool_input.file_path' | { read -r f; npx prettier --write \"$f\"; } 2>/dev/null || true" }]
    }]
  }
}
json 复制代码
// .claude/settings.local.json ------ 跟机器走:个人差异,不进 Git
{
  "permissions": {
    "allow": ["mcp__claude_ai_Microsoft_Learn__microsoft_docs_search"]
  },
  "env": { "HTTP_PROXY": "http://127.0.0.1:7890" }
}

上面用户层的例子来自一台真实的 Windows 开发机------注意所有命令规则都是 PowerShell(...),且只放行只读操作:查看状态、看日志、列目录。写操作留给 auto 模式的分类器把关。这是一个值得照抄的基线:允许列表只放「看」,「动」的部分交给模式与确认。

排错清单

症状 最常见原因 处置
某文件的设置全部不生效 JSON 语法损坏,静默失效 jq . 文件路径 校验;加 $schema 让编辑器实时报错
权限规则不匹配 Windows 上写了 Bash(...);或工具名大小写不对 对照实际弹窗里的工具名写规则,Windows 命令用 PowerShell(...)
新加的 hook 不触发 配置监视器只监听会话启动时已存在 settings 文件的目录 执行 /hooks 重载配置,或重启会话
hook 触发但没效果 命令在 hook 环境下不可用(PATH、包管理器差异) 手工构造 stdin JSON 管道测试命令本身;claude --debug 看执行日志
改配置时丢了原有规则 数组被整体替换而非合并 先读后写:编辑前完整读取原文件,追加而非覆盖数组
local 文件被误提交 由 Claude Code 创建时会自动加入 gitignore,手工创建的不会 确认 .claude/settings.local.json 在 .gitignore 里

结语

settings.json 的一百多个键,归纳起来只在回答一个问题:当一个能写文件、能跑命令、能联网的 AI 代理进入你的开发流程,信任的边界画在哪里,由谁来画。个人用它换取流畅,团队用它固化约定,企业用它落实策略------而「收紧可以来自任何一层,放宽只能来自更高层」这条原则,让三者可以叠加共存而互不拆台。下一次打开这个文件,不妨先想清楚要改的这一行属于谁,再决定它落在哪一层。

相关推荐
张三丰22 小时前
LEFT JOIN 到底什么时候会被优化器偷偷改写成 INNER JOIN
后端
用户40966601317512 小时前
MySQL 索引优化速查表:3 分钟定位慢查询根因
后端
feelmylife592 小时前
企业级 LLM Mass Gateway — 多租户高并发网关,真正难点是 streaming 下的配额计数
人工智能·后端
云技纵横2 小时前
一个 CompletableFuture,把 CPU 打满了:默认线程池的线上坑
后端
free352 小时前
垃圾回收(GC)入门:从手动管理到自动回收的底层逻辑与演进
后端·架构
洛卡卡了2 小时前
Claude Code 项目级 Skill:团队协作场景下的使用场景
人工智能·后端·claude
用户762352425912 小时前
SpringAi 1.1更新说明
后端
程序员黎剑2 小时前
我把算卦变成了一段if-else
java·vue.js·spring boot·后端·ai编程
SimonKing2 小时前
Base62编码生成6位不重复短码的背后设计
java·后端·程序员