Turning Point Academy · MCP Engineering

From API to MCP with FastAPI-MCP — Commented code

Curso MCP Engineering · M2


Module 2 · MCP Engineering · Turning Point Academy

This guide takes a REST API that already exists (built with FastAPI) and converts it into an MCP server with very few lines, using FastAPI-MCP. We serve it over Streamable HTTP so it's remote (accessible over the network), not over stdio. It's meant to be read top to bottom; you can copy and paste each block. Today's central idea: you don't rewrite the logic, you wrap it.


Step 0 — Two key words from scratch

The lesson's idea: each endpoint of your API can become an MCP tool that the model can invoke. You don't duplicate code; you reuse it.


Step 1 — Install the dependencies

# fastapi     -> the API framework (you may already have had it)
# fastapi-mcp -> converts your existing API into an MCP server
# uvicorn     -> sets your app listening for requests over the network (HTTP)
# NOTE: we pin the fastapi-mcp version (it evolves quickly).
pip install fastapi "fastapi-mcp>=0.3" uvicorn
Pin the version. fastapi-mcp changes often between releases; pinning >=0.3 (or better, anchoring an exact version in your project, for example fastapi-mcp==0.3.x) keeps an update from breaking the code in this guide. It's a young library: what's called one way today may change name or method tomorrow.

Step 2 — The starting point: a FastAPI API (without MCP)

This is a minimal calculator as a REST API. It still has nothing to do with MCP: they're normal HTTP endpoints.

# main.py
from fastapi import FastAPI

# 'app' is your FastAPI application: the central object that groups all the endpoints.
app = FastAPI(title="Calculadora API")


@app.post("/multiply")
def multiply(a: float, b: float) -> float:
    """Multiply two numbers and return the result."""
    # The docstring above becomes the tool's DESCRIPTION in MCP.
    # That's why it's worth writing it clearly: the model reads it to decide when to use it.
    return a * b


@app.post("/add")
def add(a: float, b: float) -> float:
    """Add two numbers and return the result."""
    return a + b

@app.post("/multiply") is a decorator: it tells FastAPI that, when a POST request arrives at the URL /multiply, it should run that function. Each function like this is an endpoint. Notice that the docstring ("""Multiply two numbers...""") is not just a note for you: FastAPI-MCP will use it as the tool's description, and the model reads it to decide when to call it. Clear docstrings = tools the agent uses well.


Step 3 — Mount MCP on top of the existing API

Here's the key step. With three lines we expose the endpoints above as MCP tools — without rewriting the logic.

# main.py (continued)
from fastapi_mcp import FastApiMCP

# 1) We wrap the EXISTING app in an MCP layer.
#    'name' is how the server presents itself to the MCP agent/client.
mcp = FastApiMCP(app, name="calculator MCP")

# 2) mount() hooks the MCP server INSIDE the same app.
#    From here on, /multiply and /add are not just HTTP endpoints:
#    they're also tools that a model can discover and call.
mcp.mount()

What happened, line by line:


Step 4 — Complete file

# main.py
from fastapi import FastAPI
from fastapi_mcp import FastApiMCP

app = FastAPI(title="Calculadora API")


@app.post("/multiply")
def multiply(a: float, b: float) -> float:
    """Multiply two numbers and return the result."""
    return a * b


@app.post("/add")
def add(a: float, b: float) -> float:
    """Add two numbers and return the result."""
    return a + b


# We convert the existing API into an MCP server and mount it:
mcp = FastApiMCP(app, name="calculator MCP")
mcp.mount()

Step 5 — Serve over Streamable HTTP (remote transport)

Since this server lives on the network (others connect to it over the internet), we serve it over Streamable HTTP — MCP's remote transport (spec 2025-03-26) — and not over stdio, which is only for local tools. We bring it up with uvicorn:

# 'main:app' = file main.py, object 'app'
# --host 0.0.0.0 -> listens on all network interfaces
# --port 8000    -> port where it becomes available
uvicorn main:app --host 0.0.0.0 --port 8000

With that, your MCP server becomes accessible over HTTP for any compatible client to connect. To test it before plugging it into a real client, open the MCP Inspector, choose the HTTP transport and paste your server's URL (for example, http://localhost:8000).

⚠️ Don't use the HTTP+SSE transport (2024): it's deprecated. For new servers, Streamable HTTP is the way. stdio, on the other hand, is only for local tools that run on your own machine.
🔎 Verify the transport in your version. The mount/transport API of fastapi-mcp changes between versions. Confirm that the one you installed serves over Streamable HTTP (not over the deprecated SSE): check the README of that exact version and test the connection in the Inspector. If the mount method you see here doesn't exist in your version, find the equivalent in its README — the concept (wrap + mount + serve over HTTP) is the same.

Step 6 — Convert or build from scratch?

There's no single answer; it depends on where you're standing:

Simple rule: does the API already exist? Convert it. Starting from scratch? Build it.


Common errors (and how to fix them)


To remember: FastApiMCP(app) + mount() convert an existing API into MCP; uvicorn serves it over Streamable HTTP so it's remote. You don't rewrite anything: you wrap what you already have.