3 releases (breaking)
| 0.3.0 | Feb 1, 2026 |
|---|---|
| 0.2.0 | Jan 15, 2026 |
| 0.1.0 | Jan 15, 2026 |
#490 in Concurrency
92KB
2K
SLoC
thulp-registry
Async thread-safe tool registry for Thulp.
Overview
This crate provides a registry for managing tool definitions with support for dynamic registration, tagging, and discovery. The registry is designed for concurrent access in async environments.
Features
- Dynamic Registration: Register and unregister tools at runtime
- Thread-Safe: Concurrent access via
RwLockfor safe multi-threaded use - Tool Discovery: Find tools by name or tag
- Tagging System: Organize tools with custom tags
- Batch Operations: Register multiple tools at once
- Async Design: Built on tokio for async operations
Installation
Add to your Cargo.toml:
[dependencies]
thulp-registry = "0.2"
Usage
Creating a Registry
use thulp_registry::ToolRegistry;
let registry = ToolRegistry::new();
Registering Tools
use thulp_registry::ToolRegistry;
use thulp_core::{ToolDefinition, Parameter, ParameterType};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let registry = ToolRegistry::new();
// Create a tool definition
let tool = ToolDefinition::builder("read_file")
.description("Read file contents")
.parameter(
Parameter::builder("path")
.param_type(ParameterType::String)
.required(true)
.build()
)
.build();
// Register the tool
registry.register(tool).await?;
// Check if registered
assert!(registry.contains("read_file").await);
Ok(())
}
Registering Multiple Tools
use thulp_registry::ToolRegistry;
use thulp_core::{ToolDefinition, Parameter, ParameterType};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let registry = ToolRegistry::new();
let tools = vec![
ToolDefinition::builder("read_file")
.description("Read file")
.build(),
ToolDefinition::builder("write_file")
.description("Write file")
.build(),
ToolDefinition::builder("delete_file")
.description("Delete file")
.build(),
];
registry.register_many(tools).await?;
assert_eq!(registry.count().await, 3);
Ok(())
}
Retrieving Tools
use thulp_registry::ToolRegistry;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let registry = ToolRegistry::new();
// ... register tools ...
// Get specific tool
if let Some(tool) = registry.get("read_file").await? {
println!("Found tool: {}", tool.name);
}
// List all tools
let tools = registry.list().await?;
for tool in tools {
println!("Tool: {}", tool.name);
}
Ok(())
}
Tagging Tools
use thulp_registry::ToolRegistry;
use thulp_core::ToolDefinition;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let registry = ToolRegistry::new();
// Register tools
registry.register(ToolDefinition::builder("read_file").build()).await?;
registry.register(ToolDefinition::builder("write_file").build()).await?;
registry.register(ToolDefinition::builder("http_get").build()).await?;
// Tag tools by category
registry.tag("read_file", "filesystem").await?;
registry.tag("write_file", "filesystem").await?;
registry.tag("http_get", "network").await?;
// Find tools by tag
let fs_tools = registry.find_by_tag("filesystem").await?;
assert_eq!(fs_tools.len(), 2);
let net_tools = registry.find_by_tag("network").await?;
assert_eq!(net_tools.len(), 1);
Ok(())
}
Unregistering Tools
use thulp_registry::ToolRegistry;
use thulp_core::ToolDefinition;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let registry = ToolRegistry::new();
registry.register(ToolDefinition::builder("temp_tool").build()).await?;
assert!(registry.contains("temp_tool").await);
// Remove the tool
let removed = registry.unregister("temp_tool").await?;
assert!(removed.is_some());
assert!(!registry.contains("temp_tool").await);
Ok(())
}
Clearing the Registry
use thulp_registry::ToolRegistry;
#[tokio::main]
async fn main() {
let registry = ToolRegistry::new();
// ... register tools ...
// Clear all tools
registry.clear().await;
assert_eq!(registry.count().await, 0);
}
Thread Safety
The registry uses tokio::sync::RwLock internally, allowing multiple readers or a single writer at any time. All operations are safe to use from multiple async tasks concurrently.
use thulp_registry::ToolRegistry;
use std::sync::Arc;
#[tokio::main]
async fn main() {
let registry = Arc::new(ToolRegistry::new());
// Spawn multiple tasks that access the registry
let handles: Vec<_> = (0..10)
.map(|i| {
let reg = registry.clone();
tokio::spawn(async move {
// Safe concurrent access
let count = reg.count().await;
println!("Task {} sees {} tools", i, count);
})
})
.collect();
for handle in handles {
handle.await.unwrap();
}
}
Testing
cargo test -p thulp-registry
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Dependencies
~6–21MB
~224K SLoC