register all mcp tools with namespace (#17404)

stacked on #17402.

MCP tools returned by `tool_search` (deferred tools) get registered in
our `ToolRegistry` with a different format than directly available
tools. this leads to two different ways of accessing MCP tools from our
tool catalog, only one of which works for each. fix this by registering
all MCP tools with the namespace format, since this info is already
available.

also, direct MCP tools are registered to responsesapi without a
namespace, while deferred MCP tools have a namespace. this means we can
receive MCP `FunctionCall`s in both formats from namespaces. fix this by
always registering MCP tools with namespace, regardless of deferral
status.

make code mode track `ToolName` provenance of tools so it can map the
literal JS function name string to the correct `ToolName` for
invocation, rather than supporting both in core.

this lets us unify to a single canonical `ToolName` representation for
each MCP tool and force everywhere to use that one, without supporting
fallbacks.
This commit is contained in:
sayan-oai
2026-04-15 21:02:59 +08:00
committed by GitHub
Unverified
parent 9402347f34
commit 0df7e9a820
41 changed files with 1170 additions and 432 deletions
+29 -28
View File
@@ -116,7 +116,7 @@ struct CoreTurnHost {
impl CodeModeTurnHost for CoreTurnHost {
async fn invoke_tool(
&self,
tool_name: String,
tool_name: ToolName,
input: Option<JsonValue>,
cancellation_token: CancellationToken,
) -> Result<JsonValue, String> {
@@ -288,38 +288,39 @@ async fn build_nested_router(exec: &ExecContext) -> ToolRouter {
async fn call_nested_tool(
exec: ExecContext,
tool_runtime: ToolCallRuntime,
tool_name: String,
tool_name: ToolName,
input: Option<JsonValue>,
cancellation_token: CancellationToken,
) -> Result<JsonValue, FunctionCallError> {
if tool_name == PUBLIC_TOOL_NAME {
if tool_name.namespace.is_none() && tool_name.name == PUBLIC_TOOL_NAME {
return Err(FunctionCallError::RespondToModel(format!(
"{PUBLIC_TOOL_NAME} cannot invoke itself"
)));
}
let payload = if let Some(tool_info) = exec
.session
.resolve_mcp_tool_info(&tool_name, /*namespace*/ None)
.await
{
match serialize_function_tool_arguments(&tool_name, input) {
Ok(raw_arguments) => ToolPayload::Mcp {
server: tool_info.server_name,
tool: tool_info.tool.name.to_string(),
raw_arguments,
},
Err(error) => return Err(FunctionCallError::RespondToModel(error)),
}
} else {
match build_nested_tool_payload(tool_runtime.find_spec(&tool_name), &tool_name, input) {
Ok(payload) => payload,
Err(error) => return Err(FunctionCallError::RespondToModel(error)),
}
};
let (tool_call_name, payload) =
if let Some(tool_info) = exec.session.resolve_mcp_tool_info(&tool_name).await {
let raw_arguments = match serialize_function_tool_arguments(&tool_name, input) {
Ok(raw_arguments) => raw_arguments,
Err(error) => return Err(FunctionCallError::RespondToModel(error)),
};
(
tool_info.canonical_tool_name(),
ToolPayload::Mcp {
server: tool_info.server_name,
tool: tool_info.tool.name.to_string(),
raw_arguments,
},
)
} else {
match build_nested_tool_payload(tool_runtime.find_spec(&tool_name), &tool_name, input) {
Ok(payload) => (tool_name, payload),
Err(error) => return Err(FunctionCallError::RespondToModel(error)),
}
};
let call = ToolCall {
tool_name: ToolName::plain(tool_name.clone()),
tool_name: tool_call_name,
call_id: format!("{PUBLIC_TOOL_NAME}-{}", uuid::Uuid::new_v4()),
payload,
};
@@ -339,7 +340,7 @@ fn tool_kind_for_spec(spec: &ToolSpec) -> codex_code_mode::CodeModeToolKind {
fn tool_kind_for_name(
spec: Option<ToolSpec>,
tool_name: &str,
tool_name: &ToolName,
) -> Result<codex_code_mode::CodeModeToolKind, String> {
spec.as_ref()
.map(tool_kind_for_spec)
@@ -348,7 +349,7 @@ fn tool_kind_for_name(
fn build_nested_tool_payload(
spec: Option<ToolSpec>,
tool_name: &str,
tool_name: &ToolName,
input: Option<JsonValue>,
) -> Result<ToolPayload, String> {
let actual_kind = tool_kind_for_name(spec, tool_name)?;
@@ -363,7 +364,7 @@ fn build_nested_tool_payload(
}
fn build_function_tool_payload(
tool_name: &str,
tool_name: &ToolName,
input: Option<JsonValue>,
) -> Result<ToolPayload, String> {
let arguments = serialize_function_tool_arguments(tool_name, input)?;
@@ -371,7 +372,7 @@ fn build_function_tool_payload(
}
fn serialize_function_tool_arguments(
tool_name: &str,
tool_name: &ToolName,
input: Option<JsonValue>,
) -> Result<String, String> {
match input {
@@ -385,7 +386,7 @@ fn serialize_function_tool_arguments(
}
fn build_freeform_tool_payload(
tool_name: &str,
tool_name: &ToolName,
input: Option<JsonValue>,
) -> Result<ToolPayload, String> {
match input {