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
117
118
119
120
121
122
123
124
125
126
127
128
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
}
/// Whether this tool is safe to execute concurrently with other tool
/// calls emitted in the same LLM turn.
///
/// When the model returns several tool calls at once they are grouped into
/// a single execution step and run in parallel. Tools that mutate shared
/// state (write a file, save content, edit a document) should override this
/// to `false` so the batch is serialized and writes cannot race. Read-only
/// tools (search, fetch, list) can keep the default and run in parallel.
fn concurrency_safe(&self) -> bool {
true // Default: most tools are read-only / independent
}
/// 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"))
}
}