Skip to main content

ToolRegistry

Struct ToolRegistry 

Source
pub struct ToolRegistry<Ctx = ()>
where Ctx: Send + Sync + 'static,
{ /* private fields */ }
Expand description

A registry of tool handlers, indexed by name.

Generic over context type Ctx which is passed to tool handlers on execution. Default is () for backwards compatibility.

Provides validation of tool call arguments against their schemas and parallel execution of multiple tool calls.

§Interceptors

Tool execution can be wrapped with interceptors for cross-cutting concerns like logging, approval gates, or rate limiting:

use llm_stack::ToolRegistry;
use llm_stack::tool::tool_fn;
use llm_stack::intercept::{InterceptorStack, ToolExec, Approval, ApprovalDecision};

let mut registry: ToolRegistry<()> = ToolRegistry::new()
    .with_interceptors(
        InterceptorStack::<ToolExec<()>>::new()
            .with(Approval::new(|req| {
                if req.name.starts_with("dangerous_") {
                    ApprovalDecision::Deny("Not allowed".into())
                } else {
                    ApprovalDecision::Allow
                }
            }))
    );

Implementations§

Source§

impl<Ctx: Send + Sync + 'static> ToolRegistry<Ctx>

Source

pub fn new() -> Self

Creates an empty registry.

Source

pub fn register( &mut self, handler: impl ToolHandler<Ctx> + 'static, ) -> &mut Self

Registers a tool handler.

If a handler with the same name already exists, it is replaced.

Source

pub fn register_shared( &mut self, handler: Arc<dyn ToolHandler<Ctx>>, ) -> &mut Self

Registers a shared tool handler.

Source

pub fn get(&self, name: &str) -> Option<&Arc<dyn ToolHandler<Ctx>>>

Returns the handler for the given tool name.

Source

pub fn contains(&self, name: &str) -> bool

Returns whether a tool with the given name is registered.

Source

pub fn definitions(&self) -> Vec<ToolDefinition>

Returns the definitions of all registered tools.

Pass this to ChatParams::tools to tell the model which tools are available.

Source

pub fn len(&self) -> usize

Returns the number of registered tools.

Source

pub fn is_empty(&self) -> bool

Returns true if no tools are registered.

Source

pub fn without<'a>(&self, names: impl IntoIterator<Item = &'a str>) -> Self

Returns a new registry excluding the named tools.

Useful for creating scoped registries in Master/Worker patterns where workers should not have access to certain tools (e.g., spawn_task).

§Example
use llm_stack::ToolRegistry;

let master_registry: ToolRegistry<()> = ToolRegistry::new();
// ... register tools ...

// Workers can't spawn or use admin tools
let worker_registry = master_registry.without(["spawn_task", "admin_tool"]);
Source

pub fn only<'a>(&self, names: impl IntoIterator<Item = &'a str>) -> Self

Returns a new registry with only the named tools.

Useful for creating minimal registries with specific capabilities.

§Example
use llm_stack::ToolRegistry;

let full_registry: ToolRegistry<()> = ToolRegistry::new();
// ... register tools ...

// Read-only registry with just search tools
let search_registry = full_registry.only(["search_docs", "search_web"]);
Source

pub fn with_interceptors( self, interceptors: InterceptorStack<ToolExec<Ctx>>, ) -> Self

Sets the interceptor stack for all tool executions.

Interceptors run in the order added (first = outermost). They can inspect, modify, or block tool calls before they reach the handler.

§Example
use llm_stack::ToolRegistry;
use llm_stack::tool::tool_fn;
use llm_stack::intercept::{InterceptorStack, ToolExec, Approval, ApprovalDecision, Retry};

let registry: ToolRegistry<()> = ToolRegistry::new()
    .with_interceptors(
        InterceptorStack::<ToolExec<()>>::new()
            .with(Approval::new(|req| {
                if req.name == "dangerous" {
                    ApprovalDecision::Deny("Not allowed".into())
                } else {
                    ApprovalDecision::Allow
                }
            }))
            .with(Retry::default())
    );
Source

pub async fn execute(&self, call: &ToolCall, ctx: &Ctx) -> ToolResult

Executes a single tool call with schema validation and optional retry.

  1. Looks up the handler by ToolCall::name
  2. Validates arguments against the tool’s parameter schema
  3. Runs the call through interceptors (if any)
  4. Invokes the handler with the provided context
  5. If the tool has retry configuration and execution fails, retries with exponential backoff

Returns a ToolResult (always succeeds at the outer level). Execution errors are captured in ToolResult::is_error.

Source

pub async fn execute_all( &self, calls: &[ToolCall], ctx: &Ctx, parallel: bool, ) -> Vec<ToolResult>

Executes multiple tool calls, preserving order.

When parallel is true, all calls run concurrently via futures::future::join_all. When false, they run sequentially.

Trait Implementations§

Source§

impl<Ctx> Clone for ToolRegistry<Ctx>
where Ctx: Send + Sync + 'static,

Source§

fn clone(&self) -> Self

Clone the registry.

This is cheap — it clones Arc pointers to handlers, not the handlers themselves.

1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<Ctx> Debug for ToolRegistry<Ctx>
where Ctx: Send + Sync + 'static,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<Ctx> Default for ToolRegistry<Ctx>
where Ctx: Send + Sync + 'static,

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl McpRegistryExt for ToolRegistry<()>

Source§

async fn register_mcp_service<S: McpService + 'static>( &mut self, service: &Arc<S>, ) -> Result<usize, McpError>

Registers all tools from an MCP service. Read more
Source§

async fn register_mcp_tools_by_name<S: McpService + 'static>( &mut self, service: &Arc<S>, tool_names: &[&str], ) -> Result<usize, McpError>

Registers specific tools from an MCP service by name. Read more

Auto Trait Implementations§

§

impl<Ctx> Freeze for ToolRegistry<Ctx>

§

impl<Ctx = ()> !RefUnwindSafe for ToolRegistry<Ctx>

§

impl<Ctx> Send for ToolRegistry<Ctx>

§

impl<Ctx> Sync for ToolRegistry<Ctx>

§

impl<Ctx> Unpin for ToolRegistry<Ctx>

§

impl<Ctx = ()> !UnwindSafe for ToolRegistry<Ctx>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more