Tools: Giving Claude New Abilities
What a Tool Is
Tools are the first of MCP's three server primitives. A tool is an executable function the model can choose to call to perform an action — searching flights, sending a message, querying a database, editing a file. Each tool has a name, a description, and a typed input schema (JSON Schema) so the model knows exactly how to call it.
Tools are MODEL-controlled
This is the defining property: the language model decides when to call a tool, based on the user's request and context. You expose the tool; Claude chooses whether and when to use it. (Contrast this with resources, which are app-controlled, and prompts, which are user-controlled — the next lessons.)
| Tool method | Purpose | Returns |
|---|---|---|
| tools/list | Discover available tools | Array of tool definitions with input schemas |
| tools/call | Execute a specific tool | The tool's execution result (a content array) |
The two protocol operations behind every tool, discovered then invoked.
Defining a Tool with the Python SDK
The official Python SDK (FastMCP) turns tool creation into ordinary Python. You decorate a function with @mcp.tool(); the SDK reads the function's type hints and docstring to generate the JSON Schema automatically. No hand-written schemas. We'll build a small document-management server to make this concrete — documents live in a simple in-memory dictionary.
from mcp.server.fastmcp import FastMCP
from pydantic import Field
mcp = FastMCP("DocumentMCP", log_level="ERROR")
docs = {
"deposition.md": "This deposition covers the testimony of Angela Smith, P.E.",
"report.pdf": "The report details the state of a 20m condenser tower.",
"plan.md": "The plan outlines the steps for the project's implementation.",
}@mcp.tool(
name="read_doc_contents",
description="Read the contents of a document and return it as a string."
)
def read_document(
doc_id: str = Field(description="Id of the document to read")
):
if doc_id not in docs:
raise ValueError(f"Doc with id {doc_id} not found")
return docs[doc_id]A Tool That Takes Action
Tools aren't just for reading — their real power is doing things. Here's an edit tool that performs a find-and-replace inside a document. Note three best practices baked in: clear parameter descriptions (so Claude calls it correctly), an exact-match contract for the text to replace, and explicit error handling via a normal Python exception.
@mcp.tool(
name="edit_document",
description="Edit a document by replacing a string in the document's content with a new string."
)
def edit_document(
doc_id: str = Field(description="Id of the document that will be edited"),
old_str: str = Field(description="The text to replace. Must match exactly, including whitespace."),
new_str: str = Field(description="The new text to insert in place of the old text.")
):
if doc_id not in docs:
raise ValueError(f"Doc with id {doc_id} not found")
docs[doc_id] = docs[doc_id].replace(old_str, new_str)Descriptions are not optional polish
The model selects and fills in tools almost entirely from their name, description, and parameter descriptions. Vague descriptions cause wrong tool choices and malformed arguments. Treat each description as part of the prompt — because to the model, it is.
Safety: Tools Act, So Guard Them
Because tools are model-controlled and can change the world (write to a database, send an email, spend money), MCP emphasizes human oversight. Tools may require user approval before they run, and applications commonly add controls around them.
- •Approval dialogs for individual tool executions (especially destructive ones).
- •Per-tool permission settings to pre-approve safe operations.
- •A UI that shows which tools are available and lets users toggle them per conversation.
- •Activity logs recording every tool execution and its result.
Schema validates shape, not intent
The JSON Schema ensures arguments are well-formed, but it can't judge whether an action SHOULD happen. High-stakes operations (refunds, deletes, transfers) deserve programmatic gates or human approval — not just a good description. We'll revisit this in the security lesson.
Next primitive
Tools let Claude DO things. But sometimes you just want to feed the app data for context or UI — without the model deciding to call anything. That's the job of resources, next.
Key Takeaways
- ✓Tools are the first server primitive: executable functions the model can call to take actions, each with a name, description, and JSON Schema input.
- ✓Tools are MODEL-controlled — Claude decides when to call them based on context (vs app-controlled resources and user-controlled prompts).
- ✓Two protocol operations: tools/list (discover) and tools/call (execute); a call returns a content array.
- ✓With the Python SDK you define a tool by decorating a function with @mcp.tool(); type hints + Field descriptions generate the schema automatically — no hand-written JSON.
- ✓Name and parameter descriptions are effectively part of the prompt — vague ones cause wrong tool selection and bad arguments.
- ✓Because tools take real actions, add human oversight: approval dialogs, per-tool permissions, and activity logs; schema validates shape, not whether an action should happen.
Check Your Understanding
Test what you learned in this lesson.
Q1.What does it mean that tools are 'model-controlled'?
Q2.When using the Python SDK, how is a tool's input schema created?
Q3.Which two protocol operations correspond to tools?
Q4.Why does MCP emphasize human oversight (approval dialogs, logs) specifically for tools?
Practice This Lesson