Skip to content

Class-based Tools Usage Guide

Hub tools are registered to ToolRegistry using the register_from_class method. This allows developers to extend the functionality of ToolRegistry by creating custom tool classes with reusable methods.

API changes

Previously (before 0.4.12), the method register_static_tools and the concept of StaticMethodIntegration were used for registering static methods from classes. These have now been replaced by register_from_class. Similarly, register_static_tools_async has also been replaced. Both old methods are planned to be deprecated soon, so please migrate to the new interfaces as soon as possible. For backward compatibility, register_static_tools remains as an alias to register_from_class.

Registering Custom Class Methods

The register_from_class method in ToolRegistry allows you to easily register methods from custom classes, whether they are static methods or instance methods. Below, we explore two distinct use cases: registering classes with only static methods and registering instance-based classes.

Registering a Class with Static Methods

Classes that exclusively use static methods can be registered directly without creating instances. Use the namespace=True argument to help organize tools under a namespace derived from the class name.

from toolregistry import ToolRegistry

class StaticExample:
    @staticmethod
    def greet(name: str) -> str:
        return f"Hello, {name}!"

registry = ToolRegistry()
registry.register_from_class(StaticExample, namespace=True)

# List registered tools
print(registry.get_available_tools())
# Output: ['static_example.greet']

# Call a registered tool
print(registry["static_example.greet"]("Alice"))  # Hello, Alice!

Registering a Class with Instance Methods

For classes utilizing instance methods, you need to create an instance and pass it to the registry along with the class definition. This ensures that the methods have access to instance-specific data.

from toolregistry import ToolRegistry

class InstanceExample:
    def __init__(self, name: str):
        self.name = name

    def greet(self, name: str) -> str:
        return f"Hello, {name}! I'm {self.name}."

# Creating an instance of the class
example_instance = InstanceExample("Bob")
registry = ToolRegistry()

# Register methods using the instance
registry.register_from_class(InstanceExample, instance=example_instance)

# List registered tools
print(registry.get_available_tools())
# Output: ['instance_example.greet']

# Call a registered tool
print(registry["instance_example.greet"]("Alice"))  # Hello, Alice! I'm Bob.

Additional Example: A Predefined Class from a Tool Hub

For predefined classes with pre-implemented functionalities (e.g., BaseCalculator, Calculator), registration is straightforward:

from toolregistry import ToolRegistry
from toolregistry.hub import BaseCalculator

registry = ToolRegistry()
registry.register_from_class(BaseCalculator)  # Basic registration for methods of a class

These examples highlight how to manage varying needs for class-based registrations, allowing users to adapt ToolRegistry for diverse scenarios.

Registering Inherited Methods with traverse_mro

By default, register_from_class() traverses the MRO (Method Resolution Order) and registers both directly defined and inherited methods. This means inherited public methods from parent classes are automatically included.

from toolregistry import ToolRegistry

class BaseCalculator:
    @staticmethod
    def add(a: int, b: int) -> int:
        """Add two numbers."""
        return a + b

    @staticmethod
    def subtract(a: int, b: int) -> int:
        """Subtract two numbers."""
        return a - b

class ScientificCalculator(BaseCalculator):
    @staticmethod
    def power(base: float, exp: float) -> float:
        """Raise base to the power of exp."""
        return base ** exp

registry = ToolRegistry()

# Default behavior (traverse_mro=True): inherited methods are included
registry.register_from_class(ScientificCalculator, namespace=True)
print(registry.get_available_tools())
# Output: ['scientific_calculator-add', 'scientific_calculator-subtract', 'scientific_calculator-power']

# With traverse_mro=False: only methods defined directly on the class are registered
registry2 = ToolRegistry()
registry2.register_from_class(ScientificCalculator, namespace=True, traverse_mro=False)
print(registry2.get_available_tools())
# Output: ['scientific_calculator-power']

If you want to restrict registration to only methods defined directly on the class (excluding inherited ones), explicitly pass traverse_mro=False.

namespace Option

Using namespace=True parameter adds the class name as a namespace prefix to tool names:

registry.register_from_class(BaseCalculator, namespace=True)

This will register tools with names like base_calculator-add, base_calculator-subtract, etc.

Advantages of using namespace:

  1. Avoids naming conflicts between methods with same names in different classes
  2. More clearly identifies tool source
  3. Maintains naming consistency

Example Code

from toolregistry import ToolRegistry
from toolregistry.hub import Calculator, FileOps

# Create tool registry
registry = ToolRegistry()

# Register Calculator tools (with namespace)
registry.register_from_class(Calculator, namespace=True)

# Register FileOps tools (without namespace)
registry.register_from_class(FileOps)

# Get available tools list
print(registry.get_available_tools())
# Output: ['calculator-list_allowed_fns', 'calculator-help', 'calculator-evaluate', 'read_file', 'write_file', ...]