Building Servers: The FastMCP Framework
Scenario
You need to expose a complex `calculate_tax` function to an LLM. Historically, this meant spending an hour writing a massive, perfectly nested JSON Schema dictionary by hand, and hoping you didn't miss a bracket.
A learner is asked to expose a `calculate_tax` function to an LLM. They start writing a complex nested JSON dictionary by hand. What modern Python feature should they use instead?
Mental model
FastMCP is like FastAPI, but for AI agents instead of web browsers.
Building an MCP server from scratch requires manually handling JSON-RPC lifecycles, parsing incoming messages, and hand-writing massive JSON Schema definitions for every tool. FastMCP abstracts all of that away. You write standard Python functions with type hints and a docstring. You add an `@mcp.tool` decorator. FastMCP automatically reads your types and docstrings, generates the required JSON Schema, validates inputs, and handles all the network plumbing.
Deep dive
Before FastMCP, exposing a tool to an LLM required writing two things: the actual execution logic, and a massive JSON dictionary describing that logic. If your code changed, you had to manually update the JSON Schema. This was error-prone and tedious.
Manual JSON Schema generation is tedious and error-prone.
FastMCP solves this using Python's native type hints and docstrings. When you decorate a function with `@mcp.tool`, the framework inspects your code at runtime. It maps Python types (like `str` or `int`) directly to JSON Schema types, and it extracts your docstring to serve as the tool's description for the LLM.
FastMCP auto-generates JSON Schemas from Python type hints and docstrings.
This means your Python docstring is literally the prompt instruction payload the LLM reads. If you write a bad docstring, the LLM won't know how to use your tool. You must write docstrings for the AI, clearly explaining what the tool does and what the parameters mean.
Your Python docstring becomes the LLM's prompt instruction.
FastMCP also simplifies Resources. A basic Resource serves static text (like a config file). But what if you need to expose 10,000 user profiles? You shouldn't register 10,000 static resources. Instead, FastMCP supports Resource Templates.
Resources aren't just static files; they can be dynamically generated.
Using a parameterized URI template like `user://{id}`, you define a function that dynamically fetches and returns the correct profile whenever the Host requests a specific ID. The Host can list the templates, and the LLM (or user) can request dynamic data on demand.
Resource Templates use parameterized URIs to serve dynamic content.
Code examples
The manual way (Don't do this)
# The old way: Hand-writing the JSON Schema
tool_schema = {
"name": "calculate_tax",
"description": "Calculates sales tax for a given amount.",
"inputSchema": {
"type": "object",
"properties": {
"amount": {"type": "number", "description": "The purchase amount"}
},
"required": ["amount"]
}
}
This boilerplate is fragile. If you change the parameter name in your code, you must remember to update this schema manually, or the LLM will fail to call it.
The FastMCP way: Auto-generated schemas
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("TaxServer")
@mcp.tool()
def calculate_tax(amount: float) -> float:
"""Calculates sales tax for a given amount.
Args:
amount: The purchase amount in USD.
"""
return amount * 0.08
This is all you write. FastMCP reads the `float` type hint and the docstring to dynamically generate the exact JSON Schema shown above. The LLM reads your docstring to understand the tool.
Dynamic Resource Templates
@mcp.resource("user://{user_id}")
def get_user_profile(user_id: str) -> str:
"""Fetch a user profile by ID."""
# Imagine a database call here
return f"Profile data for {user_id}"
Instead of crashing the server by registering 10,000 static resources, this template dynamically handles any request matching the `user://{user_id}` pattern.
Common mistakes
- Manually writing JSON Schemas: Because direct OpenAI function calling historically required passing strict JSON Schema dictionaries, developers assume MCP requires the same boilerplate. Reality: FastMCP completely eliminates this by using Python type hints and standard docstrings to automatically generate the required JSON Schema at runtime.
- Assuming Resources are only static: Basic tutorials usually demonstrate resources by serving a hardcoded text file. Developers needing to expose thousands of records attempt to write a loop that registers a separate static resource for each one, crashing the server. Reality: FastMCP supports Resource Templates (e.g., `user://{id}`) to dynamically generate content on demand.
Glossary
- FastMCP
- A high-level Python framework that abstracts away JSON-RPC boilerplate, allowing developers to build MCP servers using simple function decorators.
- JSON Schema
- A standard format for describing the structure of JSON data. FastMCP automatically generates this from Python type hints and docstrings.
- Resource Template
- A parameterized URI (like `user://{id}`) that allows a server to dynamically generate and serve resource content based on the client's request.
Recall questions
- How does FastMCP map native Python functions to compliant MCP tools?
- How are JSON Schemas generated dynamically without manual configuration?
- A developer needs to expose 10,000 unique user profiles and attempts to write a script that registers 10,000 separate static resources, crashing the server. What should they use instead?
- A learner is asked to expose a `calculate_tax` function to an LLM. They spend 20 minutes writing a complex nested JSON dictionary. What is the modern FastMCP alternative?
Questions & answers
How do you avoid writing manual JSON schemas when building an MCP server in Python?
You use a high-level framework like FastMCP. By applying the `@mcp.tool` decorator to a function, FastMCP uses Python's runtime reflection to read the type hints and docstrings, automatically generating the required JSON schema and input validation.
Approach: Mention the `@mcp.tool` decorator. Explain that type hints and docstrings are converted directly into the JSON Schema the LLM consumes.
How would you serve 10,000 unique user profiles as MCP resources without overloading the server?
Instead of statically registering 10,000 individual resources, I would use a Resource Template. By defining a parameterized URI like `user://{id}`, the FastMCP server can dynamically generate and serve the exact profile requested by the host at runtime.
Approach: Contrast static resources with dynamic Resource Templates. Mention parameterized URIs.
Continue learning
Previous: MCP Primitives: Tools, Resources, and Prompts