How to Add and Utilize Custom Tools in MetaGPT Agent Actions: A Complete Guide
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– Contains theTOOL_REGISTRYsingleton and the@register_tooldecorator that captures function metadata and source code.metagpt/tools/tool_data_type.py– Defines Pydantic modelsToolSchemaandToolthat store tool names, descriptions, tags, and source references.metagpt/tools/tool_recommend.py– ImplementsBM25ToolRecommender, 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 lines 94-115.
Function-Based Tools
Create a standalone function with type hints and a docstring, then apply the decorator:
# 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 and registers it under the name magic_function.
Class-Based Tools
For tools requiring multiple methods or state initialization, register a class:
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) 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 validates tool availability in its set_plan_and_tool method (lines 49-57):
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) passes tool_info to the WriteAnalysisCode action:
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:
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:
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), 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
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
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:
{'arg1': 'AIAIAI', 'arg2': 35}
Key Files for Tool Development
| File | Purpose |
|---|---|
metagpt/tools/tool_registry.py |
Implements TOOL_REGISTRY singleton and @register_tool decorator |
metagpt/tools/tool_data_type.py |
Defines Tool and ToolSchema Pydantic models |
metagpt/tools/tool_recommend.py |
Contains BM25ToolRecommender for tool selection |
metagpt/roles/di/data_interpreter.py |
Example role consuming the tools parameter |
examples/di/custom_tool.py |
Reference implementation of custom tool definitions |
Summary
- Register tools using the
@register_tooldecorator 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
toolsparameter, which initializes a scopedBM25ToolRecommender. - Enable execution through the
tool_infoschema injection system, allowing LLMs to generate correct import statements and function calls. - Retrieve results via the role's working memory after
ExecuteNbCodeprocesses 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →