# Custom Tools
Implement the `Tool` trait to add new capabilities to the agent.
## The Tool trait
```rust
#[async_trait]
pub trait Tool: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn parameters(&self) -> serde_json::Value; // JSON Schema object
async fn call(&self, args: serde_json::Value) -> ToolResult;
}
```
## Minimal example
```rust
use recursive::tools::{Tool, ToolResult};
use serde_json::{json, Value};
use async_trait::async_trait;
pub struct GetCurrentTime;
#[async_trait]
impl Tool for GetCurrentTime {
fn name(&self) -> &str { "get_current_time" }
fn description(&self) -> &str {
"Returns the current UTC time in ISO 8601 format."
}
fn parameters(&self) -> Value {
json!({
"type": "object",
"properties": {},
"required": []
})
}
async fn call(&self, _args: Value) -> ToolResult {
let now = chrono::Utc::now().to_rfc3339();
ToolResult::success(now)
}
}
```
Then register it:
```rust
let tools = ToolRegistry::local()
.register(Arc::new(GetCurrentTime));
```
## ToolResult
```rust
pub struct ToolResult {
pub content: String,
pub is_error: bool,
}
impl ToolResult {
pub fn success(content: impl Into<String>) -> Self { ... }
pub fn error(content: impl Into<String>) -> Self { ... }
}
```
## Built-in tools reference
| `read_file` | `ReadFile` | Read file contents (sandboxed) |
| `write_file` | `WriteFile` | Write or create a file |
| `apply_patch` | `ApplyPatch` | Apply a V4A patch |
| `list_dir` | `ListDir` | List directory contents |
| `run_shell` | `RunShell` | Execute a shell command |
| `search_files` | `SearchFiles` | Regex search across files |
| `web_fetch` | `WebFetch` | HTTP GET (requires `web_fetch` feature) |
| `remember` | `Remember` | Store a value in memory |
| `recall` | `Recall` | Retrieve a value from memory |
| `forget` | `Forget` | Delete a memory entry |
## Sandbox safety
All built-in filesystem tools resolve paths through `tools::resolve_within(workspace, path)`, which rejects paths that escape the workspace root (via `..`, symlinks, absolute paths, etc.).
When building custom tools that access the filesystem, use the same helper:
```rust
use recursive::tools::resolve_within;
let safe_path = resolve_within(&self.workspace, &user_provided_path)?;
```