register all mcps w/ namespace

This commit is contained in:
Sayan Sisodiya
2026-04-11 19:05:04 -07:00
parent 1325bcd3f6
commit b4e66fb8ec
11 changed files with 219 additions and 54 deletions
+11 -7
View File
@@ -284,28 +284,32 @@ async fn call_nested_tool(
)));
}
let payload = if let Some(tool_info) = exec
let (tool_call_name, 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 {
let raw_arguments = match serialize_function_tool_arguments(&tool_name, input) {
Ok(raw_arguments) => raw_arguments,
Err(error) => return Err(FunctionCallError::RespondToModel(error)),
};
(
ToolName::namespaced(tool_info.callable_namespace, tool_info.callable_name),
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,
Ok(payload) => (ToolName::plain(tool_name.clone()), 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,
};
+25 -14
View File
@@ -1572,29 +1572,40 @@ impl JsReplManager {
},
);
let payload = if let Some(tool_info) = exec
let (tool_call_name, payload) = if let Some(tool_info) = exec
.session
.resolve_mcp_tool_info(&req.tool_name, /*namespace*/ None)
.await
{
crate::tools::context::ToolPayload::Mcp {
server: tool_info.server_name,
tool: tool_info.tool.name.to_string(),
raw_arguments: req.arguments.clone(),
}
(
codex_tools::ToolName::namespaced(
tool_info.callable_namespace,
tool_info.callable_name,
),
crate::tools::context::ToolPayload::Mcp {
server: tool_info.server_name,
tool: tool_info.tool.name.to_string(),
raw_arguments: req.arguments.clone(),
},
)
} else if is_freeform_tool(&router.specs(), &req.tool_name) {
crate::tools::context::ToolPayload::Custom {
input: req.arguments.clone(),
}
(
codex_tools::ToolName::plain(req.tool_name.clone()),
crate::tools::context::ToolPayload::Custom {
input: req.arguments.clone(),
},
)
} else {
crate::tools::context::ToolPayload::Function {
arguments: req.arguments.clone(),
}
(
codex_tools::ToolName::plain(req.tool_name.clone()),
crate::tools::context::ToolPayload::Function {
arguments: req.arguments.clone(),
},
)
};
let tool_name = req.tool_name.clone();
let call = crate::tools::router::ToolCall {
tool_name: codex_tools::ToolName::plain(tool_name.clone()),
tool_name: tool_call_name,
call_id: req.id.clone(),
payload,
};
+11 -8
View File
@@ -126,16 +126,15 @@ impl ToolRouter {
call_id,
..
} => {
let mcp_tool = session
if let Some(tool_info) = session
.resolve_mcp_tool_info(&name, namespace.as_deref())
.await;
let tool_name = match namespace {
Some(namespace) => ToolName::namespaced(namespace, name),
None => ToolName::plain(name),
};
if let Some(tool_info) = mcp_tool {
.await
{
Ok(Some(ToolCall {
tool_name,
tool_name: ToolName::namespaced(
tool_info.callable_namespace,
tool_info.callable_name,
),
call_id,
payload: ToolPayload::Mcp {
server: tool_info.server_name,
@@ -144,6 +143,10 @@ impl ToolRouter {
},
}))
} else {
let tool_name = match namespace {
Some(namespace) => ToolName::namespaced(namespace, name),
None => ToolName::plain(name),
};
Ok(Some(ToolCall {
tool_name,
call_id,
+11 -5
View File
@@ -11,6 +11,7 @@ use codex_tools::DiscoverableTool;
use codex_tools::ToolHandlerKind;
use codex_tools::ToolNamespace;
use codex_tools::ToolRegistryPlanDeferredTool;
use codex_tools::ToolRegistryPlanMcpTool;
use codex_tools::ToolRegistryPlanParams;
use codex_tools::ToolUserShellType;
use codex_tools::ToolsConfig;
@@ -29,16 +30,21 @@ pub(crate) fn tool_user_shell_type(user_shell: &Shell) -> ToolUserShellType {
}
}
struct McpToolPlanInputs {
mcp_tools: HashMap<String, rmcp::model::Tool>,
struct McpToolPlanInputs<'a> {
mcp_tools: Vec<ToolRegistryPlanMcpTool<'a>>,
tool_namespaces: HashMap<String, ToolNamespace>,
}
fn map_mcp_tools_for_plan(mcp_tools: &HashMap<String, ToolInfo>) -> McpToolPlanInputs {
fn map_mcp_tools_for_plan(mcp_tools: &HashMap<String, ToolInfo>) -> McpToolPlanInputs<'_> {
McpToolPlanInputs {
mcp_tools: mcp_tools
.iter()
.map(|(name, tool)| (name.clone(), tool.tool.clone()))
.map(|(qualified_name, tool)| ToolRegistryPlanMcpTool {
qualified_name: qualified_name.clone(),
callable_name: tool.callable_name.clone(),
callable_namespace: tool.callable_namespace.clone(),
tool: &tool.tool,
})
.collect(),
tool_namespaces: mcp_tools
.iter()
@@ -114,7 +120,7 @@ pub(crate) fn build_specs_with_discoverable_tools(
ToolRegistryPlanParams {
mcp_tools: mcp_tool_plan_inputs
.as_ref()
.map(|inputs| &inputs.mcp_tools),
.map(|inputs| inputs.mcp_tools.as_slice()),
deferred_mcp_tools: deferred_mcp_tool_sources.as_deref(),
tool_namespaces: mcp_tool_plan_inputs
.as_ref()
+37
View File
@@ -911,6 +911,43 @@ fn search_tool_registers_namespaced_mcp_tool_aliases() {
assert!(registry.has_handler(&mcp_alias));
}
#[test]
fn direct_mcp_tools_register_namespaced_handlers() {
let config = test_config();
let model_info = construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
let available_models = Vec::new();
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
available_models: &available_models,
features: &features,
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (_, registry) = build_specs(
&tools_config,
Some(HashMap::from([(
"mcp__test_server__echo".to_string(),
mcp_tool_info(mcp_tool(
"echo",
"Echo",
serde_json::json!({"type": "object"}),
)),
)])),
/*deferred_mcp_tools*/ None,
&[],
)
.build();
assert!(registry.has_handler(&ToolName::namespaced("mcp__test_server__", "echo")));
assert!(!registry.has_handler(&ToolName::plain("mcp__test_server__echo")));
}
#[test]
fn test_mcp_tool_property_missing_type_defaults_to_string() {
let config = test_config();