PTENES
MODULE 3.3

🎨 Understanding Decorators and Schemas

Discover how FastMCP uses Python decorators and JSON schemas to register tools and validate parameters automatically.

6
Topics
25
Minutes
Intermediate
Level
Theoretical
Type
1

🎨 What Are Decorators

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.

💎 Core Concept

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.

💡 Practical Tip

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.

2

📋 @mcp.tool() in Detail

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.

💎 Core Concept

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.

📊 Data and Research

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.

❌ Avoid
  • •Functions without docstrings (tool without a description)
  • •Parameters without type hints (incomplete schema)
  • •Generic function names like “process”
  • •Forgetting the parentheses: @mcp.tool vs @mcp.tool()
✅ Do
  • •Always include a descriptive docstring
  • •Type hints for all parameters
  • •Specific names: “buscar_clima,” “criar_tarefa”
  • •Always use @mcp.tool() with parentheses
3

📝 Parameter Schemas

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.

💎 Core Concept

FastMCP automatically converts Python type hints into JSON Schema:

str
→ "type": "string"
int
→ "type": "integer"
float
→ "type": "number"
bool
→ "type": "boolean"
list
→ "type": "array"
dict
→ "type": "object"
Optional[str]
→ "type": "string" (optional)

The model uses this schema to validate the parameters before sending them — ensuring the tool always receives data in the correct format.

💡 Practical Tip

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.

4

📤 Return Types

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.

💎 Core Concept

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.

❌ Avoid
  • •Silently return None
  • •Return non-serializable objects
  • •Return generic error messages
  • •Return data without explanatory context
✅ Do
  • •Return clear, informative strings
  • •Use dicts for structured data
  • •Include descriptive error messages
  • •Add context to the result for the model
📋 Return Serialization Flow
Step 1

The Python function returns a value (str, dict, list, or another supported type).

Step 2

FastMCP detects the return type and converts it to the MCP content format (TextContent for strings, JSON for dicts/lists).

Step 3

The result is wrapped in a JSON-RPC response and sent back to the client, which passes it on to the model.

5

📖 Descriptions and Documentation

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.

💎 Core Concept

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.

💡 Practical Tip

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."

❌ Poor descriptions
  • •"Process data" — what? Which data?
  • •"Do the thing" — what thing?
  • •"Helper function" — helper for what?
  • •Without a docstring (empty description)
✅ Good Descriptions
  • •"Gets the weather forecast for a city"
  • •"Calculates the sum of two integers"
  • •"Sends an email to the specified recipient"
  • •"List the 10 most recent GitHub repositories"
6

⚡ Best Practices

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.

💎 Core Concept

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.

❌ Avoid
  • •Tools that do too many things (single responsibility principle)
  • •Parameters without type hints (incomplete schema)
  • •Silent errors with no explanatory message
  • •Too many required parameters when they aren’t necessary
✅ Do
  • •One tool = one clear responsibility
  • •Type hints + docstring for every tool
  • •Error handling with helpful messages
  • •Use Optional and defaults for flexibility
🚨 Alert

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.

📝 Module Summary

  • ✓ Python decorators use @ to modify functions — FastMCP uses them to register tools without extra code.
  • ✓ O @mcp.tool() automatically extracts: name (of the function), description (from the docstring), and parameters (from the type hints).
  • ✓ Python type hints (str, int, float, bool, list, dict, Optional) are automatically converted to JSON Schema.
  • ✓ Tools can return str, dict, or list—the FastMCP automatically serializes them to the MCP format.
  • ✓ Docstrings are the interface with the model — the better the description, the more correctly the model uses the tool.
  • ✓ Best practices: clear names, complete type hints, descriptive docstrings, defaults, and Optional when appropriate.

Next Module: 3.4 — Calculator Server