Start dormant MCP servers for hook calls (#55263)

## Why

MCP tool hooks need to invoke model-hidden tools even when a subagent's cached server has not started. Their timeout must also cover server startup.

## What changed

- Let cached servers with allowed model-hidden tools remain dormant until called.
- Wait for the selected server when executing an MCP hook, leaving unrelated cached servers dormant, and apply the hook timeout to the entire operation.
- Capture tool namespace attribution after startup and retain it only while catalog revisions remain unchanged.
- Validate the server, environment, and tool filter before starting a server or capturing attribution.

## Testing

Extend integration coverage for cached subagent hooks under both startup readiness modes, startup timeouts without context injection, and namespace attribution for hook and token-budget context while keeping tools hidden from the model. Verify that a direct call starts a cached server whose tools are hidden from model exposure.

GitOrigin-RevId: 7a54b69aa2515722aeb925e03e1667bf63aa67ca
This commit is contained in:
Charlie Marsh
2026-10-19 11:40:15 +00:00
committed by copyberry
parent 71ab2ea77b
commit cc44ffb3e2
6 changed files with 413 additions and 52 deletions
+9 -6
View File
@@ -1,5 +1,6 @@
use std::sync::Arc;
use anyhow::Context;
use anyhow::bail;
use codex_hooks::HookMcpCall;
use codex_hooks::HookMcpExecutor;
@@ -25,18 +26,20 @@ impl HookMcpExecutor for CoreHookMcpExecutor {
Value::String(self.thread_id.to_string()),
);
let call_result = self
.runtime
.latest_call_tool(
let call_result = tokio::time::timeout(
call.timeout,
self.runtime.latest_call_tool(
&call.server,
&call.tool,
call.environment_id.as_deref(),
Some(Value::Object(call.input)),
Some(Value::Object(metadata)),
Some(call.timeout),
/*wait_for_server*/ false,
)
.await?;
/*wait_for_server*/ true,
),
)
.await
.context("MCP hook timed out")??;
let result = call_result.result;
let text = result
.content