feat(subagents): inherit MCP tools in child runtimes (#2422)

Harvested from #2377 with thanks to @buko.

Threads the parent MCP tool pool into child SubAgentRuntime construction and registers MCP-backed tools for child agents when MCP is enabled, while leaving the broader mention-browser/provider/config work for focused follow-ups.

Validation:
- cargo fmt --all -- --check
- CARGO_TARGET_DIR=/Volumes/VIXinSSD/codewhale-pr2377-target cargo test -p codewhale-tui tools::subagent
- CARGO_TARGET_DIR=/Volumes/VIXinSSD/codewhale-target/harvest-2377-recheck cargo test -p codewhale-tui tools::subagent --all-features
This commit is contained in:
Hunter Bown
2026-05-31 03:16:53 -07:00
committed by GitHub
parent 4085f71e2b
commit 58e45d384e
4 changed files with 44 additions and 12 deletions
+14
View File
@@ -697,6 +697,12 @@ impl Engine {
continue;
};
let mcp_pool = if self.config.features.enabled(Feature::Mcp) {
self.ensure_mcp_pool().await.ok()
} else {
None
};
let mut runtime = SubAgentRuntime::new(
client,
self.session.model.clone(),
@@ -714,6 +720,7 @@ impl Engine {
)
.with_max_spawn_depth(self.config.max_spawn_depth)
.with_step_api_timeout(self.config.subagent_api_timeout)
.with_mcp_pool(mcp_pool)
.background_runtime();
let route = resolve_subagent_assignment_route(
&runtime,
@@ -1179,6 +1186,12 @@ impl Engine {
None
};
let mcp_pool = if self.config.features.enabled(Feature::Mcp) {
self.ensure_mcp_pool().await.ok()
} else {
None
};
let tool_registry = match mode {
AppMode::Agent | AppMode::Yolo => {
if self.config.features.enabled(Feature::Subagents) {
@@ -1199,6 +1212,7 @@ impl Engine {
)
.with_max_spawn_depth(self.config.max_spawn_depth)
.with_step_api_timeout(self.config.subagent_api_timeout)
.with_mcp_pool(mcp_pool.clone())
.with_parent_completion_tx(self.tx_subagent_completion.clone());
if let Some(context) = fork_context_for_runtime.clone() {
rt = rt.with_fork_context(context);
-1
View File
@@ -777,7 +777,6 @@ impl ToolRegistryBuilder {
/// MCP tools are marked `defer_loading` by default (except discovery
/// helpers) to keep the model-visible catalog compact.
#[must_use]
#[allow(dead_code)]
pub fn with_mcp_tools(
mut self,
mcp_pool: std::sync::Arc<tokio::sync::Mutex<crate::mcp::McpPool>>,
+29 -11
View File
@@ -786,6 +786,8 @@ pub struct SubAgentRuntime {
pub parent_completion_tx: Option<mpsc::UnboundedSender<SubAgentCompletion>>,
/// Snapshot of the request prefix visible to an opt-in forked child.
pub fork_context: Option<SubAgentForkContext>,
/// The parent's MCP pool if available.
pub mcp_pool: Option<std::sync::Arc<tokio::sync::Mutex<crate::mcp::McpPool>>>,
/// Per-step DeepSeek API timeout for the child's `create_message` call.
/// Resolved from `[subagents] api_timeout_secs` (clamped to 1..=1800) at
/// engine construction so a slow but legitimate model turn does not
@@ -825,10 +827,21 @@ impl SubAgentRuntime {
mailbox: None,
parent_completion_tx: None,
fork_context: None,
mcp_pool: None,
step_api_timeout: DEFAULT_STEP_API_TIMEOUT,
}
}
/// Attach an MCP pool so the subagent can execute MCP tools.
#[must_use]
pub fn with_mcp_pool(
mut self,
pool: Option<std::sync::Arc<tokio::sync::Mutex<crate::mcp::McpPool>>>,
) -> Self {
self.mcp_pool = pool;
self
}
/// Override the per-step DeepSeek API timeout (default
/// `DEFAULT_STEP_API_TIMEOUT`). Called by the engine after reading
/// `[subagents] api_timeout_secs`. Tests may use this to fail fast
@@ -959,6 +972,7 @@ impl SubAgentRuntime {
mailbox: self.mailbox.clone(),
parent_completion_tx: self.parent_completion_tx.clone(),
fork_context: self.fork_context.clone(),
mcp_pool: self.mcp_pool.clone(),
step_api_timeout: self.step_api_timeout,
}
}
@@ -4762,17 +4776,21 @@ impl SubAgentToolRegistry {
// review, RLM, sub-agent management (so grandchildren can spawn),
// plus per-child fresh todo/plan state.
let context = runtime.context.clone();
let registry = ToolRegistryBuilder::new()
.with_full_agent_surface(
Some(runtime.client.clone()),
runtime.model.clone(),
runtime.manager.clone(),
runtime.clone(),
runtime.allow_shell,
todo_list,
plan_state,
)
.build(context);
let mut registry = ToolRegistryBuilder::new().with_full_agent_surface(
Some(runtime.client.clone()),
runtime.model.clone(),
runtime.manager.clone(),
runtime.clone(),
runtime.allow_shell,
todo_list,
plan_state,
);
if let Some(pool) = runtime.mcp_pool.as_ref() {
registry = registry.with_mcp_tools(std::sync::Arc::clone(pool));
}
let registry = registry.build(context);
Self {
allowed_tools: explicit_allowed_tools,
+1
View File
@@ -1736,6 +1736,7 @@ fn stub_runtime() -> SubAgentRuntime {
mailbox: None,
parent_completion_tx: None,
fork_context: None,
mcp_pool: None,
step_api_timeout: DEFAULT_STEP_API_TIMEOUT,
}
}