Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions codex-rs/core/src/tools/code_mode/execute_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ impl ToolExecutor<ToolInvocation> for CodeModeExecuteHandler {
ToolName::plain(PUBLIC_TOOL_NAME)
}

fn spec(&self) -> Option<ToolSpec> {
Some(self.spec.clone())
fn spec(&self) -> ToolSpec {
self.spec.clone()
}

async fn handle(
Expand Down
4 changes: 2 additions & 2 deletions codex-rs/core/src/tools/code_mode/wait_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ impl ToolExecutor<ToolInvocation> for CodeModeWaitHandler {
ToolName::plain(WAIT_TOOL_NAME)
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_wait_tool())
fn spec(&self) -> ToolSpec {
create_wait_tool()
}

async fn handle(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ impl ToolExecutor<ToolInvocation> for ReportAgentJobResultHandler {
ToolName::plain("report_agent_job_result")
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_report_agent_job_result_tool())
fn spec(&self) -> ToolSpec {
create_report_agent_job_result_tool()
}

async fn handle(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ impl ToolExecutor<ToolInvocation> for SpawnAgentsOnCsvHandler {
ToolName::plain("spawn_agents_on_csv")
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_spawn_agents_on_csv_tool())
fn spec(&self) -> ToolSpec {
create_spawn_agents_on_csv_tool()
}

async fn handle(
Expand Down
4 changes: 2 additions & 2 deletions codex-rs/core/src/tools/handlers/apply_patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,8 @@ impl ToolExecutor<ToolInvocation> for ApplyPatchHandler {
ToolName::plain("apply_patch")
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_apply_patch_freeform_tool(self.multi_environment))
fn spec(&self) -> ToolSpec {
create_apply_patch_freeform_tool(self.multi_environment)
}

async fn handle(
Expand Down
8 changes: 4 additions & 4 deletions codex-rs/core/src/tools/handlers/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use tracing::warn;

pub struct DynamicToolHandler {
tool_name: ToolName,
spec: Option<ToolSpec>,
spec: ToolSpec,
exposure: ToolExposure,
search_text: String,
}
Expand All @@ -50,7 +50,7 @@ impl DynamicToolHandler {
};
Some(Self {
tool_name,
spec: Some(spec),
spec,
exposure: if tool.defer_loading {
ToolExposure::Deferred
} else {
Expand All @@ -67,7 +67,7 @@ impl ToolExecutor<ToolInvocation> for DynamicToolHandler {
self.tool_name.clone()
}

fn spec(&self) -> Option<ToolSpec> {
fn spec(&self) -> ToolSpec {
self.spec.clone()
}

Expand Down Expand Up @@ -130,7 +130,7 @@ impl CoreToolRuntime for DynamicToolHandler {
fn search_info(&self) -> Option<ToolSearchInfo> {
ToolSearchInfo::from_spec(
self.search_text.clone(),
self.spec()?,
self.spec(),
Some(ToolSearchSourceInfo {
name: "Dynamic tools".to_string(),
description: Some("Tools provided by the current Codex thread.".to_string()),
Expand Down
49 changes: 29 additions & 20 deletions codex-rs/core/src/tools/handlers/extension_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl ToolExecutor<ToolInvocation> for ExtensionToolAdapter {
self.0.tool_name()
}

fn spec(&self) -> Option<ToolSpec> {
fn spec(&self) -> ToolSpec {
self.0.spec()
}

Expand Down Expand Up @@ -130,25 +130,23 @@ mod tests {
codex_tools::ToolName::plain("extension_echo")
}

fn spec(&self) -> Option<codex_tools::ToolSpec> {
Some(codex_tools::ToolSpec::Function(
codex_tools::ResponsesApiTool {
name: "extension_echo".to_string(),
description: "Echoes arguments.".to_string(),
strict: true,
parameters: codex_tools::parse_tool_input_schema(&json!({
"type": "object",
"properties": {
"message": { "type": "string" },
},
"required": ["message"],
"additionalProperties": false,
}))
.expect("extension schema should parse"),
output_schema: None,
defer_loading: None,
},
))
fn spec(&self) -> codex_tools::ToolSpec {
codex_tools::ToolSpec::Function(codex_tools::ResponsesApiTool {
name: "extension_echo".to_string(),
description: "Echoes arguments.".to_string(),
strict: true,
parameters: codex_tools::parse_tool_input_schema(&json!({
"type": "object",
"properties": {
"message": { "type": "string" },
},
"required": ["message"],
"additionalProperties": false,
}))
.expect("extension schema should parse"),
output_schema: None,
defer_loading: None,
})
}

async fn handle(
Expand All @@ -171,6 +169,17 @@ mod tests {
codex_tools::ToolName::plain("extension_echo")
}

fn spec(&self) -> codex_tools::ToolSpec {
codex_tools::ToolSpec::Function(codex_tools::ResponsesApiTool {
name: "extension_echo".to_string(),
description: "Captures arguments.".to_string(),
strict: false,
parameters: codex_tools::JsonSchema::default(),
output_schema: None,
defer_loading: None,
})
}

async fn handle(
&self,
call: codex_tools::ToolCall,
Expand Down
4 changes: 2 additions & 2 deletions codex-rs/core/src/tools/handlers/goal/create_goal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ impl ToolExecutor<ToolInvocation> for CreateGoalHandler {
ToolName::plain(CREATE_GOAL_TOOL_NAME)
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_create_goal_tool())
fn spec(&self) -> ToolSpec {
create_create_goal_tool()
}

async fn handle(
Expand Down
4 changes: 2 additions & 2 deletions codex-rs/core/src/tools/handlers/goal/get_goal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ impl ToolExecutor<ToolInvocation> for GetGoalHandler {
ToolName::plain(GET_GOAL_TOOL_NAME)
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_get_goal_tool())
fn spec(&self) -> ToolSpec {
create_get_goal_tool()
}

async fn handle(
Expand Down
4 changes: 2 additions & 2 deletions codex-rs/core/src/tools/handlers/goal/update_goal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ impl ToolExecutor<ToolInvocation> for UpdateGoalHandler {
ToolName::plain(UPDATE_GOAL_TOOL_NAME)
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_update_goal_tool())
fn spec(&self) -> ToolSpec {
create_update_goal_tool()
}

async fn handle(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ impl ToolExecutor<ToolInvocation> for ListAvailablePluginsToInstallHandler {
ToolName::plain(LIST_AVAILABLE_PLUGINS_TO_INSTALL_TOOL_NAME)
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_list_available_plugins_to_install_tool())
fn spec(&self) -> ToolSpec {
create_list_available_plugins_to_install_tool()
}

fn supports_parallel_tool_calls(&self) -> bool {
Expand Down
82 changes: 47 additions & 35 deletions codex-rs/core/src/tools/handlers/mcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,25 @@ use serde_json::Value;

pub struct McpHandler {
tool_info: ToolInfo,
spec: ToolSpec,
exposure: ToolExposure,
}

impl McpHandler {
pub fn new(tool_info: ToolInfo) -> Self {
pub fn new(tool_info: ToolInfo) -> Result<Self, serde_json::Error> {
Self::with_exposure(tool_info, ToolExposure::Direct)
}

pub fn with_exposure(tool_info: ToolInfo, exposure: ToolExposure) -> Self {
Self {
pub fn with_exposure(
tool_info: ToolInfo,
exposure: ToolExposure,
) -> Result<Self, serde_json::Error> {
let spec = create_tool_spec(&tool_info)?;
Ok(Self {
tool_info,
spec,
exposure,
}
})
}
}

Expand All @@ -51,32 +57,8 @@ impl ToolExecutor<ToolInvocation> for McpHandler {
self.tool_info.canonical_tool_name()
}

fn spec(&self) -> Option<ToolSpec> {
let tool_name = self.tool_name();
let namespace_name = tool_name.namespace.as_ref()?;
let tool = mcp_tool_to_responses_api_tool(&tool_name, &self.tool_info.tool).ok()?;
let description = self
.tool_info
.namespace_description
.as_deref()
.map(str::trim)
.filter(|description| !description.is_empty())
.map(str::to_string)
.or_else(|| {
self.tool_info
.connector_name
.as_deref()
.map(str::trim)
.filter(|connector_name| !connector_name.is_empty())
.map(|connector_name| format!("Tools for working with {connector_name}."))
})
.unwrap_or_default();

Some(ToolSpec::Namespace(ResponsesApiNamespace {
name: namespace_name.clone(),
description,
tools: vec![ResponsesApiNamespaceTool::Function(tool)],
}))
fn spec(&self) -> ToolSpec {
self.spec.clone()
}

fn exposure(&self) -> ToolExposure {
Expand Down Expand Up @@ -152,7 +134,7 @@ impl CoreToolRuntime for McpHandler {

ToolSearchInfo::from_spec(
build_mcp_search_text(&self.tool_info),
self.spec()?,
self.spec(),
source_info,
)
}
Expand Down Expand Up @@ -223,6 +205,32 @@ impl CoreToolRuntime for McpHandler {
}
}

fn create_tool_spec(tool_info: &ToolInfo) -> Result<ToolSpec, serde_json::Error> {
let tool_name = tool_info.canonical_tool_name();
let tool = mcp_tool_to_responses_api_tool(&tool_name, &tool_info.tool)?;
let description = tool_info
.namespace_description
.as_deref()
.map(str::trim)
.filter(|description| !description.is_empty())
.map(str::to_string)
.or_else(|| {
tool_info
.connector_name
.as_deref()
.map(str::trim)
.filter(|connector_name| !connector_name.is_empty())
.map(|connector_name| format!("Tools for working with {connector_name}."))
})
.unwrap_or_default();

Ok(ToolSpec::Namespace(ResponsesApiNamespace {
name: tool_info.callable_namespace.clone(),
description,
tools: vec![ResponsesApiNamespaceTool::Function(tool)],
}))
}

fn mcp_hook_tool_input(raw_arguments: &str) -> Value {
if raw_arguments.trim().is_empty() {
return Value::Object(Map::new());
Expand Down Expand Up @@ -306,7 +314,8 @@ mod tests {
.to_string(),
};
let (session, turn) = make_session_and_context().await;
let handler = McpHandler::new(tool_info("memory", "mcp__memory__", "create_entities"));
let handler = McpHandler::new(tool_info("memory", "mcp__memory__", "create_entities"))
.expect("MCP tool spec should build");
assert_eq!(
handler.pre_tool_use_payload(&ToolInvocation {
session: session.into(),
Expand Down Expand Up @@ -336,7 +345,8 @@ mod tests {
arguments: json!({ "message": "hello" }).to_string(),
};
let (session, turn) = make_session_and_context().await;
let handler = McpHandler::new(tool_info("foo", "mcp__foo__", "exec_command"));
let handler = McpHandler::new(tool_info("foo", "mcp__foo__", "exec_command"))
.expect("MCP tool spec should build");

assert_eq!(
handler.pre_tool_use_payload(&ToolInvocation {
Expand All @@ -362,7 +372,8 @@ mod tests {
arguments: json!({ "message": "hello" }).to_string(),
};
let (session, turn) = make_session_and_context().await;
let handler = McpHandler::new(tool_info("foo", "mcp__foo__", "exec_command"));
let handler = McpHandler::new(tool_info("foo", "mcp__foo__", "exec_command"))
.expect("MCP tool spec should build");

let invocation = handler
.with_updated_hook_input(
Expand Down Expand Up @@ -411,7 +422,8 @@ mod tests {
truncation_policy: codex_utils_output_truncation::TruncationPolicy::Bytes(1024),
};
let (session, turn) = make_session_and_context().await;
let handler = McpHandler::new(tool_info("filesystem", "mcp__filesystem__", "read_file"));
let handler = McpHandler::new(tool_info("filesystem", "mcp__filesystem__", "read_file"))
.expect("MCP tool spec should build");
let invocation = ToolInvocation {
session: session.into(),
turn: turn.into(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ impl ToolExecutor<ToolInvocation> for ListMcpResourceTemplatesHandler {
ToolName::plain("list_mcp_resource_templates")
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_list_mcp_resource_templates_tool())
fn spec(&self) -> ToolSpec {
create_list_mcp_resource_templates_tool()
}

fn supports_parallel_tool_calls(&self) -> bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ impl ToolExecutor<ToolInvocation> for ListMcpResourcesHandler {
ToolName::plain("list_mcp_resources")
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_list_mcp_resources_tool())
fn spec(&self) -> ToolSpec {
create_list_mcp_resources_tool()
}

fn supports_parallel_tool_calls(&self) -> bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ impl ToolExecutor<ToolInvocation> for ReadMcpResourceHandler {
ToolName::plain("read_mcp_resource")
}

fn spec(&self) -> Option<ToolSpec> {
Some(create_read_mcp_resource_tool())
fn spec(&self) -> ToolSpec {
create_read_mcp_resource_tool()
}

fn supports_parallel_tool_calls(&self) -> bool {
Expand Down
Loading
Loading