Skip to main content

systemprompt_agent/services/a2a_server/processing/strategies/
tool_executor.rs

1//! `ToolExecutorTrait` implementation executing tools in context.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use 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}