Building Servers: The FastMCP Framework

RoadmapsAI Engineering

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

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

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

Next: Agentic Reversals: MCP Sampling and Elicitation

Return to AI Engineering Roadmap