DS DevShelfHub Projects · AI tools
Build with CrewAI Intermediate · 15 min read Page 7 of 29

CrewAI Tools: Built-ins, @tool, and BaseTool for Python Agents

By DevShelfHub

Built-in tools, the @tool decorator, BaseTool subclasses for stateful tools, error handling, and the rules that keep agents from drowning in tools. Pair with the core concepts tutorial and run_tool() reference when you wire custom executors.

Series progress7 / 29
CrewAI tools tutorial — CrewAI Tools: Built-ins, @tool, and BaseTool for Python Agents

Why Tools Matter

An LLM alone can only reason over what's in its prompt. Tools let agents take action: search the web, read a file, query a database, hit an API.

Built-in Tools

Install crewai-tools and you get a dozen ready-to-use tools.

Common built-ins

PYTHON
from crewai_tools import (
    SerperDevTool,         # Google search via Serper API
    ScrapeWebsiteTool,     # fetch + parse HTML
    FileReadTool,          # read a local file
    DirectoryReadTool,     # list a folder
    PDFSearchTool,         # search a PDF semantically
    CSVSearchTool,         # search rows in a CSV
)

search = SerperDevTool()
scraper = ScrapeWebsiteTool()
file_reader = FileReadTool()

researcher = Agent(
    role="Web Research Analyst",
    goal="Find current info on {topic}",
    backstory="...",
    tools=[search, scraper],
)

API reference for the built-ins above: SerperDevTool, ScrapeWebsiteTool, FirecrawlScrapeWebsiteTool, FileReadTool, FileWriterTool, and PDFSearchTool.

Custom Tools with @tool

Anything you can write as a Python function, you can give to an agent. The docstring becomes the tool's description — write it carefully, the model uses it to decide when to call.

Custom tool

PYTHON
from crewai.tools import tool
import requests

@tool("Get current stock price")
def get_stock_price(ticker: str) -> str:
    """Fetch the latest stock price for a US ticker symbol.

    Args:
        ticker: Uppercase ticker symbol, e.g. AAPL, MSFT.

    Returns:
        A short string: "AAPL: $187.32 (+0.4%)"
    """
    resp = requests.get(f"https://api.example.com/quote/{ticker}").json()
    return f"{ticker}: ${resp[\'price\']} ({resp[\'change_pct\']}%)"

analyst = Agent(
    role="Equity Analyst",
    goal="Answer questions about US stocks",
    backstory="You are precise and use real-time data.",
    tools=[get_stock_price],
)

💡 Tip: The function's docstring is what the LLM sees. Spend time on it — it's a prompt.

Class-Based Tools (BaseTool)

For tools that need state (DB connection pool, auth token), subclass BaseTool. Implement _run() for synchronous paths and add _arun() when the tool wraps async HTTP or database drivers used from kickoff_async.

Stateful custom tool

PYTHON
from crewai.tools import BaseTool
from pydantic import BaseModel, Field

class QueryInput(BaseModel):
    sql: str = Field(..., description="A read-only SELECT query")

class PostgresQueryTool(BaseTool):
    name: str = "Run a SELECT query against the analytics DB"
    description: str = "Execute read-only SQL. Only SELECT statements allowed."
    args_schema: type[BaseModel] = QueryInput

    def __init__(self, conn):
        super().__init__()
        self._conn = conn

    def _run(self, sql: str) -> str:
        if not sql.strip().lower().startswith("select"):
            return "Error: only SELECT queries are allowed."
        with self._conn.cursor() as cur:
            cur.execute(sql)
            return str(cur.fetchall()[:50])

Handling Tool Errors

Tools throw real exceptions. Wrap risky calls — a raised exception aborts the agent's turn, but a returned error string lets the agent retry or recover.

Defensive tool

PYTHON
@tool("Search internal wiki")
def wiki_search(query: str) -> str:
    """Search the internal Confluence wiki. Returns top 3 page titles."""
    try:
        results = wiki_client.search(query, limit=3)
        if not results:
            return "No matches found."
        return "\n".join(f"- {r.title}: {r.url}" for r in results)
    except TimeoutError:
        return "Wiki is slow right now. Try again in 10 seconds."
    except Exception as e:
        return f"Wiki error: {e}. Try a different query."

Rules of Thumb

  • Keep agents to 2–4 tools. Bigger toolsets confuse the model.
  • Name and document for the LLM, not the human. The model picks tools by name + description.
  • Return strings, not raw objects. The LLM only sees the string.
  • Cap output size. A tool that returns 50KB blows the context window. Truncate to ~2KB.
  • Async crews need BaseTool._arun(). Implement the awaitable body when you use kickoff_async and httpx-style clients so you do not block the event loop.

CrewAI tools FAQ

What is the difference between CrewAI built-in tools and custom tools?

Built-in tools ship in crewai-tools for common integrations like search and files. Custom tools are functions or BaseTool classes you write when you need private APIs, stateful connections, or domain-specific guardrails.

Why does the @tool docstring matter in CrewAI?

The docstring is the tool description the model reads when deciding whether to call the tool. Clear args, constraints, and return format reduce hallucinated invocations and bad parameters.

When should I subclass BaseTool instead of using @tool?

Subclass BaseTool when you need persistent state such as connection pools, typed args_schema with Pydantic, or shared setup across many calls that would be awkward as a plain function closure.

How should CrewAI tools handle errors?

Return actionable error strings for recoverable failures so the agent can retry. Reserve raised exceptions for truly fatal cases where continuing would corrupt data or violate policy.

How many tools should a CrewAI agent carry at once?

Aim for roughly two to four focused tools per agent. Large tool menus increase mistaken calls, inflate prompts, and slow routing decisions.

Where can I read CrewAI tool API details after this tutorial?

Open the DevShelfHub CrewAI API reference for BaseTool, run_tool patterns, and related classes, then cross-link back to this tutorial while you wire production guardrails.

Quick jump: API Reference