<!-- title: Skill权限真相：allowed-tools不是白名单 -->

## 概述

本教程聚焦一个被广泛误解的 Claude Code Skills 机制：**`allowed-tools` 并不限制工具访问**。很多人以为在 frontmatter 里写 `allowed-tools: Read Grep` 就能把 Skill 锁成只读沙箱——事实并非如此。读完本文，你将掌握 Skill 的完整权限治理模型，学会用 `disallowed-tools`、`paths`、`disable-model-invocation` 三字段给自定义技能上真正的锁，并能识别"项目级 Skill 自授广权限"这一团队安全陷阱。适合已经写过基础 Skill、希望把技能安全地引入团队或生产流程的开发者。

## 前置条件

- 已安装 Claude Code（v2.1.145 及以上，以使用最新的 Skill 与 frontmatter 特性）
- 理解 `SKILL.md` 的基本结构（frontmatter + Markdown 正文）
- 有一个 git 仓库用于动手实践
- 了解 `settings.json` 权限规则（`allow` / `ask` / `deny`）的基本概念

---

## 一、核心认知：`allowed-tools` 不是白名单

这是整篇文章最重要的一段，请先记住这句话：

> **`allowed-tools` 授予权限（免确认），不限制可用工具。真正的限制要用 `disallowed-tools`。**

官方文档原文写得很清楚："`allowed-tools` 字段在 Skill 激活期间，为列出的工具授予许可，让 Claude 无需逐次征求批准即可使用。**它并不限制哪些工具可用：每个工具依然可以被调用**，未被列出的工具仍受你的权限设置管辖。"

换句话说，下面这段配置是一个**极其常见**的误解：

```yaml
---
name: my-reader
description: 只读分析工具
allowed-tools: Read Grep
---
```

很多人的心智模型是："这个 Skill 只能用 Read 和 Grep，是个白名单。" 实际行为是：Read 和 Grep 会被**预批准**（不再弹确认框），但 Write、Edit、Bash 这些工具**依然全部可用**——只是它们会走正常的权限确认流程。如果用户处于宽松的权限模式（比如 `acceptEdits`），这个"只读"Skill 照样能改文件。

社区甚至有专门的 issue 在讨论这个反直觉行为：标题就叫《allowed-tools in SKILL.md frontmatter does not restrict tool access》。这不是 bug，而是设计：`allowed-tools` 的语义是"预批准 / 静默许可"，不是"隔离 / 白名单"。

理解这一点后，权限治理的思路就清晰了：**要禁止，用 `disallowed-tools`；要预批准，用 `allowed-tools`；要限定何时激活，用 `paths`；要控制谁能触发，用 `disable-model-invocation` 与 `user-invocable`。**

## 二、五大权限字段速查表

先把治理工具箱摆全。下表是官方支持的、与权限/激活相关的 frontmatter 字段（全部可选）：

| 字段 | 作用 | 默认 | 关键语义 |
|:--|:--|:--|:--|
| `allowed-tools` | 预批准（免确认） | 继承会话权限 | **授予**许可，不限制 |
| `disallowed-tools` | 从工具池**移除** | 无 | **真限制**；下一条消息后清除 |
| `paths` | 限定自动激活的文件范围 | 无（任意文件都触发） | glob 模式，命中才加载 |
| `disable-model-invocation` | 阻止 Claude 自动触发 | `false` | 设为 `true` 后仅人工 `/name` 调用 |
| `user-invocable` | 是否出现在 `/` 菜单 | `true` | 设为 `false` 后仅 Claude 自动调用 |

此外，`context: fork` + `agent` 可把 Skill 放进隔离的子智能体上下文执行，`model` / `effort` 可在 Skill 激活期间临时切换模型与算力档位——这些虽不直接是"权限"，但都属于"Skill 激活期间的行为控制"，可配合权限字段一起设计。

## 三、逐字段实战

### 3.1 `disallowed-tools`：真正的围栏

`disallowed-tools` 会把列出的工具**从 Claude 的可用工具池中移除**，是 frontmatter 层面唯一的"硬禁止"机制。注意它的一个重要特性：**限制在你发送下一条消息后清除**——它不是会话级的永久封锁，而是"当前这轮 Skill 任务"内的临时禁用。

```yaml
---
name: audit-only
description: 只读审计，绝不允许修改文件
disable-model-invocation: true
disallowed-tools: Write Edit NotebookEdit
allowed-tools: Read Grep Glob
---
```

这个配置的效果：

- `Read Grep Glob` 被预批准 → 读取、搜索全程无确认框，体验流畅
- `Write Edit NotebookEdit` 被移除 → 即使 Claude 想"顺手帮你修个 bug"，工具根本不在池里，调用直接失败
- `disable-model-invocation: true` → 只有你手动 `/audit-only` 才会跑，Claude 不会自作主张

这就构成了一个语义上真正的"只读 Skill"：**预批准只读 + 硬禁止写入 + 手动触发**。

### 3.2 `paths`：把激活范围钉死在特定文件

`disallowed-tools` 解决"能做什么"，`paths` 解决"何时激活"。它接受 glob 模式（逗号分隔字符串或 YAML 列表），格式与 CLAUDE.md 的 path-specific 规则一致。设置后，Claude **只有在你操作的文件命中模式时**才会自动加载该 Skill。

```yaml
---
name: sql-migration-review
description: 审查 SQL 迁移脚本中的破坏性操作
paths: db/migrations/**/*.sql, migrations/*.sql
disallowed-tools: Write Edit
---
```

效果：你编辑 `README.md` 时它沉默不加载（省 token）；你一打开 `db/migrations/2026_06_drop_users.sql`，它立刻进入待命，准备审查 `DROP TABLE`、`DELETE` 无 `WHERE` 这类高危语句。

**常见坑**：`paths` 只影响"Claude 自动加载"的时机；如果你手动 `/sql-migration-review` 强制调用，无论当前在编辑什么文件它都会跑。`paths` 是"自动触发的闸门"，不是"调用的闸门"。

### 3.3 谁能触发：两个互补字段

| 配置 | 你能调用 | Claude 能调用 | 何时进上下文 |
|:--|:--|:--|:--|
| （默认） | 是 | 是 | description 常驻；调用后载入全文 |
| `disable-model-invocation: true` | 是 | **否** | description 也不进上下文 |
| `user-invocable: false` | **否** | 是 | description 常驻 |

经验法则：

- **有副作用、要把握时机**的操作（`/commit`、`/deploy`、`/publish`）→ `disable-model-invocation: true`。你不希望 Claude 觉得"代码看起来准备好了"就自己部署。
- **纯背景知识**（解释遗留系统怎么工作）→ `user-invocable: false`。`/legacy-system-context` 不是一个有意义的用户动作，但你希望 Claude 在相关时自动知晓。

## 四、实战案例：`precommit-audit` 提交前自审 Skill

下面构建一个完整、可运行的 Skill：在提交前扫描暂存区，审计硬编码密钥、调试残留、TODO/FIXME、缺少测试等问题。它必须**严格只读**——只能看、不能改、更不能替你提交。

目录结构：

```text
.claude/skills/precommit-audit/
├── SKILL.md            # 入口 + 治理字段（必需）
└── reference.md        # 详细检查规则（按需加载）
```

### 4.1 SKILL.md（入口）

```yaml
---
name: precommit-audit
description: 提交前只读自审。扫描暂存区改动，报告硬编码密钥、调试残留、TODO/FIXME、缺少测试等问题。Use when the user is about to commit, asks to review staged changes, or says "提交前检查一下".
disable-model-invocation: true
disallowed-tools: Write Edit NotebookEdit
allowed-tools: Read Grep Glob Bash(git status:*) Bash(git diff:*) Bash(git log:*)
paths: "**/*"
---

## 暂存区上下文（动态注入，运行前已替换为真实输出）

- 待提交文件清单：!`git diff --cached --name-only`
- 完整暂存差异：!`git diff --cached`

## 任务

只读分析上面的差异，按 `reference.md` 的检查清单逐项核对，输出一份结构化报告。**严禁修改任何文件、严禁执行 git add/commit**——你没有写入工具，也不应该尝试绕过。

报告格式：
1. **🔴 高危**：硬编码密钥 / credentials / `.env` 内容被提交
2. **🟡 警告**：`console.log` / `print` / `debugger` 等调试残留
3. **🔵 提示**：未解决的 TODO/FIXME、缺少对应测试的新函数
4. **✅ 通过项**：本次改动中表现良好的实践

若暂存区为空，直接回复"没有待提交的改动"。

## 补充规则
- 完整检查项与示例见 [reference.md](reference.md)，仅当差异中涉及该类问题时加载。
```

注意这几处设计的用意：

1. `!`git diff --cached`` 用**动态上下文注入**：命令在 Claude 看到正文**之前**就执行完毕，输出替换占位符。Claude 收到的是真实 diff，而不是"去跑一下 git diff"的指令——这把"可能跑错命令"的风险降到了零。
2. `allowed-tools` 只预批准**只读类** git 子命令（用 `Bash(git status:*)` 精确匹配，而非 `Bash(git:*)` 全量放行），所以即便想跑也不会被预批准 `git commit`。
3. `disallowed-tools: Write Edit NotebookEdit` 保证文件绝不被改。
4. `disable-model-invocation: true` 保证只有你敲 `/precommit-audit` 才会触发。

### 4.2 reference.md（按需加载的详细规则）

```markdown
# precommit-audit 详细检查清单

## 高危（必须拦截）
- `password = "..."`、`api_key = "..."`、`AKIA[0-9A-Z]{16}`（AWS key）
- 私钥头 `-----BEGIN ... PRIVATE KEY-----`
- 内网 IP、数据库连接串、JWT secret

## 警告（建议拦截）
- `console.log` / `print(` / `debugger` / `dd(` / `var_dump(`
- 注释掉的大段代码

## 提示（酌情提醒）
- 新增 public 函数但仓库内无对应测试文件
- `TODO` / `FIXME` / `XXX` 标记
```

为什么把清单拆出去？官方 Tip 是"SKILL.md 控制在 500 行以内，详细参考材料移到独立文件"。一旦 Skill 被调用，正文会**常驻上下文**直到会话结束，每一行都是反复消耗的 token 成本。把不常用的细节放进 `reference.md`，只在正文里写"命中时再加载"，平时几乎零成本。

### 4.3 测试预期

在一个有暂存改动的仓库里：

```text
/precommit-audit
```

预期 Claude 输出一份分级报告，且全过程**不弹出任何文件写入确认框**（因为没有写入工具），**不触发提交**。如果你发现它居然改了文件，那说明 `disallowed-tools` 没生效——见第七节排错。

## 五、Skill vs Hook：两条治理路径要分清

一个常见的困惑："我上篇用 hooks 做过提交守卫，这里又用 Skill 做自审，两者什么关系？" 区别如下：

| 维度 | Hooks（settings.json） | Skill（SKILL.md） |
|:--|:--|:--|
| 执行者 | **Claude Code 框架**，确定性地 | **Claude 模型**，按指令编排 |
| 触发 | 事件驱动（PreToolUse、Stop 等） | 手动 `/name` 或语义自动 |
| 可解释性 | 脚本输出，机械 | 自然语言报告，可对话追问 |
| 适合 | 硬性规则（"禁止提交含密钥"） | 软性建议（"这里有 3 处可疑，你看下"） |

实践上两者互补：用 **hooks 做确定性闸门**（哪怕 Claude 想提交也会被脚本拦下），用 **Skill 做可解释的预审**（提交前先 `/precommit-audit` 看一眼报告，决定要不要修）。本文聚焦 Skill 这一路。

## 六、工作流程与权限决策图

下图展示 Skill 从休眠到激活、再到每次工具调用如何被治理的完整链路：

```mermaid
flowchart TD
    U[用户提问 / 编辑文件] --> M{匹配 description<br/>且 paths 命中?}
    M -->|是| L[加载 SKILL.md 进上下文<br/>激活权限层]
    M -->|否| S[保持休眠 不占 token]
    L --> T[模型发起工具调用]
    T --> C{工具在 allowed-tools?}
    C -->|是| D[免确认放行]
    C -->|否| E{工具在 disallowed-tools?}
    E -->|是| F[工具已移出池 调用失败]
    E -->|否| G[走 settings.json 权限规则]
    G --> R{allow / ask / deny}
    R -->|allow| D
    R -->|ask| H[弹确认框]
    R -->|deny| F
    H -->|批准| D
    H -->|拒绝| F
    D --> N[继续执行]
    F --> N
    N --> NEXT[发送下一条消息:<br/>disallowed-tools 清除]
```

## 七、常见问题与排错

**Q1：我写了 `allowed-tools: Read`，为什么 Claude 还是能调用 Write？**
因为 `allowed-tools` 只预批准、不限制。要禁止写，请加 `disallowed-tools: Write Edit`。

**Q2：`disallowed-tools` 似乎过了一会儿就失效了？**
这是设计。它在"下一条用户消息"后清除，属于当前任务轮次内的临时禁用，不是会话级封锁。需要长期禁止某工具，请用 `settings.json` 的 `deny` 规则。

**Q3：项目级 `.claude/skills/` 里写了 `allowed-tools`，但 Skill 跑起来还是反复弹确认。**
项目级 Skill 的 `allowed-tools` 要在你**接受该目录的工作区信任对话框（workspace trust dialog）后**才生效，与 `.claude/settings.json` 的权限规则同理。检查是否已信任该仓库。

**Q4：`paths` 设了，但在别的文件里手动 `/skill` 还是能跑？**
对。`paths` 只控制"自动加载"的时机，手动调用不受限。如需禁止在特定路径调用，得靠 `disallowed-tools` 或权限规则。

**Q5：Bash 怎么做"只读"？我不想让 Skill 跑 `rm`。**
frontmatter 对 Bash 是粗粒度的：`disallowed-tools: Bash` 会一刀切禁掉所有 bash（连 `git status` 也不行）。正确做法是用 `allowed-tools` 精确预批准只读子命令（`Bash(git status:*)`、`Bash(git diff:*)`），其余 bash 走 `settings.json` 的 `deny` 规则（如 `Bash(rm:*)`）。**细粒度 bash 治理仍依赖 settings.json，frontmatter 是粗筛。**

## 八、团队安全提示：警惕 Skill 自授广权限

官方文档有一条容易被忽视的安全警告：对于提交进项目 `.claude/skills/` 的 Skill，`allowed-tools` 在你接受信任对话框后才生效，而**一个 Skill 可以为自己授予很宽的工具访问权限**。这意味着：

- 克隆陌生仓库、运行其内置 Skill 前，**务必审查 `SKILL.md` 的 `allowed-tools` / `disallowed-tools`**。一个写着 `allowed-tools: Bash(*)` 的 Skill 等于把无确认 shell 交了出去。
- 团队里用企业级 managed settings 锁定高危工具的 `deny` 规则，可以兜底覆盖任何 Skill 的 `allowed-tools`——因为 `disallowed-tools` 和 settings 的 deny 才是硬限制，`allowed-tools` 绕不过它们。
- 把"是否允许自动触发"也纳入 review：`disable-model-invocation: false` + 宽 `allowed-tools` 的 Skill，可能在你不注意时被 Claude 自动激活并执行副作用操作。

记住这条治理优先级链：**`settings.json` deny > `disallowed-tools` > `allowed-tools`（仅预批准）**。要构建真正不可越界的 Skill，从链的两端同时设防。

## 九、延伸阅读

- **Claude Code 官方 Skills 文档**（`code.claude.com/docs/en/skills`）：本文所有字段语义的权威来源，含动态上下文注入、子智能体执行、`$ARGUMENTS` 替换等进阶特性。
- **官方 Frontmatter Reference**（GitHub `anthropics/claude-code` 仓库 `plugins/plugin-dev/skills/command-development/references/frontmatter-reference.md`）：`allowed-tools`、`model`、`argument-hint` 每个字段的格式与最佳实践。
- **Tools Reference**（`code.claude.com/docs/en/tools-reference`）：每个工具的权限要求，配合设计 `allowed-tools` / `disallowed-tools` 时查阅。
- **Agent Skills 开放标准**（`agentskills.io`）：Claude Code Skills 遵循的跨工具标准，了解可移植性边界。

## 十、训练营适用性评估

- **适用类型**：企业内训安全模块、AI 工程化进阶营、DevOps 自动化训练营。强调"把 LLM 工具放进生产流程前的权限治理"，与安全合规主题高度契合。
- **不适合**：纯零基础入门营——需要对 Skill 结构和权限模式有前置认知。
- **预计教学时长**：讲解 25 分钟 + 动手构建 `precommit-audit` 40 分钟 + 排错与团队策略讨论 20 分钟，合计约 85 分钟。
- **考核点**：能否准确说出 `allowed-tools` 与 `disallowed-tools` 的语义差异；能否独立设计一个"预批准只读 + 硬禁止写入 + 限定 paths + 手动触发"的治理完备 Skill；能否识别并审查陌生仓库 Skill 的权限风险。

## 参考来源

1. [Extend Claude with skills — Claude Code Docs](https://code.claude.com/docs/en/skills) — 官方 Skills 文档，本文 frontmatter 字段语义、动态注入、生命周期、信任对话框等核心内容的权威来源
2. [Command Frontmatter Reference — anthropics/claude-code](https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/command-development/references/frontmatter-reference.md) — 官方 frontmatter 参考文档，`allowed-tools`、`model`、`argument-hint` 字段格式与 Bash 命令过滤语法的出处
3. [allowed-tools in SKILL.md frontmatter does not restrict tool access — GitHub Issue #37683](https://github.com/anthropics/claude-code/issues/37683) — 社区 issue，印证"`allowed-tools` 不限制工具访问"这一反直觉行为的设计本质
4. [Tools reference — Claude Code Docs](https://code.claude.com/docs/en/tools-reference) — 官方工具权限参考，配合设计 `allowed-tools` / `disallowed-tools` 与 settings.json 规则时查阅
5. [Claude Code Skill Frontmatter: Every YAML Option Explained](https://allahabadi.dev/blogs/ai/claude-code-skills-frontmatter-complete-guide/) — 第三方 frontmatter 全字段解读，可作为字段速查的补充阅读

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
