Discover how FastMCP uses Python decorators and JSON schemas to register tools and validate parameters automatically.
Decorators are one of Python’s most elegant features. Using the syntax @, you can modify a function's behavior without changing its code. FastMCP uses this feature to turn ordinary Python functions into MCP tools—without configuration files, manual registration, or boilerplate code. Just add the decorator, and the magic happens.
A decorator in Python is a function that receives another function as an argument and returns a modified version of it. The syntax @decorator is just a shortcut for funcao = decorator(funcao). With FastMCP, the decorator @mcp.tool() does three things: (1) registers the function in the server’s tool list; (2) extracts metadata (name, parameters, description) using Python introspection; and (3) keeps the original function intact — it continues to work normally like any Python function.
Think of a decorator as a badge that marks the function as "available to AI". Just as a product on a store shelf needs a price tag to be sold, a Python function needs the decorator @mcp.tool() to be visible as an MCP tool. Without the badge, the function exists, but no one outside the code knows it’s there. With the badge, it appears in the catalog of tools the model can use.
The decorator @mcp.tool() is the most important component of FastMCP. It does the heavy lifting of connecting your Python code to the MCP protocol, automatically extracting everything the client needs to know about the tool: its name, description, and parameter schema. Let’s look at exactly what it extracts and how.
The decorator @mcp.tool() automatically extracts three essential pieces of information from the function: Name — derived directly from the Python function name (e.g., def buscar_clima becomes the tool buscar_clima); Description — extracted from the function's docstring, which the model reads to understand when and how to use the tool; and Parameters — extracted from the type hints and automatically converted to JSON Schema for validation. All of this happens without any manual configuration.
FastMCP’s "convention over configuration" approach is inspired by frameworks such as Ruby on Rails and FastAPI. Developer productivity studies show that eliminating boilerplate code can increase development speed by up to 40%. With FastMCP, the same tool that would require ~30 lines of manual configuration (name, JSON schema, handler) is defined in just 3–5 lines of idiomatic Python with the decorator.
Python type hints are the foundation of FastMCP’s schema system. Each Python type is automatically converted into an equivalent JSON Schema type, which the AI model uses to know exactly which parameters to send when calling the tool. The more precise the type hints, the more accurately the model will use the tool.
FastMCP automatically converts Python type hints into JSON Schema:
The model uses this schema to validate the parameters before sending them — ensuring the tool always receives data in the correct format.
Always use type hints as specific as possible. Instead of list, use list[str]. Instead of dict, use dict[str, int]. The more information the schema contains, the more accurately the model will fill in the parameters. A parameter marked as Optional[str] = None tells the model it can omit this field when it isn't relevant.
What a tool returns is just as important as what it receives. FastMCP supports various return types and handles serialization automatically. Strings are returned directly, dictionaries and lists are converted to JSON, and complex types are handled transparently. Understanding return types helps you build tools that communicate results clearly to the model.
MCP tools can return different types of data: str — the most common, returns plain text that the model can use directly; dict — for structured data, automatically serialized as JSON; list — for collections of items, also serialized as JSON. FastMCP converts any of these types into the correct MCP response format. For most cases, returning a str providing the formatted information is the simplest and most effective option.
The Python function returns a value (str, dict, list, or another supported type).
FastMCP detects the return type and converts it to the MCP content format (TextContent for strings, JSON for dicts/lists).
The result is wrapped in a JSON-RPC response and sent back to the client, which passes it on to the model.
Your functions’ docstrings aren’t just good programming practice — in the context of MCP, they’re the interface between the AI model and the tool. The model reads the description to decide when to use the tool and how to fill in its parameters. A poor description means the model will use the tool at the wrong time or with incorrect parameters. A good description means the tool will be used perfectly.
A docstring from the function becomes the description of the tool in the MCP protocol. The AI model reads this description to make three decisions: (1) When to use — "is this tool relevant to what the user asked?"; (2) How to use it — “what parameters should I pass?”; and (3) What to expect—"what kind of result does this tool return?". The clearer, more specific, and more informative the description, the better the model uses the tool. Vague descriptions lead to imprecise use.
Write docstrings as if you were explaining the tool to a colleague who has never seen your code. Be specific about what the tool does, mention when it should be used, and describe what each parameter means. For example: instead of "Fetch data," write "Fetch the current weather forecast for a Brazilian city. Return the temperature, humidity, and weather conditions."
Building MCP tools that work is easy. Building tools that work well requires attention to best practices. Clear names, complete type hints, descriptive docstrings, smart use of defaults and Optional — all of this directly affects the quality of the end user's experience with AI. Here are the practices that distinguish amateur tools from professional ones.
Best practices for MCP tools include: Clear names — use descriptive verbs such as buscar_clima, criar_tarefa, enviar_email; Complete type hints — annotate all parameters and the return value with precise types; Descriptive docstrings — explain what the tool does, when to use it, and what each parameter means; Smart defaults — use default values when a parameter has a more common value; and Optional — use Optional[tipo] for fields that aren't required, allowing the model to omit them when they aren't relevant.
Never assume the model will send the correct parameters — always validate inputs inside the function. Although JSON Schema helps, the model can send unexpected values (empty strings, out-of-range numbers, etc.). Use simple validations at the start of the function and return clear error messages when the data is invalid. A tool that fails with a helpful message is much better than one that crashes silently.
@mcp.tool() automatically extracts: name (of the function), description (from the docstring), and parameters (from the type hints).
Next Module: 3.4 — Calculator Server