mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
register all mcps w/ namespace
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user