[BUG] Subagent frontmatter `disallowedTools` silently ignores MCP glob patterns (`mcp__<server>__*`) while `tools` honors them

Resolved 💬 1 comment Opened Jun 11, 2026 by doublefx Closed Jun 12, 2026

Preflight Checklist

  • [x] I have searched existing issues — #17928 reported this and was auto-closed as stale (locked), with the bot inviting a new issue. This is that new issue, with a fresh reproduction and a narrower scope: the allow side has since been fixed; the deny side is still broken.
  • [x] This is a single bug report
  • [x] Reproduced on Claude Code 2.1.170

What's Wrong?

In a custom agent's YAML frontmatter, the server-scoped glob mcp__<server>__* is honored in tools (allowlist) but silently ignored in disallowedTools (denylist). The bare server-prefix form (mcp__<server>) is silently ignored too. Only exact tool names work deny-side.

Verified empirically (each variant run as a Workflow-tool agentType agent; enforcement observed at the ToolSearch/deferred-tool layer):

| Frontmatter | Result |
|---|---|
| tools: Bash, Read, ToolSearch (no MCP) | ✅ all MCP invisible, as documented |
| tools: …, mcp__<srv>__<tool> (exact) | ✅ only that tool reachable |
| tools: …, mcp__<srv>__* (glob) | ✅ honored — all 8 server tools granted, other servers blocked |
| disallowedTools: mcp__<srv>__<tool> (exact) | ✅ holds — tool removed from index, siblings reachable |
| disallowedTools: mcp__<srv>__* (glob) | ❌ silently ignored — all 8 tools discoverable, schema loads, call succeeds |
| disallowedTools: mcp__<srv> (server prefix) | ❌ silently ignored — same |

Why this matters

  1. The failure is silent. A denylist that doesn't deny gives a false sense of confinement — the author believes the agent is cut off from a server, but every tool remains discoverable and callable, with no warning at agent load or call time.
  2. It contradicts the documented permission-rule grammar. The permissions docs state deny rules support tool-name globs ("*", "mcp__*"), and allow rules support globs after a literal server prefix (mcp__puppeteer__*). The frontmatter allowlist now follows that grammar; the frontmatter denylist does not.
  3. It's asymmetric within the same file. The same pattern string is honored on one line (tools) and ignored on the next (disallowedTools).

What Should Happen?

disallowedTools should support the same glob grammar as tools (and as permission deny rules) — or, at minimum, reject unsupported patterns loudly (startup warning naming the agent and the ignored entry) instead of silently granting access.

Steps to Reproduce

  1. Have any MCP server connected in the session (mine: a plugin MCP server with 8 tools).
  2. Create .claude/agents/deny-probe.md:

``yaml
---
name: deny-probe
description: Probe — server-wide MCP deny via glob
disallowedTools: mcp__<server>__*
---
Probe instructions: ToolSearch for the server's tools, load one schema, call it, report verbatim outcomes.
``

  1. Start a fresh session (the agent registry is a session-start snapshot) and invoke the agent (Agent tool or Workflow agentType).
  2. Observe: all the server's tools are returned by ToolSearch, the schema loads, and the call succeeds. Repeat with an exact tool name in disallowedTools to see the correct behavior (tool vanishes from the index).

Environment

  • Claude Code 2.1.170 (headless via Agent SDK; agents defined in .claude/agents/, exercised as Workflow agentType)
  • WSL2 Ubuntu
  • Reference: #17928 (original report, covered both sides; allow side fixed since, deny side reproduced today)

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗