@@ -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
@@ -160,7 +160,7 @@ from dataclasses import dataclass
160
160
161
161
from fake_database import Database # Replace with your actual DB type
162
162
163
- from mcp.server.fastmcp import Context, FastMCP
163
+ from mcp.server.fastmcp import FastMCP
164
164
165
165
# Create a named server
166
166
mcp = FastMCP(" My App" )
@@ -192,9 +192,10 @@ mcp = FastMCP("My App", lifespan=app_lifespan)
192
192
193
193
# Access type-safe lifespan context in tools
194
194
@mcp.tool ()
195
- def query_db (ctx : Context ) -> str :
195
+ def query_db () -> str :
196
196
""" 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" ]
198
199
return db.query()
199
200
```
200
201
@@ -314,27 +315,42 @@ async def long_task(files: list[str], ctx: Context) -> str:
314
315
Authentication can be used by servers that want to expose tools accessing protected resources.
315
316
316
317
` 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.
318
319
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" ],
333
347
),
348
+ required_scopes = [" myscope" ],
349
+ ),
334
350
)
335
351
```
336
352
337
- See [ OAuthServerProvider ] ( mcp/server/auth/provider.py ) for more details.
353
+ See [ OAuthAuthorizationServerProvider ] ( src/ mcp/server/auth/provider.py) for more details.
338
354
339
355
## Running Your Server
340
356
@@ -387,8 +403,92 @@ python server.py
387
403
mcp run server.py
388
404
```
389
405
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
+
390
486
### Mounting to an Existing ASGI Server
391
487
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
+
392
492
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.
393
493
394
494
``` python
@@ -410,6 +510,43 @@ app = Starlette(
410
510
app.router.routes.append(Host(' mcp.acme.corp' , app = mcp.sse_app()))
411
511
```
412
512
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
+
413
550
For more information on mounting applications in Starlette, see the [ Starlette documentation] ( https://www.starlette.io/routing/#submounting-routes ) .
414
551
415
552
## Examples
@@ -582,9 +719,11 @@ if __name__ == "__main__":
582
719
asyncio.run(run())
583
720
```
584
721
722
+ Caution: The ` mcp run ` and ` mcp dev ` tool doesn't support low-level server.
723
+
585
724
### Writing MCP Clients
586
725
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 ) :
588
727
589
728
``` python
590
729
from mcp import ClientSession, StdioServerParameters, types
@@ -648,6 +787,82 @@ if __name__ == "__main__":
648
787
asyncio.run(run())
649
788
```
650
789
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
+
651
866
### MCP Primitives
652
867
653
868
The MCP protocol defines three core primitives that servers can implement:
0 commit comments