Skip to main content

ai_agents_tools/
lib.rs

1//! Tool system for AI Agents framework
2
3pub mod builtin;
4mod condition;
5pub mod mcp;
6mod provider;
7mod registry;
8pub mod security;
9mod types;
10
11pub use ai_agents_core::{
12    CommandBindingKind, CommandPolicyBinding, DomainPolicyBinding, PathAccessMode, PathBindingKind,
13    PathPolicyBinding, PermissionOutcome, ResultLimitBinding, ResultLimitKind, Tool,
14    ToolActorContext, ToolApprovalRecord, ToolApprovalStatus, ToolCallClassification,
15    ToolCallSource, ToolCancellationToken, ToolExecutionContext, ToolExecutionLimits,
16    ToolExecutionRecord, ToolExecutionRequest, ToolInfo, ToolInvoker, ToolOperationKind,
17    ToolPolicyBindings, ToolPolicyDecisionRecord, ToolResult, ToolSafetyMetadata,
18    ToolSideEffectLevel,
19};
20pub use condition::{
21    ConditionEvaluator, EvaluationContext, LLMGetter, SimpleLLMGetter, ToolCallRecord,
22};
23pub use provider::{ProviderHealth, ToolDescriptor, ToolProvider, ToolProviderError};
24pub use registry::{ResolvedTool, ToolIdentity, ToolRegistry, ToolSchemaPromptMode};
25pub use types::{
26    CommandRequest, CommandResponse, CommandRunner, CommandRunnerSlot, DiagnosticItem,
27    DiagnosticSeverity, DiagnosticsProvider, DiagnosticsProviderSlot, DiagnosticsRequest,
28    DiagnosticsResponse, FileVersionEvidence, FileVersionStore, ProcessCommandRunner,
29    QuestionHandler, QuestionHandlerSlot, QuestionRequest, QuestionResponse, StaticCommandRunner,
30    StaticDiagnosticsProvider, StaticWebSearchProvider, TodoItem, TodoStatus, TodoStore,
31    ToolAliases, ToolContext, ToolMetadata, ToolProviderType, TrustLevel, UnavailableCommandRunner,
32    UnavailableDiagnosticsProvider, UnavailableWebSearchProvider, WebSearchProvider,
33    WebSearchProviderSlot, WebSearchRequest, WebSearchResponse, WebSearchResultItem,
34    WebSearchSafeSearch, file_version_evidence,
35};
36
37pub use builtin::HttpTool;
38pub use builtin::{
39    AskUserTool, CalculatorTool, CommandTool, CopyPathTool, DateTimeTool, DeletePathTool,
40    DiagnosticsTool, EchoTool, FileEditTool, FileInfoTool, FileListTool, FileReadTool, FileTool,
41    FileWriteTool, GitDiffTool, GitStatusTool, GlobTool, GrepTool, JsonTool, MathTool,
42    MovePathTool, PatchTool, RandomTool, SleepTool, TemplateTool, TextTool, TodoTool,
43    WebFetchResolver, WebFetchTool, WebFetchTransport, WebFetchTransportRequest,
44    WebFetchTransportResponse, WebSearchTool,
45};
46
47pub use security::{
48    CommandPolicyConfig, CommandRuleConfig, CommandTemplateConfig, DomainPolicyConfig,
49    MAX_TOOL_TIMEOUT_MS, NoWritePolicyBehavior, OperationPolicyConfig, PathPolicyConfig,
50    SecurityCheckResult, ToolPolicyConfig, ToolSecurityConfig, ToolSecurityEngine,
51};
52
53use schemars::JsonSchema;
54use serde::Deserialize;
55use std::sync::Arc;
56use thiserror::Error;
57
58#[derive(Debug, Error)]
59pub enum ToolError {
60    #[error("Tool not found: {0}")]
61    NotFound(String),
62    #[error("Tool already registered: {0}")]
63    AlreadyRegistered(String),
64    #[error("Duplicate: {0}")]
65    Duplicate(String),
66    #[error("Tool execution failed: {0}")]
67    ExecutionFailed(String),
68    #[error("Invalid arguments: {0}")]
69    InvalidArguments(String),
70    #[error("Provider error: {0}")]
71    Provider(String),
72}
73
74pub fn generate_schema<T: JsonSchema>() -> serde_json::Value {
75    let schema = schemars::schema_for!(T);
76    serde_json::to_value(schema).unwrap_or_else(|_| serde_json::json!({}))
77}
78
79pub(crate) fn deserialize_optional_positive_usize<'de, D>(
80    deserializer: D,
81) -> std::result::Result<Option<usize>, D::Error>
82where
83    D: serde::Deserializer<'de>,
84{
85    let value = Option::<usize>::deserialize(deserializer)?;
86    validate_positive_max_results(value).map_err(serde::de::Error::custom)?;
87    Ok(value)
88}
89
90pub(crate) fn validate_positive_max_results(
91    value: Option<usize>,
92) -> std::result::Result<(), &'static str> {
93    if value == Some(0) {
94        return Err("max_results must be greater than 0");
95    }
96    Ok(())
97}
98
99pub fn create_builtin_registry() -> ToolRegistry {
100    let mut registry = ToolRegistry::new();
101    registry
102        .register(Arc::new(CalculatorTool::new()))
103        .expect("failed to register calculator");
104    registry
105        .register(Arc::new(EchoTool::new()))
106        .expect("failed to register echo");
107    registry
108        .register(Arc::new(DateTimeTool::new()))
109        .expect("failed to register datetime");
110    registry
111        .register(Arc::new(JsonTool::new()))
112        .expect("failed to register json");
113    registry
114        .register(Arc::new(RandomTool::new()))
115        .expect("failed to register random");
116    registry
117        .register(Arc::new(FileTool::new()))
118        .expect("failed to register file");
119    registry
120        .register(Arc::new(GlobTool::new()))
121        .expect("failed to register glob");
122    registry
123        .register(Arc::new(GrepTool::new()))
124        .expect("failed to register grep");
125    let file_versions = registry.file_version_store();
126    registry
127        .register(Arc::new(FileReadTool::with_version_store(
128            file_versions.clone(),
129        )))
130        .expect("failed to register file_read");
131    registry
132        .register(Arc::new(FileWriteTool::with_version_store(
133            file_versions.clone(),
134        )))
135        .expect("failed to register file_write");
136    registry
137        .register(Arc::new(FileEditTool::with_version_store(
138            file_versions.clone(),
139        )))
140        .expect("failed to register file_edit");
141    registry
142        .register(Arc::new(PatchTool::with_version_store(file_versions)))
143        .expect("failed to register patch");
144    registry
145        .register(Arc::new(CopyPathTool::new()))
146        .expect("failed to register copy_path");
147    registry
148        .register(Arc::new(MovePathTool::new()))
149        .expect("failed to register move_path");
150    registry
151        .register(Arc::new(DeletePathTool::new()))
152        .expect("failed to register delete_path");
153    registry
154        .register(Arc::new(FileListTool::new()))
155        .expect("failed to register file_list");
156    registry
157        .register(Arc::new(FileInfoTool::new()))
158        .expect("failed to register file_info");
159    registry
160        .register(Arc::new(GitStatusTool::new()))
161        .expect("failed to register git_status");
162    registry
163        .register(Arc::new(GitDiffTool::new()))
164        .expect("failed to register git_diff");
165    registry
166        .register(Arc::new(DiagnosticsTool::new(
167            registry.diagnostics_provider_slot(),
168        )))
169        .expect("failed to register diagnostics");
170    registry
171        .register(Arc::new(AskUserTool::new(registry.question_handler_slot())))
172        .expect("failed to register ask_user");
173    registry
174        .register(Arc::new(TodoTool::new(registry.todo_store())))
175        .expect("failed to register todo");
176    registry
177        .register(Arc::new(SleepTool::new()))
178        .expect("failed to register sleep");
179    registry
180        .register(Arc::new(WebFetchTool::with_extractor_slot(
181            registry.web_fetch_extractor_slot(),
182        )))
183        .expect("failed to register web_fetch");
184    registry
185        .register(Arc::new(WebSearchTool::with_provider_slot(
186            registry.web_search_provider_slot(),
187        )))
188        .expect("failed to register web_search");
189    registry
190        .register(Arc::new(CommandTool::new(registry.command_runner_slot())))
191        .expect("failed to register command");
192    registry
193        .register(Arc::new(TextTool::new()))
194        .expect("failed to register text");
195    registry
196        .register(Arc::new(TemplateTool::new()))
197        .expect("failed to register template");
198    registry
199        .register(Arc::new(MathTool::new()))
200        .expect("failed to register math");
201    registry
202        .register(Arc::new(HttpTool::new()))
203        .expect("failed to register http");
204    registry
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn builtin_max_results_schemas_require_positive_values() {
213        let registry = create_builtin_registry();
214        for tool_id in [
215            "glob",
216            "grep",
217            "file_list",
218            "git_status",
219            "diagnostics",
220            "web_search",
221        ] {
222            let schema = registry.get(tool_id).unwrap().input_schema();
223            assert_eq!(
224                schema["properties"]["max_results"]["minimum"],
225                serde_json::json!(1),
226                "{tool_id} must advertise positive max_results"
227            );
228        }
229    }
230}