Build MCP tool runtimes before router planning (#33149)

## What changed

- Convert filtered MCP tool metadata into `CoreToolRuntime` instances before constructing the tool router.
- Pass those runtimes through the shared tool-planning path instead of maintaining separate direct and deferred MCP tool lists.
- Preserve MCP visibility, app policy filtering, and direct versus deferred exposure behavior.

GitOrigin-RevId: 67f7fbeecc2992318c859e038cd97938833911db
This commit is contained in:
jif authored and copyberry committed 2026-07-14 20:36:29 +00:00
1 parent eb80df0317
commit 1447cee36b
9 files changed
+141 -190

No files matched your search

+17 -22
View File
@@ -4,6 +4,8 @@ use crate::config::Config;
use crate::session::step_context::StepContext;
use crate::session::tests::make_session_and_context;
use crate::tools::context::ToolPayload;
use crate::tools::handlers::McpHandler;
use crate::tools::registry::CoreToolRuntime;
use crate::turn_diff_tracker::TurnDiffTracker;
use codex_extension_api::ExtensionData;
use codex_extension_api::ExtensionRegistry;
@@ -105,21 +107,14 @@ fn extension_tool_test_registry() -> Arc<ExtensionRegistry<Config>> {
#[tokio::test]
async fn parallel_support_does_not_match_namespaced_local_tool_names() -> anyhow::Result<()> {
let (session, turn) = make_session_and_context().await;
let (_, turn) = make_session_and_context().await;
let turn = Arc::new(turn);
let step_context = StepContext::for_test(Arc::clone(&turn));
let mcp_tools = session
.services
.latest_mcp_runtime()
.manager()
.list_all_tools()
.await;
let router = ToolRouter::from_context(
step_context.as_ref(),
ToolRouterParams {
tool_suggest_candidates: None,
deferred_mcp_tools: None,
mcp_tools: Some(mcp_tools),
tool_runtimes: Vec::new(),
extension_tool_executors: Vec::new(),
dynamic_tools: turn.dynamic_tools.as_slice(),
},
@@ -217,21 +212,20 @@ async fn mcp_parallel_support_uses_handler_data() -> anyhow::Result<()> {
step_context.as_ref(),
ToolRouterParams {
tool_suggest_candidates: None,
deferred_mcp_tools: None,
mcp_tools: Some(vec![
mcp_tool_info(
tool_runtimes: vec![
mcp_runtime(mcp_tool_info(
"echo",
/*supports_parallel_tool_calls*/ true,
"mcp__echo__",
"query_with_delay",
),
mcp_tool_info(
)),
mcp_runtime(mcp_tool_info(
"hello_echo",
/*supports_parallel_tool_calls*/ false,
"mcp__hello_echo__",
"query_with_delay",
),
]),
)),
],
extension_tool_executors: Vec::new(),
dynamic_tools: turn.dynamic_tools.as_slice(),
},
@@ -268,8 +262,7 @@ async fn tools_without_handlers_do_not_support_parallel() -> anyhow::Result<()>
step_context.as_ref(),
ToolRouterParams {
tool_suggest_candidates: None,
deferred_mcp_tools: None,
mcp_tools: None,
tool_runtimes: Vec::new(),
extension_tool_executors: Vec::new(),
dynamic_tools: turn.dynamic_tools.as_slice(),
},
@@ -325,8 +318,7 @@ async fn specs_filter_deferred_dynamic_tools() -> anyhow::Result<()> {
step_context.as_ref(),
ToolRouterParams {
tool_suggest_candidates: None,
deferred_mcp_tools: None,
mcp_tools: None,
tool_runtimes: Vec::new(),
extension_tool_executors: Vec::new(),
dynamic_tools: &dynamic_tools,
},
@@ -368,6 +360,10 @@ fn mcp_tool_info(
}
}
fn mcp_runtime(tool_info: codex_mcp::ToolInfo) -> Arc<dyn CoreToolRuntime> {
Arc::new(McpHandler::new(tool_info).expect("MCP tool spec should build"))
}
#[tokio::test]
async fn extension_tool_executors_are_model_visible_and_dispatchable() -> anyhow::Result<()> {
let (mut session, turn) = make_session_and_context().await;
@@ -393,8 +389,7 @@ async fn extension_tool_executors_are_model_visible_and_dispatchable() -> anyhow
step_context.as_ref(),
ToolRouterParams {
tool_suggest_candidates: None,
deferred_mcp_tools: None,
mcp_tools: None,
tool_runtimes: Vec::new(),
extension_tool_executors: extension_tool_executors(&session),
dynamic_tools: turn.dynamic_tools.as_slice(),
},