Originally published at claudeguide.io/mcp-server-fastapi-python
Build an MCP Server in Python with FastAPI: Tools, Resources, Auth (2026)
MCP servers expose tools (callable functions) and resources (readable context) to Claude — the cleanest 2026 stack is the official mcp Python SDK on top of FastAPI in ~50 lines of code. You get streaming HTTP transport, Pydantic-typed tool schemas, bearer-token auth, and a single ASGI app you can ship to Fly.io, Render, or a Mac mini. This guide walks the full path — from pip install mcp to a production server wired into Claude Code via claude mcp add — with working code for tools, streaming resources, and OAuth-style authentication. Tested against MCP spec 2025-06-18 and mcp-python-sdk v1.x.
What is MCP, and when do you need a custom server?
The Model Context Protocol is a JSON-RPC contract Claude clients (Claude Code, claude.ai desktop, the API harness) speak to external programs. The program advertises tools and resources; the client routes user intent to those tools. You need a custom MCP server when the integration you want isn't already in the public registry — internal databases, proprietary APIs, custom workflows over your own filesystem. If a vendor MCP exists (GitHub, Linear, Stripe, Postgres), use that first; build only what's missing.
Stack choice: mcp SDK vs raw FastAPI vs HTTPX-only
Three plausible stacks, picked in order of escape hatch:
-
mcpSDK + FastAPI (recommended). The SDK'sFastMCPwraps schema generation, transport negotiation (stdio + streaming HTTP), and JSON-RPC framing. Mounting it inside FastAPI gives you middleware, OpenAPI co-existence, and ASGI deploys. - Raw FastAPI, hand-rolling JSON-RPC. Only do this if you're forking the protocol — the spec is moving, and you'll spend a week catching up to each release.
- HTTPX-only client wrappers are not servers; they belong inside a tool, not as a replacement.
Pick FastMCP. The escape hatch is always "add a plain FastAPI route alongside" — both can mount on the same Uvicorn worker.
Minimal working server
Install the SDK and FastAPI:
pip install "mcp[cli]
## Streaming resource: a GitHub repo file listing
Resources are read-only context Claude can pull in without invoking a tool. Streaming matters when the payload is large — Claude can start reasoning before the body finishes:
python
resources/repo.py
import httpx
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("acme-tools")
@mcp.resource("repo://{owner}/{name}/tree")
async def repo_tree(owner: str, name: str) -
Frequently Asked Questions
Stdio or HTTP transport?
Stdio is right for a server that runs as a subprocess of one client (Claude Desktop launching a local Python script). HTTP — specifically streamable-http — is right for anything multi-user, anything deployed, and anything you want to test with mcp-inspector over the network. If in doubt, ship HTTP; you can always wrap stdio later.
How do I authenticate users?
Bearer tokens for service accounts and CI. OAuth (RFC 7591 dynamic client registration, as profiled in the MCP spec) for end users — authlib plus the SDK's auth helpers handles registration, refresh, and scope. Don't invent a custom header scheme; clients won't speak it.
Can MCP servers call external APIs?
Yes — that's the most common shape. A tool is just a Python function; call any REST/GraphQL/database client you like inside it. Keep network calls async, time them out aggressively (10–30s), and surface upstream errors as ToolError with the upstream status code in the message.
What's the rate limit?
There is no protocol-level rate limit — you set it. Claude clients respect server-side 429s, so return them honestly: a Retry-After header on a starlette.responses.Response with status 429 is enough. For per-tool quotas, gate with asyncio.Semaphore plus a token bucket (e.g. aiolimiter).
How do I version my server?
Two layers. The version argument to FastMCP("name", version="1.4.0") is advertised in the initialize handshake — bump on tool-shape changes. Separately, deploy URL paths (/mcp/v1, /mcp/v2) when you make breaking schema changes; let old clients stay on v1 while new clients adopt v2. Never silently change a tool's argument schema in place.
Where to go next
- Claude MCP best practices — patterns for tool design, prompt budgeting, and observability
- Claude Code MCP (Korean track) — same material, Korean-language deep dive
- Claude API error handling — turn upstream failures into tool errors Claude can recover from
A working MCP server is a 50-line file. A production MCP server is the same 50 lines plus auth, error correlation, async hygiene, and a versioned URL — none of which are hard, but all of which need to be there on day one. Start with FastMCP, ship the bearer path first, layer OAuth when real users arrive, and treat tool descriptions as the single highest-leverage prompt-engineering surface in your stack.
Top comments (0)