Tool

Trait Tool 

Source
pub trait Tool: Send + Sync {
    // Required methods
    fn execute<'life0, 'life1, 'async_trait>(
        &'life0 self,
        params: Value,
        context: &'life1 ApplicationContext,
    ) -> Pin<Box<dyn Future<Output = Result<JobResult, ToolError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn name(&self) -> &str;
    fn description(&self) -> &str;

    // Provided method
    fn schema(&self) -> Value { ... }
}
Expand description

A trait defining the execution interface for tools.

This is compatible with rig::Tool and provides the foundation for executing tools within the riglr ecosystem. Tools represent individual operations that can be executed by the ToolWorker.

§Design Principles

  • Stateless: Tools should not maintain internal state between executions
  • Idempotent: When possible, tools should be safe to retry
  • Error-aware: Tools should classify errors as retriable or permanent
  • Resource-conscious: Tools should handle rate limits and timeouts gracefully

§Error Handling

Tools should carefully distinguish between different types of errors:

  • Retriable errors: Network timeouts, rate limits, temporary service unavailability
  • Permanent errors: Invalid parameters, insufficient funds, authorization failures
  • System errors: Internal configuration issues, unexpected state

§Examples

§Basic Tool Implementation

use riglr_core::{Tool, JobResult};
use async_trait::async_trait;
use serde_json::Value;

struct HttpFetcher;

#[async_trait]
impl Tool for HttpFetcher {
    async fn execute(&self, params: Value, _context: &ApplicationContext) -> Result<JobResult, ToolError> {
        let url = params["url"].as_str()
            .ok_or("Missing required parameter: url")?;

        // Mock HTTP client behavior for this example
        if url.starts_with("https://") {
            let body = r#"{"data": "success"}"#;
            Ok(JobResult::success(&serde_json::json!({"body": body}))?)
        } else if url.contains("error") {
            // Simulate client error
            Ok(JobResult::Failure {
                error: ToolError::permanent_string("Client error: Invalid URL")
            })
        } else if url.contains("timeout") {
            // Simulate timeout
            Ok(JobResult::Failure {
                error: ToolError::retriable_string("Request timeout: Connection timed out")
            })
        } else {
            // Simulate server error
            Ok(JobResult::Failure {
                error: ToolError::retriable_string("Server error: HTTP 503")
            })
        }
    }

    fn name(&self) -> &str {
        "http_fetcher"
    }
}

§Tool with Resource-Aware Naming

use riglr_core::{Tool, JobResult};
use async_trait::async_trait;
use serde_json::Value;

// The name starts with "solana_" which will use the solana_rpc resource limit
struct SolanaBalanceChecker;

#[async_trait]
impl Tool for SolanaBalanceChecker {
    async fn execute(&self, params: Value, _context: &ApplicationContext) -> Result<JobResult, ToolError> {
        let address = params["address"].as_str()
            .ok_or("Missing required parameter: address")?;

        // This tool will be rate-limited by the "solana_rpc" resource limit
        // because its name starts with "solana_"

        // Implementation would use Solana RPC client here...
        let balance = 1.5; // Mock balance

        Ok(JobResult::success(&serde_json::json!({
            "address": address,
            "balance": balance,
            "unit": "SOL"
        }))?)
    }

    fn name(&self) -> &str {
        "solana_balance_check"  // Name prefix determines resource pool
    }
}

§Tool Using Signer Context

use riglr_core::{Tool, JobResult, SignerContext};
use async_trait::async_trait;
use serde_json::Value;

struct TransferTool;

#[async_trait]
impl Tool for TransferTool {
    async fn execute(&self, params: Value, _context: &ApplicationContext) -> Result<JobResult, ToolError> {
        // Check if we have a signer context
        if !SignerContext::is_available().await {
            return Ok(JobResult::Failure {
                error: ToolError::permanent_string("Transfer operations require a signer context")
            });
        }

        let signer = SignerContext::current().await
            .map_err(|_| "Failed to get signer context")?;

        let recipient = params["recipient"].as_str()
            .ok_or("Missing required parameter: recipient")?;
        let amount = params["amount"].as_f64()
            .ok_or("Missing required parameter: amount")?;

        // Use the signer to perform the transfer...
        // This is just a mock implementation
        Ok(JobResult::success_with_tx(
            &serde_json::json!({
                "recipient": recipient,
                "amount": amount,
                "status": "completed"
            }),
            "mock_tx_hash_12345"
        )?)
    }

    fn name(&self) -> &str {
        "transfer"
    }
}

Required Methods§

Source

fn execute<'life0, 'life1, 'async_trait>( &'life0 self, params: Value, context: &'life1 ApplicationContext, ) -> Pin<Box<dyn Future<Output = Result<JobResult, ToolError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Execute the tool with the given parameters.

This method performs the core work of the tool. It receives parameters as a JSON value and should return a JobResult indicating success or failure.

§Parameters
  • params - JSON parameters passed to the tool. Tools should validate and extract required parameters, returning appropriate errors for missing or invalid data.
§Returns
  • Ok(JobResult::Success) - Tool executed successfully with result data
  • Ok(JobResult::Failure { retriable: true }) - Tool failed but can be retried
  • Ok(JobResult::Failure { retriable: false }) - Tool failed permanently
  • Err(Box<dyn Error>) - Unexpected system error occurred
§Error Classification Guidelines

Return retriable failures (JobResult::Failure { error: ToolError::retriable_string(...) }) for:

  • Network timeouts or connection errors
  • Rate limiting (HTTP 429, RPC rate limits) - use ToolError::rate_limited_string for these
  • Temporary service unavailability (HTTP 503)
  • Blockchain congestion or temporary RPC failures

Return permanent failures (JobResult::Failure { error: ToolError::permanent_string(...) }) for:

  • Invalid parameters or malformed requests - use ToolError::invalid_input_string for these
  • Authentication/authorization failures
  • Insufficient funds or balance
  • Invalid blockchain addresses or transaction data
§Examples
use riglr_core::{Tool, JobResult};
use async_trait::async_trait;
use serde_json::Value;

struct WeatherTool;

#[async_trait]
impl Tool for WeatherTool {
    async fn execute(&self, params: Value, _context: &ApplicationContext) -> Result<JobResult, ToolError> {
        // Validate parameters
        let city = params["city"].as_str()
            .ok_or("Missing required parameter: city")?;

        if city.is_empty() {
            return Ok(JobResult::Failure {
                error: ToolError::invalid_input_string("City name cannot be empty")
            });
        }

        // Simulate API call with mock weather service
        let weather_data = if city == "InvalidCity" {
            return Ok(JobResult::Failure {
                error: ToolError::permanent_string(format!("City not found: {}", city))
            });
        } else if city == "TimeoutCity" {
            return Ok(JobResult::Failure {
                error: ToolError::retriable_string("Network timeout")
            });
        } else {
            serde_json::json!({
                "city": city,
                "temperature": 22,
                "condition": "sunny"
            })
        };

        Ok(JobResult::success(&weather_data)?)
    }

    fn name(&self) -> &str {
        "weather"
    }
}
Source

fn name(&self) -> &str

Get the name of this tool.

The tool name is used for:

  • Tool registration and lookup in the worker
  • Job identification and logging
  • Resource limit mapping (based on name prefixes)
  • Metrics and monitoring
§Naming Conventions

Tool names should follow these patterns for automatic resource management:

  • solana_* - Uses “solana_rpc” resource pool (e.g., “solana_balance”, “solana_transfer”)
  • evm_* - Uses “evm_rpc” resource pool (e.g., “evm_balance”, “evm_swap”)
  • web_* - Uses “http_api” resource pool (e.g., “web_fetch”, “web_scrape”)
  • Others - Use default resource pool
§Returns

A string identifier for this tool that must be unique within a worker.

§Examples
use riglr_core::Tool;

struct BitcoinPriceChecker;

fn name(&self) -> &str {
    "web_bitcoin_price"  // Will use "http_api" resource pool
}
Source

fn description(&self) -> &str

A concise, AI-friendly description of this tool’s purpose.

This should be a short, human-readable sentence that explains what the tool does and when to use it. It’s intended for AI models and end-user UIs, and is separate from developer-facing rustdoc.

When using the #[tool] macro from riglr-macros, this value is:

  • Taken from the explicit #[tool(description = "...")] attribute when provided
  • Otherwise derived from the item’s doc comments
  • Falls back to an empty string if neither is present

Provided Methods§

Source

fn schema(&self) -> Value

Returns the JSON schema for this tool’s parameters.

This schema is used by AI models to understand what parameters the tool accepts and their types. It should be a valid JSON Schema object describing the parameters structure.

The default implementation returns a generic object schema that accepts any properties, which may not work well with all AI providers. Tools should override this to provide their actual parameter schema.

Implementors§