Skill allowed_tools 白名单 enforcer Plugin(v0.5.3+)。
当某些 Skill 在 SKILL.md frontmatter 声明 allowed_tools 字段时,
本 Plugin 在 :before_tool hook 计算 active skills 的 allowed_tools 并集,
阻止不在白名单内的工具调用。
SKILL.md frontmatter 示例
---
name: elixir-testing
description: Elixir 测试最佳实践
allowed_tools:
- read_file
- grep
- shell
---使用
skills = CMDC.Skill.discover([{:project, "./.cmdc/skills"}])
CMDC.create_agent(
model: "anthropic:claude-sonnet-4-5",
tools: [CMDC.Tool.ReadFile, CMDC.Tool.Grep, CMDC.Tool.WriteFile, CMDC.Tool.Shell],
plugins: [
{CMDC.Plugin.Builtin.SkillGuard,
skills: skills,
active: ["elixir-testing"],
enforce_mode: :strict}
]
)选项
:skills——[CMDC.Skill.t()]Plugin 启动时全量传入(与Options.skills_dirs自动 discover 的 Skills 一致):active——[String.t()]当前激活的 Skill name 列表;默认全部:skills都激活:enforce_mode——:strict(默认,block_tool)/:warn(仅日志不 block)
allowed_tools 并集计算规则
- 无 active skill 或
active = []→:unrestricted(所有工具允许) - 任一 active skill 的
allowed_tools为 nil →:unrestricted(该 skill 显式声明不限制,并集自然不限制) - 所有 active skills 都设了
allowed_tools→ 取并集,工具调用必须在并集内
并集语义(而非交集)让"用户主动激活某 skill 就放开对应工具"语义自然。
block_tool 返回示例
{:error, "Tool 'edit_file' not in active Skills' allowed_tools whitelist " <>
"(active: ["elixir-testing"], allowed: ["read_file", "grep", "shell"])"}优先级
priority/0 返回 20,在 SecurityGuard(10)之后但在大多数业务 Plugin 之前。
与其他 Plugin 的关系
SecurityGuard优先 → 路径 / 命令黑名单先 enforceSkillGuard次之 → Skill 上下文白名单 enforce- 业务 Plugin (priority >= 100) 最后
不影响的行为
- 工具列表本身(
Options.tools)不改 —— Plugin 只在调用时拦截,不"卸载"工具 - 不影响 SystemPrompt 注入 —— Skill 的 description/snippet 照常注入
- 不影响 Skill 加载链路(
Skill.discover/Skill.load)
Summary
Functions
计算指定 active skills 的 allowed_tools 并集。
Functions
@spec compute_allowed_tools(%{required(String.t()) => CMDC.Skill.t()}, [String.t()]) :: :unrestricted | [String.t()]
计算指定 active skills 的 allowed_tools 并集。
返回:
:unrestricted—— 无 active skill 或 任一 active skill 未设 allowed_tools[String.t()]—— allowed_tools 并集(去重)
示例
iex> skills = %{
...> "a" => %Skill{name: "a", description: "", path: "/", allowed_tools: ["read", "write"]},
...> "b" => %Skill{name: "b", description: "", path: "/", allowed_tools: ["grep"]}
...> }
iex> CMDC.Plugin.Builtin.SkillGuard.compute_allowed_tools(skills, ["a", "b"])
["read", "write", "grep"]
iex> CMDC.Plugin.Builtin.SkillGuard.compute_allowed_tools(%{}, [])
:unrestricted