Build an MCP Server in Python with FastMCP
The official Python SDK includes FastMCP, a high-level API where type hints and docstrings become the tool schema. A working server takes only a few lines.
1. Create the project
uv init notes-mcp && cd notes-mcp
uv add "mcp[cli]"
2. Write the server
# server.py
import sqlite3
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("notes")
db = sqlite3.connect("notes.db", check_same_thread=False)
db.execute("CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, title TEXT, body TEXT)")
@mcp.tool()
def add_note(title: str, body: str) -> str:
"""Save a note. Returns the new note's id."""
cur = db.execute("INSERT INTO notes (title, body) VALUES (?, ?)", (title, body))
db.commit()
return f"Saved note {cur.lastrowid}"
@mcp.tool()
def search_notes(query: str, limit: int = 5) -> list[dict]:
"""Search notes whose title or body contains the query text."""
rows = db.execute(
"SELECT id, title, body FROM notes WHERE title LIKE ? OR body LIKE ? LIMIT ?",
(f"%{query}%", f"%{query}%", limit),
).fetchall()
return [{"id": r[0], "title": r[1], "body": r[2]} for r in rows]
@mcp.resource("notes://count")
def note_count() -> str:
"""How many notes are stored."""
return str(db.execute("SELECT COUNT(*) FROM notes").fetchone()[0])
if __name__ == "__main__":
mcp.run() # stdio by default
3. Test it
uv run mcp dev server.py # starts the server with the MCP Inspector
How FastMCP builds the schema
- Parameter names and type hints become JSON Schema properties.
- Parameters with defaults become optional.
- The docstring becomes the tool description, so write it for the model.
- Return values are serialised for you; returning structured data is fine.
Going remote
To serve over HTTP instead of stdio, run mcp.run(transport="streamable-http"). Add authentication before exposing it beyond localhost.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.