@@ -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
@@ -309,6 +309,33 @@ async def long_task(files: list[str], ctx: Context) -> str:
309309 return " Processing complete"
310310```
311311
312+ ### Authentication
313+
314+ Authentication can be used by servers that want to expose tools accessing protected resources.
315+
316+ ` mcp.server.auth ` implements an OAuth 2.0 server interface, which servers can use by
317+ providing an implementation of the ` OAuthServerProvider ` protocol.
318+
319+ ```
320+ mcp = FastMCP("My App",
321+ auth_server_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"],
333+ ),
334+ )
335+ ```
336+
337+ See [ OAuthServerProvider] ( src/mcp/server/auth/provider.py ) for more details.
338+
312339## Running Your Server
313340
314341### Development Mode
@@ -360,8 +387,95 @@ python server.py
360387mcp run server.py
361388```
362389
390+ Note that ` mcp run ` or ` mcp dev ` only supports server using FastMCP and not the low-level server variant.
391+
392+ ### Streamable HTTP Transport
393+
394+ > ** Note** : Streamable HTTP transport is superseding SSE transport for production deployments.
395+
396+ ``` python
397+ from mcp.server.fastmcp import FastMCP
398+
399+ # Stateful server (maintains session state)
400+ mcp = FastMCP(" StatefulServer" )
401+
402+ # Stateless server (no session persistence)
403+ mcp = FastMCP(" StatelessServer" , stateless_http = True )
404+
405+ # Stateless server (no session persistence, no sse stream with supported client)
406+ mcp = FastMCP(" StatelessServer" , stateless_http = True , json_response = True )
407+
408+ # Run server with streamable_http transport
409+ mcp.run(transport = " streamable-http" )
410+ ```
411+
412+ You can mount multiple FastMCP servers in a FastAPI application:
413+
414+ ``` python
415+ # echo.py
416+ from mcp.server.fastmcp import FastMCP
417+
418+ mcp = FastMCP(name = " EchoServer" , stateless_http = True )
419+
420+
421+ @mcp.tool (description = " A simple echo tool" )
422+ def echo (message : str ) -> str :
423+ return f " Echo: { message} "
424+ ```
425+
426+ ``` python
427+ # math.py
428+ from mcp.server.fastmcp import FastMCP
429+
430+ mcp = FastMCP(name = " MathServer" , stateless_http = True )
431+
432+
433+ @mcp.tool (description = " A simple add tool" )
434+ def add_two (n : int ) -> int :
435+ return n + 2
436+ ```
437+
438+ ``` python
439+ # main.py
440+ import contextlib
441+ from fastapi import FastAPI
442+ from mcp.echo import echo
443+ from mcp.math import math
444+
445+
446+ # Create a combined lifespan to manage both session managers
447+ @contextlib.asynccontextmanager
448+ async def lifespan (app : FastAPI):
449+ async with contextlib.AsyncExitStack() as stack:
450+ await stack.enter_async_context(echo.mcp.session_manager.run())
451+ await stack.enter_async_context(math.mcp.session_manager.run())
452+ yield
453+
454+
455+ app = FastAPI(lifespan = lifespan)
456+ app.mount(" /echo" , echo.mcp.streamable_http_app())
457+ app.mount(" /math" , math.mcp.streamable_http_app())
458+ ```
459+
460+ For low level server with Streamable HTTP implementations, see:
461+ - Stateful server: [ ` examples/servers/simple-streamablehttp/ ` ] ( examples/servers/simple-streamablehttp/ )
462+ - Stateless server: [ ` examples/servers/simple-streamablehttp-stateless/ ` ] ( examples/servers/simple-streamablehttp-stateless/ )
463+
464+
465+
466+ The streamable HTTP transport supports:
467+ - Stateful and stateless operation modes
468+ - Resumability with event stores
469+ - JSON or SSE response formats
470+ - Better scalability for multi-node deployments
471+
472+
363473### Mounting to an Existing ASGI Server
364474
475+ > ** Note** : SSE transport is being superseded by [ Streamable HTTP transport] ( https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http ) .
476+
477+ 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.
478+
365479You 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.
366480
367481``` python
@@ -383,6 +497,43 @@ app = Starlette(
383497app.router.routes.append(Host(' mcp.acme.corp' , app = mcp.sse_app()))
384498```
385499
500+ When mounting multiple MCP servers under different paths, you can configure the mount path in several ways:
501+
502+ ``` python
503+ from starlette.applications import Starlette
504+ from starlette.routing import Mount
505+ from mcp.server.fastmcp import FastMCP
506+
507+ # Create multiple MCP servers
508+ github_mcp = FastMCP(" GitHub API" )
509+ browser_mcp = FastMCP(" Browser" )
510+ curl_mcp = FastMCP(" Curl" )
511+ search_mcp = FastMCP(" Search" )
512+
513+ # Method 1: Configure mount paths via settings (recommended for persistent configuration)
514+ github_mcp.settings.mount_path = " /github"
515+ browser_mcp.settings.mount_path = " /browser"
516+
517+ # Method 2: Pass mount path directly to sse_app (preferred for ad-hoc mounting)
518+ # This approach doesn't modify the server's settings permanently
519+
520+ # Create Starlette app with multiple mounted servers
521+ app = Starlette(
522+ routes = [
523+ # Using settings-based configuration
524+ Mount(" /github" , app = github_mcp.sse_app()),
525+ Mount(" /browser" , app = browser_mcp.sse_app()),
526+ # Using direct mount path parameter
527+ Mount(" /curl" , app = curl_mcp.sse_app(" /curl" )),
528+ Mount(" /search" , app = search_mcp.sse_app(" /search" )),
529+ ]
530+ )
531+
532+ # Method 3: For direct execution, you can also pass the mount path to run()
533+ if __name__ == " __main__" :
534+ search_mcp.run(transport = " sse" , mount_path = " /search" )
535+ ```
536+
386537For more information on mounting applications in Starlette, see the [ Starlette documentation] ( https://www.starlette.io/routing/#submounting-routes ) .
387538
388539## Examples
@@ -480,7 +631,7 @@ server = Server("example-server", lifespan=server_lifespan)
480631# Access lifespan context in handlers
481632@server.call_tool ()
482633async def query_db (name : str , arguments : dict ) -> list :
483- ctx = server.request_context
634+ ctx = server.get_context()
484635 db = ctx.lifespan_context[" db" ]
485636 return await db.query(arguments[" query" ])
486637```
@@ -555,9 +706,11 @@ if __name__ == "__main__":
555706 asyncio.run(run())
556707```
557708
709+ Caution: The ` mcp run ` and ` mcp dev ` tool doesn't support low-level server.
710+
558711### Writing MCP Clients
559712
560- The SDK provides a high-level client interface for connecting to MCP servers:
713+ 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 ) :
561714
562715``` python
563716from mcp import ClientSession, StdioServerParameters, types
@@ -621,6 +774,82 @@ if __name__ == "__main__":
621774 asyncio.run(run())
622775```
623776
777+ Clients can also connect using [ Streamable HTTP transport] ( https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http ) :
778+
779+ ``` python
780+ from mcp.client.streamable_http import streamablehttp_client
781+ from mcp import ClientSession
782+
783+
784+ async def main ():
785+ # Connect to a streamable HTTP server
786+ async with streamablehttp_client(" example/mcp" ) as (
787+ read_stream,
788+ write_stream,
789+ _,
790+ ):
791+ # Create a session using the client streams
792+ async with ClientSession(read_stream, write_stream) as session:
793+ # Initialize the connection
794+ await session.initialize()
795+ # Call a tool
796+ tool_result = await session.call_tool(" echo" , {" message" : " hello" })
797+ ```
798+
799+ ### OAuth Authentication for Clients
800+
801+ The SDK includes [ authorization support] ( https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization ) for connecting to protected MCP servers:
802+
803+ ``` python
804+ from mcp.client.auth import OAuthClientProvider, TokenStorage
805+ from mcp.client.session import ClientSession
806+ from mcp.client.streamable_http import streamablehttp_client
807+ from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
808+
809+
810+ class CustomTokenStorage (TokenStorage ):
811+ """ Simple in-memory token storage implementation."""
812+
813+ async def get_tokens (self ) -> OAuthToken | None :
814+ pass
815+
816+ async def set_tokens (self , tokens : OAuthToken) -> None :
817+ pass
818+
819+ async def get_client_info (self ) -> OAuthClientInformationFull | None :
820+ pass
821+
822+ async def set_client_info (self , client_info : OAuthClientInformationFull) -> None :
823+ pass
824+
825+
826+ async def main ():
827+ # Set up OAuth authentication
828+ oauth_auth = OAuthClientProvider(
829+ server_url = " https://api.example.com" ,
830+ client_metadata = OAuthClientMetadata(
831+ client_name = " My Client" ,
832+ redirect_uris = [" http://localhost:3000/callback" ],
833+ grant_types = [" authorization_code" , " refresh_token" ],
834+ response_types = [" code" ],
835+ ),
836+ storage = CustomTokenStorage(),
837+ redirect_handler = lambda url : print (f " Visit: { url} " ),
838+ callback_handler = lambda : (" auth_code" , None ),
839+ )
840+
841+ # Use with streamable HTTP client
842+ async with streamablehttp_client(
843+ " https://api.example.com/mcp" , auth = oauth_auth
844+ ) as (read, write, _):
845+ async with ClientSession(read, write) as session:
846+ await session.initialize()
847+ # Authenticated session ready
848+ ```
849+
850+ For a complete working example, see [ ` examples/clients/simple-auth-client/ ` ] ( examples/clients/simple-auth-client/ ) .
851+
852+
624853### MCP Primitives
625854
626855The MCP protocol defines three core primitives that servers can implement:
0 commit comments