Building custom tools for CrewAI agents

Tools give your agents the ability to interact with the outside world — search the web, query databases, read files, and call APIs. Learn how to build them with the @tool decorator and BaseTool class.

What are CrewAI tools?

Without tools, an agent can only reason with the knowledge baked into its LLM. Tools extend an agent's capabilities — they let it search the web, read files, call APIs, query databases, or run any Python code.

When an agent encounters a task that requires external information or actions, it decides which tool to call, provides the input, and uses the result to continue its work. CrewAI handles the tool calling protocol automatically.

The @tool decorator

The quickest way to create a tool. Wrap any Python function with @tool and give it a name. The function's docstring becomes the tool description that the agent sees.

tools.py
from crewai.tools import tool

@tool("Search the web")
def search_tool(query: str) -> str:
    """Search the web for information on a topic."""
    # Your search implementation here
    results = search_api.search(query)
    return format_results(results)

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

The BaseTool class

For tools that need structured input validation, use the BaseTool class with a Pydantic schema. This gives you type checking, default values, and field descriptions that help the agent provide correct inputs.

custom_tool.py
from crewai.tools import BaseTool
from pydantic import BaseModel, Field

class MyCustomToolInput(BaseModel):
    """Input schema for MyCustomTool."""
    argument: str = Field(
        ..., description="Description of the argument."
    )

class MyCustomTool(BaseTool):
    name: str = "Name of my tool"
    description: str = (
        "Clear description for what this tool is useful for. "
        "Your agent will need this to know when to use it."
    )
    args_schema: type[BaseModel] = MyCustomToolInput

    def _run(self, argument: str) -> str:
        # Tool implementation goes here
        return "Result from custom tool"

Built-in tools

CrewAI ships with a set of pre-built tools in the crewai_tools package. Install with pip install crewai-tools.

SerperDevTool

Search Google via the Serper API. Returns structured search results.

ScrapeWebsiteTool

Scrape and extract content from web pages.

FileReadTool

Read files from the local filesystem.

DirectoryReadTool

List and read directory contents.

PDFSearchTool

Search and extract text from PDF documents.

CSVSearchTool

Search and query data in CSV files.

These are just a few of the most common ones. The crewai_tools package includes dozens more tools for web browsing, code execution, database access, and more.

Assigning tools to agents

Pass tools to an agent via the tools parameter. You can mix @tool functions and BaseTool instances in the same list.

crew.py
# In crew.py — attach tools in the @agent method
@agent
def researcher(self) -> Agent:
    return Agent(
        config=self.agents_config["researcher"],
        tools=[search_tool, scrape_tool, file_reader],
    )

# Or set tools directly on the Agent
researcher = Agent(
    role="Research Analyst",
    goal="Find comprehensive information",
    backstory="Expert researcher.",
    tools=[search_tool, MyCustomTool()],
)

Running tools on Crewship

Browser profile for web tools

If your tools need to scrape websites or interact with web pages, set profile = "browser" in your crewship.toml. This includes Playwright and Chromium in the build.

Environment variables for API keys

Many tools need API keys (Serper, OpenAI, etc.). Store them securely with crewship env set — they're encrypted and injected at runtime.

Artifact storage for file output

Tools that generate files can write to the artifacts/ directory. Files are automatically collected and downloadable via API.

Best practices

Write clear descriptions

The tool description tells the agent when to use it. Be specific about what it does and what input it expects. Vague descriptions lead to misuse.

Validate inputs with Pydantic

For complex tools, use the BaseTool approach with a Pydantic input schema. This gives you type validation, default values, and clear documentation for the agent.

Handle errors gracefully

Tools should return useful error messages instead of raising exceptions. The agent can use error information to retry or try a different approach.

Keep tools focused

Each tool should do one thing well. A "search_and_summarize" tool should be split into a search tool and a summarize tool — the agent can chain them itself.

Assign tools selectively

Only give an agent the tools it needs. Too many tools confuse the agent and waste tokens on tool selection. A researcher needs search tools, not file writers.

FAQ

Tools questions

Deploy your tools to production

Built your tools? Deploy them with your crew in a single command.