Skip to main content

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.

Overview

Tools are NullClaw’s capability layer for LLM function calling. Every tool implements the Tool vtable interface, enabling the agent to execute system commands, file I/O, API calls, and hardware interactions.

Tool Interface

pub const Tool = struct {
    ptr: *anyopaque,
    vtable: *const VTable,

    pub const VTable = struct {
        execute: *const fn(
            ptr: *anyopaque,
            allocator: std.mem.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: std.mem.Allocator) void = null,
    };
};

pub const ToolResult = struct {
    success: bool,
    output: []const u8,       // Owned by caller, must free
    error_msg: ?[]const u8,   // Owned by caller, must free if non-null
};

Available Tools (30+)

File System Tools

ToolDescriptionParameters
file_readRead file contentspath: string, offset?: number, limit?: number
file_writeWrite/overwrite filepath: string, content: string
file_editEdit file (search/replace)path: string, old_string: string, new_string: string
file_appendAppend to filepath: string, content: string

Shell Execution

ToolDescriptionParameters
shellRun shell commandcommand: string, timeout_secs?: number

Memory Tools

ToolDescriptionParameters
memory_storeSave memory entrykey: string, content: string, category?: string
memory_recallSearch memoryquery: string, limit?: number
memory_listList all memoriescategory?: string
memory_forgetDelete memory entrykey: string

Web Tools

ToolDescriptionParameters
http_requestHTTP GET/POSTurl: string, method?: string, headers?: object, body?: string
web_searchSearch the webquery: string, num_results?: number
web_fetchFetch webpage contenturl: string, max_chars?: number
browser_openOpen URL in browserurl: string
screenshotTake screenshotoutput_path?: string

Git Tools

ToolDescriptionParameters
gitRun git commandscommand: string

Hardware Tools

ToolDescriptionParameters
hardware_infoGet board infoboard: string
hardware_memoryRead/write memoryboard: string, address: number, value?: number
i2cI2C read/writebus: number, address: number, register: number, value?: number
spiSPI transferbus: number, data: array<number>

Scheduling Tools

ToolDescriptionParameters
cron_addAdd cron jobexpression: string, command: string
cron_listList cron jobs
cron_removeRemove cron jobid: string
cron_runRun job nowid: string
cron_updateUpdate jobid: string, expression?: string, command?: string
cron_runsList recent runslimit?: number

Agent Tools

ToolDescriptionParameters
delegateDelegate to subagentagent_id: string, task: string
spawnSpawn async subagenttask: string, timeout_secs?: number
scheduleSchedule future taskwhen: string, task: string

Messaging Tools

ToolDescriptionParameters
messageSend message to channelchannel: string, target: string, message: string
pushoverSend push notificationmessage: string, priority?: number

Integration Tools

ToolDescriptionParameters
composioExecute Composio actionaction: string, params: object
imageGet image metadatapath: string

Tool Execution Flow

Step-by-Step

  1. LLM returns tool calls in ChatResponse.tool_calls:
    {
      "id": "call_abc123",
      "name": "file_read",
      "arguments": "{\"path\":\"README.md\"}"
    }
    
  2. Agent dispatcher looks up tool by name
  3. Parses arguments from JSON string to JsonObjectMap
  4. Security policy check:
    • Workspace scoping (file paths)
    • Command allowlist (shell commands)
    • Risk classification (high-risk commands)
  5. Tool execution via vtable.execute():
    const result = try tool.execute(allocator, args);
    
  6. Result formatting:
    if (result.success) {
        // Append result.output to conversation
    } else {
        // Append result.error_msg as error
    }
    
  7. Next LLM call with tool result in context

Security Boundaries

Workspace Scoping

By default, file tools are restricted to ~/.nullclaw/workspace/:
{
  "autonomy": {
    "workspace_only": true,
    "allowed_paths": []  // Additional paths outside workspace
  }
}
Path validation:
  • Null byte injection blocked
  • Symlink escape detection
  • Absolute path resolution
  • Parent directory traversal blocked (unless in allowed_paths)

Command Allowlist

Shell tool enforces command allowlist:
{
  "autonomy": {
    "allowed_commands": ["git", "npm", "zig"],  // Prefix matching
    "block_high_risk_commands": true
  }
}
Risk levels:
  • High: rm -rf, dd, mkfs, shutdown, reboot
  • Medium: sudo, curl, wget, chmod +x
  • Low: ls, cat, echo, git status

Sandbox Isolation

Tools execute within configured sandbox backend:
{
  "security": {
    "sandbox": {
      "backend": "auto"  // landlock | firejail | bubblewrap | docker
    }
  }
}
Sandbox blocks:
  • Network access (unless allowed)
  • Filesystem access outside workspace
  • Subprocess spawning (unless allowed)
  • Syscall filtering (landlock)

Configuration

Enable/Disable Tools

{
  "tools": {
    "http_request": {
      "enabled": true,
      "allowed_domains": ["api.github.com", "*.example.com"],
      "max_response_size": 1000000,
      "timeout_secs": 30
    },
    "web_search": {
      "enabled": true,
      "search_provider": "searxng",
      "search_base_url": "https://searx.example.com"
    },
    "shell": {
      "timeout_secs": 60,
      "max_output_bytes": 100000
    }
  }
}

Tool Limits

{
  "tools": {
    "max_file_size_bytes": 10485760,  // 10 MB
    "shell_timeout_secs": 60,
    "shell_max_output_bytes": 100000,
    "web_fetch_max_chars": 50000
  }
}

Tool Implementation Guide

Minimal Tool

const std = @import("std");
const Tool = @import("root.zig").Tool;
const ToolResult = @import("root.zig").ToolResult;
const JsonObjectMap = @import("root.zig").JsonObjectMap;
const getString = @import("root.zig").getString;

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

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

        // Do something with input...
        const output = try std.fmt.allocPrint(allocator, "Processed: {s}", .{input});

        return .{ .success = true, .output = output };
    }

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

    pub const vtable = @import("root.zig").ToolVTable(MyTool);
};

Register Tool

Add to src/tools/root.zig:
pub const my_tool = @import("my_tool.zig");

pub fn allTools(...) ![]Tool {
    // ...
    const mt = try allocator.create(my_tool.MyTool);
    mt.* = .{};
    try list.append(allocator, mt.tool());
    // ...
}

Memory Tools Deep Dive

memory_store

Saves structured knowledge:
{
  "key": "alice_preference_editor",
  "content": "Alice prefers vim over emacs",
  "category": "core"
}
Categories:
  • core — long-term facts
  • daily — today’s context
  • conversation — session-specific
  • Custom categories (e.g., "project_alpha")

memory_recall

Hybrid search (FTS5 + vector similarity):
{
  "query": "What editor does Alice like?",
  "limit": 5
}
Returns scored results:
[
  {
    "key": "alice_preference_editor",
    "content": "Alice prefers vim over emacs",
    "score": 0.92,
    "timestamp": "2026-03-01T12:34:56Z"
  }
]
Vector search requires embedding provider configuration. Falls back to keyword-only (FTS5) if disabled.

Web Search Providers

The web_search tool supports multiple providers:
ProviderAPI Key RequiredFeatures
SearXNGNoSelf-hosted, privacy-first
DuckDuckGoNoFree, rate-limited
BraveYesPrivacy-focused, fast
JinaYesAI-optimized search
PerplexityYesLLM-augmented search
TavilyYesResearch-grade results
ExaYesSemantic search
Configuration:
{
  "tools": {
    "http_request": {
      "search_provider": "brave",
      "search_fallback_providers": ["jina", "duckduckgo"]
    }
  }
}
Env vars: BRAVE_API_KEY, JINA_API_KEY, etc.

Hardware Tools (IoT)

Supported Boards

  • Arduino (Uno, Mega, Nano)
  • Raspberry Pi (all models, GPIO via sysfs)
  • STM32/Nucleo (via probe-rs)
  • ESP32 (via serial)

Example: Read I2C Sensor

{
  "tool": "i2c",
  "bus": 1,
  "address": 0x48,
  "register": 0x00
}
Returns raw byte value from register.

Next Steps

Configuration

Full tool configuration reference

Security

Learn about tool security policies