claude_agent/tools/
traits.rs1use async_trait::async_trait;
4use schemars::JsonSchema;
5use serde::de::DeserializeOwned;
6
7use super::context::ExecutionContext;
8use crate::types::{ToolDefinition, ToolResult};
9
10#[async_trait]
12pub trait Tool: Send + Sync {
13 fn name(&self) -> &str;
14 fn description(&self) -> &str;
15 fn input_schema(&self) -> serde_json::Value;
16 async fn execute(&self, input: serde_json::Value, context: &ExecutionContext) -> ToolResult;
17
18 fn definition(&self) -> ToolDefinition {
19 ToolDefinition::new(self.name(), self.description(), self.input_schema())
20 }
21}
22
23#[async_trait]
28pub trait SchemaTool: Send + Sync {
29 type Input: JsonSchema + DeserializeOwned + Send;
30 const NAME: &'static str;
31 const DESCRIPTION: &'static str;
32 const STRICT: bool = false;
33
34 async fn handle(&self, input: Self::Input, context: &ExecutionContext) -> ToolResult;
35
36 fn input_schema() -> serde_json::Value {
37 let schema = schemars::schema_for!(Self::Input);
38 let mut value =
39 serde_json::to_value(schema).unwrap_or_else(|_| serde_json::json!({"type": "object"}));
40
41 if let Some(obj) = value.as_object_mut() {
42 if !obj.contains_key("properties") {
43 obj.insert(
44 "properties".to_string(),
45 serde_json::Value::Object(serde_json::Map::new()),
46 );
47 }
48 if !obj.contains_key("additionalProperties") {
49 obj.insert(
50 "additionalProperties".to_string(),
51 serde_json::Value::Bool(true),
52 );
53 }
54 }
55
56 value
57 }
58}
59
60#[async_trait]
61impl<T: SchemaTool + 'static> Tool for T {
62 fn name(&self) -> &str {
63 T::NAME
64 }
65
66 fn description(&self) -> &str {
67 T::DESCRIPTION
68 }
69
70 fn input_schema(&self) -> serde_json::Value {
71 T::input_schema()
72 }
73
74 fn definition(&self) -> ToolDefinition {
75 let mut definition = ToolDefinition::new(T::NAME, T::DESCRIPTION, T::input_schema());
76 if T::STRICT {
77 definition = definition.with_strict(true);
78 }
79 definition
80 }
81
82 async fn execute(&self, input: serde_json::Value, context: &ExecutionContext) -> ToolResult {
83 match serde_json::from_value::<T::Input>(input) {
84 Ok(typed) => SchemaTool::handle(self, typed, context).await,
85 Err(e) => ToolResult::error(format!("Invalid input: {}", e)),
86 }
87 }
88}