Chapter 3 · Part 2
Tools: let it do things
Tools are the primitive that lets an AI act — run a query, call an API, change a file. They're the POST-like verbs of your server. A calculator was a fine hello-world; now add tools that do something real.
More than one, and with real types
Add a couple of tools to server.py. Notice how much the type hints and docstring carry — a
list[str], an optional argument with a default, all become the schema automatically:
import httpx
@mcp.tool()
def search_notes(query: str, limit: int = 5) -> list[str]:
"""Search the user's notes and return the matching lines.
Use this whenever the user asks what they wrote or saved about a topic.
"""
with open("notes.txt") as f:
hits = [line.strip() for line in f if query.lower() in line.lower()]
return hits[:limit]
@mcp.tool()
def word_count(text: str) -> int:
"""Count the number of words in a piece of text."""
return len(text.split())Each decorated function becomes a tool the AI can choose. query: str and limit: int = 5
generate the input schema — limit is optional because it has a default. The return annotation
(list[str], int) tells the client what comes back.
The docstring is the interface
The model decides whether to call a tool almost entirely from its docstring. Treat it as the tool's user manual, and say when to reach for it, not just what it does.
- Weak:
"""Search notes."""— the model has to guess when it applies. - Better:
"""Search the user's notes and return matching lines. Use this whenever the user asks what they wrote or saved about a topic."""
The trigger condition is what turns a tool the model has into a tool the model uses.
Return something useful
Return plain Python — strings, numbers, lists, dicts — and FastMCP serializes it for you. A dict comes back as structured data the client can read field by field, which is usually friendlier than cramming everything into one string.
Whatever calls your server controls the arguments. search_notes only reads a fixed file — but
the moment a tool opens an arbitrary path, runs a command, or writes data, validate the input
first. We'll come back to this when we secure the server.
Tools cover doing. Next: the two primitives for reading data and reusing prompts.