Tool restrictions aren't just about capability—they're security boundaries. Treat tool access like production IAM: deny-all by default, allowlist only what each agent needs.
Tool Restrictions as Security Boundaries
[2025-12-09]: In multi-agent systems, tool restrictions aren't just about capability—they're security boundaries. Treat tool access like production IAM: deny-all by default, allowlist only what each subagent needs.
Principle: Each subagent should have the minimum tool set required for its role. This isn't just defense-in-depth—it also helps the agent stay focused on its domain by reducing distraction from irrelevant capabilities.
Common Patterns:
| Role | Tools | Rationale |
|---|---|---|
| Reviewer/Analyzer | Read, Grep, Glob | Read-only; can't accidentally modify files |
| Test Runner | Bash, Read, Grep | Execute tests and read results; no file editing |
| Builder/Implementer | Read, Edit, Write, Grep, Glob | Full modification access for implementation |
| Orchestrator | Task, Read, Glob | Routes work, has minimal direct access |
| Scout/Explorer | Read, Grep, Glob, WebFetch | Discovery only, no modification |
Implementation: In Claude Agent SDK, configure via YAML frontmatter (tools: [Read, Grep, Glob]) or the programmatic tools array. Filesystem definitions in .claude/agents/*.md make permissions visible and auditable.
Anti-Pattern: Giving all agents full tool access "for flexibility." This is the fastest path to unsafe autonomy. Instead:
- Require explicit confirmation for sensitive actions (git push, infrastructure changes)
- Restrict agents to relevant directories when possible
- Log tool usage for auditability
Production Lesson: Permission sprawl compounds. Start restrictive and expand only when you hit actual blockers. It's much easier to grant additional permissions than to clean up after an agent with too much access does something unexpected.
See Also:
- Orchestrator Pattern: Capability Minimization — How tool restriction becomes an architectural forcing function for delegation
Sources: Subagents in the SDK - Claude Docs, Claude Agent SDK Best Practices, Best practices for Claude Code subagents
MCP Tool Declarations in Frontmatter
[2025-12-09]: MCP (Model Context Protocol) tools extend agent capabilities beyond native tools. Declaring them in YAML frontmatter follows a consistent pattern across projects.
Naming Convention: mcp__<server>__<tool>
- Double underscores separate the three components
- Server names can include hyphens:
mcp__firecrawl-mcp__firecrawl_scrape - Examples:
mcp__playwright__browser_navigate(browser automation)mcp__supabase__execute_sql(database operations)mcp__kotadb__search_code(custom code search)
Two Frontmatter Fields (context-dependent):
tools:— Used in agent definitions to declare capabilitiesallowed-tools:— Used in commands to restrict available tools
Mixed Declarations: MCP tools combine naturally with native tools:
tools: mcp__playwright__browser_click, mcp__firecrawl-mcp__firecrawl_scrape, Write, Read, EditRole-Based MCP Assignment: Different agents get different MCP tool subsets:
| Role | MCP Tools | Purpose |
|---|---|---|
| Scout | search_code, list_recent_files |
Read-only exploration |
| Builder | search_code, analyze_change_impact |
Implementation support |
| Validator | browser_navigate, browser_snapshot, browser_click |
UI verification |
| Scraper | firecrawl_scrape, firecrawl_search |
Documentation fetching |
Permission Patterns at Settings Level: Glob patterns control MCP access without per-tool whitelisting:
{
"permissions": { "allow": ["mcp__kotadb__*", "mcp__playwright__*"] },
"enableAllProjectMcpServers": true
}Multi-Instance Pattern: Some projects separate staging and production:
mcp__kotadb-staging__search_codemcp__kotadb-production__search_code
Gap: Frontmatter only references tools by name—server configuration (endpoints, auth, schemas) happens elsewhere (.mcp.json, environment variables, or external config). The declaration pattern is separate from the instantiation pattern.
See Also:
- Claude Code: Subagent System — How MCP tools integrate with subagent definitions
- Orchestrator Pattern: Tool Assignment — Role-based MCP tool assignment in multi-agent workflows
Wildcard Permission Patterns
[2026-01-11]: Claude Code 2.1.0 introduced wildcard pattern matching for Bash tool permissions. This enables more flexible permission policies that reduce prompt fatigue while maintaining security boundaries.
Syntax: Bash(<pattern>) where * matches any characters
Examples:
| Pattern | Matches | Use Case |
|---|---|---|
Bash(npm *) |
npm install, npm run build, npm test |
Package management |
Bash(git *) |
git status, git commit, git push |
Version control |
Bash(docker compose *) |
docker compose up, docker compose down |
Container orchestration |
Bash(pytest *) |
pytest tests/, pytest -v |
Test execution |
Bash(make *) |
make build, make clean, make deploy |
Build automation |
Configuration in settings.json:
{
"permissions": {
"allow": [
"Bash(npm *)",
"Bash(git status)",
"Bash(git diff *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(pytest *)"
],
"deny": [
"Bash(git push *)",
"Bash(rm -rf *)"
]
}
}Security Considerations:
Wildcards expand the permission surface. Apply least-privilege principles:
- Prefer specific patterns:
Bash(npm run test)overBash(npm *) - Combine allow and deny: Allow broad patterns, deny dangerous subsets
- Test coverage: Verify patterns match intended commands before production use
Anti-Pattern: Over-Broad Wildcards
{
"permissions": {
"allow": ["Bash(*)"]
}
}This effectively disables Bash permission prompts entirely—equivalent to --dangerously-skip-permissions for shell commands. Defeats the purpose of permission controls.
Sources: Claude Code Changelog 2.1.0
Permission Bypass Vulnerabilities
[2026-01-17]: Security fixes in Claude Code 2.1.6-2.1.7 revealed attack surfaces in permission patterns that warranted documentation.
Line Continuation Injection
Shell allows command continuation with backslash. This can bypass single-line permission checks:
# Approved permission: Bash(git add)
# Attack vector:
git add \
&& rm -rf / # Continuation executes without restrictionMitigation: Claude Code 2.1.7 validates across line continuations, treating the entire multi-line command as a single unit for permission matching.
Glob Expansion Escapes
Wildcard patterns can expand beyond intended scope:
# Approved: Bash(rm temp/*)
# Risk: rm temp/* expands to include unexpected files
# if temp/ contains symlinks or unexpected entriesMitigation: Prefer explicit paths over wildcards for destructive operations. Filesystem state at execution time determines actual expansion.
Best Practices for Secure Permissions
- Prefer exact matches over wildcards:
Bash(git status)notBash(git *) - Test patterns in isolation before production deployment
- Layer defenses: combine allow-lists with explicit deny-lists
- Audit filesystem separately from permission string validation
- Log all permission decisions for post-incident analysis
Testing Permission Enforcement
Before trusting a permission pattern, verify behavior in a sandboxed environment:
# Test script: verify permission actually blocks
test_cases = [
("git add .\n&& rm -rf /", "should_block"), # line continuation
("rm ../outside/*", "should_block"), # path traversal
("git status", "should_allow"), # exact match
]Run permission tests in isolation before production use. Permission validation and filesystem state are separate concerns—test both.
Leading Questions
- How do you test that tool restrictions are actually enforced?
- What happens when an agent needs temporary elevated permissions?
- How do you handle tool access in development vs. production?
- When should tools fail loudly vs. silently when permission is denied?
- How do you audit tool usage patterns to detect permission issues?
Connections
- To Tool Selection: Restrictions affect what's selectable
- To Scaling Tools: MCP deployment patterns intersect with security
- To Orchestrator Pattern: Delegation as security enforcement
- To Production Concerns: Security in production environments