Skip to main content

ToolRegistry

Struct ToolRegistry 

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

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

use molo::tool::{SharedState, Tool, ToolContext, ToolError, ToolOutput, ToolRegistry, ToolResult, ToolSchema};
use molo::{RunContext, ToolCall};
use serde_json::json;

struct Calculator;
#[molo::async_trait]
impl Tool for Calculator {
    fn schema(&self) -> ToolSchema {
        ToolSchema::new("calculator", "Calculate", json!({ "type": "object", "properties": {} }))
    }
    async fn call(
        &self,
        _arguments: serde_json::Value,
        _context: ToolContext<'_>,
    ) -> Result<ToolResult, ToolError> {
        Ok(ToolOutput::text("42").into())
    }
}

let mut registry = ToolRegistry::new();
registry.register(Calculator);
let state = SharedState::new();
let run = RunContext::new("run-1");
let call = ToolCall {
    id: "call-1".into(),
    name: "calculator".into(),
    arguments: "{}".into(),
};
// The model requests a tool by name; error classification (not
// registered / args not JSON / execution failed) rides along with Err.
let result = registry.call(&call, &run, &state).await?;
assert_eq!(result.output_content(), Some("42"));
// Allowlist subset: the sub-registry shares the same tool instances as
// the main registry.
let sub = registry.subset(&["calculator"])?;
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<ToolResult, 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 register_with_source( &mut self, tool: impl Tool + 'static, source: ToolSource, ) -> Result<&mut Self, RegistryError>

Register a tool with host-facing source metadata.

Unlike register, this method rejects a same provider-facing name coming from a different namespace. That prevents accidental cross-extension shadowing while still allowing a host to refresh tools inside the same namespace.

§Errors

Returns RegistryError::SourceNameMismatch when the source display name does not match the tool schema name, and RegistryError::NameCollision when another namespace already owns the same provider-facing name.

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 source(&self, display_name: &str) -> Option<&ToolSource>

Source metadata for a registered tool.

Tools registered with register have no explicit source metadata and return None.

Source

pub fn names_in_namespace(&self, namespace: &ToolNamespace) -> Vec<String>

Names of tools that belong to a namespace.

Tools without explicit source metadata are treated as local tools for this query.

Source

pub fn remove_namespace(&mut self, namespace: &ToolNamespace) -> Vec<String>

Remove every tool in a namespace and return removed names in their previous registration order.

This is the source-aware unload path for extension layers such as MCP server teardown.

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::{Tool, ToolContext, ToolError, ToolRegistry, ToolResult, ToolSchema};
use serde_json::json;

struct Named(&'static str);
#[molo::async_trait]
impl Tool for Named {
    fn schema(&self) -> ToolSchema {
        ToolSchema::new(self.0, self.0, json!({}))
    }
    async fn call(
        &self,
        _arguments: serde_json::Value,
        _context: ToolContext<'_>,
    ) -> Result<ToolResult, 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 JSON argument parsing to call Tool::call directly; returns None when not registered.

Source

pub async fn call( &self, call: &ToolCall, run: &RunContext, state: &SharedState, ) -> Result<ToolResult, RegistryError>

Look up and dispatch a tool call.

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

run and state are injected into the tool through ToolContext; 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 can pass e.to_string() back to the model as a tool result, and the text is directly readable by the model.

Source

pub async fn call_named( &self, name: impl Into<String>, arguments: impl Into<String>, run: &RunContext, state: &SharedState, ) -> Result<ToolResult, RegistryError>

Constructs a ToolCall from raw fields and dispatches it.

This is a convenience wrapper for direct registry use in tests and examples. Agent loops should prefer call, because they already have the model’s original tool-call id.

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.

Source

pub fn subset_by_namespace( &self, namespace: &ToolNamespace, ) -> Result<ToolRegistry, RegistryError>

Constructs a source-aware sub-registry for one namespace.

The sub-registry shares tool instances with the main registry and preserves the main registry’s order.

§Errors

This method currently has no failure path. It returns Result to keep the signature aligned with other subset APIs and leave room for future namespace validation.

Trait Implementations§

Source§

impl Clone for ToolRegistry

Source§

fn clone(&self) -> Self

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() -> Self

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

impl<T> Extend<T> for ToolRegistry
where T: Tool + 'static,

Source§

fn extend<I>(&mut self, iter: I)
where I: IntoIterator<Item = T>,

Extends a collection with the contents of an iterator. Read more
Source§

fn extend_one(&mut self, item: T)

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
Source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
Source§

impl<T> FromIterator<T> for ToolRegistry
where T: Tool + 'static,

Source§

fn from_iter<I>(iter: I) -> Self
where I: IntoIterator<Item = T>,

Creates a value from an iterator. 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 = !

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

fn try_from(value: U) -> Result<T, !>

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