1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use anyhow::Result;
use std::{collections::HashMap, sync::Arc};
use tokio::sync::mpsc;
use crate::Part;
use crate::{
ToolCall, ToolDefinition, auth::AuthMetadata, events::AgentEvent, stores::SessionStore,
};
/// Tool execution context - lighter weight than ExecutorContext
#[derive(Debug, Clone)]
pub struct ToolContext {
/// Agent ID executing the tool
pub agent_id: String,
/// Session ID for the current conversation
pub session_id: String,
/// Task ID for the current task
pub task_id: String,
/// Run ID for the current execution
pub run_id: String,
/// Thread ID for conversation grouping
pub thread_id: String,
/// User ID if available
pub user_id: String,
/// Session store for persistent state across tool calls
pub session_store: Arc<dyn SessionStore>,
/// Event sender for emitting events during tool execution
pub event_tx: Option<Arc<mpsc::Sender<AgentEvent>>>,
/// Additional metadata for the tool. Useful in direct inline agent invocation.
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
/// Tool trait for implementing tools that can be called by agents
#[async_trait::async_trait]
pub trait Tool: Send + Sync + std::fmt::Debug + std::any::Any {
fn get_name(&self) -> String;
/// Get the tool definition for the LLM
fn get_tool_definition(&self) -> ToolDefinition {
ToolDefinition {
name: self.get_name(),
description: self.get_description(),
parameters: self.get_parameters(),
output_schema: None,
examples: self.get_tool_examples(),
prompt: self.prompt(),
}
}
fn get_parameters(&self) -> serde_json::Value;
fn get_description(&self) -> String;
fn get_tool_examples(&self) -> Option<String> {
None
}
/// Optional detailed prompt/instructions for this tool.
/// When provided, these instructions are injected into the system prompt
/// so the LLM knows HOW to use the tool (not just what parameters it takes).
/// This mirrors claude-code's per-tool `prompt()` pattern.
fn prompt(&self) -> Option<String> {
None
}
/// Check if this tool is external (handled by frontend)
fn is_external(&self) -> bool {
false // Default to false for built-in tools
}
/// Check if this tool is an MCP tool
fn is_mcp(&self) -> bool {
false // Default to false for built-in tools
}
fn is_sync(&self) -> bool {
false // Default to false for built-in tools
}
fn is_final(&self) -> bool {
false // Default to false for built-in tools
}
/// Check if this tool needs ExecutorContext instead of ToolContext
fn needs_executor_context(&self) -> bool {
false // Default to false - most tools use ToolContext
}
/// Get authentication metadata for this tool
fn get_auth_metadata(&self) -> Option<Box<dyn AuthMetadata>> {
None // Default to no authentication required
}
/// Get the plugin name this tool belongs to (nullable)
/// If this returns Some, the tool is part of a plugin
/// If None, the tool is standalone
fn get_plugin_name(&self) -> Option<String> {
None // Default to standalone tool
}
/// Execute the tool with given arguments, returning content parts
async fn execute(
&self,
tool_call: ToolCall,
context: Arc<ToolContext>,
) -> Result<Vec<Part>, anyhow::Error>;
/// Synchronous execution of the tool, returning content parts (default unsupported)
fn execute_sync(
&self,
_tool_call: ToolCall,
_context: Arc<ToolContext>,
) -> Result<Vec<Part>, anyhow::Error> {
Err(anyhow::anyhow!("Sync execution not supported"))
}
}