Module 5 — Tools shared between agents
The Analyst needs to read a file. The Reviewer needs to compare two versions of a paragraph. The Writer needs almost nothing beyond a way to save its draft. Attaching every tool to every agent turns each turn into a menu-search problem for the model and multiplies wrong tool calls. This module wires the right tools to the right agents, and no more.
Built-in tools cover the boring 80 %
The crewai_tools package ships with a small set that already handles the running project's needs:
from crewai_tools import FileReadTool, DirectoryReadTool, SerperDevTool
read_brief = FileReadTool(file_path="input/brief.md")
list_drafts = DirectoryReadTool(directory="drafts/")
web_search = SerperDevTool() # requires SERPER_API_KEY
The three cover the archetypal I/O of a documentation crew: read a known file, list a working folder, search the web when a fact is missing. FileReadTool accepts either a fixed file_path (the tool always reads the same file, useful for a briefing document) or no argument (the model must pass the path — riskier because the model can invent paths).
Two other built-ins worth knowing early: PDFSearchTool for a corpus of PDFs, and CodeInterpreterTool for tasks that need to actually run code. The full catalogue is in the crewai-tools documentation; skim it once before writing a custom tool that already exists.
Attaching tools: per agent, per task, or both
CrewAI lets you attach tools at two levels, and the difference matters.
analyst = Agent(
role="Specification Analyst",
...,
tools=[read_brief], # agent-level: available on every task
)
extract_features = Task(
description="...",
agent=analyst,
tools=[read_brief], # task-level: extra tools for this task only
)
Agent-level tools are always available to the agent, regardless of which task it runs. Use this for tools that reflect the agent's identity (the Reviewer always has the style guide reader). Task-level tools override or extend agent tools for one specific task. Use this for tools that only make sense for a specific step — a web_search that the Analyst may use during extraction but not later.
The rule of thumb: if the tool is part of who the agent is, attach it to the agent. If the tool is part of what the task is, attach it to the task. Attaching to both is legal and often unnecessary.
Custom tools: BaseTool in twenty lines
When no built-in fits, subclass BaseTool and give it a name the model will use, a description the model will read to decide whether to call it, and a _run method:
from crewai_tools import BaseTool
from pydantic import BaseModel, Field
class StyleGuideCheckArgs(BaseModel):
paragraph: str = Field(..., description="A paragraph of the draft to check")
class StyleGuideCheckTool(BaseTool):
name: str = "style_guide_check"
description: str = (
"Check a paragraph against the house style guide. "
"Returns a JSON list of violations with rule id, quoted excerpt and fix suggestion."
)
args_schema: type[BaseModel] = StyleGuideCheckArgs
def _run(self, paragraph: str) -> str:
return json.dumps(check_paragraph(paragraph))
Three details that separate a working custom tool from a broken one. First, the description is the prompt the model uses to decide whether to call the tool — it is not for humans, it is for the model. Write it in verb-first English, say what the tool needs and what it returns, and cap it around 200 characters. Second, the args_schema is validated before _run is called — a wrong argument type raises a clean error instead of a mysterious crash inside _run. Third, _run should return a string or a JSON-serialisable object; returning a Python object with a custom repr yields a Tool returned line that helps nobody.
Permissions, or how to keep the Writer off the source
CrewAI has no first-class permission system, but the pattern that works is straightforward: give each agent only the tools its goal requires, and rely on the fact that an agent cannot call a tool it does not have.
- Analyst gets
read_brief. Read-only, single file — it cannot rewrite the source, cannot list other files, cannot search the web. - Writer gets no tools at all. It receives the feature list through
context=[extract_features](module 3) and outputs a draft tooutput_file. No tool means no path exists for the Writer to invent facts by search or overwrite the brief. - Reviewer gets
read_briefandstyle_guide_check. It can verify the draft against the source and against the style rules, and produces a verdict (module 3) — nothing else. - Manager gets
read_briefandlist_drafts. It arbitrates between the Writer and the Reviewer with full visibility into both artefacts.
Notice what none of them has: write access to input/brief.md. The brief is the ground truth; a documentation crew must not be able to rewrite it. If a task ever requires editing the brief, that task belongs to a separate crew run, kicked off explicitly, with its own review.
The one tool the Writer will ask for anyway
Sooner or later, someone will suggest giving the Writer a web_search tool "just in case it needs a definition". Resist. The Writer's goal (module 2) is to turn the feature list into prose. A missing definition is a Reviewer issue to be raised, not a Writer initiative — otherwise the Writer starts filling gaps with content the Analyst never validated, and the whole separation of module 1 collapses.
A custom tool whose description says "for anything text-related" will be called by every agent, on every task, forever. Write the description like a job ad: what the tool takes, what it returns, when it is the right choice. If you cannot name the situation in one sentence, the tool is misdesigned.
Summary
- Prefer built-in tools (
FileReadTool,DirectoryReadTool,SerperDevTool,PDFSearchTool) before writing a custom one — most of the boring I/O is covered. - Attach tools per agent for identity ("the Reviewer always has the style guide") and per task for step-specific needs; both is legal and often overkill.
- Custom tools subclass
BaseToolwith a name, a description written for the model, anargs_schemavalidated before_run, and a JSON-serialisable return value. - Permissions are just tool attribution: give each agent only what its goal requires — the Writer gets no tools, the Analyst gets read-only access to the brief.
Next module: allowing the Manager to delegate to co-workers, and stopping the loops that setting allow_delegation=True can trigger.