How the tinytools Tool Trait Bridges OpenHuman Host and tinyagents

The tinytools Tool trait provides a shared, type-safe interface between the OpenHuman host and the tinyagents harness, allowing LLM tools to be defined once in the host and executed seamlessly by the agent runtime without serialization overhead or type mismatches.

The OpenHuman architecture strictly separates core business logic from agent orchestration using a vendored tinytools crate. By importing this crate, both the host (src/openhuman) and the agent harness (tinyagents) share an identical definition of the tinytools Tool trait, ensuring compile-time guarantees for tool compatibility across the architectural boundary.

Where the tinytools Tool Trait Is Defined

Core Definition in the tinytools Crate

The canonical trait definition resides in vendor/tinyagents/vendor/tinytools/crates/tinytools/src/lib.rs. This lightweight library is vendored inside the repository and consumed as a dependency by both the host and tinyagents, serving as the single source of truth for the tool interface.

Host-Side Re-export

Rather than duplicating the interface, the host re-exports the trait in src/openhuman/tools/traits.rs. This zero-cost abstraction ensures that when the host implements Tool, it implements the exact same type that tinyagents expects at runtime, eliminating any adapter boilerplate or serialization bridges.

The Tool Trait Interface

Required Methods

The trait defines six methods that establish the contract between the LLM and the host implementation:

  • fn name(&self) -> &'static str – Returns the human-readable identifier used in the tool catalogue.
  • fn description(&self) -> &'static str – Provides the description injected into the LLM prompt.
  • fn input_schema(&self) -> tinytools::json::Value – Supplies the JSON schema for validating model-generated arguments.
  • fn run(&self, args: tinytools::json::Value, ctx: &dyn tinytools::ToolRunContext) -> tinytools::ToolResult – Executes the tool logic with access to runtime context.
  • fn host_extension(&self) -> Option<&dyn std::any::Any> – Optional hook for attaching host-specific data like workspace paths.
  • fn host_call_extension(&self, data: &dyn std::any::Any) -> tinytools::ToolResult – Optional callback for delegating side effects back to the host.

Type Safety Across the Boundary

Because both sides import the same trait from vendor/tinyagents/vendor/tinytools/crates/tinytools/src/lib.rs, the Rust compiler guarantees that any struct implementing Tool in the host satisfies the interface expected by tinyagents::harness::tool_calling::dialect. There is no risk of type mismatches or version skew between the catalogue generation and the execution phase.

Implementing and Registering Host Tools

Step 1: Implement the Trait

Create a struct in src/openhuman/tools/ and implement the Tool trait with the required metadata and execution logic:

// src/openhuman/tools/example_tool.rs
use tinytools::{Tool, ToolResult, ToolRunContext};

pub struct EchoTool;

impl Tool for EchoTool {
    fn name(&self) -> &'static str { "echo" }
    
    fn description(&self) -> &'static str { 
        "Returns the supplied text unchanged." 
    }

    fn input_schema(&self) -> serde_json::Value {
        serde_json::json!({
            "type": "object",
            "properties": {
                "text": { "type": "string" }
            },
            "required": ["text"]
        })
    }

    fn run(
        &self,
        args: serde_json::Value,
        _ctx: &dyn ToolRunContext,
    ) -> ToolResult {
        let text = args.get("text")
            .and_then(|v| v.as_str())
            .ok_or_else(|| tinytools::error("Missing `text`"))?;
        Ok(tinytools::ToolContent::String(text.to_owned()))
    }
}

Step 2: Expose via the Tool Registry

Register the implementation in src/openhuman/tools/ops.rs by adding it to the all_host_tools() vector:

// src/openhuman/tools/ops.rs (excerpt)
use crate::tools::example_tool::EchoTool;

pub fn all_host_tools() -> Vec<Box<dyn Tool>> {
    vec![
        Box::new(EchoTool),
        // … other tools
    ]
}

Step 3: Startup Registration

During core initialization, the host instantiates a ToolGroup and registers the list returned by all_host_tools() with the tinyagents dispatcher in src/openhuman/agent/dispatcher.rs. This registration makes the catalogue metadata available for prompt generation and the run implementations available for runtime dispatch.

How tinyagents Discovers and Executes Tools

Tool Discovery

The tinyagents::harness::tool_calling::dialect module iterates over registered Tool objects to build a catalogue. The render_pformat_catalogue function serializes each tool's name, description, and input_schema into the system prompt, informing the LLM of available capabilities.

Execution Flow

When the model emits a tool call, the following sequence occurs:

  1. The dialect parser (tinyagents::harness::tool_calling::dialect::parse_call) extracts the tool name and arguments from the LLM output.
  2. The dispatcher in src/openhuman/agent/dispatcher.rs performs a lookup in the host's registry to obtain a &dyn Tool.
  3. The dispatcher invokes the run method, passing the parsed arguments and a ToolRunContext implementation.
  4. The context object provides access to turn budget, thread ID, and workspace root while remaining type-safe and host-specific.

Host Extensions for Advanced Use Cases

Attaching Host Data

The host_extension() method allows tools to expose references to host-owned values stored as dyn Any. This is essential for tools that need access to resources like temporary directories or sandbox tokens that cannot be hardcoded into the tool definition.

Delegating Side Effects

The host_call_extension(&self, data: &dyn Any) method enables the host to invoke callbacks on the extension data. This lets tools delegate privileged operations—such as filesystem writes or network calls—back to the host without breaking the Tool contract or introducing host dependencies into the trait.

Optional Implementation

These extension methods are strictly optional. The majority of tools implement only the core quartet (name, description, input_schema, run) while relying on the ToolRunContext parameter for runtime state.

Summary

  • The tinytools Tool trait is defined in vendor/tinyagents/vendor/tinytools/crates/tinytools/src/lib.rs and re-exported by the host in src/openhuman/tools/traits.rs to ensure zero type duplication.
  • The trait contract includes metadata methods, JSON schema validation, and the run execution hook that accepts a ToolRunContext.
  • Host tools are aggregated in src/openhuman/tools/ops.rs and registered with the dispatcher at startup in src/openhuman/agent/dispatcher.rs.
  • tinyagents uses tinyagents::harness::tool_calling::dialect for catalogue generation and parse_call for dispatch, invoking the host's run method directly through trait objects.
  • Optional host_extension and host_call_extension hooks allow tools to access host resources while maintaining strict architectural separation.

Frequently Asked Questions

What is the tinytools Tool trait in OpenHuman?

The tinytools Tool trait is a Rust interface defined in the vendored tinytools crate that standardizes how tools are named, described, validated, and executed. It acts as the single source of truth for both the OpenHuman host and the tinyagents harness, ensuring they agree on tool signatures at compile time.

How does the host register tools with tinyagents?

The host implements the Tool trait for each capability, aggregates instances in src/openhuman/tools/ops.rs within the all_host_tools() function, and passes this list to the dispatcher during initialization in src/openhuman/agent/dispatcher.rs. This registration binds the tool catalogue to the runtime executor.

What is the ToolRunContext used for?

The ToolRunContext trait provides runtime environmental data—such as budget constraints, thread identifiers, and workspace paths—to the run method. It is passed as a trait object (&dyn ToolRunContext) to maintain type erasure on the tinyagents side while allowing host-specific context implementations.

How do host extensions work?

Host extensions use the host_extension() method to attach host-specific data (as dyn Any) to a tool instance, and host_call_extension() to invoke host-side callbacks on that data. These optional methods allow tools to perform privileged operations or access external resources by delegating to the host rather than implementing logic directly in the trait.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →