Tool Trait in OpenHuman: Architecture, Implementation, and Security Model
The Tool trait in OpenHuman defines the core contract for agent-callable tools, requiring implementations to provide a run method that executes under a sandboxed context with specific permissions, while being Send + Sync for concurrent use across the runtime.
The Tool trait serves as the foundational interface enabling LLM agents to safely execute arbitrary functionality within the OpenHuman framework. Located in the tinyhumansai/openhuman repository, this trait establishes a strict contract between the agent runtime and external capabilities, ensuring that every tool invocation adheres to the project's security policies and sandboxing requirements.
Core Definition and Source Locations
The Tool trait originates in the vendored tinytools crate, a submodule of the tinyagents dependency, and is re-exported through the core source to provide a unified public API.
According to the source code, the trait is publicly exposed at:
src/openhuman/tools/traits.rs– The primary entry point that re-exportsTool,ToolResult, andToolArgs.
The actual definition resides in the vendored dependency at:
vendor/tinyagents/vendor/tinytools/src/lib.rs– Contains the original trait definition and supporting types.
This re-export pattern guarantees type compatibility across the entire system. By avoiding duplicate definitions, the core prevents type-mismatch bugs when the sandbox, policy engine, and dispatcher interact with tool implementations.
Trait Contract and Required Methods
Any type implementing the Tool trait must satisfy a minimal but strict interface designed for safety and concurrency.
Method Signature
The single required method follows this signature:
fn run(&self, ctx: &dyn ToolRunContext, args: Self::Args) -> ToolResult;
The trait uses an associated type Args for the input parameters, allowing each tool to define its own strongly-typed argument struct. These arguments must derive serde::Deserialize so the dispatcher can automatically parse JSON payloads from the LLM.
Thread Safety Requirements
OpenHuman enforces strict concurrency constraints on tool implementations. The trait requires Send + Sync bounds, ensuring that tool instances can be shared safely across multiple async tasks within the core runtime. This design supports high-throughput agent systems where multiple turns may invoke tools concurrently.
Implementing a Custom Tool
Creating a functional tool requires implementing the trait with a concrete argument type. Below is a complete implementation of a simple "echo" tool that returns the provided message.
use openhuman::tools::traits::*;
/// Arguments accepted by the tool – must derive `serde::Deserialize`.
#[derive(Debug, serde::Deserialize)]
pub struct EchoArgs {
pub message: String,
}
/// The concrete tool implementation.
pub struct EchoTool;
impl Tool for EchoTool {
type Args = EchoArgs;
fn run(
&self,
_ctx: &dyn ToolRunContext,
args: Self::Args,
) -> ToolResult {
// The tool simply returns the provided message.
ToolResult::content(args.message)
}
// Optional: declare required permission and sandbox mode.
fn permission(&self) -> PermissionLevel {
PermissionLevel::Read // no privileged access needed
}
fn sandbox_mode(&self) -> SandboxMode {
SandboxMode::None // runs without a sandbox
}
}
The implementation demonstrates three key patterns: defining a deserializable Args struct, providing the mandatory run method, and declaratively specifying security characteristics through optional trait methods.
Registration and Runtime Integration
Before a tool becomes available to the LLM, it must be registered with the runtime's tool registry located in src/openhuman/tools/ops.rs.
Registering with the Tool Registry
use openhuman::tools::ops::*; // contains the registration helper
fn register_echo_tool(registry: &mut ToolRegistry) {
registry.register(
"echo", // tool name used in the LLM schema
EchoTool, // the concrete implementation
// The schema is automatically derived from `EchoArgs`
);
}
After registration, the tool appears in the LLM's tool catalogue, enabling the model to generate structured calls. The dispatcher—implemented in src/openhuman/agent/dispatcher.rs—parses these calls and routes them to the appropriate Tool::run implementation.
Execution Hooks
The runtime supports intervention through the ToolHook trait defined in src/openhuman/agent/hooks.rs. Hooks can execute before or after a tool runs, enabling logging, validation, or modification of arguments and results without changing the tool implementation.
Security Model and Sandboxing
Every tool invocation passes through multiple security layers before execution begins.
Permission Levels and Scopes
Tools declare their required access level through the permission() method, returning a PermissionLevel enum value. The core compares this against the agent's active SecurityPolicy and the tool's ToolScope before allowing execution.
Sandboxing Strategy
If a tool requests filesystem or network access, the sandbox_mode() return value determines isolation behavior:
SandboxMode::None– Runs without restrictions (appropriate for pure computation)SandboxMode::Filesystem– Restricts file operations to specific directoriesSandboxMode::Network– Isolates network calls according to policy
The sandbox subsystem enforces these constraints at runtime, preventing tools from exceeding their declared capabilities even if compromised.
Summary
- The
Tooltrait defines a singlerunmethod accepting a context and typed arguments, returning aToolResult. - The trait is re-exported from the vendored tinytools crate at
src/openhuman/tools/traits.rsto ensure type consistency across the codebase. - Implementations must be
Send + Syncto support safe concurrent execution within the async runtime. - Tools integrate with OpenHuman's security model through
PermissionLeveldeclarations andSandboxModespecifications. - The
ToolRegistryinsrc/openhuman/tools/ops.rsexposes tools to the LLM, while thedispatcherroutes invocations andToolHookimplementations enable cross-cutting concerns.
Frequently Asked Questions
Where is the Tool trait defined in OpenHuman?
The trait is originally defined in the vendored tinytools crate at vendor/tinyagents/vendor/tinytools/src/lib.rs and publicly re-exported through src/openhuman/tools/traits.rs. This architecture ensures that all components reference the same type definition, preventing interface mismatches between the core runtime and external tool implementations.
What methods must a Tool implementation provide?
At minimum, implementers must provide the run method with the signature fn run(&self, ctx: &dyn ToolRunContext, args: Self::Args) -> ToolResult and specify the associated Args type. Optional methods include permission() and sandbox_mode(), which declare the tool's security requirements and isolation preferences to the policy engine.
How does OpenHuman ensure thread safety for tools?
The Tool trait requires Send + Sync bounds on all implementations. This constraint allows the core runtime to share tool references across multiple concurrent tasks and async workers without risking data races or undefined behavior during parallel tool invocations.
What happens during a tool invocation from the LLM?
The dispatcher (src/openhuman/agent/dispatcher.rs) parses the model-generated tool call, validates the arguments against the registered schema, and checks the tool's permissions against the active SecurityPolicy. If approved, any registered ToolHook implementations execute, followed by the actual run method invocation within the specified sandbox constraints. The resulting ToolResult is marshalled back into the model's context window.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →