--- title: "Step-by-Step Guide" description: "Learn how to create custom tools in Agency Swarm framework." icon: "map" --- In Agency Swarm, tools enable agents to perform specific actions and interact with external systems. The framework supports two approaches for creating custom tools depending on the version you're using. Framework supports 2 methods of creating tools: 1. Using agency-swarm's `BaseTool` class. 2. Using openai's `@function_tool` decorator with async functions. Explore the sections below to find a method that suits your approach the best. In Agency Swarm’s approach, tools are Python classes that inherit from `BaseTool`. They are defined using [Pydantic](https://docs.pydantic.dev/latest/), a data validation library. Each BaseTool must implement the `run` method, which is the main method that will be called when the tool is invoked by an agent. ## Step-by-step Guide To create a custom tool, typically you need to follow these steps: On top of your tool file, import the necessary modules and classes. ```python from agency_swarm.tools import BaseTool from pydantic import Field, model_validator # ... other imports ``` Create a new class that inherits from `BaseTool`. Write a clear docstring describing the tool's purpose. **This docstring is crucial as it helps agents understand how to use the tool.** ```python class Calculator(BaseTool): """ A simple calculator tool that evaluates mathematical expressions. """ ``` Use Pydantic fields to define the inputs your tool will accept. ```python expression: str = Field(..., description="The mathematical expression to evaluate.") ``` You can use [Pydantic's validators](https://docs.pydantic.dev/latest/concepts/validators/) to verify the inputs. This can be extremely effective to avoid hallucinations or other errors in production. ```python @model_validator(mode="after") def validate_expression(self): if self.expression.endswith("/0"): raise ValueError("Division by zero is not permitted") ``` Add the functionality that will be executed when the tool is called. ```python async def run(self): # Implement the tool's functionality result = eval(self.expression) return str(result) ``` The `run` method should return a string, which is the tool's output that the agent will see and use in its response. Test the tool independently to ensure it behaves as expected. We recommend adding a `if __name__ == "__main__":` block at the end of the tool file: ```python import asyncio if __name__ == "__main__": calc = Calculator(expression="2 + 2 * 3") print(asyncio.run(calc.run())) # Output should be '8' ``` After your tool works as expected, simply add it to an agent's list of `tools`. ```python from agency_swarm import Agent from .tools.calculator import Calculator agent = Agent( name="MathAgent", tools=[Calculator], # Other agent parameters ) ``` Alternatively, you can simply place the tool file in the `tools_folder` directory and it will be automatically added to the agent. ```python from agency_swarm import Agent agent = Agent( name="MathAgent", tools_folder="./tools", # Other agent parameters ) ``` Each file in the `tools_folder` should contain a class that is named exactly the same as the file name. For example, `Calculator.py` should contain a `Calculator` class. ## Full Code Example Below is the full code example for a calculator tool above. ```python # calculator.py from agency_swarm.tools import BaseTool from pydantic import Field, model_validator class Calculator(BaseTool): """ A simple calculator tool that evaluates mathematical expressions. """ expression: str = Field(..., description="The mathematical expression to evaluate.") @model_validator(mode="after") def validate_expression(self): if self.expression.endswith("/0"): raise ValueError("Division by zero is not permitted") async def run(self): result = eval(self.expression) return str(result) if __name__ == "__main__": import asyncio calc = Calculator(expression="2 + 2 * 3") print(asyncio.run(calc.run())) # Output should be '8' ``` ## Step-by-step Guide Import the necessary modules for the new function-based approach. ```python from agency_swarm import function_tool, RunContextWrapper from pydantic import BaseModel, Field, field_validator from agency_swarm import MasterContext ``` Create a Pydantic model to define your tool's input parameters. ```python class CalculatorArgs(BaseModel): expression: str = Field(..., description="The mathematical expression to evaluate") @field_validator("expression") @classmethod def validate_expression(cls, v): if "/0" in v: raise ValueError("Division by zero is not permitted") return v ``` Define an async function with the `@function_tool` decorator. ```python @function_tool async def calculator_tool(ctx: RunContextWrapper[MasterContext], args: CalculatorArgs) -> str: """ A calculator tool that evaluates mathematical expressions. Use this when you need to perform mathematical calculations. """ # Access shared context if needed calculation_count = ctx.context.get("calculations", 0) ctx.context.set("calculations", calculation_count + 1) # Perform the calculation result = eval(args.expression) return f"Result: {result}" ``` All parameters of the function tool, except for `ctx`, are treated as input parameters and included in the tool’s schema. For example, in the tool above, the input model `CalculatorArgs` is passed as the `args` parameter, so the input JSON would look like: ``` {"args": {"expression": "input_expression"}} ``` `args` is an example name, you can name your input parameters however you like, and you may define more than one input parameter if needed. This example mentions tool context, which can be used as a shared storage for all agents and tools. For more info on agent context, refer to [this page](/additional-features/agency-context) Test your tool function independently. ```python if __name__ == "__main__": import asyncio import json async def test_tool(): ctx = MasterContext(user_context={"calculations": 1}, thread_manager=None, agents={}) run_ctx = RunContextWrapper(context=ctx) args = CalculatorArgs(expression="2 + 2 * 3") # Wrap in args to comply with oai expected inputs args_json = {"args": args.model_dump()} result = await calculator_tool.on_invoke_tool(run_ctx, json.dumps(args_json)) print(result) asyncio.run(test_tool()) ``` Pass the function directly to the agent's tools list. ```python from agency_swarm import Agent agent = Agent( name="MathAgent", instructions="You are a helpful math assistant", tools=[calculator_tool], # Pass function directly model="gpt-5.4-mini" ) ``` ## Full Code Example ```python # calculator_tool.py from agency_swarm import function_tool, RunContextWrapper from pydantic import BaseModel, Field, field_validator from agency_swarm import MasterContext class CalculatorArgs(BaseModel): expression: str = Field(..., description="The mathematical expression to evaluate") @field_validator("expression") @classmethod def validate_expression(cls, v): if "/0" in v: raise ValueError("Division by zero is not permitted") return v @function_tool async def calculator_tool(ctx: RunContextWrapper[MasterContext], args: CalculatorArgs) -> str: """ A calculator tool that evaluates mathematical expressions. Use this when you need to perform mathematical calculations. """ # Access shared context if needed calculation_count = ctx.context.get("calculations", 0) ctx.context.set("calculations", calculation_count + 1) # Perform the calculation result = eval(args.expression) return f"Result: {result} (Calculation #{calculation_count + 1})" if __name__ == "__main__": import asyncio import json async def test_tool(): ctx = MasterContext(user_context={"calculations": 1}, thread_manager=None, agents={}) run_ctx = RunContextWrapper(context=ctx) args = CalculatorArgs(expression="2 + 2 * 3") args_json = {"args": args.model_dump()} result = await calculator_tool.on_invoke_tool(run_ctx, json.dumps(args_json)) print(result) asyncio.run(test_tool()) ``` ## Next Steps - Checkout [Best Practices & Tips](/core-framework/tools/custom-tools/best-practices) - Learn why [PyDantic is all you need](/core-framework/tools/custom-tools/pydantic-is-all-you-need) - Learn how to [stream events from tools](/additional-features/streaming#streaming-from-tools)