diff --git a/README.md b/README.md index 8190f81d6..2cb90770c 100644 --- a/README.md +++ b/README.md @@ -532,9 +532,7 @@ Client usage: ```python -"""MCP client example showing completion usage. - -This example demonstrates how to use the completion feature in MCP clients. +""" cd to the `examples/snippets` directory and run: uv run completion-client """ @@ -863,72 +861,99 @@ Note that `uv run mcp run` or `uv run mcp dev` only supports server using FastMC > **Note**: Streamable HTTP transport is superseding SSE transport for production deployments. + ```python +""" +Run from the repository root: + uv run examples/snippets/servers/streamable_config.py +""" + from mcp.server.fastmcp import FastMCP # Stateful server (maintains session state) mcp = FastMCP("StatefulServer") +# Other configuration options: # Stateless server (no session persistence) -mcp = FastMCP("StatelessServer", stateless_http=True) +# mcp = FastMCP("StatelessServer", stateless_http=True) # Stateless server (no session persistence, no sse stream with supported client) -mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True) +# mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True) + + +# Add a simple tool to demonstrate the server +@mcp.tool() +def greet(name: str = "World") -> str: + """Greet someone by name.""" + return f"Hello, {name}!" + # Run server with streamable_http transport -mcp.run(transport="streamable-http") +if __name__ == "__main__": + mcp.run(transport="streamable-http") ``` -You can mount multiple FastMCP servers in a FastAPI application: +_Full example: [examples/snippets/servers/streamable_config.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_config.py)_ + + +You can mount multiple FastMCP servers in a Starlette application: + ```python -# echo.py +""" +Run from the repository root: + uvicorn examples.snippets.servers.streamable_starlette_mount:app --reload +""" + +import contextlib + +from starlette.applications import Starlette +from starlette.routing import Mount + from mcp.server.fastmcp import FastMCP -mcp = FastMCP(name="EchoServer", stateless_http=True) +# Create the Echo server +echo_mcp = FastMCP(name="EchoServer", stateless_http=True) -@mcp.tool() +@echo_mcp.tool() def echo(message: str) -> str: """A simple echo tool""" return f"Echo: {message}" -``` -```python -# math.py -from mcp.server.fastmcp import FastMCP -mcp = FastMCP(name="MathServer", stateless_http=True) +# Create the Math server +math_mcp = FastMCP(name="MathServer", stateless_http=True) -@mcp.tool() +@math_mcp.tool() def add_two(n: int) -> int: """Tool to add two to the input""" return n + 2 -``` - -```python -# main.py -import contextlib -from fastapi import FastAPI -from mcp.echo import echo -from mcp.math import math # Create a combined lifespan to manage both session managers @contextlib.asynccontextmanager -async def lifespan(app: FastAPI): +async def lifespan(app: Starlette): async with contextlib.AsyncExitStack() as stack: - await stack.enter_async_context(echo.mcp.session_manager.run()) - await stack.enter_async_context(math.mcp.session_manager.run()) + await stack.enter_async_context(echo_mcp.session_manager.run()) + await stack.enter_async_context(math_mcp.session_manager.run()) yield -app = FastAPI(lifespan=lifespan) -app.mount("/echo", echo.mcp.streamable_http_app()) -app.mount("/math", math.mcp.streamable_http_app()) +# Create the Starlette app and mount the MCP servers +app = Starlette( + routes=[ + Mount("/echo", echo_mcp.streamable_http_app()), + Mount("/math", math_mcp.streamable_http_app()), + ], + lifespan=lifespan, +) ``` +_Full example: [examples/snippets/servers/streamable_starlette_mount.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_starlette_mount.py)_ + + For low level server with Streamable HTTP implementations, see: - Stateful server: [`examples/servers/simple-streamablehttp/`](examples/servers/simple-streamablehttp/) @@ -945,26 +970,6 @@ The streamable HTTP transport supports: By default, SSE servers are mounted at `/sse` and Streamable HTTP servers are mounted at `/mcp`. You can customize these paths using the methods described below. -#### Streamable HTTP servers - -The following example shows how to use `streamable_http_app()`, a method that returns a `Starlette` application object. -You can then append additional routes to that application as needed. - -```python -mcp = FastMCP("My App") - -app = mcp.streamable_http_app() -# Additional non-MCP routes can be added like so: -# from starlette.routing import Route -# app.router.routes.append(Route("/", endpoint=other_route_function)) -``` - -To customize the route from the default of "/mcp", either specify the `streamable_http_path` option for the `FastMCP` constructor, -or set `FASTMCP_STREAMABLE_HTTP_PATH` environment variable. - -Note that in Starlette and FastAPI (which is based on Starlette), the "/mcp" route will redirect to "/mcp/", -so you may need to use "/mcp/" when pointing MCP clients at your servers. - For more information on mounting applications in Starlette, see the [Starlette documentation](https://www.starlette.io/routing/#submounting-routes). #### SSE servers @@ -1336,9 +1341,7 @@ The SDK provides a high-level client interface for connecting to MCP servers usi ```python -"""MCP client example using stdio transport. - -This is a documentation example showing how to write an MCP client. +""" cd to the `examples/snippets/clients` directory and run: uv run client """ @@ -1428,14 +1431,22 @@ _Full example: [examples/snippets/clients/stdio_client.py](https://github.com/mo Clients can also connect using [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http): + ```python -from mcp.client.streamable_http import streamablehttp_client +""" +Run from the repository root: + uv run examples/snippets/clients/streamable_basic.py +""" + +import asyncio + from mcp import ClientSession +from mcp.client.streamable_http import streamablehttp_client async def main(): # Connect to a streamable HTTP server - async with streamablehttp_client("example/mcp") as ( + async with streamablehttp_client("http://localhost:8000/mcp") as ( read_stream, write_stream, _, @@ -1444,21 +1455,25 @@ async def main(): async with ClientSession(read_stream, write_stream) as session: # Initialize the connection await session.initialize() - # Call a tool - tool_result = await session.call_tool("echo", {"message": "hello"}) + # List available tools + tools = await session.list_tools() + print(f"Available tools: {[tool.name for tool in tools.tools]}") + + +if __name__ == "__main__": + asyncio.run(main()) ``` +_Full example: [examples/snippets/clients/streamable_basic.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/streamable_basic.py)_ + + ### Client Display Utilities When building MCP clients, the SDK provides utilities to help display human-readable names for tools, resources, and prompts: ```python -"""Client display utilities example. - -This example shows how to use the SDK's display utilities to show -human-readable names for tools, resources, and prompts. - +""" cd to the `examples/snippets` directory and run: uv run display-utilities-client """ diff --git a/examples/snippets/clients/completion_client.py b/examples/snippets/clients/completion_client.py index d8745ea1e..8c5615926 100644 --- a/examples/snippets/clients/completion_client.py +++ b/examples/snippets/clients/completion_client.py @@ -1,6 +1,4 @@ -"""MCP client example showing completion usage. - -This example demonstrates how to use the completion feature in MCP clients. +""" cd to the `examples/snippets` directory and run: uv run completion-client """ diff --git a/examples/snippets/clients/display_utilities.py b/examples/snippets/clients/display_utilities.py index ee2280b3a..5f1d50510 100644 --- a/examples/snippets/clients/display_utilities.py +++ b/examples/snippets/clients/display_utilities.py @@ -1,8 +1,4 @@ -"""Client display utilities example. - -This example shows how to use the SDK's display utilities to show -human-readable names for tools, resources, and prompts. - +""" cd to the `examples/snippets` directory and run: uv run display-utilities-client """ diff --git a/examples/snippets/clients/stdio_client.py b/examples/snippets/clients/stdio_client.py index 22ad933ad..74a6f09df 100644 --- a/examples/snippets/clients/stdio_client.py +++ b/examples/snippets/clients/stdio_client.py @@ -1,6 +1,4 @@ -"""MCP client example using stdio transport. - -This is a documentation example showing how to write an MCP client. +""" cd to the `examples/snippets/clients` directory and run: uv run client """ diff --git a/examples/snippets/clients/streamable_basic.py b/examples/snippets/clients/streamable_basic.py new file mode 100644 index 000000000..108439613 --- /dev/null +++ b/examples/snippets/clients/streamable_basic.py @@ -0,0 +1,29 @@ +""" +Run from the repository root: + uv run examples/snippets/clients/streamable_basic.py +""" + +import asyncio + +from mcp import ClientSession +from mcp.client.streamable_http import streamablehttp_client + + +async def main(): + # Connect to a streamable HTTP server + async with streamablehttp_client("http://localhost:8000/mcp") as ( + read_stream, + write_stream, + _, + ): + # Create a session using the client streams + async with ClientSession(read_stream, write_stream) as session: + # Initialize the connection + await session.initialize() + # List available tools + tools = await session.list_tools() + print(f"Available tools: {[tool.name for tool in tools.tools]}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/snippets/servers/streamable_config.py b/examples/snippets/servers/streamable_config.py new file mode 100644 index 000000000..e265f6381 --- /dev/null +++ b/examples/snippets/servers/streamable_config.py @@ -0,0 +1,28 @@ +""" +Run from the repository root: + uv run examples/snippets/servers/streamable_config.py +""" + +from mcp.server.fastmcp import FastMCP + +# Stateful server (maintains session state) +mcp = FastMCP("StatefulServer") + +# Other configuration options: +# Stateless server (no session persistence) +# mcp = FastMCP("StatelessServer", stateless_http=True) + +# Stateless server (no session persistence, no sse stream with supported client) +# mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True) + + +# Add a simple tool to demonstrate the server +@mcp.tool() +def greet(name: str = "World") -> str: + """Greet someone by name.""" + return f"Hello, {name}!" + + +# Run server with streamable_http transport +if __name__ == "__main__": + mcp.run(transport="streamable-http") diff --git a/examples/snippets/servers/streamable_starlette_mount.py b/examples/snippets/servers/streamable_starlette_mount.py new file mode 100644 index 000000000..19e41294b --- /dev/null +++ b/examples/snippets/servers/streamable_starlette_mount.py @@ -0,0 +1,49 @@ +""" +Run from the repository root: + uvicorn examples.snippets.servers.streamable_starlette_mount:app --reload +""" + +import contextlib + +from starlette.applications import Starlette +from starlette.routing import Mount + +from mcp.server.fastmcp import FastMCP + +# Create the Echo server +echo_mcp = FastMCP(name="EchoServer", stateless_http=True) + + +@echo_mcp.tool() +def echo(message: str) -> str: + """A simple echo tool""" + return f"Echo: {message}" + + +# Create the Math server +math_mcp = FastMCP(name="MathServer", stateless_http=True) + + +@math_mcp.tool() +def add_two(n: int) -> int: + """Tool to add two to the input""" + return n + 2 + + +# Create a combined lifespan to manage both session managers +@contextlib.asynccontextmanager +async def lifespan(app: Starlette): + async with contextlib.AsyncExitStack() as stack: + await stack.enter_async_context(echo_mcp.session_manager.run()) + await stack.enter_async_context(math_mcp.session_manager.run()) + yield + + +# Create the Starlette app and mount the MCP servers +app = Starlette( + routes=[ + Mount("/echo", echo_mcp.streamable_http_app()), + Mount("/math", math_mcp.streamable_http_app()), + ], + lifespan=lifespan, +)