Tools-rs - Tool Collection and Execution Framework
It's pronounced tools-r-us!!
Tools-rs is a framework for building, registering, and executing tools with automatic JSON schema generation for Large Language Model (LLM) integration.
Features
- Automatic Registration - Use
#[tool]to automatically register functions with compile-time discovery - JSON Schema Generation - Automatic schema generation for LLM integration with full type information
- Type Safety - Full type safety with JSON serialization at boundaries, compile-time parameter validation
- Async Support - Built for async/await from the ground up with
tokiointegration - Error Handling - Comprehensive error types with context and proper error chaining
- LLM Integration - Export function declarations for LLM function calling APIs (OpenAI, Anthropic, etc.)
- Manual Registration - Programmatic tool registration for dynamic scenarios
- Inventory System - Link-time tool collection using the
inventorycrate for zero-runtime-cost discovery
Quick Start
use json;
use ;
/// Adds two numbers.
async
/// Greets a person.
async
async
Installation
Add the following to your Cargo.toml:
[]
= "0.1.1"
= { = "1.45", = ["macros", "rt-multi-thread"] }
= "1.0"
Crate Structure
The tools-rs system is organized as a Rust workspace with three main crates:
- tools-rs: Main entry point, re-exports the most commonly used items
- tools_core: Core runtime implementation including:
- Tool collection and execution (
ToolCollection) - JSON schema generation (
ToolSchematrait) - Error handling (
ToolError,DeserializationError) - Core data structures (
FunctionCall,ToolRegistration, etc.)
- Tool collection and execution (
- tools_macros: Procedural macros for tool registration:
#[tool]attribute macro for automatic registration#[derive(ToolSchema)]for automatic schema generation
- examples: Comprehensive examples demonstrating different use cases
For more details about the codebase organization, see CODE_ORGANIZATION.md.
Compatibility
Rust Version Support
Tools-rs requires Rust 1.70 or later and supports:
- Automatically generate JSON schemas for LLM consumption
- Execute tools safely with full type checking
- Handle errors gracefully with detailed context
Function Declarations for LLMs
Tools-rs can automatically generate function declarations suitable for LLM APIs:
use ;
/// Return the current date in ISO-8601 format.
async
async
The generated declarations follow proper JSON Schema format:
Manual Registration
While the #[tool] macro provides the most convenient way to register tools, you can also register tools manually for more dynamic scenarios:
use ToolCollection;
use json;
async
Advanced Manual Registration
For complex scenarios with custom types:
use ;
use ;
async
Examples
Check out the examples directory for comprehensive sample code:
# Run the basic example - simple tool registration and calling
# Run the function declarations example - LLM integration demo
# Run the schema example - complex type schemas and validation
# Run the newtype demo - custom type wrapping examples
Each example demonstrates different aspects of the framework:
- basic: Simple tool registration with
#[tool]and basic function calls - function_declarations: Complete LLM integration workflow with JSON schema generation
- schema: Advanced schema generation for complex nested types and collections
- newtype_demo: Working with custom wrapper types and serialization patterns
API Reference
Core Functions
collect_tools()- Discover all tools registered via#[tool]macrofunction_declarations()- Generate JSON schema declarations for LLMscall_tool(name, args)- Execute a tool by name with JSON argumentscall_tool_with(name, typed_args)- Execute a tool with typed argumentscall_tool_by_name(collection, name, args)- Execute tool on specific collectionlist_tool_names(collection)- List all available tool names
Core Types
ToolCollection- Container for registered tools with execution capabilitiesFunctionCall- Represents a tool invocation with name and argumentsToolError- Comprehensive error type for tool operationsToolSchema- Trait for automatic JSON schema generationToolRegistration- Internal representation of registered toolsFunctionDecl- LLM-compatible function declaration structure
Macros
#[tool]- Attribute macro for automatic tool registration#[derive(ToolSchema)]- Derive macro for automatic schema generation
Error Handling
Tools-rs provides comprehensive error handling with detailed context:
use ;
use json;
async
Performance Considerations
Schema Caching
- JSON schemas are generated once and cached.
- Schema generation has minimal runtime overhead after first access
- Primitive types use pre-computed static schemas for optimal performance
Tool Discovery
- Tool registration happens at compile-time via the
inventorycrate - Runtime tool collection (
collect_tools()) is a zero-cost operation - Tools are stored in efficient hash maps for O(1) lookup by name
Execution Performance
- Tool calls have minimal overhead beyond JSON serialization/deserialization
- Async execution allows for concurrent tool invocation
- Error handling uses
Resulttypes to avoid exceptions and maintain performance
Memory Usage
- Tool metadata is stored statically with minimal heap allocation
- JSON schemas are shared across all instances of the same type
- Function declarations are generated on-demand and can be cached by the application
Optimization Tips
// Reuse ToolCollection instances to avoid repeated discovery
let tools = collect_tools; // Call once, reuse multiple times
// Generate function declarations once for LLM integration
let declarations = function_declarations?;
// Cache and reuse declarations across multiple LLM requests
// Use typed parameters to avoid repeated JSON parsing
use call_tool_with;
let result = call_tool_with.await?;
Troubleshooting
Common Issues
Tool not found at runtime
- Ensure the
#[tool]macro is applied to your function - Verify the function is in a module that gets compiled (not behind unused feature flags)
- Check that
inventoryis properly collecting tools withcollect_tools()
Schema generation errors
- Ensure all parameter and return types implement
ToolSchema - For custom types, add
#[derive(ToolSchema)]to struct definitions - Complex generic types may need manual
ToolSchemaimplementations
Deserialization failures
- Verify JSON arguments match the expected parameter structure
- Check that argument names match function parameter names exactly
- Use
serdeattributes like#[serde(rename = "...")]for custom field names
Async execution issues
- All tool functions must be
async fnwhen using#[tool] - Ensure you're using
tokioruntime for async execution - Tool execution is inherently async - use
.awaitwhen calling tools
Debugging Tips
// Enable debug logging to see tool registration and execution
use ;
let tools = collect_tools;
println!;
// Inspect generated schemas
let declarations = tools.json?;
println!;
Contributing
We welcome contributions!
Development Setup
# Clone the repository
# Run tests
# Run examples
License
This project is licensed under the MIT License - see the LICENSE file for details.