# descry-tool-core
[](https://crates.io/crates/descry-tool-core)
[](https://docs.rs/descry-tool-core)
[](https://opensource.org/licenses/MIT)
Core traits and types for the descry-tool framework - a modern, async-first Rust framework for building LLM-compatible tools.
## Features
- **Unified async Tool trait** - Single trait for all tools, no SyncTool/AsyncTool separation
- **Arc<ToolContext>** - Thread-safe context that works seamlessly across await points
- **Compile-time registration** - Zero-cost tool registration using `inventory`
- **Thread-safe extensions** - Concurrent extensions using `DashMap`
- **Multi-protocol adapters** - Built-in support for MCP, OpenAI, and Anthropic
- **Tower Service integration** - Optional middleware ecosystem support
- **Auto-generated schemas** - Type-safe JSON Schema with thread-local caching
- **Error chaining** - Rich error types with `thiserror` and `#[source]` support
## Quick Start
### Define a Tool
```rust
use descry_tool_core::{Tool, ToolContext, ToolError};
use serde::{Deserialize, Serialize};
use schemars::JsonSchema;
use std::sync::Arc;
#[derive(Deserialize, JsonSchema)]
struct AddParams {
a: i32,
b: i32,
}
#[derive(Serialize, JsonSchema)]
struct AddOutput {
result: i32,
}
struct AddTool;
impl Tool for AddTool {
type Params = AddParams;
type Output = AddOutput;
const NAME: &'static str = "add";
const DESCRIPTION: &'static str = "Add two numbers";
async fn call(
_ctx: Arc<ToolContext>,
params: Self::Params,
) -> Result<Self::Output, ToolError> {
Ok(AddOutput {
result: params.a + params.b,
})
}
}
// Register the tool
inventory::submit! {
descry_tool_core::ToolMeta {
name: AddTool::NAME,
description: AddTool::DESCRIPTION,
call: |ctx, params| {
Box::pin(async move {
let params: AddParams = serde_json::from_value(params)?;
let result = <AddTool as Tool>::call(ctx, params).await?;
Ok(serde_json::to_value(result)?)
})
},
schema: || AddTool::schema(),
examples: || AddTool::EXAMPLES,
}
}
```
### Use the Tool
```rust
use descry_tool_core::{call_tool, tool_names, ToolContext};
use std::sync::Arc;
#[tokio::main]
async fn main() {
// List all registered tools
let names = tool_names();
println!("Available tools: {:?}", names);
// Call a tool
let ctx = Arc::new(ToolContext::new());
let params = serde_json::json!({"a": 5, "b": 3});
let result = call_tool("add", params, ctx).await.unwrap();
println!("Result: {}", result);
}
```
## Core API
### Tool Trait
```rust
pub trait Tool: Send + Sync + 'static {
type Params: DeserializeOwned + JsonSchema + Send + Sync + 'static;
type Output: Serialize + JsonSchema + Send + Sync + 'static;
const NAME: &'static str;
const DESCRIPTION: &'static str;
const EXAMPLES: &'static [(&'static str, &'static str)] = &[];
fn schema() -> &'static serde_json::Value;
async fn call(
ctx: Arc<ToolContext>,
params: Self::Params
) -> Result<Self::Output, ToolError>;
}
```
### Context System
```rust
use descry_tool_core::ToolContext;
use std::sync::Arc;
let ctx = Arc::new(ToolContext::new());
// Store extensions
#[derive(Debug)]
struct Database;
ctx.insert(Database);
// Retrieve extensions
let db: Arc<Database> = ctx.get().unwrap();
```
### Registry Functions
```rust
// Get all registered tools
pub fn all_tools() -> impl Iterator<Item = &'static ToolMeta>
// Find tool by name
pub fn find_tool(name: &str) -> Option<&'static ToolMeta>
// Call a tool
pub async fn call_tool(
name: &str,
params: serde_json::Value,
ctx: Arc<ToolContext>
) -> Result<serde_json::Value, ToolError>
// Get tool names
pub fn tool_names() -> Vec<&'static str>
// Check if tool exists
pub fn tool_exists(name: &str) -> bool
```
## Multi-Protocol Adapters
### MCP (Model Context Protocol)
```rust
use descry_tool_core::adapters::{McpAdapter, ToolAdapter};
let mcp_spec = McpAdapter::to_spec(tool_meta);
// {
// "name": "add",
// "description": "Add two numbers",
// "inputSchema": {...}
// }
```
### OpenAI Function Calling
```rust
use descry_tool_core::adapters::{OpenAiAdapter, ToolAdapter};
let openai_spec = OpenAiAdapter::to_spec(tool_meta);
// {
// "type": "function",
// "function": {
// "name": "add",
// "description": "Add two numbers",
// "parameters": {...}
// }
// }
```
### Anthropic (Claude)
```rust
use descry_tool_core::adapters::{AnthropicAdapter, ToolAdapter};
let anthropic_spec = AnthropicAdapter::to_spec(tool_meta);
// {
// "name": "add",
// "description": "Add two numbers",
// "input_schema": {...}
// }
```
## Tower Service (Optional)
Enable the `tower` feature for middleware support:
```toml
[dependencies]
descry-tool-core = { version = "0.3", features = ["tower"] }
tower = "0.5"
```
```rust
use descry_tool_core::tower::{tool_service, ToolRequest};
use tower::{Service, ServiceBuilder};
use std::time::Duration;
let service = ServiceBuilder::new()
.timeout(Duration::from_secs(30))
.service(tool_service());
let response = service.call(request).await?;
```
## Error Handling
```rust
use descry_tool_core::ToolError;
match call_tool("add", params, ctx).await {
Ok(result) => println!("Success: {:?}", result),
Err(ToolError::ToolNotFound(name)) => {
eprintln!("Tool '{}' not found", name);
}
Err(ToolError::InvalidParameters(msg)) => {
eprintln!("Invalid parameters: {}", msg);
}
Err(e) => {
eprintln!("Error: {}", e);
}
}
```
## Examples
See the [examples](./examples/) directory:
- `basic.rs` - Basic tool definition and usage
- `macro_example.rs` - Using with `#[tool]` macro
- `complete_example.rs` - Multiple tools with error handling
- `multi_protocol.rs` - Multi-protocol adapter usage
- `tower_basic.rs` - Tower Service integration
- `tower_middleware.rs` - Tower middleware example
- `debug_schema.rs` - Schema generation debugging
## Related Crates
- [`descry-tool-macros`](https://crates.io/crates/descry-tool-macros) - Procedural macros for simplified tool definition
## Requirements
- Rust 1.85.0+ (Edition 2024)
## License
MIT