@@ -66,7 +66,7 @@ The Model Context Protocol allows applications to provide context for LLMs in a
6666
6767- Build MCP clients that can connect to any MCP server
6868- Create MCP servers that expose resources, prompts and tools
69- - Use standard transports like stdio and SSE
69+ - Use standard transports like stdio, SSE, and Streamable HTTP
7070- Handle all MCP protocol messages and lifecycle events
7171
7272## Installation
@@ -160,7 +160,7 @@ from dataclasses import dataclass
160160
161161from fake_database import Database # Replace with your actual DB type
162162
163- from mcp.server.fastmcp import Context, FastMCP
163+ from mcp.server.fastmcp import FastMCP
164164
165165# Create a named server
166166mcp = FastMCP("My App")
@@ -192,9 +192,10 @@ mcp = FastMCP("My App", lifespan=app_lifespan)
192192
193193# Access type-safe lifespan context in tools
194194@mcp.tool()
195- def query_db(ctx: Context ) -> str:
195+ def query_db() -> str:
196196 """Tool that uses initialized resources"""
197- db = ctx.request_context.lifespan_context.db
197+ ctx = mcp.get_context()
198+ db = ctx.request_context.lifespan_context["db"]
198199 return db.query()
199200```
200201
@@ -314,27 +315,42 @@ async def long_task(files: list[str], ctx: Context) -> str:
314315Authentication can be used by servers that want to expose tools accessing protected resources.
315316
316317`mcp.server.auth` implements an OAuth 2.0 server interface, which servers can use by
317- providing an implementation of the `OAuthServerProvider ` protocol.
318+ providing an implementation of the `OAuthAuthorizationServerProvider ` protocol.
318319
319- ```
320- mcp = FastMCP("My App",
321- auth_provider=MyOAuthServerProvider(),
322- auth=AuthSettings(
323- issuer_url="https://myapp.com",
324- revocation_options=RevocationOptions(
325- enabled=True,
326- ),
327- client_registration_options=ClientRegistrationOptions(
328- enabled=True,
329- valid_scopes=["myscope", "myotherscope"],
330- default_scopes=["myscope"],
331- ),
332- required_scopes=["myscope"],
320+ ```python
321+ from mcp import FastMCP
322+ from mcp.server.auth.provider import OAuthAuthorizationServerProvider
323+ from mcp.server.auth.settings import (
324+ AuthSettings,
325+ ClientRegistrationOptions,
326+ RevocationOptions,
327+ )
328+
329+
330+ class MyOAuthServerProvider(OAuthAuthorizationServerProvider):
331+ # See an example on how to implement at `examples/servers/simple-auth`
332+ ...
333+
334+
335+ mcp = FastMCP(
336+ "My App",
337+ auth_server_provider=MyOAuthServerProvider(),
338+ auth=AuthSettings(
339+ issuer_url="https://myapp.com",
340+ revocation_options=RevocationOptions(
341+ enabled=True,
342+ ),
343+ client_registration_options=ClientRegistrationOptions(
344+ enabled=True,
345+ valid_scopes=["myscope", "myotherscope"],
346+ default_scopes=["myscope"],
333347 ),
348+ required_scopes=["myscope"],
349+ ),
334350)
335351```
336352
337- See [OAuthServerProvider]( mcp/server/auth/provider.py) for more details.
353+ See [OAuthAuthorizationServerProvider](src/ mcp/server/auth/provider.py) for more details.
338354
339355## Running Your Server
340356
@@ -387,8 +403,92 @@ python server.py
387403mcp run server.py
388404```
389405
406+ Note that `mcp run` or `mcp dev` only supports server using FastMCP and not the low-level server variant.
407+
408+ ### Streamable HTTP Transport
409+
410+ > **Note**: Streamable HTTP transport is superseding SSE transport for production deployments.
411+
412+ ```python
413+ from mcp.server.fastmcp import FastMCP
414+
415+ # Stateful server (maintains session state)
416+ mcp = FastMCP("StatefulServer")
417+
418+ # Stateless server (no session persistence)
419+ mcp = FastMCP("StatelessServer", stateless_http=True)
420+
421+ # Stateless server (no session persistence, no sse stream with supported client)
422+ mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
423+
424+ # Run server with streamable_http transport
425+ mcp.run(transport="streamable-http")
426+ ```
427+
428+ You can mount multiple FastMCP servers in a FastAPI application:
429+
430+ ```python
431+ # echo.py
432+ from mcp.server.fastmcp import FastMCP
433+
434+ mcp = FastMCP(name="EchoServer", stateless_http=True)
435+
436+
437+ @mcp.tool(description="A simple echo tool")
438+ def echo(message: str) -> str:
439+ return f"Echo: {message}"
440+ ```
441+
442+ ```python
443+ # math.py
444+ from mcp.server.fastmcp import FastMCP
445+
446+ mcp = FastMCP(name="MathServer", stateless_http=True)
447+
448+
449+ @mcp.tool(description="A simple add tool")
450+ def add_two(n: int) -> int:
451+ return n + 2
452+ ```
453+
454+ ```python
455+ # main.py
456+ import contextlib
457+ from fastapi import FastAPI
458+ from mcp.echo import echo
459+ from mcp.math import math
460+
461+
462+ # Create a combined lifespan to manage both session managers
463+ @contextlib.asynccontextmanager
464+ async def lifespan(app: FastAPI):
465+ async with contextlib.AsyncExitStack() as stack:
466+ await stack.enter_async_context(echo.mcp.session_manager.run())
467+ await stack.enter_async_context(math.mcp.session_manager.run())
468+ yield
469+
470+
471+ app = FastAPI(lifespan=lifespan)
472+ app.mount("/echo", echo.mcp.streamable_http_app())
473+ app.mount("/math", math.mcp.streamable_http_app())
474+ ```
475+
476+ For low level server with Streamable HTTP implementations, see:
477+ - Stateful server: [`examples/servers/simple-streamablehttp/`](examples/servers/simple-streamablehttp/)
478+ - Stateless server: [`examples/servers/simple-streamablehttp-stateless/`](examples/servers/simple-streamablehttp-stateless/)
479+
480+ The streamable HTTP transport supports:
481+ - Stateful and stateless operation modes
482+ - Resumability with event stores
483+ - JSON or SSE response formats
484+ - Better scalability for multi-node deployments
485+
390486### Mounting to an Existing ASGI Server
391487
488+ > **Note**: SSE transport is being superseded by [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http).
489+
490+ 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.
491+
392492You can mount the SSE server to an existing ASGI server using the `sse_app` method. This allows you to integrate the SSE server with other ASGI applications.
393493
394494```python
@@ -410,6 +510,43 @@ app = Starlette(
410510app.router.routes.append(Host('mcp.acme.corp', app=mcp.sse_app()))
411511```
412512
513+ When mounting multiple MCP servers under different paths, you can configure the mount path in several ways:
514+
515+ ```python
516+ from starlette.applications import Starlette
517+ from starlette.routing import Mount
518+ from mcp.server.fastmcp import FastMCP
519+
520+ # Create multiple MCP servers
521+ github_mcp = FastMCP("GitHub API")
522+ browser_mcp = FastMCP("Browser")
523+ curl_mcp = FastMCP("Curl")
524+ search_mcp = FastMCP("Search")
525+
526+ # Method 1: Configure mount paths via settings (recommended for persistent configuration)
527+ github_mcp.settings.mount_path = "/github"
528+ browser_mcp.settings.mount_path = "/browser"
529+
530+ # Method 2: Pass mount path directly to sse_app (preferred for ad-hoc mounting)
531+ # This approach doesn't modify the server's settings permanently
532+
533+ # Create Starlette app with multiple mounted servers
534+ app = Starlette(
535+ routes=[
536+ # Using settings-based configuration
537+ Mount("/github", app=github_mcp.sse_app()),
538+ Mount("/browser", app=browser_mcp.sse_app()),
539+ # Using direct mount path parameter
540+ Mount("/curl", app=curl_mcp.sse_app("/curl")),
541+ Mount("/search", app=search_mcp.sse_app("/search")),
542+ ]
543+ )
544+
545+ # Method 3: For direct execution, you can also pass the mount path to run()
546+ if __name__ == "__main__":
547+ search_mcp.run(transport="sse", mount_path="/search")
548+ ```
549+
413550For more information on mounting applications in Starlette, see the [Starlette documentation](https://www.starlette.io/routing/#submounting-routes).
414551
415552## Examples
@@ -582,9 +719,11 @@ if __name__ == "__main__":
582719 asyncio.run(run())
583720```
584721
722+ Caution: The `mcp run` and `mcp dev` tool doesn't support low-level server.
723+
585724### Writing MCP Clients
586725
587- The SDK provides a high-level client interface for connecting to MCP servers:
726+ The SDK provides a high-level client interface for connecting to MCP servers using various [transports](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports) :
588727
589728```python
590729from mcp import ClientSession, StdioServerParameters, types
@@ -648,6 +787,82 @@ if __name__ == "__main__":
648787 asyncio.run(run())
649788```
650789
790+ Clients can also connect using [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http):
791+
792+ ```python
793+ from mcp.client.streamable_http import streamablehttp_client
794+ from mcp import ClientSession
795+
796+
797+ async def main():
798+ # Connect to a streamable HTTP server
799+ async with streamablehttp_client("example/mcp") as (
800+ read_stream,
801+ write_stream,
802+ _,
803+ ):
804+ # Create a session using the client streams
805+ async with ClientSession(read_stream, write_stream) as session:
806+ # Initialize the connection
807+ await session.initialize()
808+ # Call a tool
809+ tool_result = await session.call_tool("echo", {"message": "hello"})
810+ ```
811+
812+ ### OAuth Authentication for Clients
813+
814+ The SDK includes [authorization support](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) for connecting to protected MCP servers:
815+
816+ ```python
817+ from mcp.client.auth import OAuthClientProvider, TokenStorage
818+ from mcp.client.session import ClientSession
819+ from mcp.client.streamable_http import streamablehttp_client
820+ from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
821+
822+
823+ class CustomTokenStorage(TokenStorage):
824+ """Simple in-memory token storage implementation."""
825+
826+ async def get_tokens(self) -> OAuthToken | None:
827+ pass
828+
829+ async def set_tokens(self, tokens: OAuthToken) -> None:
830+ pass
831+
832+ async def get_client_info(self) -> OAuthClientInformationFull | None:
833+ pass
834+
835+ async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
836+ pass
837+
838+
839+ async def main():
840+ # Set up OAuth authentication
841+ oauth_auth = OAuthClientProvider(
842+ server_url="https://api.example.com",
843+ client_metadata=OAuthClientMetadata(
844+ client_name="My Client",
845+ redirect_uris=["http://localhost:3000/callback"],
846+ grant_types=["authorization_code", "refresh_token"],
847+ response_types=["code"],
848+ ),
849+ storage=CustomTokenStorage(),
850+ redirect_handler=lambda url: print(f"Visit: {url}"),
851+ callback_handler=lambda: ("auth_code", None),
852+ )
853+
854+ # Use with streamable HTTP client
855+ async with streamablehttp_client(
856+ "https://api.example.com/mcp", auth=oauth_auth
857+ ) as (read, write, _):
858+ async with ClientSession(read, write) as session:
859+ await session.initialize()
860+ # Authenticated session ready
861+ ```
862+
863+ For a complete working example, see [`examples/clients/simple-auth-client/`](examples/clients/simple-auth-client/).
864+
865+
651866### MCP Primitives
652867
653868The MCP protocol defines three core primitives that servers can implement:
0 commit comments