DEV Community

Surender Gupta
Surender Gupta

Posted on

Building a Stateless Python MCP Server with FastAPI and FastMCP

I recently built a small Python starter template for creating a stateless MCP server using FastMCP and FastAPI.

The goal was simple: expose Python functions as MCP tools over Streamable HTTP, while keeping the server easy to run, test, and deploy.

What the project includes

This server includes:

  • Stateless MCP HTTP initialization
  • JSON response mode
  • FastAPI lifecycle management
  • A health check endpoint
  • Example MCP tools
  • Structured tool responses
  • MCP Inspector testing support

The MCP server is initialized like this:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP(
    "Stateless Python MCP Server",
    stateless_http=True,
    json_response=True,
)
Enter fullscreen mode Exit fullscreen mode

The important part is:

stateless_http=True
Enter fullscreen mode Exit fullscreen mode

This makes the HTTP server stateless, which is useful when deploying MCP servers as normal web services.

FastAPI integration

The MCP server is wrapped inside a FastAPI application.

@asynccontextmanager
async def lifespan(app: FastAPI):
    async with AsyncExitStack() as stack:
        await stack.enter_async_context(mcp.session_manager.run())
        yield
Enter fullscreen mode Exit fullscreen mode

Then the MCP application is mounted:

app = FastAPI(lifespan=lifespan)
app.mount("/", mcp.streamable_http_app())
Enter fullscreen mode Exit fullscreen mode

A health endpoint is also included:

@app.get("/health")
async def health():
    return {"status": "healthy"}
Enter fullscreen mode Exit fullscreen mode

This is useful for deployment checks, containers, reverse proxies, and cloud platforms.

Example MCP tools

Here is a simple tool:

@mcp.tool()
async def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b
Enter fullscreen mode Exit fullscreen mode

And another one with structured output:

class UserResult(TypedDict):
    id: int
    name: str
    status: str


@mcp.tool()
async def get_user_by_id(user_id: int) -> UserResult:
    """Get user details by ID."""
    return {
        "id": user_id,
        "name": "Demo User",
        "status": "active",
    }
Enter fullscreen mode Exit fullscreen mode

Using clear type hints helps MCP clients understand the input and output schema.

Running the server

The server can be started with:

uv run uvicorn app:app --host 0.0.0.0 --port 8000 --reload
Enter fullscreen mode Exit fullscreen mode

Health check:

curl http://localhost:8000/health
Enter fullscreen mode Exit fullscreen mode

Expected response:

{
  "status": "healthy"
}
Enter fullscreen mode Exit fullscreen mode

Testing with MCP Inspector

For local testing:

uv run mcp dev app.py
Enter fullscreen mode Exit fullscreen mode

The MCP Inspector can list available tools and call them with sample inputs.

Why this pattern is useful

This setup is useful when you want to expose Python functions as MCP tools without tying the server to a local-only or stateful runtime.

It can be used as a base for:

  • Internal automation tools
  • Database query tools
  • API wrappers
  • AI agent integrations
  • Developer productivity tools
  • Backend services exposed through MCP

Final thoughts

This project is a simple but practical starting point for building production-ready MCP servers in Python.

The next step is to replace the demo tools with real business logic, connect them to databases or APIs, and place the server behind authentication before exposing it publicly.

Top comments (0)