# How to Create and Register Custom Toolkits with an Agent in AISuite

> Learn to create and register custom toolkits with an Agent in AISuite. Define Python functions, bundle them into a toolkit class, and pass to the agent for enhanced functionality.

- Repository: [Andrew Ng/aisuite](https://github.com/andrewyng/aisuite)
- Tags: how-to-guide
- Published: 2026-07-30

---

**To create and register custom toolkits with an agent in AISuite, define Python functions decorated with `@tool` and `ToolMetadata`, bundle them into a toolkit class, and pass the instance to the agent's `toolkits` parameter during initialization.**

AISuite (andrewyng/aisuite) separates **toolkits** (collections of low-level functions) from **agents** (the high-level orchestration layer that drives LLM interactions). Understanding how to create and register custom toolkits with an agent allows you to extend the framework with domain-specific capabilities like filesystem operations, mathematical computations, or external API calls.

## Understanding the Toolkit Architecture

AISuite implements a clean separation between tools and agents. A toolkit is a plain Python module that exposes callable functions wrapped with the `@tool` decorator and described by a `ToolMetadata` object. When you instantiate an agent, you inject functionality by passing toolkit instances via the `toolkits` argument. The framework then aggregates these tools into a unified namespace available for LLM-driven tool calling.

## Creating a Custom Toolkit Module

### Project Structure and Required Imports

Create your toolkit as a Python module inside the `aisuite/toolkits/` directory. Every custom toolkit depends on utilities from the core framework:

```python
from aisuite.utils.tools import tool, ToolMetadata

```

All tool functions must reside in files accessible within the AISuite package structure, typically under `aisuite/toolkits/`, to ensure proper discovery and imports.

### Defining Tool Functions with Metadata

Each function you expose must have a stable signature with JSON-serializable arguments. Wrap each function with the `@tool` decorator and attach a `ToolMetadata` instance specifying:

- **`category`** – logical grouping (e.g., `"filesystem"`, `"math"`)
- **`risk_level`** – `"low"`, `"medium"`, or `"high"` (used for approval gating)
- **`capabilities`** – a list of verbs describing what the tool performs
- **`requires_approval`** – boolean flag to trigger user-approval flows
- **`description`** – human-readable explanation of the function's purpose

```python
from aisuite.utils.tools import tool, ToolMetadata

@tool
def read_project_file(path: str) -> str:
    """Read contents of a file at the given path."""
    with open(path, 'r') as f:
        return f.read()

# Attach metadata to the decorated function

read_project_file = tool(read_project_file, metadata=ToolMetadata(
    category="filesystem",
    risk_level="low",
    capabilities=["read"],
    description="Read text contents from a file path."
))

```

### Building a Toolkit Class

While optional, wrapping your functions in a class allows you to store configuration state such as root paths, write permissions, or API credentials. The built-in `FileToolkit` in [`aisuite/toolkits/files.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/toolkits/files.py) demonstrates this pattern by encapsulating filesystem operations with instance-level configuration:

```python
class FileToolkit:
    def __init__(self, root_path: str, allow_write: bool = False):
        self.root_path = root_path
        self.allow_write = allow_write
    
    # Methods decorated with @tool bind to the instance

```

## Registering the Toolkit with an Agent

When constructing an agent (e.g., `aisuite.Agent` or compatible subclasses), pass your toolkit instance via the `toolkits` list parameter:

```python
from aisuite.agent import Agent
from aisuite.toolkits.my_toolkit import MyToolkit

my_toolkit = MyToolkit(root="/my/project", allow_write=True)

agent = Agent(
    model="gpt-4o-mini",
    toolkits=[my_toolkit],
)

```

The agent automatically merges all tools from the supplied toolkits into a single namespace accessible to the LLM via function calling. If any tool has `requires_approval=True`, AISuite routes those calls through the user-approval flow before execution.

## Complete Working Example

Here is a functional **MathToolkit** implementation that follows AISuite conventions:

```python

# aisuite/toolkits/math_toolkit.py

from aisuite.utils.tools import tool, ToolMetadata

class MathToolkit:
    """A toolkit exposing basic arithmetic operations."""
    
    @tool
    def add(self, a: int, b: int) -> int:
        """Return the sum of a and b."""
        return a + b
    
    @tool
    def multiply(self, a: int, b: int) -> int:
        """Return the product of a and b."""
        return a * b

# Attach metadata after class definition

MathToolkit.add = tool(MathToolkit.add, metadata=ToolMetadata(
    category="math",
    risk_level="low",
    capabilities=["add"],
    description="Add two integers."
))

MathToolkit.multiply = tool(MathToolkit.multiply, metadata=ToolMetadata(
    category="math",
    risk_level="low",
    capabilities=["multiply"],
    description="Multiply two integers."
))

```

Register this toolkit during agent initialization:

```python
from aisuite.agent import Agent
from aisuite.toolkits.math_toolkit import MathToolkit

math_toolkit = MathToolkit()
agent = Agent(model="gpt-4o-mini", toolkits=[math_toolkit])

# The LLM can now invoke add() and multiply() as tool calls

```

## Key Source Files to Study

Study these reference implementations in the andrewyng/aisuite repository to understand production patterns:

- **[`aisuite/toolkits/files.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/toolkits/files.py)** – Reference implementation of a fully-featured filesystem toolkit demonstrating list, read, write, and search operations with configuration state.
- **[`aisuite/toolkits/shell.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/toolkits/shell.py)** – Example of a command-execution toolkit that wraps subprocess calls with risk-level gating.
- **[`aisuite/utils/tools.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/utils/tools.py)** – Core `@tool` decorator implementation and `ToolMetadata` dataclass definitions used by all toolkits.
- **[`aisuite/agent.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/agent.py)** – Agent constructor implementation showing how the `toolkits` parameter aggregates tool functions for LLM access.

## Summary

- **Create** a Python module under `aisuite/toolkits/` containing your functions.
- **Decorate** each callable with `@tool` and supply `ToolMetadata` defining category, risk level, and capabilities.
- **Bundle** functions into a class when you need to store configuration or state between calls.
- **Register** the toolkit by passing an instance to the agent's `toolkits` parameter; the framework handles discovery and approval routing automatically.

## Frequently Asked Questions

### What is the difference between the `toolkits` and `tools` parameters in AISuite?

The `toolkits` parameter accepts instances of toolkit classes (like `FileToolkit` or your custom class), while `tools` typically accepts individual function references. According to the AISuite source code, `toolkits` is the preferred pattern for production agents because it bundles configuration, metadata, and multiple related functions into a manageable unit.

### How do I restrict tool execution with approval gates?

Set `requires_approval=True` in your `ToolMetadata` instance. When the agent attempts to invoke that tool, AISuite automatically pauses execution and routes the request through the user-approval flow defined in the framework. High-risk tools like shell commands in [`aisuite/toolkits/shell.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/toolkits/shell.py) use this pattern to prevent accidental destructive operations.

### Can I use plain functions without a class wrapper?

Yes. The `@tool` decorator works on standalone module-level functions. However, using a class wrapper (like `FileToolkit`) is recommended when your tools need access to instance variables such as root directories, API keys, or permission flags that should persist across multiple tool calls within the same agent session.

### Where should I store my custom toolkit files?

Place custom toolkit modules inside the `aisuite/toolkits/` directory to ensure they can import from `aisuite.utils.tools` and follow the package structure. While Python allows importing from other locations, keeping toolkits within the AISuite namespace maintains compatibility with the framework's discovery mechanisms and path expectations used by the agent constructor.