FastMCP 2 Python Servers: Create, Build, and Run
- Scope: Practical guide for authoring, packaging, containerizing, and exposing Python MCP servers with FastMCP 2.x.
- References: See full implementations under
mcp-servers/python/*/server_fastmcp.py (20 example servers available), e.g. mcp-servers/python/chunker_server/src/chunker_server/server_fastmcp.py and mcp-servers/python/url_to_markdown_server/src/url_to_markdown_server/server_fastmcp.py.
Project Layout
- Recommended structure for a new server
awesome_server:
awesome_server/
pyproject.toml
Makefile
Containerfile
README.md
src/
awesome_server/
__init__.py
server_fastmcp.py # FastMCP entry point
tools.py # optional: keep tool logic separate
tests/
test_server.py
Minimal Server (stdio + http)
- Implements a basic FastMCP server with one tool (
echo). Type hints define schemas.
# src/awesome_server/server_fastmcp.py
from fastmcp import FastMCP
mcp = FastMCP("awesome-server", version="0.1.0")
@mcp.tool
def echo(text: str) -> str:
"""Return the provided text."""
return text
def main() -> None:
"""Entry point for `python -m awesome_server.server_fastmcp`."""
mcp.run() # stdio by default
if __name__ == "__main__": # pragma: no cover
main()
Enhanced Server with Native HTTP Support
- For better flexibility, add argument parsing to support both stdio and HTTP modes natively:
# src/awesome_server/server_fastmcp.py
from fastmcp import FastMCP
import argparse
mcp = FastMCP("awesome-server", version="0.1.0")
@mcp.tool
def echo(text: str) -> str:
"""Return the provided text."""
return text
def main() -> None:
"""Entry point with transport selection."""
parser = argparse.ArgumentParser(description="Awesome FastMCP Server")
parser.add_argument("--transport", choices=["stdio", "http"], default="stdio",
help="Transport mode (stdio or http)")
parser.add_argument("--host", default="0.0.0.0", help="HTTP host")
parser.add_argument("--port", type=int, default=8000, help="HTTP port")
args = parser.parse_args()
if args.transport == "http":
mcp.run(transport="http", host=args.host, port=args.port)
else:
mcp.run()
if __name__ == "__main__": # pragma: no cover
main()
- Run over stdio:
python -m awesome_server.server_fastmcp
- Run over HTTP:
python -m awesome_server.server_fastmcp --transport http --host 0.0.0.0 --port 8000
- Alternative with CLI:
fastmcp run src/awesome_server/server_fastmcp.py:mcp --transport http --host 0.0.0.0 --port 8000
pyproject.toml (template)
- Pin FastMCP for production deployments; adjust metadata and optional extras.
[project]
name = "awesome-server"
version = "0.1.0"
description = "Example FastMCP 2 server"
authors = [
{ name = "ContextForge", email = "noreply@example.com" }
]
license = { text = "MIT" }
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"fastmcp==2.11.3",
"pydantic>=2.5.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-asyncio>=0.21.0",
"pytest-cov>=4.0.0",
"black>=23.0.0",
"mypy>=1.5.0",
"ruff>=0.0.290",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/awesome_server"]
[project.scripts]
awesome-server = "awesome_server.server_fastmcp:main"
[tool.black]
line-length = 100
target-version = ["py311"]
[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true
warn_unused_configs = true
[tool.ruff]
line-length = 100
target-version = "py311"
select = ["E", "W", "F", "B", "I", "N", "UP"]
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
addopts = "--cov=awesome_server --cov-report=term-missing"
Notes:
- Use exact FastMCP versions (
fastmcp==…) in production to avoid breaking changes.
- See richer examples in
data_analysis_server/pyproject.toml and mcp_eval_server/pyproject.toml for additional extras and entry points.
Makefile (template)
- Provides dev install, format/lint/test targets, multiple transport modes (stdio, native HTTP, SSE bridge).
# Makefile for Awesome FastMCP Server
.PHONY: help install dev-install format lint test dev serve-http serve-sse test-http mcp-info clean
PYTHON ?= python3
HTTP_PORT ?= 8000
HTTP_HOST ?= 0.0.0.0
help: ## Show help
@echo "Quick Start:"
@echo " make install Install FastMCP server"
@echo " make dev Run FastMCP server (stdio)"
@echo " make serve-http Run with native FastMCP HTTP"
@echo " make serve-sse Run with translate SSE bridge"
@echo ""
@awk 'BEGIN {FS=":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-18s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
install: ## Install in editable mode
$(PYTHON) -m pip install -e .
dev-install: ## Install with dev extras
$(PYTHON) -m pip install -e ".[dev]"
format: ## Format (black + ruff --fix)
black . && ruff check --fix .
lint: ## Lint (ruff, mypy)
ruff check . && mypy src/awesome_server
test: ## Run tests
pytest -v --cov=awesome_server --cov-report=term-missing
dev: ## Run FastMCP server (stdio)
$(PYTHON) -m awesome_server.server_fastmcp
serve-http: ## Run with native FastMCP HTTP
@echo "HTTP endpoint: http://$(HTTP_HOST):$(HTTP_PORT)/mcp/"
$(PYTHON) -m awesome_server.server_fastmcp --transport http --host $(HTTP_HOST) --port $(HTTP_PORT)
serve-sse: ## Run with mcpgateway.translate (SSE bridge)
@echo "SSE endpoint: http://$(HTTP_HOST):$(HTTP_PORT)/sse"
$(PYTHON) -m mcpgateway.translate --stdio "$(PYTHON) -m awesome_server.server_fastmcp" \
--host $(HTTP_HOST) --port $(HTTP_PORT) --expose-sse
test-http: ## Test native HTTP endpoint
curl -s -X POST -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \
http://$(HTTP_HOST):$(HTTP_PORT)/mcp/ | python3 -m json.tool | head -40 || true
mcp-info: ## Show MCP client configs
@echo "1. FastMCP Server (stdio - for Claude Desktop):"
@echo '{"command": "python", "args": ["-m", "awesome_server.server_fastmcp"]}'
@echo ""
@echo "2. Native HTTP: make serve-http"
@echo "3. SSE bridge: make serve-sse"
clean: ## Remove caches
rm -rf .pytest_cache .ruff_cache .mypy_cache __pycache__ */__pycache__ *.egg-info
Notes:
- Use
uv pip install -e . if your team standardizes on uv.
- For richer Makefiles (container build, smoke tests, docs), see
mcp_eval_server/Makefile.
Containerfile (template)
- Minimal container using
python:3.11-slim; installs your project in a virtualenv with a non-root user.
# syntax=docker/dockerfile:1
FROM python:3.11-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PATH="/app/.venv/bin:$PATH"
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl && \
rm -rf /var/lib/apt/lists/*
COPY pyproject.toml README.md ./
COPY src/ ./src/
RUN python -m venv /app/.venv && \
/app/.venv/bin/pip install --upgrade pip setuptools wheel && \
/app/.venv/bin/pip install -e .
RUN useradd -u 1001 -m appuser && chown -R 1001:1001 /app
USER 1001
CMD ["python", "-m", "awesome_server.server_fastmcp"]
Notes:
- Swap the container entrypoint to
fastmcp run /app/src/awesome_server/server_fastmcp.py:mcp --transport http --host 0.0.0.0 --port 8000 (or similar) when you need remote HTTP access.
- For hardened multi-stage builds (scratch base, non-root, healthchecks), study
data_analysis_server/Containerfile and mcp_eval_server/Containerfile.
Run Locally
- Stdio mode (for local LLM clients or direct JSON-RPC piping):
make dev
fastmcp run src/awesome_server/server_fastmcp.py:mcp
- HTTP mode:
make serve-http
- Call with curl:
curl -s -X POST http://localhost:8000/mcp/ -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
Tips & Patterns
- Keep FastMCP objects (
FastMCP, @mcp.tool, @mcp.prompt, @mcp.resource) in server_fastmcp.py; move heavy business logic into tools.py or subpackages.
- Log to stderr when running under stdio transports to avoid corrupting the protocol stream.
- Prefer Pydantic models for complex tool arguments/returns; FastMCP exposes them as structured schemas automatically.
- Add argparse for flexible transport selection (stdio/HTTP) in the same codebase.
- Combine FastMCP with the gateway by registering the HTTP endpoint (
/mcp) or by wrapping stdio servers with mcpgateway.translate if you need SSE bridging.
Best Practices (from production experience)
- Single Implementation: Use only FastMCP 2.x - avoid maintaining both MCP 1.0 and FastMCP versions
- Version Pinning: Always pin FastMCP to exact version (
fastmcp==2.11.3) to avoid breaking changes
- Error Handling: Gracefully handle missing dependencies (e.g., Graphviz) with clear error messages
- Transport Flexibility: Support multiple transports in the same server:
- stdio for Claude Desktop and local clients
- Native HTTP for REST API access
- SSE bridge via translate for streaming clients
- Testing: Write tests that work directly with processor classes, not just via MCP protocol
- Project Structure: Keep it simple - one
server_fastmcp.py file is often sufficient for small/medium servers
FastMCP 2 Resources
- Core docs: Welcome to FastMCP 2.0, Installation, Quickstart, Changelog.
- Client guides: Client overview, Authentication (Bearer), Authentication (OAuth), User elicitation, Logging, Messages, Progress, Prompts, Resources, Tools, Transports, LLM sampling.
- Server guides: Server fundamentals, Context, Tools, Resources & templates, Prompts, Logging, Progress, Middleware, Authentication, Proxy, LLM sampling.
- Operations: Running your server, Self-hosted remote MCP, FastMCP Cloud, Project configuration.
- Integrations: FastAPI, Anthropic API, OpenAI API, Claude Desktop, Cursor.
1---2name: 3010-mcp-server-python-5f6542153description: FastMCP 2 Python Servers: Create, Build, and Run4---5FastMCP 2 Python Servers: Create, Build, and Run67- Scope: Practical guide for authoring, packaging, containerizing, and exposing Python MCP servers with FastMCP 2.x.8- References: See full implementations under `mcp-servers/python/*/server_fastmcp.py` (20 example servers available), e.g. `mcp-servers/python/chunker_server/src/chunker_server/server_fastmcp.py` and `mcp-servers/python/url_to_markdown_server/src/url_to_markdown_server/server_fastmcp.py`.910**Project Layout**11- Recommended structure for a new server `awesome_server`:1213```14awesome_server/15 pyproject.toml16 Makefile17 Containerfile18 README.md19 src/20 awesome_server/21 __init__.py22 server_fastmcp.py # FastMCP entry point23 tools.py # optional: keep tool logic separate24 tests/25 test_server.py26```2728**Minimal Server (stdio + http)**29- Implements a basic FastMCP server with one tool (`echo`). Type hints define schemas.3031```python32# src/awesome_server/server_fastmcp.py33from fastmcp import FastMCP3435mcp = FastMCP("awesome-server", version="0.1.0")363738@mcp.tool39def echo(text: str) -> str:40 """Return the provided text."""41 return text424344def main() -> None:45 """Entry point for `python -m awesome_server.server_fastmcp`."""46 mcp.run() # stdio by default474849if __name__ == "__main__": # pragma: no cover50 main()51```5253**Enhanced Server with Native HTTP Support**54- For better flexibility, add argument parsing to support both stdio and HTTP modes natively:5556```python57# src/awesome_server/server_fastmcp.py58from fastmcp import FastMCP59import argparse6061mcp = FastMCP("awesome-server", version="0.1.0")626364@mcp.tool65def echo(text: str) -> str:66 """Return the provided text."""67 return text686970def main() -> None:71 """Entry point with transport selection."""72 parser = argparse.ArgumentParser(description="Awesome FastMCP Server")73 parser.add_argument("--transport", choices=["stdio", "http"], default="stdio",74 help="Transport mode (stdio or http)")75 parser.add_argument("--host", default="0.0.0.0", help="HTTP host")76 parser.add_argument("--port", type=int, default=8000, help="HTTP port")7778 args = parser.parse_args()7980 if args.transport == "http":81 mcp.run(transport="http", host=args.host, port=args.port)82 else:83 mcp.run()848586if __name__ == "__main__": # pragma: no cover87 main()88```8990- Run over stdio: `python -m awesome_server.server_fastmcp`91- Run over HTTP: `python -m awesome_server.server_fastmcp --transport http --host 0.0.0.0 --port 8000`92- Alternative with CLI: `fastmcp run src/awesome_server/server_fastmcp.py:mcp --transport http --host 0.0.0.0 --port 8000`9394**pyproject.toml (template)**95- Pin FastMCP for production deployments; adjust metadata and optional extras.9697```toml98[project]99name = "awesome-server"100version = "0.1.0"101description = "Example FastMCP 2 server"102authors = [103 { name = "ContextForge", email = "noreply@example.com" }104]105license = { text = "MIT" }106readme = "README.md"107requires-python = ">=3.11"108dependencies = [109 "fastmcp==2.11.3",110 "pydantic>=2.5.0",111]112113[project.optional-dependencies]114dev = [115 "pytest>=7.0.0",116 "pytest-asyncio>=0.21.0",117 "pytest-cov>=4.0.0",118 "black>=23.0.0",119 "mypy>=1.5.0",120 "ruff>=0.0.290",121]122123[build-system]124requires = ["hatchling"]125build-backend = "hatchling.build"126127[tool.hatch.build.targets.wheel]128packages = ["src/awesome_server"]129130[project.scripts]131awesome-server = "awesome_server.server_fastmcp:main"132133[tool.black]134line-length = 100135target-version = ["py311"]136137[tool.mypy]138python_version = "3.11"139strict = true140warn_return_any = true141warn_unused_configs = true142143[tool.ruff]144line-length = 100145target-version = "py311"146select = ["E", "W", "F", "B", "I", "N", "UP"]147148[tool.pytest.ini_options]149testpaths = ["tests"]150asyncio_mode = "auto"151addopts = "--cov=awesome_server --cov-report=term-missing"152```153154Notes:155- Use exact FastMCP versions (`fastmcp==…`) in production to avoid breaking changes.156- See richer examples in `data_analysis_server/pyproject.toml` and `mcp_eval_server/pyproject.toml` for additional extras and entry points.157158**Makefile (template)**159- Provides dev install, format/lint/test targets, multiple transport modes (stdio, native HTTP, SSE bridge).160161```makefile162# Makefile for Awesome FastMCP Server163164.PHONY: help install dev-install format lint test dev serve-http serve-sse test-http mcp-info clean165166PYTHON ?= python3167HTTP_PORT ?= 8000168HTTP_HOST ?= 0.0.0.0169170help: ## Show help171 @echo "Quick Start:"172 @echo " make install Install FastMCP server"173 @echo " make dev Run FastMCP server (stdio)"174 @echo " make serve-http Run with native FastMCP HTTP"175 @echo " make serve-sse Run with translate SSE bridge"176 @echo ""177 @awk 'BEGIN {FS=":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-18s %s\n", $$1, $$2}' $(MAKEFILE_LIST)178179install: ## Install in editable mode180 $(PYTHON) -m pip install -e .181182dev-install: ## Install with dev extras183 $(PYTHON) -m pip install -e ".[dev]"184185format: ## Format (black + ruff --fix)186 black . && ruff check --fix .187188lint: ## Lint (ruff, mypy)189 ruff check . && mypy src/awesome_server190191test: ## Run tests192 pytest -v --cov=awesome_server --cov-report=term-missing193194dev: ## Run FastMCP server (stdio)195 $(PYTHON) -m awesome_server.server_fastmcp196197serve-http: ## Run with native FastMCP HTTP198 @echo "HTTP endpoint: http://$(HTTP_HOST):$(HTTP_PORT)/mcp/"199 $(PYTHON) -m awesome_server.server_fastmcp --transport http --host $(HTTP_HOST) --port $(HTTP_PORT)200201serve-sse: ## Run with mcpgateway.translate (SSE bridge)202 @echo "SSE endpoint: http://$(HTTP_HOST):$(HTTP_PORT)/sse"203 $(PYTHON) -m mcpgateway.translate --stdio "$(PYTHON) -m awesome_server.server_fastmcp" \204 --host $(HTTP_HOST) --port $(HTTP_PORT) --expose-sse205206test-http: ## Test native HTTP endpoint207 curl -s -X POST -H 'Content-Type: application/json' \208 -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \209 http://$(HTTP_HOST):$(HTTP_PORT)/mcp/ | python3 -m json.tool | head -40 || true210211mcp-info: ## Show MCP client configs212 @echo "1. FastMCP Server (stdio - for Claude Desktop):"213 @echo '{"command": "python", "args": ["-m", "awesome_server.server_fastmcp"]}'214 @echo ""215 @echo "2. Native HTTP: make serve-http"216 @echo "3. SSE bridge: make serve-sse"217218clean: ## Remove caches219 rm -rf .pytest_cache .ruff_cache .mypy_cache __pycache__ */__pycache__ *.egg-info220```221222Notes:223- Use `uv pip install -e .` if your team standardizes on uv.224- For richer Makefiles (container build, smoke tests, docs), see `mcp_eval_server/Makefile`.225226**Containerfile (template)**227- Minimal container using `python:3.11-slim`; installs your project in a virtualenv with a non-root user.228229```Dockerfile230# syntax=docker/dockerfile:1231FROM python:3.11-slim AS base232ENV PYTHONDONTWRITEBYTECODE=1 \233 PYTHONUNBUFFERED=1 \234 PIP_NO_CACHE_DIR=1 \235 PATH="/app/.venv/bin:$PATH"236237WORKDIR /app238239RUN apt-get update && apt-get install -y --no-install-recommends \240 ca-certificates curl && \241 rm -rf /var/lib/apt/lists/*242243COPY pyproject.toml README.md ./244COPY src/ ./src/245246RUN python -m venv /app/.venv && \247 /app/.venv/bin/pip install --upgrade pip setuptools wheel && \248 /app/.venv/bin/pip install -e .249250RUN useradd -u 1001 -m appuser && chown -R 1001:1001 /app251USER 1001252253CMD ["python", "-m", "awesome_server.server_fastmcp"]254```255256Notes:257- Swap the container entrypoint to `fastmcp run /app/src/awesome_server/server_fastmcp.py:mcp --transport http --host 0.0.0.0 --port 8000` (or similar) when you need remote HTTP access.258- For hardened multi-stage builds (scratch base, non-root, healthchecks), study `data_analysis_server/Containerfile` and `mcp_eval_server/Containerfile`.259260**Run Locally**261- Stdio mode (for local LLM clients or direct JSON-RPC piping):262 - `make dev`263 - `fastmcp run src/awesome_server/server_fastmcp.py:mcp`264- HTTP mode:265 - `make serve-http`266 - Call with curl: `curl -s -X POST http://localhost:8000/mcp/ -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'`267268**Tips & Patterns**269- Keep FastMCP objects (`FastMCP`, `@mcp.tool`, `@mcp.prompt`, `@mcp.resource`) in `server_fastmcp.py`; move heavy business logic into `tools.py` or subpackages.270- Log to stderr when running under stdio transports to avoid corrupting the protocol stream.271- Prefer Pydantic models for complex tool arguments/returns; FastMCP exposes them as structured schemas automatically.272- Add argparse for flexible transport selection (stdio/HTTP) in the same codebase.273- Combine FastMCP with the gateway by registering the HTTP endpoint (`/mcp`) or by wrapping stdio servers with `mcpgateway.translate` if you need SSE bridging.274275**Best Practices (from production experience)**2761. **Single Implementation**: Use only FastMCP 2.x - avoid maintaining both MCP 1.0 and FastMCP versions2772. **Version Pinning**: Always pin FastMCP to exact version (`fastmcp==2.11.3`) to avoid breaking changes2783. **Error Handling**: Gracefully handle missing dependencies (e.g., Graphviz) with clear error messages2794. **Transport Flexibility**: Support multiple transports in the same server:280 - stdio for Claude Desktop and local clients281 - Native HTTP for REST API access282 - SSE bridge via translate for streaming clients2835. **Testing**: Write tests that work directly with processor classes, not just via MCP protocol2846. **Project Structure**: Keep it simple - one `server_fastmcp.py` file is often sufficient for small/medium servers285286**FastMCP 2 Resources**287- Core docs: [Welcome to FastMCP 2.0](https://gofastmcp.com/getting-started/welcome.md), [Installation](https://gofastmcp.com/getting-started/installation.md), [Quickstart](https://gofastmcp.com/getting-started/quickstart.md), [Changelog](https://gofastmcp.com/changelog.md).288- Client guides: [Client overview](https://gofastmcp.com/clients/client.md), [Authentication (Bearer)](https://gofastmcp.com/clients/auth/bearer.md), [Authentication (OAuth)](https://gofastmcp.com/clients/auth/oauth.md), [User elicitation](https://gofastmcp.com/clients/elicitation.md), [Logging](https://gofastmcp.com/clients/logging.md), [Messages](https://gofastmcp.com/clients/messages.md), [Progress](https://gofastmcp.com/clients/progress.md), [Prompts](https://gofastmcp.com/clients/prompts.md), [Resources](https://gofastmcp.com/clients/resources.md), [Tools](https://gofastmcp.com/clients/tools.md), [Transports](https://gofastmcp.com/clients/transports.md), [LLM sampling](https://gofastmcp.com/clients/sampling.md).289- Server guides: [Server fundamentals](https://gofastmcp.com/servers/server.md), [Context](https://gofastmcp.com/servers/context.md), [Tools](https://gofastmcp.com/servers/tools.md), [Resources & templates](https://gofastmcp.com/servers/resources.md), [Prompts](https://gofastmcp.com/servers/prompts.md), [Logging](https://gofastmcp.com/servers/logging.md), [Progress](https://gofastmcp.com/servers/progress.md), [Middleware](https://gofastmcp.com/servers/middleware.md), [Authentication](https://gofastmcp.com/servers/auth/authentication.md), [Proxy](https://gofastmcp.com/servers/proxy.md), [LLM sampling](https://gofastmcp.com/servers/sampling.md).290- Operations: [Running your server](https://gofastmcp.com/deployment/running-server.md), [Self-hosted remote MCP](https://gofastmcp.com/deployment/self-hosted.md), [FastMCP Cloud](https://gofastmcp.com/deployment/fastmcp-cloud.md), [Project configuration](https://gofastmcp.com/deployment/server-configuration.md).291- Integrations: [FastAPI](https://gofastmcp.com/integrations/fastapi.md), [Anthropic API](https://gofastmcp.com/integrations/anthropic.md), [OpenAI API](https://gofastmcp.com/integrations/openai.md), [Claude Desktop](https://gofastmcp.com/integrations/claude-desktop.md), [Cursor](https://gofastmcp.com/integrations/cursor.md).