How to Build an MCP Server in Python (FastMCP Quickstart)
Updated 2026-10-15
An MCP server exposes tools (and optionally resources and prompts) to any MCP client: Claude Desktop, Cursor, VS Code, or your own agent. With the official Python SDK’s FastMCP class, a working server is about ten lines.
Prerequisites
- Python 3.10+
- uv (recommended) or pip
- An MCP client to test with (Claude Desktop, Cursor, or the MCP Inspector)
Steps
1. Create the project
uv init weather-mcp && cd weather-mcp
uv add "mcp[cli]" httpx
With pip: pip install "mcp[cli]" httpx.
2. Write the server
# server.py
import httpx
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("weather")
@mcp.tool()
async def current_weather(latitude: float, longitude: float) -> str:
"""Get the current temperature and wind speed for a location."""
url = "https://api.open-meteo.com/v1/forecast"
params = {"latitude": latitude, "longitude": longitude, "current": "temperature_2m,wind_speed_10m"}
async with httpx.AsyncClient(timeout=10) as c:
data = (await c.get(url, params=params)).json()["current"]
return f"{data['temperature_2m']} °C, wind {data['wind_speed_10m']} km/h"
@mcp.resource("info://about")
def about() -> str:
"""Static info the client can read."""
return "Weather MCP server using the free Open-Meteo API."
if __name__ == "__main__":
mcp.run() # stdio transport by default
The function signature becomes the tool’s JSON Schema and the docstring becomes its description, so write good docstrings.
3. Test with MCP Inspector
uv run mcp dev server.py
This opens the Inspector in your browser. Click Connect → Tools → current_weather, enter coordinates, and run it.
4. Add it to Claude Desktop
Edit claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\):
{
"mcpServers": {
"weather": {
"command": "uv",
"args": ["--directory", "/ABSOLUTE/PATH/weather-mcp", "run", "server.py"]
}
}
}
Fully quit and restart Claude Desktop. The tool appears in the tools menu.
5. Add it to Cursor
Create .cursor/mcp.json in your project (or ~/.cursor/mcp.json globally) with the same mcpServers block.
6. Serve it remotely (optional)
Run over HTTP instead of stdio so remote clients can connect:
if __name__ == "__main__":
mcp.run(transport="streamable-http") # serves on http://127.0.0.1:8000/mcp
Put authentication in front of any remote server that touches private data.
Common errors
Never print to stdout in a stdio server. print() corrupts the JSON-RPC stream. Log to stderr: print("debug", file=sys.stderr).
spawn uv ENOENT in Claude Desktop: the app can’t find uv on its PATH. Use the absolute path from which uv as command.
Tool doesn’t appear: JSON syntax error in the config, or Claude wasn’t fully quit. See MCP server not connecting to Claude Desktop.