Skill allowed-tools doesn't grant permission for Bash commands

Open 💬 18 comments Opened Dec 21, 2025 by KyussCaesar

Description

When a skill defines allowed-tools in its SKILL.md frontmatter, the permission is reported as active but Bash commands matching the pattern are still denied.

Steps to Reproduce

  1. Create a skill with allowed-tools defined:
---
name: speak
description: Speak aloud using macOS TTS
allowed-tools: Bash(say -v "Samantha":*)
---
  1. Add Skill(speak) to the allow list in ~/.claude/settings.json:
{
  "permissions": {
    "allow": ["Skill(speak)", "Read"]
  }
}
  1. Run: claude -p 'demonstrate the speak skill' --output-format stream-json --verbose

Expected Behavior

The Bash command say -v "Samantha" "..." should be auto-allowed because:

  • Skill(speak) is in the allow list
  • The skill's allowed-tools grants Bash(say -v "Samantha":*)

Actual Behavior

The skill invocation succeeds and reports the correct allowedTools:

"tool_use_result": {
  "success": true,
  "commandName": "speak",
  "allowedTools": ["Bash(say -v \"Samantha\":*)"]
}

But the subsequent Bash call is denied:

{
  "type": "user",
  "message": {
    "content": [
      {
        "type": "tool_result",
        "content": "This command requires approval",
        "is_error": true
      }
    ]
  }
}

The command attempted matches the pattern exactly:

say -v "Samantha" "Hello Antony! This is a demonstration..."

Workaround

Adding Bash(say:*) directly to the global allow list works, but defeats the purpose of skill-scoped allowed-tools.

Environment

  • Claude Code version: 2.0.75
  • OS: macOS (Darwin 25.1.0)
  • Mode: -p (print/prompt mode)

Additional Notes

We also discovered that ~/.claude/settings.local.json doesn't seem to be a valid location for user settings — permissions placed there weren't loaded. Only ~/.claude/settings.json worked.

View original on GitHub ↗

18 Comments

github-actions[bot] · 5 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

zrosenbauer · 5 months ago

Still an issue.

Sasoon · 5 months ago

Yep still a problem on WSL2

mister-jack · 5 months ago

I'm observing this as well

Strernd · 5 months ago

Also experiencing this.

daspartho · 5 months ago
fablefactor · 5 months ago

Also experiencing this!

lhopki01 · 4 months ago

This happens to me too. allowed-tools seems to be completely ignored in tools.

silenvx · 4 months ago

Workaround: Added a PreToolUse hook to the SKILL.md frontmatter so it reads the allowed-tools patterns and auto-approves matching commands only while the skill is active.

---
name: my-skill
allowed-tools:
  - Bash(/path/to/cmd foo *)
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "$HOME/.claude/hooks/approve-skill-bash.py $HOME/.claude/skills/my-skill/SKILL.md"
---

https://gist.github.com/silenvx/297d9b0ac4ad4601e79b95707eea1351

Note: This implementation is optimized for my environment. Please use it as a reference and adapt it to your own setup.

EdanStarfire · 4 months ago

I'm curious on the "scoping" of the allowed-tools inside of a skill and am wondering if it's a misunderstanding of the intent.

It explicitly says that it's for usage within the scope of the skill, but what's the scope of the skill? When it's reading the skill or when the LLM has read it and is attempting to use the commands ad-hoc?

I'm suspecting the intent here might actually be that it's intended to be used for inline-only commands, and maybe not intended to be used by the LLM afterwards. For example:

Today's date is:
!`date`

versus

To get today's date, use the bash command: `date`

Is this just a mis-understanding of the intent of the allow-tools frontmatter?

lkiii · 4 months ago

The same issue in linux as well. I have created a sample skill to demonstrate this

---
name: test-skill
description: "Skill to test skills"
allowed-tools: "Bash(mkdir -p ~/.workspace-claude/tmp/*)"
disable-model-invocation: true
---

# Test skill

Create a dir with `test-dir` using `mkdir -p ~/.workspace-claude/tmp/test-dir`
yurukusa · 3 months ago

Workaround — PreToolUse hook to auto-approve commands that skills should have access to:

#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Auto-approve commands that your skill needs
if [ "$TOOL" = "Bash" ]; then
    # Add patterns your skill requires
    if echo "$CMD" | grep -qE '^\s*(npm|node|python|pip|git\s+(status|log|diff))'; then
        echo '{"decision":"approve"}'
        exit 0
    fi
fi
exit 0

Register in ~/.claude/settings.json — user-level hooks apply to all contexts including skills. This bypasses the allowed-tools permission system entirely.

npx cc-safe-setup includes a cd-git-allow hook that auto-approves common read-only commands that frequently trigger permission prompts in skills.

eko234 · 3 months ago

still suffering with this shi

alanbchristie · 3 months ago

Yes, using claude v2.1.92 (Sonnet 4.6) and although there is an example in the official documentation's Advanced patterns section (see https://code.claude.com/docs/en/skills#advanced-patterns): -

---
name: pr-summary
description: Summarize changes in a pull request
context: fork
agent: Explore
allowed-tools: Bash(gh *)
---

Addign the follwing to _my_ SKILL.md allowed-tools list has no effect: -

allowed-tools:
- Bash(docker compose down)

I'm still prompted for permission.

Adding the following works...

allowed-tools:
- Bash

But that's far to permissive!

alanbchristie · 3 months ago

Funnily enough when I ask _claude_ "Can you add Bash(docker *) to my deploy skill?" it adds Bash(docker *) to the allowed-tools but it doesn't work. When I then ask claude "why are you asking me for approval of docker compose down when it's an allowed tool in my deploy skill?" it replies with..

[...] the skill's allowed-tools declarations aren't being honoured by the harness —     
  that's the root cause.

I can still run the deployment, you'd just need to approve each tool call manually. Want me to proceed?
alanbchristie · 3 months ago

Basically allowed-tools does not honour specific Bash commands, even though it is documented.

grosser · 2 months ago

FYI this worked for me:

allowed-tools: 
- Read
- Bash(gh *)
- Bash(say *)

without it I got asked for gh permissions and with it it just did it

6farer · 1 month ago

hey. anthropic team. this is a core functionality of your skills ecosystem. is there any idea on when this could be resolved? it would simplify usage without having to run prehook scripts.