descry-tool-core 0.2.0

Core traits and types for descry-tool framework
Documentation

Descry Tool

A modern, async-first Rust framework for building LLM-compatible tools with zero-cost abstractions and compile-time registration.

Features

  • Single async Tool trait - No SyncTool/AsyncTool separation
  • Arc - No borrow checker hell, works seamlessly across await points
  • Derive-first design - One #[tool] macro generates everything
  • Thread-safe - DashMap for concurrent extensions
  • Multi-protocol adapters - Built-in MCP, OpenAI, Anthropic support
  • Tower Service integration - Middleware ecosystem support (optional)
  • Auto-generated schemas - Type-safe JSON Schema with thread-local caching

Installation

Add this to your Cargo.toml:

[dependencies]
descry-tool-core = "0.2"
descry-tool-macros = "0.2"
tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread", "sync"] }

# Optional: for async runtime
tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] }

# Optional: Tower middleware support
descry-tool-core = { version = "0.2", features = ["tower"] }
tower = "0.5"
tower-http = { version = "0.6", features = ["timeout", "limit"] }

[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread", "sync"] }

[features]
default = ["macros"]
tower = ["dep:tower"]

Quick Start

Define a Tool

use descry_tool_core::{Tool, ToolContext, ToolError};
use descry_tool_macros::tool;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::sync::Arc;

#[derive(Deserialize, JsonSchema)]
struct AddParams {
    a: i32,
    b: i32,
}

#[derive(Serialize, JsonSchema)]
struct AddOutput {
    result: i32,
}

// One macro does it all!
#[tool(
    name = "add",
    description = "Add two numbers"
)]
async fn add(_ctx: Arc<ToolContext>, params: AddParams) -> Result<AddOutput, ToolError> {
    Ok(AddOutput {
        result: params.a + params.b,
    })
}

Use the Tool

use descry_tool_core::{all_tools, call_tool, tool_names, ToolContext};
use std::sync::Arc;

#[tokio::main]
async fn main() {
    // Tools are automatically registered at compile time!
    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 Concepts

Tool Trait

The unified Tool trait with async-first design:

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>;
}

Compile-Time Registration

Tools are automatically registered using inventory:

// No manual registration needed!
// The #[tool] macro automatically generates:
//
// inventory::submit! {
//     ToolMeta {
//         name: "add",
//         description: "Add two numbers",
//         call: |ctx, params| { /* ... */ },
//         schema: || AddTool::schema(),
//         examples: || AddTool::EXAMPLES(),
//     }
// }

Context System

Thread-safe context with Arc<ToolContext>:

use descry_tool_core::ToolContext;
use std::sync::Arc;

let ctx = Arc::new(ToolContext::new());

// Insert extensions
#[derive(Debug)]
struct Database;
ctx.insert(Database);

// Get extensions
let db: Arc<Database> = ctx.get().unwrap();

Multi-Protocol Support

Generate tool specifications for different LLM platforms

use descry_tool_core::{
    adapters::{McpAdapter, OpenAiAdapter, AnthropicAdapter, ToolAdapter},
    all_tools,
};

// MCP (Model Context Protocol)
let mcp_tools: Vec<_> = all_tools()
    .map(|meta| McpAdapter::to_spec(meta))
    .collect();

// OpenAI Function Calling
let openai_tools: Vec<_> = all_tools()
    .map(|meta| OpenAiAdapter::to_spec(meta))
    .collect();

// Anthropic (Claude)
let anthropic_tools: Vec<_> = all_tools()
    .map(|meta| AnthropicAdapter::to_spec(meta))
    .collect();

Tower Middleware (Optional)

Use Tower middleware for logging, timeouts, rate limiting

use descry_tool_core::tower::{tool_service, ToolRequest};
use tower::{Service, ServiceBuilder};
use tower_http::{timeout::TimeoutLayer, limit::RateLimitLayer};
use std::time::Duration;

let service = ServiceBuilder::new()
    .layer(TimeoutLayer::new(Duration::from_secs(30)))
    .layer(RateLimitLayer::new(10, Duration::from_secs(1)))
    .service(tool_service());

let response = service.call(request).await?;

Examples

See descry-tool-core/examples/:

  • basic.rs - Basic tool definition and usage
  • macro_example.rs - Using the #[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

API Reference

Core Functions

// 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

Project Structure

descry-tool/
├── descry-tool-core/       # Core traits and types
│   ├── src/
│   │   ├── lib.rs          # Main exports
│   │   ├── tool.rs         # Tool trait
│   │   ├── context.rs      # Arc<ToolContext>
│   │   ├── error.rs        # ToolError
│   │   ├── registry.rs     # inventory registration
│   │   ├── adapters/       # Protocol adapters
│   │   └── tower.rs        # Tower Service (optional)
│   └── examples/           # Usage examples
├── descry-tool-macros/     # Procedural macros
│   └── src/
│       └── tool.rs         # #[tool] macro implementation
└── README.md