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
- REST API: a standard way for two programs to talk to each other over the internet. You send a request to a URL and it returns a response (normally in JSON).
- Endpoint: each concrete "door" of that API. For example,
POST /multiplyreceives two numbers and returns their product. An API has many endpoints, one for each thing it knows how to do.
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
- fastapi: the framework for creating the REST API.
- fastapi-mcp: the star of the show; it converts that API into an MCP server.
- uvicorn: the server that sets your app listening for requests over the network.
Pin the version.fastapi-mcpchanges often between releases; pinning>=0.3(or better, anchoring an exact version in your project, for examplefastapi-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:
FastApiMCP(app, name="calculator MCP")takes your app just as it is and builds an MCP layer on top of it. It doesn't replace it: it wraps it. Thenameis how the server presents itself to the agent.mcp.mount()hooks that MCP server inside the same app. FastAPI-MCP walks through your endpoints and generates one tool per endpoint, taking the name, the parameters (a,b) and the description (the docstring) from your own code. You didn't duplicate anything: you wrapped what you already had.
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:
- Convert with FastAPI-MCP when you already have a REST API in FastAPI that's tested: you reuse the logic, gain speed and avoid duplicating code. Ideal for bringing to MCP something that's already in production.
- Build from scratch with FastMCP (the previous lesson) when you start from zero, want fine control over each tool/resource/prompt, or your tool wasn't a web API to begin with (for example, something local that runs over stdio).
Simple rule: does the API already exist? Convert it. Starting from scratch? Build it.
Common errors (and how to fix them)
ModuleNotFoundError: No module named 'fastapi_mcp'→ you missedpip install "fastapi-mcp>=0.3", or you installed it outside the virtual environment. Activate the.venvand reinstall.- The
mount()method doesn't exist / raises an error → your version offastapi-mcpuses a different API. Check the README of that exact version and adjust; the concept doesn't change. uvicorn: command not found→ install uvicorn (pip install uvicorn) inside the active environment.- The client doesn't see the tools → confirm that uvicorn is running, first test the HTTP connection in the Inspector and verify that your version serves over Streamable HTTP.
- Address already in use (port 8000 taken) → change the port:
--port 8001.
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.