{"slug": "claude-code-skill-allowed-tools-a-one-turn-grant-not-a-sandbox", "title": "Claude Code skill allowed-tools: a one-turn grant, not a sandbox", "summary": "Anthropic's Claude Code skill system grants `allowed-tools` permissions only for the turn that invokes the skill, not for the entire session, which can confuse users expecting a persistent whitelist. The field pre-approves tools solely for that invocation, while instructions persist; `disallowed-tools` restricts tools while the skill is active. Developers should use permission settings for session-wide rules and can leverage `${CLAUDE_SKILL_DIR}` in both markdown and Bash rules to ensure scripts run without prompts.", "body_md": "You add `allowed-tools: Bash(git add *) Bash(git commit *)`\n\nto a skill. You type `/commit`\n\n, and Claude stages and commits without asking. You send one more message, ask for another commit — and the permission prompt is back.\n\nNothing broke. `allowed-tools`\n\ndid exactly what it is specified to do. It is just narrower than the name suggests.\n\n`allowed-tools`\n\npre-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.\"\n\nInvoking the skill again re-applies it for that turn. So `/commit`\n\n, then a follow-up message asking Claude to commit again, are two different worlds: the first is covered, the second is not.\n\nTwo things happen when a skill is invoked, and they have different lifetimes:\n\n| What | Lifetime |\n|---|---|\nThe rendered `SKILL.md` content |\nEnters the conversation as a single message and stays for the rest of the session |\nThe `allowed-tools` grant |\nClears when you send your next message |\n\nInstructions persist. Permissions do not.\n\nThat 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.\n\nIf you hit this, you have two honest options:\n\nThis is the part that trips people who read the field as a whitelist:\n\nIt does not restrict which tools are available: every tool remains callable, and your permission settings still govern tools that are not listed.\n\nSo a skill with `allowed-tools: Read Grep`\n\nhas **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.\n\nIf you actually want a tool taken away, that is a different field.\n\n`disallowed-tools`\n\nis the restricting half\n`disallowed-tools`\n\nremoves 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.\n\nTwo limits worth knowing:\n\n`EndConversation`\n\nwhile any other tool remains.To block a tool across all skills and prompts rather than one skill, use deny rules in your permission settings.\n\n| What you want | Where it goes | How long it lasts |\n|---|---|---|\n| Skip prompts for a few commands when this skill runs |\n`allowed-tools` in `SKILL.md`\n|\nthe invoking turn |\n| Keep a tool away from one autonomous skill |\n`disallowed-tools` in `SKILL.md`\n|\nwhile the skill is active |\n| Skip prompts for the whole session | allow rules in permission settings | your settings files |\n| Block a tool everywhere | deny rules in permission settings | your settings files |\n\nThe rule of thumb: frontmatter is for *this invocation*, settings are for *this project or user*. If you find yourself wanting `allowed-tools`\n\nto stick around, you have discovered that the rule belongs in settings.\n\nSkills 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}`\n\npoints at the skill's subdirectory inside the plugin, not the plugin root, so guessing is worse than it looks.\n\nThe fix is that Claude Code substitutes `${CLAUDE_SKILL_DIR}`\n\nin **two** places: the skill's markdown content, and Bash rules in `allowed-tools`\n\n. Use the same variable in both and the allow rule matches the exact command the body tells Claude to run:\n\n```\n---\nname: render-chart\ndescription: Render a chart from a CSV file\nallowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/render.sh *)\n---\n\nRun `${CLAUDE_SKILL_DIR}/scripts/render.sh <csv>` to render the chart.\n```\n\nInstalled at `~/.claude/skills/render-chart/`\n\n, 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.\n\nThere is a sibling variable, `${CLAUDE_PROJECT_DIR}`\n\n, for project-local scripts — that one needs Claude Code v2.1.196 or later.\n\nFor skills checked into a project's `.claude/skills/`\n\ndirectory, `allowed-tools`\n\ntakes effect after you accept the workspace trust dialog for that folder — the same as permission rules in `.claude/settings.json`\n\n.\n\nRead 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.\n\nA 10-second pass before trusting an unfamiliar project:\n\n```\ngrep -rn \"allowed-tools\\|disallowed-tools\" .claude/skills/ .claude/commands/ 2>/dev/null\n```\n\nIf a skill you did not write pre-approves `Bash(* )`\n\nor anything close to it, that is worth a second look before you accept the dialog.\n\n`Bash(git commit *)`\n\ncovers `git commit -m \"x\"`\n\n, not a command routed through something else.`disallowed-tools`\n\n.`allowed-tools`\n\nis 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.\n\nOnce 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.\n\n*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.*\n\n*More notes like this as I find them: @ai-shop.bsky.social on Bluesky.*", "url": "https://wpnews.pro/news/claude-code-skill-allowed-tools-a-one-turn-grant-not-a-sandbox", "canonical_source": "https://dev.to/rulestack/claude-code-skill-allowed-tools-a-one-turn-grant-not-a-sandbox-27p4", "published_at": "2026-08-09 14:49:21+00:00", "updated_at": "2026-08-09 15:18:02.233782+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["Claude Code", "Anthropic"], "alternates": {"html": "https://wpnews.pro/news/claude-code-skill-allowed-tools-a-one-turn-grant-not-a-sandbox", "markdown": "https://wpnews.pro/news/claude-code-skill-allowed-tools-a-one-turn-grant-not-a-sandbox.md", "text": "https://wpnews.pro/news/claude-code-skill-allowed-tools-a-one-turn-grant-not-a-sandbox.txt", "jsonld": "https://wpnews.pro/news/claude-code-skill-allowed-tools-a-one-turn-grant-not-a-sandbox.jsonld"}}