Courses/Introduction to Model Context Protocol (MCP)/Tools: Giving Claude New Abilities
Server PrimitivesLesson 7 of 21

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 methodPurposeReturns
tools/listDiscover available toolsArray of tool definitions with input schemas
tools/callExecute a specific toolThe 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.

pythonA FastMCP server and an in-memory document store to back our tools.
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.",
}
pythonA read tool. The Field description and type hint become the tool's input schema automatically.
@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.

pythonAn edit tool with three well-described parameters and built-in validation.
@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