systemprompt_agent/services/a2a_server/processing/strategies/
tool_executor.rs1use crate::services::shared::{AgentServiceError, Result};
7use async_trait::async_trait;
8use serde_json::Value;
9use systemprompt_identifiers::AiToolCallId;
10use systemprompt_models::{McpTool, RequestContext, ToolCall};
11
12use super::ExecutionContext;
13use super::plan_executor::{ToolExecutorTrait, ToolOutcome};
14
15#[derive(Debug)]
16pub struct ContextToolExecutor {
17 pub context: ExecutionContext,
18}
19
20#[async_trait]
21impl ToolExecutorTrait for ContextToolExecutor {
22 async fn execute_tool(
23 &self,
24 tool_name: &str,
25 arguments: Value,
26 tools: &[McpTool],
27 ctx: &RequestContext,
28 ) -> Result<ToolOutcome> {
29 let tool_call = ToolCall {
30 ai_tool_call_id: AiToolCallId::new(format!("call_{}", tool_name)),
31 name: tool_name.to_owned(),
32 arguments,
33 };
34
35 let (_, results) = self
36 .context
37 .ai_service
38 .execute_tools(
39 vec![tool_call],
40 tools,
41 ctx,
42 Some(&self.context.agent_runtime.tool_model_overrides),
43 )
44 .await;
45
46 let result = results.into_iter().next().ok_or_else(|| {
47 AgentServiceError::Internal(format!("Tool {} returned no result", tool_name))
48 })?;
49
50 if result.is_error.unwrap_or(false) {
51 let error_msg = result
52 .content
53 .into_iter()
54 .next()
55 .and_then(|c| {
56 if let rmcp::model::ContentBlock::Text(text_content) = c {
57 Some(text_content.text)
58 } else {
59 None
60 }
61 })
62 .unwrap_or_else(|| "Unknown error".to_owned());
63 return Err(AgentServiceError::Internal(format!(
64 "Tool {tool_name} failed: {error_msg}"
65 )));
66 }
67
68 let output = result.structured_content.ok_or_else(|| {
69 AgentServiceError::Internal(format!("Tool {tool_name} returned no structured_content"))
70 })?;
71 Ok(ToolOutcome {
72 output,
73 meta: result
74 .meta
75 .as_ref()
76 .and_then(|m| serde_json::to_value(m).ok()),
77 })
78 }
79}