---
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.

<Tabs defaultValue="BaseTool">

<Tab title="BaseTool">

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:

<Steps>
  <Step title="Add Import Statements">
    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
    ```
  </Step>

  <Step title="Define the Tool Class and Docstring">
    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.
        """
    ```
  </Step>

  <Step title="Define Input Fields">
    Use Pydantic fields to define the inputs your tool will accept.

    ```python
    expression: str = Field(..., description="The mathematical expression to evaluate.")
    ```

    <Accordion title="Custom Validation Logic (Optional)" icon="hammer">
      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")
      ```
    </Accordion>
  </Step>

  <Step title="Implement the run Method">
    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.
  </Step>

  <Step title="Test the Tool Independently">
    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'
    ```
  </Step>

  <Step title="Add the Tool to an Agent">
    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
    )
    ```

    <Accordion title="Using tools folder" icon="folder">
      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
      )
      ```

      <Note>
        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.
      </Note>
    </Accordion>
  </Step>
</Steps>

## 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'
```

</Tab>

<Tab title="FunctionTool">

## Step-by-step Guide

<Steps>
  <Step title="Add Import Statements">
    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
    ```
  </Step>

  <Step title="Define Args Schema">
    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
    ```
  </Step>

  <Step title="Create the Tool Function">
    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}"
    ```
    <Note>
    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.
    </Note>
    <Note>
    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)
    </Note>
  </Step>

  <Step title="Test the Tool">
    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())
    ```
  </Step>

  <Step title="Add Tool to Agent">
    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"
    )
    ```
  </Step>
</Steps>

## 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())
```

</Tab>
</Tabs>

## 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)
