DEV Community

Rulestack
Rulestack

Posted on

Claude Code skill allowed-tools: a one-turn grant, not a sandbox

You add allowed-tools: Bash(git add *) Bash(git commit *) to a skill. You type /commit, and Claude stages and commits without asking. You send one more message, ask for another commit — and the permission prompt is back.

Nothing broke. allowed-tools did exactly what it is specified to do. It is just narrower than the name suggests.

The one-line version

allowed-tools pre-approves the listed tools for the turn that invokes the skill. That is the entire scope. The docs are explicit about the expiry: the grant "clears when you send your next message."

Invoking the skill again re-applies it for that turn. So /commit, then a follow-up message asking Claude to commit again, are two different worlds: the first is covered, the second is not.

The asymmetry that generates the bug report

Two things happen when a skill is invoked, and they have different lifetimes:

What Lifetime
The rendered SKILL.md content Enters the conversation as a single message and stays for the rest of the session
The allowed-tools grant Clears when you send your next message

Instructions persist. Permissions do not.

That mismatch is why two contradictory-sounding statements are both true: the skill is still loaded and the skill stopped working. Claude still has the instructions. It just lost the pre-approval, so it asks.

If you hit this, you have two honest options:

  • Re-invoke the skill. That re-applies the grant for the new turn.
  • Move the permission out of the skill entirely (see below).

It is a grant, not a fence

This is the part that trips people who read the field as a whitelist:

It does not restrict which tools are available: every tool remains callable, and your permission settings still govern tools that are not listed.

So a skill with allowed-tools: Read Grep has not been sandboxed to reading and grepping. Claude can still call Bash. The field only says "these specific ones don't need a prompt right now." Everything else falls back to your normal permission rules.

If you actually want a tool taken away, that is a different field.

disallowed-tools is the restricting half

disallowed-tools removes tools from Claude's available pool while the skill is active. It is the right field for an autonomous skill that should never call something — a background loop that must not stop to ask you a question, for example.

Two limits worth knowing:

  • It clears on your next message, same as the grant.
  • Like deny rules, it cannot remove EndConversation while any other tool remains.

To block a tool across all skills and prompts rather than one skill, use deny rules in your permission settings.

Picking the right place for the rule

What you want Where it goes How long it lasts
Skip prompts for a few commands when this skill runs allowed-tools in SKILL.md the invoking turn
Keep a tool away from one autonomous skill disallowed-tools in SKILL.md while the skill is active
Skip prompts for the whole session allow rules in permission settings your settings files
Block a tool everywhere deny rules in permission settings your settings files

The rule of thumb: frontmatter is for this invocation, settings are for this project or user. If you find yourself wanting allowed-tools to stick around, you have discovered that the rule belongs in settings.

The pattern that makes a bundled script run silently

Skills can ship their own scripts. The problem is that a hardcoded path breaks the moment the skill is installed somewhere else — and for a plugin skill, ${CLAUDE_SKILL_DIR} points at the skill's subdirectory inside the plugin, not the plugin root, so guessing is worse than it looks.

The fix is that Claude Code substitutes ${CLAUDE_SKILL_DIR} in two places: the skill's markdown content, and Bash rules in allowed-tools. Use the same variable in both and the allow rule matches the exact command the body tells Claude to run:

---
name: render-chart
description: Render a chart from a CSV file
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/render.sh *)
---

Run `${CLAUDE_SKILL_DIR}/scripts/render.sh <csv>` to render the chart.
Enter fullscreen mode Exit fullscreen mode

Installed at ~/.claude/skills/render-chart/, both occurrences expand to that directory, the rule matches, and the script runs without a prompt. Move the skill and it still matches, because neither side was hardcoded.

There is a sibling variable, ${CLAUDE_PROJECT_DIR}, for project-local scripts — that one needs Claude Code v2.1.196 or later.

The line to read before you trust a repository

For skills checked into a project's .claude/skills/ directory, allowed-tools takes effect after you accept the workspace trust dialog for that folder — the same as permission rules in .claude/settings.json.

Read that in reverse: a skill in a repo can grant itself broad tool access, and clicking "trust" is what turns it on. Reviewing the skills is part of reviewing the repository, not a separate chore.

A 10-second pass before trusting an unfamiliar project:

grep -rn "allowed-tools\|disallowed-tools" .claude/skills/ .claude/commands/ 2>/dev/null
Enter fullscreen mode Exit fullscreen mode

If a skill you did not write pre-approves Bash(* ) or anything close to it, that is worth a second look before you accept the dialog.

Checklist: "my skill keeps asking for permission"

  1. Did you send a message since invoking it? The grant is per-turn. Re-invoke.
  2. Does the rule match the command as written? Bash(git commit *) covers git commit -m "x", not a command routed through something else.
  3. Are you expecting it to restrict? It never did. That is disallowed-tools.
  4. Do you want it for the whole session? Then it belongs in permission settings, not in the skill.
  5. Is it a project skill in an untrusted folder? Nothing applies until the workspace trust dialog is accepted.

The mental model

allowed-tools is a receipt you hand over at the door, not a badge you wear all day. It gets you past the prompt for the turn you invoked the skill, and then it is spent. Everything durable — what Claude may do session-wide, what it may never do — lives in your permission settings.

Once you stop expecting the frontmatter to be a sandbox, the field becomes genuinely useful: it removes the friction from the two or three commands a skill actually needs, without quietly widening what Claude can do for the rest of the day.


Written while building Rulestack — packs of Cursor rules, Claude Code skills, and AGENTS.md setups for people who would rather not rediscover this by trial and error.

More notes like this as I find them: @ai-shop.bsky.social on Bluesky.

Top comments (0)