//! Example: Implementing a custom Tool for ZeroClaw //! //! This shows how to add a new tool the agent can use. //! Tools are the agent's hands — they let it interact with the world. use anyhow::Result; use async_trait::async_trait; use serde_json::{json, Value}; /// Mirrors src/tools/traits.rs #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct ToolResult { pub success: bool, pub output: String, pub error: Option, } #[async_trait] pub trait Tool: Send + Sync { fn name(&self) -> &str; fn description(&self) -> &str; fn parameters_schema(&self) -> Value; async fn execute(&self, args: Value) -> Result; } /// Example: A tool that fetches a URL and returns the status code pub struct HttpGetTool; #[async_trait] impl Tool for HttpGetTool { fn name(&self) -> &str { "http_get" } fn description(&self) -> &str { "Fetch a URL and return the HTTP status code and content length" } fn parameters_schema(&self) -> Value { json!({ "type": "object", "properties": { "url": { "type": "string", "description": "URL to fetch" } }, "required": ["url"] }) } async fn execute(&self, args: Value) -> Result { let url = args["url"] .as_str() .ok_or_else(|| anyhow::anyhow!("Missing 'url' parameter"))?; match reqwest::get(url).await { Ok(resp) => { let status = resp.status().as_u16(); let len = resp.content_length().unwrap_or(0); Ok(ToolResult { success: status < 400, output: format!("HTTP {status} — {len} bytes"), error: None, }) } Err(e) => Ok(ToolResult { success: false, output: String::new(), error: Some(format!("Request failed: {e}")), }), } } } fn main() { println!("This is an example — see CONTRIBUTING.md for integration steps."); println!("Register your tool in src/tools/mod.rs default_tools()"); }