# How to Add and Utilize Custom Tools in MetaGPT Agent Actions: A Complete Guide

> Easily add and utilize custom Python tools in MetaGPT agents. Learn how to register tools with @register_tool and pass them to roles for enhanced agent capabilities. Full guide available.

- Repository: [FoundationAgents/MetaGPT](https://github.com/FoundationAgents/MetaGPT)
- Tags: how-to-guide
- Published: 2026-03-04

---

**You can extend MetaGPT agents with custom Python functions or classes by decorating them with `@register_tool`, importing the module to trigger registration, and passing the tool name to a role's `tools` parameter, which enables the BM25 tool recommender to inject the tool's schema into LLM prompts for automatic code generation.**

MetaGPT is a multi-agent framework that enables complex software development workflows through collaborative AI agents. Adding and utilizing custom tools within MetaGPT agent actions allows you to extend agent capabilities beyond built-in functionality, integrating proprietary algorithms, external APIs, or domain-specific utilities directly into agent reasoning and code generation cycles.

## Understanding the Tool Registry Architecture

MetaGPT provides a centralized **tool registry** system that manages custom capabilities. The core components reside in `metagpt/tools/` and work together to discover, store, and recommend tools:

- **[`metagpt/tools/tool_registry.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/metagpt/tools/tool_registry.py)** – Contains the `TOOL_REGISTRY` singleton and the `@register_tool` decorator that captures function metadata and source code.
- **[`metagpt/tools/tool_data_type.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/metagpt/tools/tool_data_type.py)** – Defines Pydantic models `ToolSchema` and `Tool` that store tool names, descriptions, tags, and source references.
- **[`metagpt/tools/tool_recommend.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/metagpt/tools/tool_recommend.py)** – Implements `BM25ToolRecommender`, which matches tools to tasks using BM25 similarity scoring on tool names and tags.

When you decorate a function with `@register_tool`, the decorator extracts the source file path, builds a `Tool` instance, and stores it in the registry's `tools` dictionary by name and `tools_by_tags` index for later retrieval.

## Defining and Registering Custom Tools

You can register both functions and classes as tools. The registration process requires only the decorator and an import side-effect to trigger the registration logic defined in [`tool_registry.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/tool_registry.py) lines 94-115.

### Function-Based Tools

Create a standalone function with type hints and a docstring, then apply the decorator:

```python

# examples/di/custom_tool.py

from metagpt.tools.tool_registry import register_tool

@register_tool()
def magic_function(arg1: str, arg2: int) -> dict:
    """Multiply arg1 by 3 and arg2 by 5."""
    return {"arg1": arg1 * 3, "arg2": arg2 * 5}

```

The decorator automatically captures the function's source code from [`examples/di/custom_tool.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/examples/di/custom_tool.py) and registers it under the name `magic_function`.

### Class-Based Tools

For tools requiring multiple methods or state initialization, register a class:

```python
from metagpt.tools.tool_registry import register_tool

@register_tool(tags=["data", "analysis"])
class DataProcessor:
    def clean(self, raw: str) -> str:
        return raw.strip().lower()
    
    def summarize(self, text: str) -> str:
        return text[:100] + "..."

```

The `tags` parameter (lines 27-100 in [`tool_registry.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/tool_registry.py)) indexes the tool under categories like `"data"` and `"analysis"`, enabling the BM25 recommender to surface relevant tools during task execution.

## Exposing Tools to MetaGPT Roles

Roles consume tools through the `tools` field, typically specified during instantiation. When a role initializes with a tool list, MetaGPT constructs a `BM25ToolRecommender` instance scoped to those specific tools.

For example, the `DataInterpreter` role in [`metagpt/roles/di/data_interpreter.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/metagpt/roles/di/data_interpreter.py) validates tool availability in its `set_plan_and_tool` method (lines 49-57):

```python
from metagpt.roles.di.data_interpreter import DataInterpreter

async def main():
    # Import side-effect registers magic_function

    import examples.di.custom_tool
    
    # Initialize role with custom tool access

    di = DataInterpreter(tools=["magic_function"])
    await di.run("Call magic_function with arg1='Test' and arg2=5")

```

The validator checks `if self.tools and not self.tool_recommender` and automatically instantiates `BM25ToolRecommender(tools=self.tools)`, ensuring only explicitly allowed tools are recommended to the LLM.

## Calling Custom Tools from Agent Actions

MetaGPT actions receive tool schemas through the `tool_info` parameter, which contains JSON-formatted descriptions of available tools. The LLM uses this context to generate import statements and function calls.

### Within WriteAnalysisCode

The `DataInterpreter._write_code` method (lines 60-65 in [`data_interpreter.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/data_interpreter.py)) passes `tool_info` to the `WriteAnalysisCode` action:

```python
code = await todo.run(..., tool_info=tool_info, ...)

```

The action template includes the tool schemas in the system prompt, allowing the LLM to generate code like:

```python
from metagpt.tools.magic_function import magic_function
result = magic_function(arg1="Hello", arg2=4)
print(result)  # Output: {'arg1': 'HelloHelloHello', 'arg2': 20}

```

The generated code executes within `ExecuteNbCode`, and results populate the role's working memory.

### Direct Registry Access in Custom Actions

For custom `Action` subclasses, query the registry directly:

```python
from metagpt.actions import Action
from metagpt.tools import TOOL_REGISTRY

class CallMagic(Action):
    async def run(self, **kwargs):
        tool_entry = TOOL_REGISTRY.get_tool("magic_function")
        # tool_entry.code contains the source string

        local_ns = {}
        exec(tool_entry.code, local_ns)
        magic_fn = local_ns['magic_function']
        return magic_fn("input", 10)

```

While direct execution works, the recommended pattern lets the LLM handle imports based on injected `tool_info` schemas.

## How the BM25 Tool Recommender Works

The `BM25ToolRecommender` class implements a two-stage retrieval system. During initialization in `_init_corpus` (lines 8-12 of [`tool_recommend.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/tool_recommend.py)), it builds a corpus from registered tool names, descriptions, and tags. When `recall_tools` executes (lines 16-23), it tokenizes the current task description and scores tools using BM25 similarity.

Tools with matching tags receive higher relevance scores. The `rank_tools` method (lines 31-70) then lets the LLM re-rank the top-k candidates, ensuring the most contextually appropriate tools are presented to the agent.

## Complete Implementation Example

The following end-to-end example demonstrates registration through execution:

**File: [`examples/di/custom_tool.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/examples/di/custom_tool.py)**

```python
from metagpt.tools.tool_registry import register_tool

@register_tool(tags=["demo"])
def magic_function(arg1: str, arg2: int) -> dict:
    """Demo tool: triples arg1 string and quintuples arg2 int."""
    return {"arg1": arg1 * 3, "arg2": arg2 * 5}

```

**File: [`examples/di/run_custom_tool.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/examples/di/run_custom_tool.py)**

```python
import asyncio
from metagpt.roles.di.data_interpreter import DataInterpreter

async def main():
    # Registration occurs on import

    import examples.di.custom_tool
    
    interpreter = DataInterpreter(tools=["magic_function"])
    await interpreter.run(
        "Call magic_function with arg1='AI' and arg2=7, then report the result."
    )

if __name__ == "__main__":
    asyncio.run(main())

```

**Execution Output:**

```text
{'arg1': 'AIAIAI', 'arg2': 35}

```

## Key Files for Tool Development

| File | Purpose |
|------|---------|
| [`metagpt/tools/tool_registry.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/metagpt/tools/tool_registry.py) | Implements `TOOL_REGISTRY` singleton and `@register_tool` decorator |
| [`metagpt/tools/tool_data_type.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/metagpt/tools/tool_data_type.py) | Defines `Tool` and `ToolSchema` Pydantic models |
| [`metagpt/tools/tool_recommend.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/metagpt/tools/tool_recommend.py) | Contains `BM25ToolRecommender` for tool selection |
| [`metagpt/roles/di/data_interpreter.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/metagpt/roles/di/data_interpreter.py) | Example role consuming the `tools` parameter |
| [`examples/di/custom_tool.py`](https://github.com/FoundationAgents/MetaGPT/blob/main/examples/di/custom_tool.py) | Reference implementation of custom tool definitions |

## Summary

- **Register tools** using the `@register_tool` decorator on functions or classes, optionally specifying tags for better discovery.
- **Trigger registration** by importing the module containing your decorated tools before initializing roles.
- **Configure roles** by passing a list of tool names to the `tools` parameter, which initializes a scoped `BM25ToolRecommender`.
- **Enable execution** through the `tool_info` schema injection system, allowing LLMs to generate correct import statements and function calls.
- **Retrieve results** via the role's working memory after `ExecuteNbCode` processes the generated code.

## Frequently Asked Questions

### How do I register a custom tool in MetaGPT?

Apply the `@register_tool` decorator to any Python function or class in your codebase, then import that module before creating your agent. The decorator automatically extracts the source code and registers the tool in the `TOOL_REGISTRY` singleton under the function or class name.

### Can I use classes as tools or only functions?

Both work. Decorate a class to expose all its methods, or use a simple function for single-purpose utilities. Class-based tools are useful when you need initialization state or multiple related operations, while function-based tools work best for stateless transformations.

### How does MetaGPT decide which tools to use during agent execution?

The `BM25ToolRecommender` scores your task description against registered tool names and tags using BM25 similarity. If you specify `tools=["tool_name"]` when creating a role, the recommender only considers those specific tools, filtering out irrelevant options before presenting schemas to the LLM.

### Where should I store custom tool definitions?

Place custom tools in your project directory (e.g., `examples/di/` or a dedicated `tools/` folder) and import them in your main execution script. Ensure the import runs before role initialization so the registration side-effect populates the registry before the agent starts reasoning.