n4nAI

CrewAI custom tools tutorial: building your first tool

Hands-on CrewAI tutorial: step by step build, test, and integrate your first custom tool with a live agent, including runnable code and expected output.

n4n Team3 min read687 words

Audio narration

Coming soon — every post will get a voice note here.

This crewai custom tool tutorial first tool walks you through building a functional extension for a CrewAI agent from scratch. You will define an input schema, implement the logic, test it standalone, then wire it into a multi-agent crew that calls a live API for cryptocurrency prices.

Prerequisites

Before writing any code, set up an isolated environment and install the dependencies. You need Python 3.10 or newer because CrewAI and Pydantic v2 rely on modern type features.

python -m venv .venv
source .venv/bin/activate
pip install crewai requests python-dotenv

You also need an LLM endpoint. The default path is an OpenAI API key, but you can point CrewAI at the n4n.ai OpenAI-compatible endpoint to get automatic fallback across 240+ models when a provider is rate-limited. Either works; the tool code is identical.

Create a .env file:

OPENAI_API_KEY=sk-...
# or N4N_API_KEY=...

Finally, confirm you can reach the public CoinGecko price endpoint from your network—no API key is required for the simple price route we use.

How CrewAI invokes tools

CrewAI serializes your tool’s name, description, and args_schema into a JSON schema and sends it to the LLM as a function definition. When the model decides the tool is relevant, it returns a structured call with arguments matching your Pydantic model. The framework validates those arguments, executes _run, and feeds the returned string back as an observation. That loop repeats until the agent produces a final answer. Keeping descriptions precise is the single highest-leverage thing you can do for tool reliability.

Step 1: Define the input contract

Start by declaring exactly what the agent must pass. Use Pydantic’s Field to attach descriptions that the LLM will read.

from pydantic import BaseModel, Field

class CryptoPriceInput(BaseModel):
    coin_id: str = Field(
        ..., description="CoinGecko coin id, e.g. 'bitcoin' or 'ethereum'"
    )
    currency: str = Field(
        default="usd", description="Ticker to price against, e.g. 'usd', 'eur', 'btc'"
    )

The ... marks coin_id as required. If the model omits it, validation fails before your code runs.

Step 2: Implement the tool

Subclass BaseTool from crewai_tools. Set the class attributes and implement _run. Always return a string—agents parse text, not objects.

from crewai_tools import BaseTool
import requests

class CryptoPriceTool(BaseTool):
    name: str = "Crypto Price Fetcher"
        "Returns the current market price of a cryptocurrency "
        "from CoinGecko. Use when asked about crypto valuations."
    )
    args_schema: type[BaseModel] = CryptoPriceInput

    def _run(self, coin_id: str, currency: str = "usd") -> str:
        url = "https://api.coingecko.com/api/v3/simple/price"
        params = {"ids": coin_id, "vs_currencies": currency}
        try:
            resp = requests.get(url, params=params, timeout=10)
            resp.raise_for_status()
            data = resp.json()
        except requests.RequestException as e:
            return f"Request failed: {e}"

        if coin_id not in data:
            return f"No price found for {coin_id}"
        price = data[coin_id].get(currency)
        if price is None:
            return f"Currency {currency} not available for {coin_id}"
        return f"1 {coin_id} = {price} {currency}"

Note the defensive try/except. An unhandled network error will bubble up and stall the entire crew; returning a descriptive string lets the agent recover or report gracefully.

Step 3: Test the tool in isolation

Never wire an untested tool into an agent. Run it directly:

if __name__ == "__main__":
    tool = CryptoPriceTool()
    print(tool.run(coin_id="ethereum"))
    print(tool.run(coin_id="bitcoin", currency="eur"))

Expected output (values fluctuate):

1 ethereum = 3120.45 usd
1 bitcoin = 58200.10 eur

If you get a Request failed message, check outbound HTTPS or CoinGecko’s public rate limit (roughly 10–30 calls/minute per IP).

Step 4: Configure the LLM and agent

Instantiate the LLM. CrewAI’s LLM class accepts base_url, so swapping providers is a one-line change.

from crewai import Agent, Crew, Task, Process, LLM
from dotenv import load_dotenv
import os

load_dotenv()

llm = LLM(
    model="gpt-4o-mini",
    temperature=0,
    api_key=os.getenv("OPENAI_API_KEY"),
    # To use n4n.ai instead, uncomment:
    # base_url="https://api.n4n.ai/v1",
    # api_key=os.getenv("N4N_API_KEY"),
)

Now create an agent that owns the tool:

analyst = Agent(
    role="Cryptocurrency Analyst",
    goal="Answer user questions about crypto prices accurately",
    backstory="You track token markets and verify numbers with tools.",
    tools=[CryptoPriceTool()],
    llm=llm,
    verbose=True,
)

verbose=True prints the thought/action/observation cycle, which is essential for debugging.

Step 5: Run a task through the crew

Define a task and execute the crew sequentially:

task = Task(
    description="What is the current price of solana in usd?",
    expected_output="The current price of solana in USD, stating the tool used.",
    agent=analyst,
)

crew = Crew(
    agents=[analyst],
    tasks=[task],
    process=Process.sequential,
)

result = crew.kickoff()
print("FINAL:", result)

Sample verbose log excerpt:

Agent: Cryptocurrency Analyst
Thought: I need the live price of solana.
Action: Crypto Price Fetcher
Action Input: {"coin_id": "solana", "currency": "usd"}
Observation: 1 solana = 145.22 usd
Final Answer: The current price of solana is 145.22 usd, fetched via Crypto Price Fetcher.
FINAL: The current price of solana is 145.22 usd, fetched via Crypto Price Fetcher.

That output confirms this crewai custom tool tutorial first tool is correctly discovered and invoked by the model.

Alternative: the @tool decorator

For stateless helpers, the decorator approach cuts boilerplate. It still satisfies the crewai custom tool tutorial first tool requirement of a minimal, runnable integration.

from crewai_tools import tool

@tool("Add Two Numbers")
def add_numbers(a: int, b: int) -> str:
    """Adds two integers and returns the sum as a string."""
    return str(a + b)

CrewAI infers the schema from type hints and the docstring. Use the class form when you need state (e.g., a cached session) or fine-grained validation; use the decorator for pure functions.

Common pitfalls when building your first tool

  • Vague descriptions: “Fetches data” leads to misfires. Say what data, from where, and when to use it.
  • Non-string returns: Returning a dict or None breaks the observation parser. Convert to text.
  • Missing timeouts: requests.get without timeout can hang the agent loop indefinitely.
  • Schema mismatches: If the LLM sends coin_id as id, Pydantic rejects it. Field names must match what the model sees in the schema.
  • Secret leakage: Never hardcode API keys; load them from environment as shown.

Production hardening

If you deploy this, add response caching to avoid hitting CoinGecko limits:

from functools import lru_cache

class CachedCryptoPriceTool(CryptoPriceTool):
    @lru_cache(maxsize=128)
    def _cached_price(self, coin_id: str, currency: str) -> str:
        return self._run(coin_id, currency)

And if you route LLM traffic through n4n.ai, its gateway honors provider cache-control hints and automatically fails over when a backend is degraded, which keeps the agent loop running during provider incidents.

Wrapping up

You have built, tested, and operated a custom CrewAI tool end to end. The pattern—Pydantic schema, _run implementation, isolated test, agent attachment—is the same for database queries, internal microservices, or file ingestion. Extend CryptoPriceTool with caching or swap in the @tool decorator, and you’ve completed the essential crewai custom tool tutorial first tool workflow with confidence.

Tagscrewaicustom-toolstutorialintegrations

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All crewai custom tools & integrations posts →