How do you build an MCP server in Python?
Short answer: install the official SDK with uv add "mcp[cli]" (or pip install "mcp[cli]"), create an MCPServer, turn your Python functions into tools with @mcp.tool() and call mcp.run(). Then connect it to Claude Code with claude mcp add. A complete server fits in about 20 lines:
from mcp.server import MCPServer
mcp = MCPServer("Notes")
notes: dict[str, str] = {}
@mcp.tool()
def add_note(title: str, text: str) -> str:
"""Save a note with a title."""
notes[title] = text
return f"Saved note '{title}'"
@mcp.tool()
def search_notes(query: str) -> list[str]:
"""Return the titles of the notes that contain the query."""
q = query.lower()
return [t for t, body in notes.items() if q in t.lower() or q in body.lower()]
if __name__ == "__main__":
mcp.run()The type hints (title: str) become the tool's input schema and the docstring becomes its description: the AI reads both to decide when to call it. This code uses version 2 of the SDK (mcp 2.3.0, tested with Python 3.13).
Create the project, add the SDK, connect the server to Claude Code and check that it's connected.
What an MCP server is
MCP (Model Context Protocol) is an open standard that lets AI applications such as Claude Code, Claude Desktop or Cursor use your code. An MCP server can offer three things:
| What it offers | What it's for | In Python |
|---|---|---|
| Tools | Actions the AI can run: search, save, call an API | @mcp.tool() |
| Resources | Data the AI can read, by URI | @mcp.resource("notes://{title}") |
| Prompts | Reusable prompt templates | @mcp.prompt() |
The AI app is the client: it starts your server, asks which tools it has and calls them when they help with the task. If you want the theory first, read what an MCP server is.
Step 1: create the project
You need Python 3.10 or later. The SDK recommends uv to manage the project:
uv init mcp-notes
cd mcp-notes
uv add "mcp[cli]"With pip, inside a virtual environment:
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "mcp[cli]"The cli extra adds the mcp command (mcp dev, mcp run, mcp install). Check it with uv run mcp version:
MCP version 2.3.0If you don't have Python yet, see how to install Python.
Step 2: write the server
Create server.py with the code from the top of this page. You can add a resource so the AI can read one note by its URI:
@mcp.resource("notes://{title}")
def read_note(title: str) -> str:
"""Read one note by its title."""
return notes.get(title, "Note not found")Three rules that make tools work well:
- Type every parameter. Without types the AI doesn't know what to send.
- Write a clear docstring. It's what the AI reads to choose the tool.
- Return simple data: text, numbers, lists or dicts. The SDK turns them into the MCP result.
mcp.run() uses the stdio transport by default: the client starts server.py as a process and talks to it through stdin and stdout. Don't use print() in a stdio server, because it writes into that channel; use logging, which writes to stderr.
