Python implementation of the Model Context Protocol (MCP)
Important: this documents v2 of the SDK, which is in alpha. Pre-releases are published to PyPI as
2.0.0aN, and each alpha may contain breaking changes from the previous one.v2 is a major rework of the SDK, both to support the 2026-07-28 MCP specification release and to fix long-standing architectural issues. See the migration guide for what's changed. We're targeting a beta on 2026-06-30 and a stable v2 on 2026-07-27, alongside the spec release. Before stable, we plan to add a significant set of backwards compatibility shims so the final upgrade is much smaller than today's diff.
v1.x is the only stable release line and remains recommended for production. It is in maintenance mode and continues to receive critical bug fixes and security patches. Installers never select a pre-release unless you opt in (for example
pip install mcp==2.0.0aN), so existing installs are unaffected. If your package depends onmcp, add a<2upper bound to your version constraint (for examplemcp>=1.27,<2) before the stable release lands.Try the alpha and tell us what breaks: #python-sdk-dev on the MCP Contributors Discord. For v1 documentation, see the v1.x README.
The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This Python SDK implements the full MCP specification, making it easy to:
We recommend using uv to manage your Python projects.
If you haven't created a uv-managed project yet, create one:
bash
uv init mcp-server-demo
cd mcp-server-demo
Then add MCP to your project dependencies:
bash
uv add "mcp[cli]==2.0.0a1"
Alternatively, for projects using pip for dependencies:
pip install "mcp[cli]==2.0.0a1"
While v2 is in pre-release, you must pin the version explicitly: unpinned installs resolve to the latest stable v1.x release, which these docs do not describe. Check the release history for the newest pre-release. The same applies to ad-hoc commands: use
uv run --with "mcp==2.0.0a1"rather thanuv run --with mcp.
To run the mcp command with uv:
uv run mcp
Let's create a simple MCP server that exposes a calculator tool and some data:
"""MCPServer quickstart example.
Run from the repository root:
uv run examples/snippets/servers/mcpserver_quickstart.py
"""
from mcp.server.mcpserver import MCPServer
# Create an MCP server
mcp = MCPServer("Demo")
# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
# Add a dynamic greeting resource
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"
# Add a prompt
@mcp.prompt()
def greet_user(name: str, style: str = "friendly") -> str:
"""Generate a greeting prompt"""
styles = {
"friendly": "Please write a warm, friendly greeting",
"formal": "Please write a formal, professional greeting",
"casual": "Please write a casual, relaxed greeting",
}
return f"{styles.get(style, styles['friendly'])} for someone named {name}."
# Run with streamable HTTP transport
if __name__ == "__main__":
mcp.run(transport="streamable-http", json_response=True)
Full example: examples/snippets/servers/mcpserver_quickstart.py
You can install this server in Claude Code and interact with it right away. First, run the server:
uv run --with "mcp==2.0.0a1" examples/snippets/servers/mcpserver_quickstart.py
Then add it to Claude Code:
claude mcp add --transport http my-server http://localhost:8000/mcp
Alternatively, you can test it with the MCP Inspector. Start the server as above, then in a separate terminal:
npx -y @modelcontextprotocol/inspector
In the inspector UI, connect to http://localhost:8000/mcp.
The Model Context Protocol (MCP) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions.
MCP follows a client-server model, where LLM applications act as clients and connect to MCP servers to access capabilities such as data retrieval and tool execution in a consistent format.
MCP servers can:
The MCPServer server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:
"""Example showing lifespan support for startup/shutdown with strong typing."""
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from dataclasses import dataclass
from mcp.server.mcpserver import Context, MCPServer
# Mock database class for example
class Database:
"""Mock database class for example."""
@classmethod
async def connect(cls) -> "Database":
"""Connect to database."""
return cls()
async def disconnect(self) -> None:
"""Disconnect from database."""
pass
def query(self) -> str:
"""Execute a query."""
return "Query result"
@dataclass
class AppContext:
"""Application context with typed dependencies."""
db: Database
@asynccontextmanager
async def app_lifespan(server: MCPServer) -> AsyncIterator[AppContext]:
"""Manage application lifecycle with type-safe context."""
# Initialize on startup
db = await Database.connect()
try:
yield AppContext(db=db)
finally:
# Cleanup on shutdown
await db.disconnect()
# Pass lifespan to server
mcp = MCPServer("My App", lifespan=app_lifespan)
# Access type-safe lifespan context in tools
@mcp.tool()
def query_db(ctx: Context[AppContext]) -> str:
"""Tool that uses initialized resources."""
db = ctx.request_context.lifespan_context.db
return db.query()
Full example: examples/snippets/servers/lifespan_example.py
Resources are how you expose data to LLMs. They're similar to GET endpoints in a REST API - they provide data but shouldn't perform significant computation or have side effects:
from mcp.server.mcpserver import MCPServer
mcp = MCPServer(name="Resource Example")
@mcp.resource("file://documents/{name}")
def read_document(name: str) -> str:
"""Read a document by name."""
# This would normally read from disk
return f"Content of {name}"
@mcp.resource("config://settings")
def get_settings() -> str:
"""Get application settings."""
return """{
"theme": "dark",
"language": "en",
"debug": false
}"""
Full example: examples/snippets/servers/basic_resource.py
Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:
from mcp.server.mcpserver import MCPServer
mcp = MCPServer(name="Tool Example")
@mcp.tool()
def sum(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
@mcp.tool()
def get_weather(city: str, unit: str = "celsius") -> str:
"""Get weather for a city."""
# This would normally call a weather API
return f"Weather in {city}: 22degrees{unit[0].upper()}"
Full example: examples/snippets/servers/basic_tool.py
Tools can optionally receive a Context object by including a parameter with the Context type annotation. This context is automatically injected by the MCPServer framework and provides access to MCP capabilities:
```python from mcp.server.mcpserver import Context, MCPServer
mcp = MCPServer(name="Progress Example")
@mcp.tool() async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str: """Execute a task with progress updates.""" await ctx.info(f"Starting: {task_name}") # pyright: ignore[reportDeprecated]
for i in range(steps):
progress = (i + 1) / steps
await ctx.report_progress(
progress=progress,
total=1.0,
message=f"Step {i + 1}/{steps}",
)
await ctx.debug(f"Completed step {i + 1}")
$ claude mcp add python-sdk \
-- python -m otcore.mcp_server <graph>