Sub-agents can hold MCP servers the parent session doesn't

Claude Code sub-agents can hold MCP servers the parent lacks. Covers tools, disallowedTools, and mcpServers frontmatter.

Sub-agents can hold MCP servers the parent session doesn't
Share

Anthropic's answer to "how do I build a team of Claude agents" isn't a framework — it's a ladder of primitives, and the first genuinely useful rung is a sub-agent that can hold MCP servers the parent session never touches.

What mcpServers gives a Claude Code sub-agent

What mcpServers gives a Claude Code sub-agent (source: cdn.prod.website-files.com)

A Claude Code sub-agent can declare its own MCP servers in frontmatter, and the parent session never loads them. According to Anthropic's sub-agents documentation, an inline server in a sub-agent's mcpServers field connects when the sub-agent starts and disconnects when it finishes . That keeps the server's tool descriptions and token overhead out of the main conversation's context window — the parent stays lean while the specialist gets equipped.

There are two ways to list a server. An inline entry spins up a fresh connection scoped to the sub-agent's run; a string reference reuses an already-named session connection instead of starting a new process . The rest of the definition is minimal by design: a sub-agent is a Markdown file in .claude/agents/, and only name and description are required. Everything else — mcpServers, tools, disallowedTools, model, permissionMode, maxTurns — is optional .

Anthropic's canonical example is a browser-tester sub-agent given an inline Playwright MCP server plus a referenced GitHub server. Defining Playwright inline keeps its tool descriptions out of the parent context while still handing the sub-agent full browser control . The relationship is easy to model — the parent's server list stays empty while the sub-agent carries its own:

from dataclasses import dataclass


@dataclass(frozen=True)
class MCPServer:
    name: str


@dataclass(frozen=True)
class Session:
    name: str
    mcp_servers: tuple[MCPServer, ...] = ()


parent = Session("parent")
sub_agent = Session("sub-agent", (MCPServer("private-filesystem"),))

print(f"{parent.name} MCP servers: {[s.name for s in parent.mcp_servers]}")
print(f"{sub_agent.name} MCP servers: {[s.name for s in sub_agent.mcp_servers]}")

assert not parent.mcp_servers
assert sub_agent.mcp_servers[0] not in parent.mcp_servers

That snippet is a small illustrative model (verified to run, printing an empty parent list and ['private-filesystem'] for the sub-agent) — not the real Claude Code internals, but an accurate picture of the isolation the mcpServers field buys you.

Building a sub-agent definition: mcpServers, tools, and disallowedTools

Building a sub-agent definition: mcpServers, tools, and disallowedTools

A sub-agent is a single Markdown file with YAML frontmatter. Drop it at .claude/agents/<name>.md for project scope or ~/.claude/agents/<name>.md for user scope. Only name and description are required . On name conflicts, project definitions win over user ones, and the full resolution order is managed settings → --agents CLI → project → user → plugin .

PrecedenceSource
1Managed settings
2--agents CLI flag
3Project .claude/agents/
4User ~/.claude/agents/
5Plugin agents/

Two frontmatter fields control the tool pool. tools is an allowlist — only the tools you list are reachable — and disallowedTools is a denylist that removes tools the sub-agent would otherwise inherit . Both accept MCP server-level patterns: mcp__<server> targets one server, mcp__<server>__* matches all of its tools, and mcp__* grants or revokes every MCP server in one entry . Pair these with mcpServers when you want to add a server the parent lacks, and narrow tools to keep the grant tight.

The description field is not cosmetic — it drives automatic delegation. Claude reads it to match incoming work to a sub-agent, so write explicit trigger keywords for the tasks you want routed here rather than a vague summary .

Beyond those, the optional frontmatter is broad: mcpServers (inline object or string reference), tools, disallowedTools, model (use inherit to mirror the parent), permissionMode, maxTurns, hooks, skills, memory, background, isolation, and color . Keep the surface small; the docs caution against making every task its own dedicated agent, since more options make delegation less accurate .

Where this breaks: agent teams don't inherit a sub-agent's MCP assignments

Screenshot of https://code.claude.com/docs/en/agent-teams

Agent teams are a different rung of the ladder, and per-agent inline MCP routing does not survive the jump. Enable the experimental layer by setting CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 in settings.json or the environment . One session becomes the immutable lead and spawns teammates; each teammate is a full, independent Claude Code session with its own context window, coordinating through a shared task list and a peer-to-peer mailbox — not an in-session delegate reporting back to the main agent .

Here is the catch. When you reuse a .claude/agents/ definition as a teammate, its tools allowlist and model are honored — but its mcpServers and skills frontmatter are not applied. Teammates load MCP servers from project and user settings like any normal session, plus CLAUDE.md context, and they do not inherit the lead's conversation history .

The practical consequence: the inline mcpServers field you wrote for the sub-agent path is a sub-agent and main-thread --agent feature only. The full peer-team layer reads .mcp.json, not individual agent frontmatter — a confirmed limit as of v2.1.219 (July 24, 2026) . If a teammate needs a private MCP server, put it in project or user scope, not the agent file.

Other team-layer limits to plan around, current as of July 2026: no session resumption for in-process teammates, one team per session, no nested teams, a fixed lead, and no per-teammate permission modes at spawn . Sub-agents also became background-by-default in v2.1.198 (July 1, 2026), so behavior differs from earlier releases .

Allowlist wildcards and team coordination: what to try next

To expose a single tool from a large MCP server without granting the rest, pair a narrow tools allowlist with a wildcard denylist. The pattern mcp__<server>__* in disallowedTools removes a whole server family at once, so you can permit one specific tool and block everything else that server ships . This keeps least-privilege enforceable even as an upstream server adds tools you never reviewed.

Deferred MCP tool loading is on by default: at startup only tool names and server instructions load, and full schemas resolve on demand. Set ENABLE_TOOL_SEARCH=auto:N for threshold control; tool_reference blocks require Sonnet 4.5, Haiku 4.5, or Opus 4.5 or later . This is why a sub-agent can carry many MCP tools without bloating context.

If you do move to agent teams, coordination runs through two primitives instead of per-agent MCP wiring: a shared task list (pending / in-progress / completed with dependency tracking) and a mailbox for direct peer messaging. Teammates self-claim the next unblocked task rather than waiting on the lead .

Anthropic's recommended ceiling is 3–5 teammates with roughly 5–6 tasks each, and because every teammate is a separate Claude instance, token cost scales roughly linearly with team size . The takeaway: for sequential or single-file work, a Markdown sub-agent with its own mcpServers is the cheaper, stable path — reserve teams for genuinely parallel, multi-lens work.

Frequently asked questions

Can a Claude Code sub-agent use MCP servers not listed in the parent's .mcp.json?

Yes. The mcpServers frontmatter field in a .claude/agents/*.md file can define inline server configs — which connect when the sub-agent starts and disconnect when it finishes — or reference existing named connections by string to reuse a session's live connection . The parent session never loads those tool descriptions, so a sub-agent can hold servers the parent lacks entirely.

What's the difference between tools and disallowedTools in a sub-agent definition?

tools is an allowlist — only the tools you list are available to the sub-agent, and everything else is withheld. disallowedTools is a denylist — every inherited tool is available except the ones you name . Both accept MCP patterns such as mcp__<server>, mcp__<server>__*, and mcp__*, so you can grant or remove an entire server family in a single entry.

Do agent-team teammates get the mcpServers defined in a sub-agent file?

No. When a sub-agent definition is reused as a teammate under CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1, its tools allowlist and model are honored, but the mcpServers and skills frontmatter are ignored . Teammates load MCP servers from project and user settings exactly like a normal session, so inline per-agent MCP routing does not carry into a team.

How does the description field control which tasks get routed to a sub-agent?

Claude reads the description field to match incoming work to the right sub-agent for automatic delegation . Pack it with explicit trigger keywords that name the tasks you want dispatched there — more specific phrasing produces more reliable routing. The docs also warn against making everything a dedicated agent, since more options make delegation less accurate.

Does an inline MCP server in a sub-agent affect the parent session's context window?

No — that is the architectural point. In Anthropic's documented browser-tester example, defining a Playwright server inline keeps its tool descriptions out of the parent's context while still equipping the sub-agent . This lets you attach a specialized protocol layer to one worker without adding token overhead to the main session.

Enjoyed this article? Subscribe to get new stories by email whenever they're published.

Subscribe