Tools

Tools are callable functions agents use to search, read files, query data, or hit APIs. They sit beside MCPs (remote tool servers), Apps, Skills (domain instructions), and Knowledge (retrieved facts) — see official Agent Capabilities. This chapter covers crewai_tools and custom tools.

pip install 'crewai[tools]'
# or: uv add 'crewai[tools]'

Attach tools to agents

from crewai import Agent, Task, Crew
from crewai_tools import (
    DirectoryReadTool,
    FileReadTool,
    SerperDevTool,
    WebsiteSearchTool,
)

search = SerperDevTool()
docs = DirectoryReadTool(directory="./blog-posts")
files = FileReadTool()
web_rag = WebsiteSearchTool()

researcher = Agent(
    role="Market Research Analyst",
    goal="Provide up-to-date market analysis of the AI industry",
    backstory="An expert analyst with a keen eye for market trends.",
    tools=[search, web_rag],
    verbose=True,
)

writer = Agent(
    role="Content Writer",
    goal="Craft engaging blog posts about the AI industry",
    backstory="A skilled writer with a passion for technology.",
    tools=[docs, files],
    verbose=True,
)

SerperDevTool needs SERPER_API_KEY. In JSONC, "tools": ["SerperDevTool"] loads by class name; custom tools use "custom:" pointing at tools/*.py.

Built-ins (full table: Tools concepts): FileReadTool, FileWriterTool, DirectoryReadTool, *SearchTool variants (PDF / CSV / site RAG), ScrapeWebsiteTool, CodeInterpreterTool, RagTool, LlamaIndexTool, LangChainTool.

Document retrieval should follow this site’s RAG chunking and eval advice. Implement the index in LlamaIndex and wrap it with LlamaIndexTool. LangChain tools wrap via LangChainTool for a CrewAI agent.


Custom: @tool

from crewai.tools import tool

@tool("Name of my tool")
def my_tool(question: str) -> str:
    """Clear description of when to use this tool."""
    return "Result from your custom tool"

Async functions work the same (async def under @tool). The docstring becomes the tool description — say when to use it and what arguments mean.


Custom: subclass BaseTool

from typing import Type
from pydantic import BaseModel, Field
from crewai.tools import BaseTool

class MyToolInput(BaseModel):
    argument: str = Field(..., description="Description of the argument.")

class MyCustomTool(BaseTool):
    name: str = "Name of my tool"
    description: str = "What this tool does. Vital for the agent to choose it."
    args_schema: Type[BaseModel] = MyToolInput

    def _run(self, argument: str) -> str:
        return "Tool's result"

You may return a Pydantic model as a typed output; agents typically see JSON fields. For business failures, prefer official ToolFailure (crewai.tools.tool_failure) over an error-looking string. tool_failure_policy (ignore / warn / raise) controls what happens next.


MCP

CrewAI can expose MCP servers as tools (crewai-tools adapters, or the MCP DSL / mcps field on agents). Transports include stdio, SSE, and Streamable HTTP. Security matches this site’s MCP tutorial: only trusted servers, least privilege. Follow official MCP pages; do not invent client APIs.


Practical notes

  • Fewer tools is better: give each role only what it needs.
  • Task-level tools=[...] tightens permissions (e.g. writers cannot search the web).
  • Built-in caching cuts repeat external calls; tune with cache_function.
  • RAG quality issues belong in the RAG tutorial (chunking / recall), not in “add another agent”.

Next steps

评论