### mcp install command examples Source: https://py.sdk.modelcontextprotocol.io/v2/run Registers the server with Claude Desktop. -v KEY=VALUE and -f .env record environment variables in the entry. Claude Desktop launches the server in its own process, so your shell's environment is not present. ```bash uv run mcp install server.py --name "Bookshop" uv run mcp install server.py -v API_KEY=abc123 -f .env ``` -------------------------------- ### mcp dev command examples Source: https://py.sdk.modelcontextprotocol.io/v2/run Runs the server under the MCP Inspector. --with adds packages to the environment, --with-editable installs your own package. Requires npx on PATH because the Inspector is a Node.js app. ```bash uv run mcp dev server.py uv run mcp dev server.py --with pandas --with numpy uv run mcp dev server.py --with-editable . ``` -------------------------------- ### Install MCP SDK with uv Source: https://py.sdk.modelcontextprotocol.io/v2 Install the MCP Python SDK with the CLI extra using uv. The [cli] extra provides the mcp command, which is useful for development. ```bash uv add "mcp[cli]" ``` -------------------------------- ### Install OpenTelemetry SDK and OTLP exporter Source: https://py.sdk.modelcontextprotocol.io/v2/run/opentelemetry Command to install the OpenTelemetry SDK and OTLP exporter, enabling span collection. After installation, configure an exporter to see the spans. ```bash uv add opentelemetry-sdk opentelemetry-exporter-otlp ``` -------------------------------- ### Example output for reserve_book call Source: https://py.sdk.modelcontextprotocol.io/v2/handlers/dependencies Example output when calling the reserve_book tool with the title 'Dune'. ```text Reserved 'Dune' (6 copies left). ``` -------------------------------- ### Example output for search_books Source: https://py.sdk.modelcontextprotocol.io/v2/handlers/context Example output when calling search_books with 'dune'. The request id changes on each call because every request gets its own Context. ```text [request 3] Found 3 books matching 'dune'. ``` -------------------------------- ### Run Bookshop server with streamable-http Source: https://py.sdk.modelcontextprotocol.io/v2/protocol-versions Starts the Bookshop server.py from The Client tutorial over streamable HTTP. Run this in one terminal before executing any client.py snippet on this page in a second terminal. ```bash uv run mcp run server.py --transport streamable-http ``` -------------------------------- ### Client with elicitation_callback Source: https://py.sdk.modelcontextprotocol.io/v2/troubleshooting Client setup that passes the elicitation_callback to the Client constructor. Registering the callback is the capability declaration; no additional switch is needed. This example calls the book_table tool with a date argument. ```python async def main() -> None: async with Client("http://localhost:8000/mcp", elicitation_callback=handle_elicitation) as client: result = await client.call_tool("book_table", {"date": "Friday"}) ``` -------------------------------- ### Example output: server capabilities dictionary Source: https://py.sdk.modelcontextprotocol.io/v2/get-started/first-steps The output of running client.py shows the server's declared capabilities: prompts, resources, and tools, each with a list_changed flag. This is the first thing a connecting client learns. ```text {'prompts': {'list_changed': True}, 'resources': {'subscribe': True, 'list_changed': True}, 'tools': {'list_changed': True}} ``` -------------------------------- ### Run the server with the MCP Inspector Source: https://py.sdk.modelcontextprotocol.io/v2/get-started/first-steps Starts the server over HTTP using the streamable-http transport so the Inspector can connect. The Inspector will show one tab per primitive. ```bash uv run mcp dev server.py ``` -------------------------------- ### Install MCP SDK with pip Source: https://py.sdk.modelcontextprotocol.io/v2 Install the MCP Python SDK with the CLI extra using pip. The [cli] extra provides the mcp command, which is useful for development. ```bash pip install "mcp[cli]" ``` -------------------------------- ### mcp.run() Source: https://py.sdk.modelcontextprotocol.io/v2/run Documentation for the `mcp.run()` method, which starts the MCP server. It covers the stdio transport (default) and the streamable-http transport, including all transport-specific keyword arguments. ```APIDOC ## `mcp.run()` ### Description Starts the MCP server. It blocks for the life of the server. With no argument, the transport is `stdio`. It sits under `if __name__ == "__main__":` because everything that loads your server (`mcp dev`, `mcp run`, `mcp install`, your tests) imports this file. The guard keeps an import from turning into a running server. ### Method `run()` ### Parameters - **transport** (string) - Optional - The transport to use. Defaults to `stdio`. Supported values: `stdio`, `streamable-http`. - **host** (string) - Optional - Where to listen. Defaults to `127.0.0.1`. - **port** (integer) - Optional - Where to listen. Defaults to `8000`. - **streamable_http_path** (string) - Optional - Where the MCP endpoint lives. Defaults to `/mcp`. - **json_response** (boolean) - Optional - Answer each POST with a single JSON body instead of an SSE stream. Defaults to `True`. - **stateless_http** (boolean) - Optional - A fresh transport per request, no session tracking. Defaults to `False`. - **max_request_body_size** (integer) - Optional - Largest accepted request body in bytes. Defaults to 4 MiB; larger requests receive HTTP 413 before parsing or session creation. - **session_idle_timeout** (integer) - Optional - Seconds a legacy session may sit with nothing in flight before the server closes it. Default 1800. `None` disables it. - **max_sessions** (integer) - Optional - How many legacy sessions one process holds at once. Default 10 000. `None` removes the limit. - **event_store**, **retry_interval**, **transport_security** - Optional - Resumability and DNS-rebinding protection. Covered in Deploy & scale. ### Example ```python from mcp.server import MCPServer mcp = MCPServer("Bookshop") @mcp.tool() def search_books(query: str) -> str: """Search the catalog by title or author.""" return f"Found 3 books matching {query!r}." if __name__ == "__main__": mcp.run() ``` ### Notes - `run()` is synchronous. It blocks for the life of the server. - With no argument, the transport is `stdio`. - It sits under `if __name__ == "__main__":` because everything that loads your server (`mcp dev`, `mcp run`, `mcp install`, your tests) imports this file. The guard keeps an import from turning into a running server. - Transport options go to `run()`, not to `MCPServer(...)`. The constructor describes what your server is: name, version, instructions. `run()` describes how it is served. Get it backwards and Python answers before MCP is even involved: `TypeError: MCPServer.__init__() got an unexpected keyword argument 'port'`. ``` -------------------------------- ### Lowlevel Server: decorator handlers to on_* constructor params Source: https://py.sdk.modelcontextprotocol.io/v2/migration Shows the migration from decorator-based handler registration to constructor on_* parameters for the lowlevel Server class. The v1 example uses @server.list_tools() and @server.call_tool() decorators, while the v2 example passes on_list_tools and on_call_tool keyword arguments. In v2, handlers receive (ctx, params) and return full result types like ListToolsResult and CallToolResult. ```python from mcp.server.lowlevel.server import Server import mcp.types as types server = Server("my-server") @server.list_tools() async def handle_list_tools(): return [types.Tool(name="my_tool", description="A tool", inputSchema={})] @server.call_tool() async def handle_call_tool(name: str, arguments: dict): return [types.TextContent(type="text", text=f"Called {name}")] ``` ```python from mcp.server import Server, ServerRequestContext from mcp.types import ( CallToolRequestParams, CallToolResult, ListToolsResult, PaginatedRequestParams, TextContent, Tool, ) async def handle_list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult: return ListToolsResult(tools=[Tool(name="my_tool", description="A tool", input_schema={"type": "object"})]) async def handle_call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) -> CallToolResult: return CallToolResult( content=[TextContent(type="text", text=f"Called {params.name}")], is_error=False, ) server = Server("my-server", on_list_tools=handle_list_tools, on_call_tool=handle_call_tool) ``` -------------------------------- ### Before (v1): httpx AsyncClient Source: https://py.sdk.modelcontextprotocol.io/v2/migration Before migrating to v2, the SDK used httpx and httpx-sse. This example shows how to create an AsyncClient with a timeout using httpx. ```python import httpx http_client = httpx.AsyncClient(timeout=httpx.Timeout(30, read=300)) ``` -------------------------------- ### Lowlevel Server main() with stdio_server and server.run Source: https://py.sdk.modelcontextprotocol.io/v2/migration Shows a v1 main() that carries over untouched to v2. It uses stdio_server() to get read/write streams and passes them to server.run() with initialization options that enable the tools_changed notification. ```python async def main() -> None: async with stdio_server() as (read_stream, write_stream): await server.run( read_stream, write_stream, server.create_initialization_options(notification_options=NotificationOptions(tools_changed=True)), ) ``` -------------------------------- ### Client.complete Source: https://py.sdk.modelcontextprotocol.io/v2/client Demonstrates how to call the client's complete method to autocomplete prompt and resource-template arguments. ```APIDOC ## [Client] complete ### Description Completes a prompt or resource-template argument as the user types, using a completion handler on the server. ### Method complete ### Endpoint Client.complete ### Parameters - **ref** (PromptReference | ResourceTemplateReference) - Required - Which prompt or template you're filling in. - **argument** (object) - Required - `{"name": ..., "value": ...}`: the argument and what the user has typed so far. ### Request Example ```python result = await client.complete( ref=PromptReference(type="ref/prompt", name="recommend"), argument={"name": "genre", "value": "p"}, ) ``` ### Response #### Success Response - **result.completion.values** (list) - The completion suggestions. For example, typing "p" may return `['poetry']`. ``` -------------------------------- ### Use the v2 Client with anyio Source: https://py.sdk.modelcontextprotocol.io/v2/whats-new Demonstrates the v2 first-class Client, which replaces the v1 nested layers of transport context manager, ClientSession, and manual session.initialize(). The async context manager connects to the server URL and exposes server_info, server_capabilities, protocol_version, and instructions. ```python import anyio from mcp import Client async def main() -> None: async with Client("http://localhost:8000/mcp") as client: print(client.server_info) print(client.server_capabilities) print(client.protocol_version) print(client.instructions) if __name__ == "__main__": anyio.run(main) ``` -------------------------------- ### Your first tool: search_books Source: https://py.sdk.modelcontextprotocol.io/v2/servers/tools Declares a tool named search_books with required query and limit parameters. The SDK generates a JSON schema from type hints and sends it during tools/list. The docstring becomes the description the model sees. ```python from mcp.server import MCPServer mcp = MCPServer("Bookshop") @mcp.tool() def search_books(query: str, limit: int) -> str: """Search the catalog by title or author.""" return f"Found 3 books matching {query!r} (showing up to {limit})." ``` -------------------------------- ### Run the client script Source: https://py.sdk.modelcontextprotocol.io/v2/get-started/first-steps Executes the client.py script to connect to the server and display the capabilities. ```bash python client.py ``` -------------------------------- ### Install mcp with CLI extra using uv or pip Source: https://py.sdk.modelcontextprotocol.io/v2/get-started/installation Install the Python SDK with the CLI extra using either uv or pip. The CLI extra adds typer and python-dotenv for the mcp command-line tool (mcp dev, mcp run, mcp install). ```bash uv add "mcp[cli]" ``` ```bash pip install "mcp[cli]" ``` -------------------------------- ### Add host lifespan to start MCP session manager Source: https://py.sdk.modelcontextprotocol.io/v2/troubleshooting The fix: add a lifespan to the host Starlette app that enters mcp.session_manager.run(). This starts the session manager so the mounted MCP app works. ```python @asynccontextmanager async def lifespan(app: Starlette) -> AsyncIterator[None]: async with mcp.session_manager.run(): yield app = Starlette(routes=[Mount("/", app=mcp.streamable_http_app())], lifespan=lifespan) ``` -------------------------------- ### brand://guidelines Source: https://py.sdk.modelcontextprotocol.io/v2/servers/media Example of a resource that returns a Markdown document. ```APIDOC ## [RESOURCE] brand://guidelines ### Description A resource that provides the brand guidelines as a Markdown document. ### Method Resource ### Endpoint `brand://guidelines` ### Parameters None ### Request Example No request body. ### Response #### Success Response - **str** (string) - The Markdown content of the brand guidelines. #### Response Example ```python "# Brand guidelines\n\nUse the primary colour for calls to action.\n" ``` ``` -------------------------------- ### First-class Client with elicitation callback Source: https://py.sdk.modelcontextprotocol.io/v2/whats-new Shows a first-class Client with an elicitation callback. The callback returns an ElicitResult with action "accept" and content. The main function creates both a legacy client (mode="legacy") and a modern client, calls the "reserve" tool, and prints the protocol version and structured content. Requires anyio.run to execute. ```python async def answer(context: ClientRequestContext, params: ElicitRequestParams) -> ElicitResult: return ElicitResult(action="accept", content={"copies": 2}) ``` ```python async def main() -> None: async with ( Client("http://localhost:8000/mcp", mode="legacy", elicitation_callback=answer) as legacy, Client("http://localhost:8000/mcp", elicitation_callback=answer) as modern, ): for client in (legacy, modern): result = await client.call_tool("reserve", {"title": "Dune"}) print(client.protocol_version, result.structured_content) if __name__ == "__main__": anyio.run(main) ``` -------------------------------- ### brand_guidelines Source: https://py.sdk.modelcontextprotocol.io/v2/servers/media Example of a tool that returns an EmbeddedResource content block. ```APIDOC ## [TOOL] brand_guidelines ### Description Returns the brand guidelines as a Markdown document, embedded as a resource. ### Method Tool ### Endpoint `brand_guidelines` ### Parameters None ### Request Example No request body. ### Response #### Success Response - **EmbeddedResource** (object) - A content block containing a `TextResourceContents` with the resource URI, MIME type, and text. #### Response Example ```python EmbeddedResource(resource=TextResourceContents(uri="brand://guidelines", mime_type="text/markdown", text=guidelines())) ``` ``` -------------------------------- ### v2: Pass instructions by keyword Source: https://py.sdk.modelcontextprotocol.io/v2/migration Shows the v2 constructor call with instructions passed by keyword. The positional order changed to name, title, description, instructions, website_url, icons, version. ```python from mcp.server.mcpserver import MCPServer mcp = MCPServer("Demo", instructions="You answer questions about the weather.") ``` -------------------------------- ### logo_from_bytes Source: https://py.sdk.modelcontextprotocol.io/v2/servers/media Example of a tool that returns an Image content block using raw bytes. ```APIDOC ## [TOOL] logo_from_bytes ### Description Returns the brand logo as a PNG image, using raw bytes instead of a file path. ### Method Tool ### Endpoint `logo_from_bytes` ### Parameters None ### Request Example No request body. ### Response #### Success Response - **Image** (object) - An image content block with `data` and `format` fields. #### Response Example ```python Image(data=png, format="png") ``` ``` -------------------------------- ### Return audio with Audio helper (server.py) Source: https://py.sdk.modelcontextprotocol.io/v2/servers/media Audio is the same shape as Image. Keep logo.png and add chime.wav. The tool returns an AudioContent block with base64 data and MIME type audio/wav. No output schema. ```python from pathlib import Path from mcp.server import MCPServer from mcp.server.mcpserver import Audio, Image mcp = MCPServer("Brand kit") LOGO_FILE = Path(__file__).parent / "logo.png" CHIME_FILE = Path(__file__).parent / "chime.wav" @mcp.tool() def logo() -> Image: """The brand logo as a PNG.""" return Image(path=LOGO_FILE) @mcp.tool() def chime() -> Audio: """The notification chime as a WAV.""" return Audio(path=CHIME_FILE) ``` -------------------------------- ### Resource Definitions Source: https://py.sdk.modelcontextprotocol.io/v2/servers/resources Examples of defining MCP resources with different return types and MIME types. ```APIDOC ## [RESOURCE] docs://readme ### Description Returns a markdown string describing how to use the server. ### Resource URI `docs://readme` ### MIME Type `text/markdown` ### Parameters None ### Return Value - **str** - The markdown content of the resource. ### Example ```python @mcp.resource("docs://readme", mime_type="text/markdown") def readme() -> str: """How to use this server.""" return "# Bookshop\n\nSearch the catalog with the `search_books` tool." ``` ``` ```APIDOC ## [RESOURCE] stats://catalog ### Description Returns live counts for the catalog as a JSON object. ### Resource URI `stats://catalog` ### MIME Type `application/json` ### Parameters None ### Return Value - **dict[str, int]** - A dictionary with keys `books` and `authors`. ### Example ```python @mcp.resource("stats://catalog", mime_type="application/json") def catalog_stats() -> dict[str, int]: """Live counts for the catalog.""" return {"books": 1204, "authors": 391} ``` ``` ```APIDOC ## [RESOURCE] covers://placeholder ### Description Returns a 1x1 transparent GIF image as bytes. ### Resource URI `covers://placeholder` ### MIME Type `image/gif` ### Parameters None ### Return Value - **bytes** - The binary content of the image, base64-encoded in the client's `blob` field. ### Example ```python @mcp.resource("covers://placeholder", mime_type="image/gif") def placeholder_cover() -> bytes: """A 1x1 transparent GIF, shown when a book has no cover.""" return base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") ``` ``` -------------------------------- ### Before (v1): ServerSession.elicit_form() with requestedSchema Source: https://py.sdk.modelcontextprotocol.io/v2/migration v1 call to ServerSession.elicit_form() using the old requestedSchema parameter name. This is the before example for the migration to requested_schema. ```python result = await ctx.session.elicit_form( message="Your name?", requestedSchema={ "type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"], }, ) ``` -------------------------------- ### Client read_resource: Before and After Source: https://py.sdk.modelcontextprotocol.io/v2/migration Shows the change in client.read_resource() from accepting AnyUrl to accepting plain strings, with conversion guidance. ```python # Before (v1) from pydantic import AnyUrl await client.read_resource(AnyUrl("test://resource")) ``` ```python # After (v2) await client.read_resource("test://resource") # Or if you have an AnyUrl from elsewhere: await client.read_resource(str(my_any_url)) ``` -------------------------------- ### Tool attributes example Source: https://py.sdk.modelcontextprotocol.io/v2/client Shows the attributes of a tool object returned by list_tools(). The title is optional and may be None if not provided when the tool was registered. ```python tool.name # 'search_books' tool.title # 'Search the catalog' tool.description # 'Search the catalog by title or author.' ``` -------------------------------- ### After (v2): Constructing values without wrapper Source: https://py.sdk.modelcontextprotocol.io/v2/migration Shows the v2 pattern for constructing values without the wrapper call, including a multi-line example with params. ```python await session.send_notification(InitializedNotification()) await session.send_request(PingRequest(), EmptyResult) # Params are constructed as before; only the outer wrapper is gone await session.send_notification( CancelledNotification(params=CancelledNotificationParams(request_id=request_id, reason="timeout")) ) ``` -------------------------------- ### Bookshop server (server.py) - MCP server with tools, resources, prompts, and completions Source: https://py.sdk.modelcontextprotocol.io/v2/client Defines the Bookshop MCP server using the python-sdk. It registers two tools (search_books, lookup_book), two resources (catalog://genres, catalog://genres/{genre}), a prompt (recommend), and a completion handler (complete_genre). The server is run with `uv run mcp run server.py --transport streamable-http` and serves at http://localhost:8000/mcp. ```python from pydantic import BaseModel from mcp.server import MCPServer from mcp.server.mcpserver.exceptions import ToolError from mcp.types import Completion, CompletionArgument, CompletionContext, PromptReference, ResourceTemplateReference mcp = MCPServer("Bookshop", instructions="Search the catalog before recommending a book.") GENRES = ["fiction", "non-fiction", "poetry"] class Book(BaseModel): title: str author: str year: int @mcp.tool(title="Search the catalog") def search_books(query: str, limit: int = 10) -> str: """Search the catalog by title or author.""" return f"Found 3 books matching {query!r} (showing up to {limit})." @mcp.tool() def lookup_book(title: str) -> Book: """Look up a book by its exact title.""" if title != "Dune": raise ToolError(f"No book titled {title!r} in the catalog.") return Book(title="Dune", author="Frank Herbert", year=1965) @mcp.resource("catalog://genres") def genres() -> list[str]: """The genres the catalog is organised by.""" return GENRES @mcp.resource("catalog://genres/{genre}") def books_in_genre(genre: str) -> str: """Every title we stock in one genre.""" return f"3 books filed under {genre}." @mcp.prompt(title="Recommend a book") def recommend(genre: str) -> str: """Ask for a recommendation in a genre.""" return f"Recommend one {genre} book from the catalog and say why." @mcp.completion() async def complete_genre( ref: PromptReference | ResourceTemplateReference, argument: CompletionArgument, context: CompletionContext | None, ) -> Completion | None: return Completion(values=[genre for genre in GENRES if genre.startswith(argument.value)]) ``` ```bash uv run mcp run server.py --transport streamable-http ``` -------------------------------- ### Create MCP server with MCPServer Source: https://py.sdk.modelcontextprotocol.io/v2 Complete MCP server example in server.py. It exposes a tool named add and a templated resource greeting://{name}. The SDK generates JSON Schema from type hints and handles protocol details automatically. ```python from mcp.server import MCPServer mcp = MCPServer("Demo") @mcp.tool() def add(a: int, b: int) -> int: """Add two numbers.""" return a + b @mcp.resource("greeting://{name}") def greeting(name: str) -> str: """Greet someone by name.""" return f"Hello, {name}!" ``` -------------------------------- ### ExceptionGroup traceback for unknown resource Source: https://py.sdk.modelcontextprotocol.io/v2/troubleshooting The actual traceback produced by the previous client example. The real error is the last line, MCPError: No forecast for 'Atlantis'. ```text + Exception Group Traceback (most recent call last): | ... | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception) +-+---------------- 1 ---------------- | Exception Group Traceback (most recent call last): | ... | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception) +-+---------------- 1 ---------------- | Traceback (most recent call last): | ... | mcp.shared.exceptions.MCPError: No forecast for 'Atlantis'. +------------------------------------ ```