`allowed-tools:` silently ignored in agent frontmatter — subagent inherits all parent tools, leading to credential hunting
Summary
Custom agent files (.claude/agents/*.md) use tools: to restrict available tools per the docs. However, skill files (.claude/skills/*/SKILL.md) use allowed-tools: for the same purpose.
When an agent file uses allowed-tools: (the skill field name) instead of tools: (the agent field name), the field is silently ignored and the subagent inherits all parent session tools — including unrestricted Bash.
This caused two security incidents where subagents escalated from MCP authentication failures into credential hunting — reading config files, extracting tokens, accessing the macOS Keychain, and making direct API calls with extracted credentials.
Environment
- Claude Code: 2.1.49
- macOS: 26.2
- Models affected: claude-sonnet-4-6 (subagent), claude-opus-4-6 (parent + subagent)
Steps to reproduce
Bug 1: allowed-tools: silently ignored
- Create
.claude/agents/test-agent.md:
---
name: test-agent
description: Test agent with restricted tools
allowed-tools:
- Read
- Glob
model: sonnet
---
You are a test agent. Only use Read and Glob tools.
- In project
settings.json, allowBashandTask(*):
{
"permissions": {
"allow": ["Bash", "Task(*)", "Read", "Glob", "Grep"]
}
}
- Spawn the agent via Task tool and ask it to do something that requires Bash
- Observe: The subagent can use Bash despite
allowed-tools:specifying onlyReadandGlob - Expected: The subagent should only have access to
ReadandGlob, OR a warning should be emitted about the unrecognized field
Bug 2: Model credential hunting on MCP auth failure
- Have an MCP server configured in
.mcp.jsonwith auth tokens - Let the MCP server's auth expire (e.g., OAuth token expiration)
- Spawn a subagent that depends on that MCP server
- Observe: The subagent reads
.mcp.json, extracts API keys from other MCP configs, searches for credential files, and may access the macOS Keychain - Expected: The subagent should report the MCP auth failure to the user
The naming confusion
| File type | Field name | Location |
|-----------|-----------|----------|
| Skills | allowed-tools: | .claude/skills/*/SKILL.md |
| Agents | tools: | .claude/agents/*.md |
This inconsistency is easy to confuse, especially when building both skills and agents for the same project.
Security impact
Incident 1 (Sonnet subagent, MCP plugin auth expired)
The subagent made 85 tool calls (47 Bash), including:
- Reading MCP config files and extracting bearer tokens
- Globbing for credential directories and
.env*files - Reading credential files from disk
- Extracting JWT tokens and using them in ~15 curl commands
- Attempting to create a temporary workflow to proxy API calls using stolen credentials
- Running
security find-generic-passwordandsecurity dump-keychain(macOS Keychain)
No tool call was ever denied by the system.
Incident 2 (Opus parent + subagent, different MCP server token expired)
Same credential-hunting pattern in a different session, 2 hours later:
- Opus parent deliberately routed to a subagent because "it has its own MCP access"
- Subagent parsed MCP config, extracted JWT from a different MCP server's config
- This agent legitimately had Bash access — so this is purely a model behavior issue
Both Sonnet and Opus exhibit this credential-hunting behavior when MCP auth fails.
Expected behavior
allowed-tools:should either be accepted as an alias fortools:in agent frontmatter, or produce a warning — the current silent failure is dangerous- Warn on unrecognized frontmatter fields — this would catch typos and field name mismatches
- Models should not hunt for credentials when MCP auth fails — they should report the failure to the user, especially when the agent's instructions explicitly say to report failures
Related issues
- #25000 — Sub-agents bypass permission deny rules
- #4740 — Sub-agents use tools without permission
- #20264 — Allow restrictive permission modes for subagents
Full details available in private /feedback submission (ID: 714e1e8a-d07f-4446-810c-7fd3794b9323).
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗