MCP-server-as-a-FastAPI-application

BibekBarik18/MCP-server-as-a-FastAPI-application

3.1

If you are the rightful owner of MCP-server-as-a-FastAPI-application and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.

The Model Context Protocol (MCP) server is a framework designed to facilitate communication between machine learning models and applications, leveraging the FastAPI framework for deployment.

Expose an MCP server via FastAPI

Build once. Deploy anywhere. Expose your Model Context Protocol (MCP) tools over a clean FastAPI surface.

Overview

This project shows how to:

  • Turn a simple MCP server into a FastAPI application
  • Run and manage the MCP lifecycle with FastAPI lifespan hooks
  • Mount one or more MCP apps under path prefixes
  • Deploy to common cloud platforms with container or process-based runners

What is MCP here?

MCP (Model Context Protocol) lets you expose “tools” callable by clients. The FastMCP helper from mcp.server.fastmcp converts those tools into an HTTP-ready app you can embed or mount in frameworks like FastAPI.


Project structure

.
├─ math_server.py     # Defines a simple MCP tool surface
├─ main.py            # FastAPI app that mounts the MCP app
├─ requirements.txt  # Or pyproject.toml / poetry.lock

Example MCP server

math_server.py

from mcp.server.fastmcp import FastMCP

mcp = FastMCP(name="MathServer", stateless_http=True)

@mcp.tool(description="A simple add tool")
def add_two(n: int) -> int:
    return n + 2

Key points:

  • FastMCP collects decorated tools and exposes them over HTTP and WebSocket.
  • stateless_http=True optimizes for ephemeral, per-request execution. Great for serverless and autoscaling.

Embedding MCP in FastAPI

main.py

import contextlib
from fastapi import FastAPI
from math_server import mcp
import os

@contextlib.asynccontextmanager
async def lifespan(app: FastAPI):
    async with contextlib.AsyncExitStack() as stack:
        # Start MCP runtime and keep it alive for the app lifecycle
        await stack.enter_async_context(mcp.session_[manager.run](http://manager.run)())
        yield

app = FastAPI(lifespan=lifespan)

# Mount the MCP app at a path prefix
app.mount("/echo", mcp.streamable_http_app())

PORT = int(os.environ.get("PORT", 10000))

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=PORT)

How it works:

  • Lifespan: keep the MCP session manager open for app lifetime so tools are ready across workers.
  • Mounting: the MCP sub-app lives under /echo. If the MCP app serves /mcp, the full path is /echo/mcp.

Run locally

Using Uvicorn:

uvicorn main:app --reload

Endpoints:

Note: If you change the port via PORT, update your requests accordingly.


Multiple MCP servers under one host

Mount several MCP sub-apps under a shared host:

import contextlib
from fastapi import FastAPI
from echo_server import mcp as echo_mcp
from math_server import mcp as math_mcp
import os

@contextlib.asynccontextmanager
async def lifespan(app: FastAPI):
    async with contextlib.AsyncExitStack() as stack:
        await stack.enter_async_context(echo_mcp.session_[manager.run](http://manager.run)())
        await stack.enter_async_context(math_mcp.session_[manager.run](http://manager.run)())
        yield

app = FastAPI(lifespan=lifespan)
app.mount("/echo", echo_mcp.streamable_http_app())
app.mount("/math", math_mcp.streamable_http_app())

PORT = int(os.environ.get("PORT", 10000))

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=PORT)

This yields:

  • /echo/mcp
  • /math/mcp

You can also add regular FastAPI routes at /api/... alongside these mounts.


Security considerations

  • Add authentication at the FastAPI layer. API keys or OAuth are common.
  • Apply rate limiting or a WAF/CDN for public endpoints.
  • Validate tool inputs. Check types and ranges to avoid misuse.

Performance tips

  • For concurrency, run multiple workers:

    uvicorn main:app --host 0.0.0.0 --port $PORT --workers 2
    
  • Keep MCP stateless for fast cold starts and horizontal scaling.


Troubleshooting

  • Getting 404s on /mcp? Check the mount prefix. With app.mount("/echo", ...), the path is /echo/mcp.
  • Port mismatches: align PORT env var, container exposure, and client requests.
  • Verify your platform’s health checks target a live route.

Summary

  • Define tools with FastMCP and @mcp.tool.
  • Manage MCP lifecycle in FastAPI’s lifespan.
  • Mount the MCP app with app.mount(prefix, mcp.streamable_http_app()).
  • Deploy to any cloud that runs ASGI apps or containers.
  • Scale out easily and host multiple MCP servers behind a single URL space.