use std::time::Duration; use tracing::error; use crate::codex::Session; use crate::models::FunctionCallOutputPayload; use crate::models::ResponseInputItem; use crate::protocol::Event; use crate::protocol::EventMsg; use crate::protocol::McpToolCallBeginEvent; use crate::protocol::McpToolCallEndEvent; /// Handles the specified tool call dispatches the appropriate /// `McpToolCallBegin` and `McpToolCallEnd` events to the `Session`. pub(crate) async fn handle_mcp_tool_call( sess: &Session, sub_id: &str, call_id: String, server: String, tool_name: String, arguments: String, timeout: Option, ) -> ResponseInputItem { // Parse the `arguments` as JSON. An empty string is OK, but invalid JSON // is not. let arguments_value = if arguments.trim().is_empty() { None } else { match serde_json::from_str::(&arguments) { Ok(value) => Some(value), Err(e) => { error!("failed to parse tool call arguments: {e}"); return ResponseInputItem::FunctionCallOutput { call_id: call_id.clone(), output: FunctionCallOutputPayload { content: format!("err: {e}"), success: Some(false), }, }; } } }; let tool_call_begin_event = EventMsg::McpToolCallBegin(McpToolCallBeginEvent { call_id: call_id.clone(), server: server.clone(), tool: tool_name.clone(), arguments: arguments_value.clone(), }); notify_mcp_tool_call_event(sess, sub_id, tool_call_begin_event).await; // Perform the tool call. let result = sess .call_tool(&server, &tool_name, arguments_value, timeout) .await .map_err(|e| format!("tool call error: {e}")); let tool_call_end_event = EventMsg::McpToolCallEnd(McpToolCallEndEvent { call_id: call_id.clone(), result: result.clone(), }); notify_mcp_tool_call_event(sess, sub_id, tool_call_end_event.clone()).await; ResponseInputItem::McpToolCallOutput { call_id, result } } async fn notify_mcp_tool_call_event(sess: &Session, sub_id: &str, event: EventMsg) { sess.send_event(Event { id: sub_id.to_string(), msg: event, }) .await; }