36
36
- [ Sampling] ( #sampling )
37
37
- [ Logging and Notifications] ( #logging-and-notifications )
38
38
- [ Authentication] ( #authentication )
39
+ - [ FastMCP Properties] ( #fastmcp-properties )
40
+ - [ Session Properties] ( #session-properties-and-methods )
41
+ - [ Request Context Properties] ( #request-context-properties )
39
42
- [ Running Your Server] ( #running-your-server )
40
43
- [ Development Mode] ( #development-mode )
41
44
- [ Claude Desktop Integration] ( #claude-desktop-integration )
42
45
- [ Direct Execution] ( #direct-execution )
46
+ - [ Streamable HTTP Transport] ( #streamable-http-transport )
43
47
- [ Mounting to an Existing ASGI Server] ( #mounting-to-an-existing-asgi-server )
44
48
- [ Advanced Usage] ( #advanced-usage )
45
49
- [ Low-Level Server] ( #low-level-server )
46
50
- [ Writing MCP Clients] ( #writing-mcp-clients )
51
+ - [ Client Display Utilities] ( #client-display-utilities )
52
+ - [ OAuth Authentication for Clients] ( #oauth-authentication-for-clients )
47
53
- [ Parsing Tool Results] ( #parsing-tool-results )
48
54
- [ MCP Primitives] ( #mcp-primitives )
49
55
- [ Server Capabilities] ( #server-capabilities )
@@ -191,6 +197,7 @@ from contextlib import asynccontextmanager
191
197
from dataclasses import dataclass
192
198
193
199
from mcp.server.fastmcp import Context, FastMCP
200
+ from mcp.server.session import ServerSession
194
201
195
202
196
203
# Mock database class for example
@@ -236,7 +243,7 @@ mcp = FastMCP("My App", lifespan=app_lifespan)
236
243
237
244
# Access type-safe lifespan context in tools
238
245
@mcp.tool ()
239
- def query_db (ctx : Context) -> str :
246
+ def query_db (ctx : Context[ServerSession, AppContext] ) -> str :
240
247
""" Tool that uses initialized resources."""
241
248
db = ctx.request_context.lifespan_context.db
242
249
return db.query()
@@ -303,6 +310,36 @@ def get_weather(city: str, unit: str = "celsius") -> str:
303
310
_ Full example: [ examples/snippets/servers/basic_tool.py] ( https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_tool.py ) _
304
311
<!-- /snippet-source -->
305
312
313
+ Tools can optionally receive a Context object by including a parameter with the ` Context ` type annotation. This context is automatically injected by the FastMCP framework and provides access to MCP capabilities:
314
+
315
+ <!-- snippet-source examples/snippets/servers/tool_progress.py -->
316
+ ``` python
317
+ from mcp.server.fastmcp import Context, FastMCP
318
+ from mcp.server.session import ServerSession
319
+
320
+ mcp = FastMCP(name = " Progress Example" )
321
+
322
+
323
+ @mcp.tool ()
324
+ async def long_running_task (task_name : str , ctx : Context[ServerSession, None ], steps : int = 5 ) -> str :
325
+ """ Execute a task with progress updates."""
326
+ await ctx.info(f " Starting: { task_name} " )
327
+
328
+ for i in range (steps):
329
+ progress = (i + 1 ) / steps
330
+ await ctx.report_progress(
331
+ progress = progress,
332
+ total = 1.0 ,
333
+ message = f " Step { i + 1 } / { steps} " ,
334
+ )
335
+ await ctx.debug(f " Completed step { i + 1 } " )
336
+
337
+ return f " Task ' { task_name} ' completed "
338
+ ```
339
+
340
+ _ Full example: [ examples/snippets/servers/tool_progress.py] ( https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/tool_progress.py ) _
341
+ <!-- /snippet-source -->
342
+
306
343
#### Structured Output
307
344
308
345
Tools will return structured results by default, if their return type
@@ -410,7 +447,7 @@ def get_user(user_id: str) -> UserProfile:
410
447
411
448
# Classes WITHOUT type hints cannot be used for structured output
412
449
class UntypedConfig :
413
- def __init__ (self , setting1 , setting2 ):
450
+ def __init__ (self , setting1 , setting2 ): # type: ignore [ reportMissingParameterType ]
414
451
self .setting1 = setting1
415
452
self .setting2 = setting2
416
453
@@ -496,17 +533,53 @@ _Full example: [examples/snippets/servers/images.py](https://github.com/modelcon
496
533
497
534
### Context
498
535
499
- The Context object gives your tools and resources access to MCP capabilities:
536
+ The Context object is automatically injected into tool and resource functions that request it via type hints. It provides access to MCP capabilities like logging, progress reporting, resource reading, user interaction, and request metadata.
537
+
538
+ #### Getting Context in Functions
539
+
540
+ To use context in a tool or resource function, add a parameter with the ` Context ` type annotation:
541
+
542
+ ``` python
543
+ from mcp.server.fastmcp import Context, FastMCP
544
+
545
+ mcp = FastMCP(name = " Context Example" )
546
+
547
+
548
+ @mcp.tool ()
549
+ async def my_tool (x : int , ctx : Context) -> str :
550
+ """ Tool that uses context capabilities."""
551
+ # The context parameter can have any name as long as it's type-annotated
552
+ return await process_with_context(x, ctx)
553
+ ```
554
+
555
+ #### Context Properties and Methods
556
+
557
+ The Context object provides the following capabilities:
558
+
559
+ - ` ctx.request_id ` - Unique ID for the current request
560
+ - ` ctx.client_id ` - Client ID if available
561
+ - ` ctx.fastmcp ` - Access to the FastMCP server instance (see [ FastMCP Properties] ( #fastmcp-properties ) )
562
+ - ` ctx.session ` - Access to the underlying session for advanced communication (see [ Session Properties and Methods] ( #session-properties-and-methods ) )
563
+ - ` ctx.request_context ` - Access to request-specific data and lifespan resources (see [ Request Context Properties] ( #request-context-properties ) )
564
+ - ` await ctx.debug(message) ` - Send debug log message
565
+ - ` await ctx.info(message) ` - Send info log message
566
+ - ` await ctx.warning(message) ` - Send warning log message
567
+ - ` await ctx.error(message) ` - Send error log message
568
+ - ` await ctx.log(level, message, logger_name=None) ` - Send log with custom level
569
+ - ` await ctx.report_progress(progress, total=None, message=None) ` - Report operation progress
570
+ - ` await ctx.read_resource(uri) ` - Read a resource by URI
571
+ - ` await ctx.elicit(message, schema) ` - Request additional information from user with validation
500
572
501
573
<!-- snippet-source examples/snippets/servers/tool_progress.py -->
502
574
``` python
503
575
from mcp.server.fastmcp import Context, FastMCP
576
+ from mcp.server.session import ServerSession
504
577
505
578
mcp = FastMCP(name = " Progress Example" )
506
579
507
580
508
581
@mcp.tool ()
509
- async def long_running_task (task_name : str , ctx : Context, steps : int = 5 ) -> str :
582
+ async def long_running_task (task_name : str , ctx : Context[ServerSession, None ] , steps : int = 5 ) -> str :
510
583
""" Execute a task with progress updates."""
511
584
await ctx.info(f " Starting: { task_name} " )
512
585
@@ -624,6 +697,7 @@ Request additional information from users. This example shows an Elicitation dur
624
697
from pydantic import BaseModel, Field
625
698
626
699
from mcp.server.fastmcp import Context, FastMCP
700
+ from mcp.server.session import ServerSession
627
701
628
702
mcp = FastMCP(name = " Elicitation Example" )
629
703
@@ -639,12 +713,7 @@ class BookingPreferences(BaseModel):
639
713
640
714
641
715
@mcp.tool ()
642
- async def book_table (
643
- date : str ,
644
- time : str ,
645
- party_size : int ,
646
- ctx : Context,
647
- ) -> str :
716
+ async def book_table (date : str , time : str , party_size : int , ctx : Context[ServerSession, None ]) -> str :
648
717
""" Book a table with date availability check."""
649
718
# Check if date is available
650
719
if date == " 2024-12-25" :
@@ -680,13 +749,14 @@ Tools can interact with LLMs through sampling (generating text):
680
749
<!-- snippet-source examples/snippets/servers/sampling.py -->
681
750
``` python
682
751
from mcp.server.fastmcp import Context, FastMCP
752
+ from mcp.server.session import ServerSession
683
753
from mcp.types import SamplingMessage, TextContent
684
754
685
755
mcp = FastMCP(name = " Sampling Example" )
686
756
687
757
688
758
@mcp.tool ()
689
- async def generate_poem (topic : str , ctx : Context) -> str :
759
+ async def generate_poem (topic : str , ctx : Context[ServerSession, None ] ) -> str :
690
760
""" Generate a poem using LLM sampling."""
691
761
prompt = f " Write a short poem about { topic} "
692
762
@@ -715,12 +785,13 @@ Tools can send logs and notifications through the context:
715
785
<!-- snippet-source examples/snippets/servers/notifications.py -->
716
786
``` python
717
787
from mcp.server.fastmcp import Context, FastMCP
788
+ from mcp.server.session import ServerSession
718
789
719
790
mcp = FastMCP(name = " Notifications Example" )
720
791
721
792
722
793
@mcp.tool ()
723
- async def process_data (data : str , ctx : Context) -> str :
794
+ async def process_data (data : str , ctx : Context[ServerSession, None ] ) -> str :
724
795
""" Process data with logging."""
725
796
# Different log levels
726
797
await ctx.debug(f " Debug: Processing ' { data} ' " )
@@ -808,6 +879,99 @@ For a complete example with separate Authorization Server and Resource Server im
808
879
809
880
See [ TokenVerifier] ( src/mcp/server/auth/provider.py ) for more details on implementing token validation.
810
881
882
+ ### FastMCP Properties
883
+
884
+ The FastMCP server instance accessible via ` ctx.fastmcp ` provides access to server configuration and metadata:
885
+
886
+ - ` ctx.fastmcp.name ` - The server's name as defined during initialization
887
+ - ` ctx.fastmcp.instructions ` - Server instructions/description provided to clients
888
+ - ` ctx.fastmcp.settings ` - Complete server configuration object containing:
889
+ - ` debug ` - Debug mode flag
890
+ - ` log_level ` - Current logging level
891
+ - ` host ` and ` port ` - Server network configuration
892
+ - ` mount_path ` , ` sse_path ` , ` streamable_http_path ` - Transport paths
893
+ - ` stateless_http ` - Whether the server operates in stateless mode
894
+ - And other configuration options
895
+
896
+ ``` python
897
+ @mcp.tool ()
898
+ def server_info (ctx : Context) -> dict :
899
+ """ Get information about the current server."""
900
+ return {
901
+ " name" : ctx.fastmcp.name,
902
+ " instructions" : ctx.fastmcp.instructions,
903
+ " debug_mode" : ctx.fastmcp.settings.debug,
904
+ " log_level" : ctx.fastmcp.settings.log_level,
905
+ " host" : ctx.fastmcp.settings.host,
906
+ " port" : ctx.fastmcp.settings.port,
907
+ }
908
+ ```
909
+
910
+ ### Session Properties and Methods
911
+
912
+ The session object accessible via ` ctx.session ` provides advanced control over client communication:
913
+
914
+ - ` ctx.session.client_params ` - Client initialization parameters and declared capabilities
915
+ - ` await ctx.session.send_log_message(level, data, logger) ` - Send log messages with full control
916
+ - ` await ctx.session.create_message(messages, max_tokens) ` - Request LLM sampling/completion
917
+ - ` await ctx.session.send_progress_notification(token, progress, total, message) ` - Direct progress updates
918
+ - ` await ctx.session.send_resource_updated(uri) ` - Notify clients that a specific resource changed
919
+ - ` await ctx.session.send_resource_list_changed() ` - Notify clients that the resource list changed
920
+ - ` await ctx.session.send_tool_list_changed() ` - Notify clients that the tool list changed
921
+ - ` await ctx.session.send_prompt_list_changed() ` - Notify clients that the prompt list changed
922
+
923
+ ``` python
924
+ @mcp.tool ()
925
+ async def notify_data_update (resource_uri : str , ctx : Context) -> str :
926
+ """ Update data and notify clients of the change."""
927
+ # Perform data update logic here
928
+
929
+ # Notify clients that this specific resource changed
930
+ await ctx.session.send_resource_updated(AnyUrl(resource_uri))
931
+
932
+ # If this affects the overall resource list, notify about that too
933
+ await ctx.session.send_resource_list_changed()
934
+
935
+ return f " Updated { resource_uri} and notified clients "
936
+ ```
937
+
938
+ ### Request Context Properties
939
+
940
+ The request context accessible via ` ctx.request_context ` contains request-specific information and resources:
941
+
942
+ - ` ctx.request_context.lifespan_context ` - Access to resources initialized during server startup
943
+ - Database connections, configuration objects, shared services
944
+ - Type-safe access to resources defined in your server's lifespan function
945
+ - ` ctx.request_context.meta ` - Request metadata from the client including:
946
+ - ` progressToken ` - Token for progress notifications
947
+ - Other client-provided metadata
948
+ - ` ctx.request_context.request ` - The original MCP request object for advanced processing
949
+ - ` ctx.request_context.request_id ` - Unique identifier for this request
950
+
951
+ ``` python
952
+ # Example with typed lifespan context
953
+ @dataclass
954
+ class AppContext :
955
+ db: Database
956
+ config: AppConfig
957
+
958
+ @mcp.tool ()
959
+ def query_with_config (query : str , ctx : Context) -> str :
960
+ """ Execute a query using shared database and configuration."""
961
+ # Access typed lifespan context
962
+ app_ctx: AppContext = ctx.request_context.lifespan_context
963
+
964
+ # Use shared resources
965
+ connection = app_ctx.db
966
+ settings = app_ctx.config
967
+
968
+ # Execute query with configuration
969
+ result = connection.execute(query, timeout = settings.query_timeout)
970
+ return str (result)
971
+ ```
972
+
973
+ _ Full lifespan example: [ examples/snippets/servers/lifespan_example.py] ( https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/lifespan_example.py ) _
974
+
811
975
## Running Your Server
812
976
813
977
### Development Mode
@@ -1081,6 +1245,7 @@ Run from the repository root:
1081
1245
1082
1246
from collections.abc import AsyncIterator
1083
1247
from contextlib import asynccontextmanager
1248
+ from typing import Any
1084
1249
1085
1250
import mcp.server.stdio
1086
1251
import mcp.types as types
@@ -1109,7 +1274,7 @@ class Database:
1109
1274
1110
1275
1111
1276
@asynccontextmanager
1112
- async def server_lifespan (_server : Server) -> AsyncIterator[dict ]:
1277
+ async def server_lifespan (_server : Server) -> AsyncIterator[dict[ str , Any] ]:
1113
1278
""" Manage server startup and shutdown lifecycle."""
1114
1279
# Initialize resources on startup
1115
1280
db = await Database.connect()
@@ -1141,7 +1306,7 @@ async def handle_list_tools() -> list[types.Tool]:
1141
1306
1142
1307
1143
1308
@server.call_tool ()
1144
- async def query_db (name : str , arguments : dict ) -> list[types.TextContent]:
1309
+ async def query_db (name : str , arguments : dict[ str , Any] ) -> list[types.TextContent]:
1145
1310
""" Handle database query tool call."""
1146
1311
if name != " query_db" :
1147
1312
raise ValueError (f " Unknown tool: { name} " )
@@ -1395,7 +1560,7 @@ server_params = StdioServerParameters(
1395
1560
1396
1561
# Optional: create a sampling callback
1397
1562
async def handle_sampling_message (
1398
- context : RequestContext, params : types.CreateMessageRequestParams
1563
+ context : RequestContext[ClientSession, None ] , params : types.CreateMessageRequestParams
1399
1564
) -> types.CreateMessageResult:
1400
1565
print (f " Sampling request: { params.messages} " )
1401
1566
return types.CreateMessageResult(
0 commit comments