Skip to content
Writing
Skill

/tool-permission-system

Design and implement a layered, configurable permission/safety system for agent tools. Use this skill when building an agent that needs to control which tool calls are auto-allowed, which require user confirmation, and which are denied — especially when the system must be

BOOST
From plugin
book2skills
19238 skills
Install
$ npx -y skills add simbajigege/book2skills --skill tool-permission-system --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/tool-permission-system

Context preview

The summary Claude sees to decide when to auto-load this skill.

Design and implement a layered, configurable permission/safety system for agent tools. Use this skill when building an agent that needs to control which tool calls are auto-allowed, which require user confirmation, and which are denied — especially when the system must be

SKILL.md

tool-permission-system.SKILL.md
name: tool-permission-system
description: 'Design and implement a layered, configurable permission/safety system for agent tools. Use this skill when building an agent that needs to control which tool calls are auto-allowed, which require user confirmation, and which are denied — especially when the system must be configurable across multiple scopes (project/user/enterprise) and extensible via hooks. Triggers on: "权限系统", "工具安全", "tool permission", "permission system", "tool safety", "allow/deny rules", "hook system", "构建安全机制".'

Tool Permission System

Core Idea

Every time an agent calls a tool, a **permission pipeline** runs before execution. This pipeline is the single place that decides: auto-allow, ask the user, or deny. The pipeline is layered — different stakeholders (enterprise admin, user, project team, session) can each contribute rules, with higher layers overriding lower ones.

Tool call request
  ↓
[硬否决] Deny rules → immediate deny
  ↓
[强制确认] Ask rules → force prompt (even in bypass mode)
  ↓
[工具自身] Tool's checkPermissions() → tool-specific logic
  ↓
[安全绕过免疫] Safety checks (.git/, .claude/, shell configs) → prompt, immune to bypass
  ↓
[模式快速通过] Bypass / acceptEdits mode → immediate allow
  ↓
[白名单] Allow rules → immediate allow
  ↓
[默认] passthrough → prompt user (ask)

**外层包装**(作用于整条流水线之后):

  • `dontAsk` 模式:把所有 `ask` 转为 `deny`(用于无交互的后台 agent)
  • `auto` 模式:把所有 `ask` 转给 AI 分类器判断,而不是打断用户
  • `headless` 模式:先跑 PermissionRequest hooks,hooks 没回应就自动 deny

Workflow

1. 定义三种决策行为

type PermissionBehavior = 'allow' | 'deny' | 'ask'

type PermissionDecision =
  | { behavior: 'allow'; updatedInput?: unknown; decisionReason?: DecisionReason }
  | { behavior: 'ask';   message: string; suggestions?: PermissionUpdate[] }
  | { behavior: 'deny';  message: string; decisionReason: DecisionReason }

2. 建立分层规则来源

规则来源按优先级从高到低排列:

policySettings    ← 企业管理员,用户不可覆盖
userSettings      ← 用户全局 (~/.agent/settings.json)
projectSettings   ← 项目级 (.agent/settings.json,可提交 git)
localSettings     ← 本地私有 (.agent/settings.local.json)
cliArg            ← 启动参数
command           ← 运行时命令
session           ← 当次会话临时

每条规则的格式:`ToolName` 或 `ToolName(content)`。

3. 实现权限决策函数

async function hasPermission(tool, input, context): Promise<PermissionDecision> {
  // Step 1: deny rules (优先级最高,含企业强制)
  const denyRule = findMatchingRule(context.denyRules, tool, input)
  if (denyRule) return { behavior: 'deny', message: '...', decisionReason: { type: 'rule', rule: denyRule } }

  // Step 2: ask rules (强制弹框,绕过模式也无法跳过)
  const askRule = findMatchingRule(context.askRules, tool, input)
  if (askRule) return { behavior: 'ask', message: '...' }

  // Step 3: 工具自身的 checkPermissions()
  const toolResult = await tool.checkPermissions(input, context)
  if (toolResult.behavior === 'deny') return toolResult
  if (toolResult.behavior === 'ask' && toolResult.decisionReason?.type === 'rule') return toolResult  // ask rule 免疫 bypass
  if (toolResult.behavior === 'ask' && toolResult.decisionReason?.type === 'safetyCheck') return toolResult  // 安全检查免疫 bypass

  // Step 4: bypass 模式快速通过
  if (context.mode === 'bypassPermissions') return { behavior: 'allow', updatedInput: input }

  // Step 5: allow rules 白名单
  const allowRule = findMatchingRule(context.allowRules, tool, input)
  if (allowRule) return { behavior: 'allow', updatedInput: input }

  // Step 6: 默认转 ask
  return { behavior: 'ask', message: `Agent requested to use ${tool.name}` }
}

4. 为每个工具定义安全属性接口(fail-closed 默认值)

工具与权限系统的接合点是工具接口上的一组**安全属性**。关键设计:所有属性都遵循**失败关闭(fail-closed)**——开发者不声明时,系统按"最保守"假设处理,必须主动声明"我是安全的"才放宽。

// 工厂函数用 TOOL_DEFAULTS 填充未声明的属性
const TOOL_DEFAULTS = {
  isEnabled:         () => true,
  isConcurrencySafe: () => false,   // 默认不并发(怕数据竞争)
  isReadOnly:        () => false,   // 默认假设会写入
  isDestructive:     () => false,   // 默认假设不可逆操作要谨慎
  checkPermissions:  (input) => ({ behavior: 'allow', updatedInput: input }), // 默认交给中央权限系统
}
function buildTool(def) { return { ...TOOL_DEFAULTS, ...def } }

| 属性 | 返回 | 谁来问 / 影响什么 | |---|---|---| | `isReadOnly(input)` | boolean | 权限系统:只读操作可绕过部分限制 | | `isDestructive(input)` | boolean | 权限系统:不可逆操作需更严格确认 | | `isConcurrencySafe(input)` | boolean | Agent Loop:能否与其他工具并发执行(默认 false → 串行) | | `checkPermissions(input, ctx)` | PermissionResult | 权限系统:工具专属权限逻辑(流水线 1c) | | `validateInput(input, ctx)` | ValidationResult | Agent Loop:执行前的输入合法性校验 |

**`checkPermissions` 在流水线里的位置是"夹心结构"**:通用 deny/ask 规则在它**之前**(且 bypass 也拦不住),通用 allow 白名单在它**之后**。所以工具自检既挡不住企业 deny,也不必重复实现通用 allow——只管工具特有的逻辑:

class MyTool implements Tool {
  isReadOnly = () => false
  isConcurrencySafe = () => false

  async checkPermissions(input, context): Promise<PermissionResult> {
    // 检查工具特定规则(如 Bash 检查具体命令前缀)
    const allowRules = getRuleContentsForTool(context, this, 'allow')
    if (allowRules.has(getCommandPrefix(input.command))) {
      return { behavior: 'allow' }
    }

    // 检查危险路径(命中后 type:'safetyCheck' → bypass 也拦不住,见下方说明)
    if (isDangerousPath(input.path)) {
      return {
        behavior: 'ask',
        message: '...',
        decisionReason: { type: 'safetyCheck', reason: '...', classifierApprovable: false }
      }
    }

    return { behavior: 'passthrough', message: '...' }  // 没意见 → 交给外层
  }
}

**危险路径黑名单**(`safetyCheck`)是一份硬编码的敏感文件/目录清单,即使 bypass / acceptEdits / 配了 allow 规则也强制弹框,防两类攻击:① 代码执行(`.git/` hooks、`.bashrc`/`.zshrc` 等 shell 启动脚本、`.vscode`/`.idea` 任务配置);② AI 改自己的护栏(`.claude/`、`.mcp.json`、`.claude.json` —— agent 不能通过"正常编辑文件"给自己提权)。完整清单见 `references/dangerous-patterns.ts`。

5. 实现 Hook 系统(可选但推荐)

Hook 让用户/企业在工具生命周期各节点插入自定义逻辑:

// 配置格式(settings.json)
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "MyTool",          // 可选,工具名过滤
      "hooks": [{
        "type": "command",          // command | prompt | agent | http
        "command": "check-safety.sh $TOOL_INPUT"
      }]
    }],
    "PostToolUse": [{
      "matcher": "F
Read more
Ships withbook2skills

Transform the wisdom of classic books into AI Agent Skills. book2skills is an open-source project focused on systematically distilling high-quality practical books into Agent Skills format, enabling AI agents like Claude and Claude Code to directly apply the

Get the whole plugin

Other skills on book2skills.