@@ -66,7 +66,7 @@ The Model Context Protocol allows applications to provide context for LLMs in a
66
66
67
67
- Build MCP clients that can connect to any MCP server
68
68
- 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
70
70
- Handle all MCP protocol messages and lifecycle events
71
71
72
72
## Installation
@@ -309,6 +309,33 @@ async def long_task(files: list[str], ctx: Context) -> str:
309
309
return " Processing complete"
310
310
```
311
311
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
+
312
339
## Running Your Server
313
340
314
341
### Development Mode
@@ -360,8 +387,95 @@ python server.py
360
387
mcp run server.py
361
388
```
362
389
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
+
363
473
### Mounting to an Existing ASGI Server
364
474
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
+
365
479
You 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.
366
480
367
481
``` python
@@ -383,6 +497,43 @@ app = Starlette(
383
497
app.router.routes.append(Host(' mcp.acme.corp' , app = mcp.sse_app()))
384
498
```
385
499
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
+
386
537
For more information on mounting applications in Starlette, see the [ Starlette documentation] ( https://www.starlette.io/routing/#submounting-routes ) .
387
538
388
539
## Examples
@@ -480,7 +631,7 @@ server = Server("example-server", lifespan=server_lifespan)
480
631
# Access lifespan context in handlers
481
632
@server.call_tool ()
482
633
async def query_db (name : str , arguments : dict ) -> list :
483
- ctx = server.request_context
634
+ ctx = server.get_context()
484
635
db = ctx.lifespan_context[" db" ]
485
636
return await db.query(arguments[" query" ])
486
637
```
@@ -555,9 +706,11 @@ if __name__ == "__main__":
555
706
asyncio.run(run())
556
707
```
557
708
709
+ Caution: The ` mcp run ` and ` mcp dev ` tool doesn't support low-level server.
710
+
558
711
### Writing MCP Clients
559
712
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 ) :
561
714
562
715
``` python
563
716
from mcp import ClientSession, StdioServerParameters, types
@@ -621,6 +774,82 @@ if __name__ == "__main__":
621
774
asyncio.run(run())
622
775
```
623
776
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
+
624
853
### MCP Primitives
625
854
626
855
The MCP protocol defines three core primitives that servers can implement:
0 commit comments