> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nullclaw/nullclaw/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools Overview

> NullClaw's extensible tool system for LLM function calling

# Tools Overview

NullClaw provides 30+ built-in tools that enable agents to perform actions in the real world. Tools follow a vtable-based architecture that allows easy extension and testing.

## Available Tools

NullClaw ships with comprehensive tool coverage across multiple domains:

### File Operations (4 tools)

* `file_read` — Read file contents with workspace scoping
* `file_write` — Write or overwrite files with path security
* `file_edit` — Find and replace text in files
* `file_append` — Append content to files

### Shell & Git (2 tools)

* `shell` — Execute shell commands with security policy validation
* `git_operations` — Structured Git operations (status, diff, log, commit, add, checkout, stash)

### Memory (4 tools)

* `memory_store` — Persist facts to long-term memory
* `memory_recall` — Search memory with hybrid retrieval
* `memory_list` — List all stored memories
* `memory_forget` — Delete memory entries

### Web & Browser (4 tools)

* `http_request` — Make HTTP requests with domain allowlisting
* `web_search` — Search via multiple providers (Brave, DuckDuckGo, Jina, Perplexity, etc.)
* `web_fetch` — Fetch and parse web pages with Jina Reader
* `browser` — Open URLs and fetch page content
* `browser_open` — Open allowlisted URLs in default browser

### Scheduling & Delegation (3 tools)

* `schedule` — Schedule future tool executions
* `delegate` — Delegate tasks to named agents
* `spawn` — Launch async subagent tasks

### Hardware (5 tools)

* `hardware_info` — Query connected hardware boards
* `hardware_memory` — Read/write hardware memory regions
* `i2c` — I2C bus operations
* `spi` — SPI bus operations (planned)
* `screenshot` — Capture screen regions

### Cron (6 tools)

* `cron_add` — Register cron jobs
* `cron_list` — List scheduled jobs
* `cron_remove` — Delete jobs
* `cron_runs` — View job execution history
* `cron_run` — Manually trigger job
* `cron_update` — Modify existing job

### Integrations (4 tools)

* `composio` — Composio API integration
* `pushover` — Pushover notifications
* `message` — Send messages to channels
* `image` — Image analysis and manipulation

## Tool Architecture

### Tool vtable Interface

All tools implement the `Tool` vtable interface defined in `src/tools/root.zig`:

```zig theme={null}
pub const Tool = struct {
    ptr: *anyopaque,
    vtable: *const VTable,

    pub const VTable = struct {
        execute: *const fn (ptr: *anyopaque, allocator: Allocator, args: JsonObjectMap) anyerror!ToolResult,
        name: *const fn (ptr: *anyopaque) []const u8,
        description: *const fn (ptr: *anyopaque) []const u8,
        parameters_json: *const fn (ptr: *anyopaque) []const u8,
        deinit: ?*const fn (ptr: *anyopaque, allocator: Allocator) void = null,
    };
};
```

Every tool must provide:

* `name()` — Tool identifier for LLM function calling
* `description()` — Human-readable description
* `parameters_json()` — JSON Schema for tool parameters
* `execute()` — Core execution logic
* `deinit()` — Optional cleanup function

### ToolResult

Tools return a `ToolResult` struct indicating success/failure:

```zig theme={null}
pub const ToolResult = struct {
    success: bool,
    output: []const u8,      // Heap-allocated, caller must free
    error_msg: ?[]const u8,  // Heap-allocated error if not null
};
```

**Ownership rules:**

* `output` and `error_msg` are owned by the caller
* Use `allocator.free()` to free them after use
* Exception: static strings from `ToolResult.ok("")` or `ToolResult.fail("literal")` must NOT be freed

### Tool Execution Flow

1. **LLM generates function call** with tool name and JSON arguments
2. **Dispatcher parses JSON** into `std.json.ObjectMap`
3. **Tool vtable execute()** is called with parsed args
4. **Tool validates inputs** using `root.getString()`, `root.getInt()`, `root.getBool()` helpers
5. **Tool performs action** (read file, run shell command, etc.)
6. **ToolResult returned** with success/failure and output
7. **Result serialized** back to LLM as assistant message

### Argument Extraction Helpers

Tools use type-safe helpers to extract arguments from the parsed JSON:

```zig theme={null}
const path = root.getString(args, "path") orelse 
    return ToolResult.fail("Missing 'path' parameter");

const limit = root.getInt(args, "limit") orelse 5;

const cached = root.getBool(args, "cached") orelse false;
```

## Tool Registration

Tools are registered in factory functions:

### Default Tools (3 core tools)

```zig theme={null}
pub fn defaultTools(
    allocator: std.mem.Allocator,
    workspace_dir: []const u8,
) ![]Tool {
    // Returns: shell, file_read, file_write, file_edit
}
```

### All Tools (30+ tools)

```zig theme={null}
pub fn allTools(
    allocator: std.mem.Allocator,
    workspace_dir: []const u8,
    opts: struct {
        http_enabled: bool = false,
        browser_enabled: bool = false,
        memory: ?Memory = null,
        // ... more config
    },
) ![]Tool
```

### Subagent Tools (restricted subset)

```zig theme={null}
pub fn subagentTools(
    allocator: std.mem.Allocator,
    workspace_dir: []const u8,
    opts: struct {
        http_enabled: bool = false,
        allowed_paths: []const []const u8 = &.{},
    },
) ![]Tool
```

Subagent tools exclude:

* `message` — prevent cross-channel side effects
* `spawn`, `delegate` — prevent infinite loops
* `schedule` — prevent uncontrolled scheduling
* `memory_*` — prevent memory pollution
* `composio`, `browser_open` — reduce blast radius

## Tool Configuration

Tools accept configuration at initialization:

```zig theme={null}
const st = try allocator.create(shell.ShellTool);
st.* = .{
    .workspace_dir = workspace_dir,
    .allowed_paths = opts.allowed_paths,
    .timeout_ns = 60 * std.time.ns_per_s,
    .max_output_bytes = 1_048_576,
    .policy = opts.policy,  // SecurityPolicy for command validation
};
```

## Security Model

### Workspace Path Scoping

All file/shell tools enforce workspace path scoping:

* Relative paths resolved within `workspace_dir`
* Absolute paths require `allowed_paths` configuration
* Symlinks validated after resolution
* Path traversal (`../`) blocked by `isPathSafe()`
* System-sensitive paths (e.g., `/etc/passwd`) blocked

### Command Validation

Shell and Git tools sanitize inputs:

* Block shell metacharacters (`;`, `|`, `>`, `` ` ``, `$()`, etc.)
* Block dangerous git options (`--exec=`, `--pager=`, `-c`)
* Validate against `SecurityPolicy.allowed_commands`
* Require approval for medium/high risk commands

### Domain Allowlisting

HTTP/browser tools enforce domain allowlists:

* Only `https://` URLs accepted (no `http://`)
* Host extracted and validated against `allowed_domains`
* Subdomain matching supported (`api.example.com` matches `example.com`)
* Localhost and private IPs blocked by default

## Creating a Custom Tool

Example tool implementation:

```zig theme={null}
const std = @import("std");
const root = @import("root.zig");
const Tool = root.Tool;
const ToolResult = root.ToolResult;
const JsonObjectMap = root.JsonObjectMap;

pub const MyTool = struct {
    config_value: []const u8,

    pub const tool_name = "my_tool";
    pub const tool_description = "Does something useful";
    pub const tool_params =
        \\{"type":"object","properties":{"input":{"type":"string"}},"required":["input"]}
    ;

    const vtable = root.ToolVTable(@This());

    pub fn tool(self: *MyTool) Tool {
        return .{
            .ptr = @ptrCast(self),
            .vtable = &vtable,
        };
    }

    pub fn execute(self: *MyTool, allocator: std.mem.Allocator, args: JsonObjectMap) !ToolResult {
        const input = root.getString(args, "input") orelse
            return ToolResult.fail("Missing 'input' parameter");

        // Do work...
        const output = try std.fmt.allocPrint(allocator, "Processed: {s}", .{input});
        return ToolResult{ .success = true, .output = output };
    }
};
```

Register in factory:

```zig theme={null}
const my_tool = try allocator.create(MyTool);
my_tool.* = .{ .config_value = "example" };
try tool_list.append(allocator, my_tool.tool());
```

## Testing Tools

Tools include comprehensive test coverage:

```zig theme={null}
test "my_tool basic execution" {
    var mt = MyTool{ .config_value = "test" };
    const t = mt.tool();
    const parsed = try root.parseTestArgs("{\"input\": \"hello\"}");
    defer parsed.deinit();
    const result = try t.execute(std.testing.allocator, parsed.value.object);
    defer if (result.output.len > 0) std.testing.allocator.free(result.output);
    defer if (result.error_msg) |e| std.testing.allocator.free(e);
    
    try std.testing.expect(result.success);
    try std.testing.expect(std.mem.indexOf(u8, result.output, "hello") != null);
}
```

## Schema Cleaning

NullClaw automatically cleans JSON schemas for LLM compatibility using `src/tools/schema.zig`:

* **Gemini** — strips most validation keywords (minLength, pattern, format, etc.)
* **Anthropic** — removes \$ref and definitions
* **OpenAI** — minimal cleaning (most permissive)

Schemas are cleaned before sending to the LLM:

```zig theme={null}
const cleaned = try SchemaCleanr.cleanForGemini(allocator, tool.parametersJson());
```

## Next Steps

* [File Operations](/tools/file-operations) — Read, write, edit, append files
* [Shell](/tools/shell) — Execute shell commands with security policies
* [Memory](/tools/memory) — Persistent memory with hybrid search
* [Browser](/tools/browser) — Web browsing and content fetching
* [Git](/tools/git) — Structured Git operations
