Deploying FastMCP Servers

Transport Selection

TransportWhen to use
stdioCLI tools, subprocess spawning (Claude Desktop, Claude Code)
sseHTTP streaming, web clients, remote access
streamable-httpLong-running operations, task-based tools
httpStateless HTTP (simplest, no streaming)

As a PyPI Package

The distribution pattern used by largefile, diffchunk, md-server:

# pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "my-mcp-tool"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = ["fastmcp"]

[project.scripts]
my-mcp-tool = "my_mcp_tool.main:cli_main"

Users install and run with:

uvx --from my-mcp-tool my-mcp-tool
# or
pip install my-mcp-tool && my-mcp-tool

MCP Config (for clients)

Standard mcp_config.json or Claude Desktop config:

{
  "mcpServers": {
    "my-tool": {
      "command": "uvx",
      "args": ["--from", "my-mcp-tool", "my-mcp-tool"],
      "env": {
        "API_KEY": "..."
      }
    }
  }
}

HTTP Deployment

For remote/shared servers:

# server.py
from fastmcp import FastMCP

mcp = FastMCP("MyServer")
# ... tools ...

if __name__ == "__main__":
    mcp.run(transport="sse", host="0.0.0.0", port=8000)

Behind a reverse proxy (nginx, Caddy), expose the SSE endpoint.

ASGI Integration

Embed in an existing FastAPI/Starlette app:

from fastapi import FastAPI
from fastmcp import FastMCP

app = FastAPI()
mcp = FastMCP("Embedded")

# Register tools on mcp...

# Mount MCP at a path
mcp_app = mcp.http_app(transport="sse")
app.mount("/mcp", mcp_app)

Custom Routes

Add health checks, metrics, or other HTTP endpoints alongside MCP:

from starlette.requests import Request
from starlette.responses import JSONResponse

@mcp.custom_route("/health", methods=["GET"])
async def health(request: Request) -> JSONResponse:
    return JSONResponse({"status": "ok"})

@mcp.custom_route("/metrics", methods=["GET"])
async def metrics(request: Request) -> JSONResponse:
    return JSONResponse({"tools": len(mcp._tool_manager.tools)})