Skip to main content

ToolRegistry

Struct ToolRegistry 

Source
pub struct ToolRegistry { /* private fields */ }
Expand description

Tool registry: holds tools, responsible for lookup and execution by name.

use molo::tool::{SharedState, Tool, ToolError, ToolRegistry, ToolSchema};
use serde_json::json;

struct Calculator;
#[molo::async_trait]
impl Tool for Calculator {
    fn schema(&self) -> ToolSchema {
        ToolSchema {
            name: "calculator".into(),
            description: "Calculate".into(),
            parameters: json!({ "type": "object", "properties": {} }),
        }
    }
    async fn call(
        &self,
        _arguments: serde_json::Value,
        _state: &SharedState,
    ) -> Result<String, ToolError> {
        Ok("42".into())
    }
}

let mut registry = ToolRegistry::new();
registry.register(Calculator);
let state = SharedState::new();
// The model requests a tool by name; error classification (not
// registered / args not JSON / execution failed) rides along with Err.
let result = registry.call("calculator", "{}", &state).await?;
assert_eq!(result, "42");
// Allowlist subset: the sub-registry shares the same tool instances as
// the main registry.
let sub = registry.subset(&["calculator"]).unwrap();
assert_eq!(sub.names(), vec!["calculator"]);
  • same-named tools: later registration replaces — registering a same-named tool replaces it in place, so the registry never holds duplicates (the semantics of updating a registered tool, no new entry, stable order);
  • internally held as a single IndexMap<String, Arc<dyn Tool>>: O(1) lookup by name while preserving registration order (order affects how the model chooses tools on the wire);
  • Arc<dyn Tool> sharing: tool instances can be shared across registries — the sub-registry produced by subset shares the same tool instances as the main registry (the scenario where a main agent creates sub-agents with a restricted tool set);
  • call returns Result<String, RegistryError> — classification rides along with Err (tool not found / args not JSON / execution failed), and Err’s Display is the “error-to-text” the agent loop can pass straight back to the model (see RegistryError); callers that need to bypass the registry’s argument parsing can grab the tool directly with get;
  • Debug prints the registration-name list (in registration order, handy for debugging).

Implementations§

Source§

impl ToolRegistry

Source

pub fn new() -> Self

Create an empty registry.

Source

pub fn register(&mut self, tool: impl Tool + 'static) -> &mut Self

Register a tool; returns self for chaining.

When a same-named tool is registered again, the later one replaces the earlier (keeping its original position), which fits updating a registered tool with a new instance.

Source

pub fn names(&self) -> Vec<String>

Names of currently registered tools, in registration order (same-named tools already deduplicated).

Source

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

Remove a registered tool; returns true when removed, false when the tool does not exist.

For swapping tool sets at runtime (e.g. removing framework-injected tools when switching assembly modes); remaining tools keep their registration order.

Source

pub fn retain(&mut self, keep: impl FnMut(&str) -> bool) -> Vec<String>

Bulk-trim in place by name: removes tools whose names fail keep, returning the removed tool names (in original registration order); remaining tools keep their registration order.

Complements subset: subset leaves this table untouched and produces an allowlist sub-table sharing tool instances with the main table; this method mutates this table in place, suitable for bulk-removing tools at runtime — e.g. clearing all tools of an MCP server by its namespace prefix when unloading it:

use molo::tool::{SharedState, Tool, ToolError, ToolRegistry, ToolSchema};
use serde_json::json;

struct Named(&'static str);
#[molo::async_trait]
impl Tool for Named {
    fn schema(&self) -> ToolSchema {
        ToolSchema {
            name: self.0.into(),
            description: self.0.into(),
            parameters: json!({}),
        }
    }
    async fn call(
        &self,
        _arguments: serde_json::Value,
        _state: &SharedState,
    ) -> Result<String, ToolError> {
        Ok("ok".into())
    }
}

let mut registry = ToolRegistry::new();
registry
    .register(Named("filesystem__read_file"))
    .register(Named("filesystem__list_dir"))
    .register(Named("calculator"));
// Strip all tools of an MCP server (their names carry the
// "filesystem__" prefix):
let removed = registry.retain(|name| !name.starts_with("filesystem__"));
assert_eq!(removed, ["filesystem__read_file", "filesystem__list_dir"]);
assert_eq!(registry.names(), ["calculator"]);
Source

pub fn schemas(&self) -> Vec<ToolSchema>

All tools’ definitions for the model, in registration order (no duplicates, guaranteed at registration).

Source

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

Get a tool reference by name, bypassing the registry’s argument parsing to call Tool::call directly; returns None when not registered.

Source

pub async fn call( &self, name: &str, arguments: &str, state: &SharedState, ) -> Result<String, RegistryError>

Look up and execute a tool by name.

A single call completes three steps — “lookup → argument parsing → execution”; failure classifications are described by RegistryError:

state is injected into the tool at call time (see SharedState); the agent loop passes its own state straight through, so tools read and write the caller-provided instance.

Tool panics do not escape this method: the panic is caught and converted into RegistryError::Execution, with the message carrying the tool name and panic content, for the caller to pass back to the model.

§Errors

The three failure classes are described above; Err’s Display is the “error-to-text” — the agent loop passes e.to_string() back to the model as a ToolResult, and the text is directly readable by the model.

Source

pub fn subset(&self, names: &[&str]) -> Result<ToolRegistry, MissingTools>

Trim a sub-registry by name (an allowlist) sharing the same tool instances as the main registry.

Used to restrict a sub-agent’s tool set: when the main agent creates a sub-agent, this method trims an allowlisted registry, and both tables share the same tool instances (consistent state). The sub-registry keeps the main registry’s registration order.

§Errors

When an allowlisted name is not found in the main registry, MissingTools is returned; what to do with the missing list (error / warn / silent) is the caller’s decision — the library does not choose for the caller.

Trait Implementations§

Source§

impl Clone for ToolRegistry

Source§

fn clone(&self) -> ToolRegistry

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl Debug for ToolRegistry

Source§

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

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

impl Default for ToolRegistry

Source§

fn default() -> ToolRegistry

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

Auto Trait Implementations§

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

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: Sized + 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: Sized + 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> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

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