Skip to content

[Bug] Sub-agents opened via agent_open do not have access to MCP tools #2362

@buko

Description

@buko

[Bug] Sub-agents opened via agent_open do not have access to MCP tools

Problem

agent_open sub-agents cannot use MCP tools (Brave Search, Tavily, or any other MCP servers configured by the parent session). The parent has full MCP tool access, but sub-agents receive only native CodeWhale tools (web_search, fetch_url, file ops, shell, etc.). This cripples multi-agent architectures that depend on sub-agents performing high-quality web research.

Claude Code does not have this limitation. The reference implementation explicitly states: "Subagents inherit the internal tools and MCP tools available in the main conversation by default." Claude Code allows restricting access via tools (allowlist) or disallowedTools (denylist), and even supports defining MCP servers inline in sub-agent frontmatter so that only the sub-agent — not the parent — sees specific tools.

Experimental confirmation: Three independent probes confirmed zero MCP tools in sub-agent inventory. Probes were run with both fork_context=false (fresh child) and fork_context=true (forked context). Neither mode includes mcp__-prefixed tools, list_mcp_resources, or tool_search_tool_regex in the sub-agent's tool registry.

Impact: Users who configure MCP servers (Brave Search, Tavily, GitHub, etc.) cannot leverage sub-agents for parallel research. The parent session must run all MCP searches serially, inflating context and preventing the parallel sub-agent architecture that Claude Code users rely on.

Proposed Solution

Implement a relay model that gives sub-agents access to MCP tools without requiring new MCP connections per sub-agent:

Code Changes

1. In the agent_open handler: When building the sub-agent's tool definition list, include MCP tool schemas from the parent's active MCP server connections. The parent's tool registry already holds these schemas — they need to be serialized into the sub-agent's tool definitions.

// Pseudocode for the agent_open handler
let mut subagent_tools = native_tools.clone();

// NEW: include MCP tool schemas from parent
for server in parent_session.mcp_servers.active() {
    for tool in server.list_tools() {
        subagent_tools.push(tool.with_prefix(format!("mcp__{}__", server.name)));
    }
}

let subagent = SubAgent::new(subagent_tools, prompt);

2. In the tool execution dispatch: When a sub-agent invokes an MCP tool, intercept the call. The parent runtime executes the call through its own active MCP server connections and returns the result to the sub-agent.

// Pseudocode for tool dispatch
fn execute_subagent_tool(call: ToolCall, parent_session: &Session) -> ToolResult {
    if call.name.starts_with("mcp__") {
        // Relay through parent's MCP connections
        let (server_name, tool_name) = parse_mcp_tool_name(&call.name);
        let server = parent_session.mcp_servers.get(&server_name)?;
        server.call_tool(tool_name, call.arguments)
    } else {
        // Native tool dispatch (existing behavior)
        native_dispatch(call)
    }
}

3. (Optional) Add an mcp_servers allowlist parameter to agent_open:

{
  "name": "investigator",
  "prompt": "Research this topic thoroughly",
  "mcp_servers": ["brave-search", "tavily"]
}

When mcp_servers is specified, only those servers' tools are exposed to the sub-agent. When omitted, all MCP tools are inherited (matching Claude Code's default).

4. Update the agent_open tool description to document MCP tool behavior.

Files Likely Involved

File Change
agent_open handler Add MCP tool schemas to sub-agent tool definition list
Tool execution dispatch Intercept MCP tool calls and relay through parent connections
MCP client layer Expose MCP tool schemas for sub-agent context serialization
agent_open tool description Document mcp_servers parameter if added

Scope

  • 3-4 files changed
  • Additive — no existing behavior changes for sub-agents that don't call MCP tools
  • No transport refactoring — the relay model avoids requiring sub-agents to open their own MCP connections
  • No breaking changes — native tool behavior is preserved

Alternatives Considered

  • Require sub-agents to open their own MCP connections — This would require migrating MCP servers from stdio to Streamable HTTP transport (only HTTP supports multi-session). It also requires each sub-agent to incur the connection/initialization overhead. The relay model avoids both costs.
  • Allowlist-only (no default inheritance) — Safer but deviates from Claude Code's behavior. The Claude Code default (inherit all, restrict with allowlist) is more useful and proven in production.
  • System prompt rule — "Use Brave Search MCP tools when searching" — Ineffective. The investigation into MCP tool preference (see related issues below) demonstrated that global system prompt rules are routinely ignored. This problem requires a structural fix, not a prompt workaround.

Context

This is part of a broader investigation into MCP tool preference in CodeWhale. The user has configured both Brave Search and Tavily MCP servers and found that:

  1. Models default to web_search/fetch_url instead of MCP tools (problem addressed separately via Agent Skills)
  2. Sub-agents cannot use MCP tools at all (this issue)
  3. Both problems compound to make reliable web research impossible via sub-agents

The MCP specification technically supports multi-session access (many-to-one: multiple clients sharing one server), but only over Streamable HTTP transport. Most MCP servers — including Brave Search and Tavily — are configured with stdio transport, which is process-scoped. The relay model is the correct solution because it avoids requiring transport changes.

Claude Code Reference

From code.claude.com/docs/en/sub-agents:

"Subagents inherit the internal tools and MCP tools available in the main conversation by default."

"To restrict tools, use either the tools field (allowlist) or the disallowedTools field (denylist)."

"Inline definitions use the same schema as .mcp.json server entries. To keep an MCP server out of the main conversation entirely and avoid its tool descriptions consuming context there, define it inline here. The subagent gets the tools; the parent conversation does not."

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationImprovements or additions to documentation

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions