@@ -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