Jeremiah Lowin commited on
Commit
1a68f2c
·
1 Parent(s): fb12cad

Update documentation

Browse files
docs/docs.json CHANGED
@@ -69,8 +69,18 @@
69
  "pages": [
70
  "servers/tools",
71
  "servers/resources",
72
- "servers/prompts",
73
- "servers/context"
 
 
 
 
 
 
 
 
 
 
74
  ]
75
  },
76
  {
 
69
  "pages": [
70
  "servers/tools",
71
  "servers/resources",
72
+ "servers/prompts"
73
+ ]
74
+ },
75
+ {
76
+ "group": "Advanced Features",
77
+ "icon": "stars",
78
+ "pages": [
79
+ "servers/context",
80
+ "servers/elicitation",
81
+ "servers/logging",
82
+ "servers/progress",
83
+ "servers/sampling"
84
  ]
85
  },
86
  {
docs/servers/context.mdx CHANGED
@@ -6,7 +6,7 @@ icon: rectangle-code
6
  ---
7
  import { VersionBadge } from '/snippets/version-badge.mdx'
8
 
9
- When defining FastMCP [tools](/servers/tools), [resources](/servers/resources), resource templates, or [prompts](/servers/prompts), your functions might need to interact with the underlying MCP session or access server capabilities. FastMCP provides the `Context` object for this purpose.
10
 
11
  ## What Is Context?
12
 
@@ -37,10 +37,10 @@ To use the context object within any of your functions, simply add a parameter t
37
 
38
  #### Tools
39
 
40
- ```python
41
  from fastmcp import FastMCP, Context
42
 
43
- mcp = FastMCP(name="ContextDemo")
44
 
45
  @mcp.tool
46
  async def process_file(file_uri: str, ctx: Context) -> str:
@@ -53,7 +53,11 @@ async def process_file(file_uri: str, ctx: Context) -> str:
53
 
54
  <VersionBadge version="2.2.5" />
55
 
56
- ```python
 
 
 
 
57
  @mcp.resource("resource://user-data")
58
  async def get_user_data(ctx: Context) -> dict:
59
  """Fetch personalized user data based on the request context."""
@@ -71,7 +75,11 @@ async def get_user_profile(user_id: str, ctx: Context) -> dict:
71
 
72
  <VersionBadge version="2.2.5" />
73
 
74
- ```python
 
 
 
 
75
  @mcp.prompt
76
  async def data_analysis_request(dataset: str, ctx: Context) -> str:
77
  """Generate a request to analyze data with contextual information."""
@@ -89,10 +97,10 @@ While the simplest way to access context is through function parameter injection
89
  FastMCP provides dependency functions that allow you to retrieve the active context from anywhere within a server request's execution flow:
90
 
91
  ```python {2,9}
92
- from fastmcp import FastMCP, Context
93
  from fastmcp.server.dependencies import get_context
94
 
95
- mcp = FastMCP(name="DependencyDemo")
96
 
97
  # Utility function that needs context but doesn't receive it as a parameter
98
  async def process_data(data: list[float]) -> dict:
@@ -114,279 +122,71 @@ async def analyze_dataset(dataset_name: str) -> dict:
114
 
115
  ## Context Capabilities
116
 
117
- ### Logging
118
-
119
- Send log messages back to the MCP client. This is useful for debugging and providing visibility into function execution during a request.
120
-
121
- ```python
122
- @mcp.tool
123
- async def analyze_data(data: list[float], ctx: Context) -> dict:
124
- """Analyze numerical data with logging."""
125
- await ctx.debug("Starting analysis of numerical data")
126
- await ctx.info(f"Analyzing {len(data)} data points")
127
-
128
- try:
129
- result = sum(data) / len(data)
130
- await ctx.info(f"Analysis complete, average: {result}")
131
- return {"average": result, "count": len(data)}
132
- except ZeroDivisionError:
133
- await ctx.warning("Empty data list provided")
134
- return {"error": "Empty data list"}
135
- except Exception as e:
136
- await ctx.error(f"Analysis failed: {str(e)}")
137
- raise
138
- ```
139
-
140
- **Available Logging Methods:**
141
 
142
- - **`ctx.debug(message: str)`**: Low-level details useful for debugging
143
- - **`ctx.info(message: str)`**: General information about execution
144
- - **`ctx.warning(message: str)`**: Potential issues that didn't prevent execution
145
- - **`ctx.error(message: str)`**: Errors that occurred during execution
146
- - **`ctx.log(level: Literal["debug", "info", "warning", "error"], message: str, logger_name: str | None = None)`**: Generic log method supporting custom logger names
147
-
148
- ### Progress Reporting
149
 
150
- For long-running operations, notify the client about the progress. This allows clients to display progress indicators and provide a better user experience.
151
 
152
  ```python
153
- @mcp.tool
154
- async def process_items(items: list[str], ctx: Context) -> dict:
155
- """Process a list of items with progress updates."""
156
- total = len(items)
157
- results = []
158
-
159
- for i, item in enumerate(items):
160
- # Report progress as percentage
161
- await ctx.report_progress(progress=i, total=total)
162
-
163
- # Process the item (simulated with a sleep)
164
- await asyncio.sleep(0.1)
165
- results.append(item.upper())
166
-
167
- # Report 100% completion
168
- await ctx.report_progress(progress=total, total=total)
169
-
170
- return {"processed": len(results), "results": results}
171
  ```
172
 
173
- **Method signature:**
174
-
175
- - **`ctx.report_progress(progress: float, total: float | None = None)`**
176
- - `progress`: Current progress value (e.g., 24)
177
- - `total`: Optional total value (e.g., 100). If provided, clients may interpret this as a percentage.
178
-
179
- Progress reporting requires the client to have sent a `progressToken` in the initial request. If the client doesn't support progress reporting, these calls will have no effect.
180
 
181
- ### Resource Access
182
 
183
- Read data from resources registered with your FastMCP server. This allows functions to access files, configuration, or dynamically generated content.
184
 
185
  ```python
186
- @mcp.tool
187
- async def summarize_document(document_uri: str, ctx: Context) -> str:
188
- """Summarize a document by its resource URI."""
189
- # Read the document content
190
- content_list = await ctx.read_resource(document_uri)
191
-
192
- if not content_list:
193
- return "Document is empty"
194
-
195
- document_text = content_list[0].content
196
-
197
- # Example: Generate a simple summary (length-based)
198
- words = document_text.split()
199
- total_words = len(words)
200
-
201
- await ctx.info(f"Document has {total_words} words")
202
-
203
- # Return a simple summary
204
- if total_words > 100:
205
- summary = " ".join(words[:100]) + "..."
206
- return f"Summary ({total_words} words total): {summary}"
207
- else:
208
- return f"Full document ({total_words} words): {document_text}"
209
  ```
210
 
211
- **Method signature:**
212
-
213
- - **`ctx.read_resource(uri: str | AnyUrl) -> list[ReadResourceContents]`**
214
- - `uri`: The resource URI to read
215
- - Returns a list of resource content parts (usually containing just one item)
216
-
217
- The returned content is typically accessed via `content_list[0].content` and can be text or binary data depending on the resource.
218
 
219
  ### LLM Sampling
220
 
221
  <VersionBadge version="2.0.0" />
222
 
223
- Request the client's LLM to generate text based on provided messages. This is useful when your function needs to leverage the LLM's capabilities to process data or generate responses.
224
-
225
- ```python
226
- @mcp.tool
227
- async def analyze_sentiment(text: str, ctx: Context) -> dict:
228
- """Analyze the sentiment of a text using the client's LLM."""
229
- # Create a sampling prompt asking for sentiment analysis
230
- prompt = f"Analyze the sentiment of the following text as positive, negative, or neutral. Just output a single word - 'positive', 'negative', or 'neutral'. Text to analyze: {text}"
231
-
232
- # Send the sampling request to the client's LLM (provide a hint for the model you want to use)
233
- response = await ctx.sample(prompt, model_preferences="claude-3-sonnet")
234
-
235
- # Process the LLM's response
236
- sentiment = response.text.strip().lower()
237
-
238
- # Map to standard sentiment values
239
- if "positive" in sentiment:
240
- sentiment = "positive"
241
- elif "negative" in sentiment:
242
- sentiment = "negative"
243
- else:
244
- sentiment = "neutral"
245
-
246
- return {"text": text, "sentiment": sentiment}
247
- ```
248
-
249
- **Method signature:**
250
-
251
- - **`ctx.sample(messages: str | list[str | SamplingMessage], system_prompt: str | None = None, temperature: float | None = None, max_tokens: int | None = None, model_preferences: ModelPreferences | str | list[str] | None = None) -> TextContent | ImageContent`**
252
- - `messages`: A string or list of strings/message objects to send to the LLM
253
- - `system_prompt`: Optional system prompt to guide the LLM's behavior
254
- - `temperature`: Optional sampling temperature (controls randomness)
255
- - `max_tokens`: Optional maximum number of tokens to generate (defaults to 512)
256
- - `model_preferences`: Optional model selection preferences (e.g., a model hint string, list of hints, or a ModelPreferences object)
257
- - Returns the LLM's response as TextContent or ImageContent
258
-
259
- When providing a simple string, it's treated as a user message. For more complex scenarios, you can provide a list of messages with different roles.
260
 
261
  ```python
262
- @mcp.tool
263
- async def generate_example(concept: str, ctx: Context) -> str:
264
- """Generate a Python code example for a given concept."""
265
- # Using a system prompt and a user message
266
- response = await ctx.sample(
267
- messages=f"Write a simple Python code example demonstrating '{concept}'.",
268
- system_prompt="You are an expert Python programmer. Provide concise, working code examples without explanations.",
269
- temperature=0.7,
270
- max_tokens=300
271
- )
272
-
273
- code_example = response.text
274
- return f"```python\n{code_example}\n```"
275
  ```
276
 
277
- See [Client Sampling](/clients/client#llm-sampling) for more details on how clients handle these requests.
278
 
279
- ### User Elicitation
280
 
281
- <VersionBadge version="2.10.0" />
282
 
283
- Request structured input from users during tool execution. This allows tools to interactively ask for missing parameters, clarification, or additional context as needed.
284
 
285
  ```python
286
- from dataclasses import dataclass
287
-
288
- @dataclass
289
- class UserInfo:
290
- name: str
291
- age: int
292
-
293
- @mcp.tool
294
- async def collect_user_info(ctx: Context) -> str:
295
- """Collect user information through interactive prompts."""
296
- # Request structured user information
297
- result = await ctx.elicit(
298
- message="Please provide your information",
299
- response_type=UserInfo
300
- )
301
-
302
- if result.action == "accept":
303
- user = result.data
304
- return f"Hello {user.name}, you are {user.age} years old"
305
- elif result.action == "decline":
306
- return "Information not provided"
307
- else: # cancel
308
- return "Operation cancelled"
309
  ```
310
 
311
- **Method signature:**
312
 
313
- - **`ctx.elicit(message: str, response_type: type = str) -> ElicitationResult`**
314
- - `message`: The prompt message to display to the user
315
- - `response_type`: The Python type defining the expected response structure (dataclass, primitive type, etc.)
316
- - Returns an `ElicitationResult` with `action` ("accept", "decline", "cancel") and `data` (when accepted)
317
-
318
- **Supported Response Types:**
319
 
320
- - **Primitive types**: `str`, `int`, `float`, `bool`
321
- - **Literal types**: `Literal["option1", "option2"]` for constrained choices
322
- - **Enum types**: Python enums for predefined options
323
- - **Dataclass types**: Custom structured data with multiple fields
324
 
325
  ```python
326
- from typing import Literal
327
- from enum import Enum
328
-
329
- class Priority(Enum):
330
- LOW = "low"
331
- MEDIUM = "medium"
332
- HIGH = "high"
333
-
334
- @dataclass
335
- class TaskInfo:
336
- title: str
337
- priority: Priority
338
- urgent: bool
339
-
340
- @mcp.tool
341
- async def create_task(ctx: Context) -> str:
342
- """Create a task with user-provided details."""
343
- # Multiple elicitation calls for different information
344
-
345
- # Simple string input
346
- title_result = await ctx.elicit("What's the task title?", response_type=str)
347
- if title_result.action != "accept":
348
- return "Task creation cancelled"
349
-
350
- # Enum selection
351
- priority_result = await ctx.elicit("What's the priority?", response_type=Priority)
352
- if priority_result.action != "accept":
353
- return "Task creation cancelled"
354
-
355
- # Boolean choice
356
- urgent_result = await ctx.elicit("Is this urgent?", response_type=bool)
357
- if urgent_result.action != "accept":
358
- return "Task creation cancelled"
359
-
360
- return f"Created task: {title_result.data} (Priority: {priority_result.data.value}, Urgent: {urgent_result.data})"
361
  ```
362
 
363
- **Pattern Matching Support:**
364
-
365
- FastMCP provides typed result classes for pattern matching:
366
-
367
- ```python
368
- from fastmcp.server.elicitation import (
369
- AcceptedElicitation,
370
- DeclinedElicitation,
371
- CancelledElicitation
372
- )
373
-
374
- @mcp.tool
375
- async def pattern_example(ctx: Context) -> str:
376
- result = await ctx.elicit("Enter your name:", response_type=str)
377
-
378
- match result:
379
- case AcceptedElicitation(data=name):
380
- return f"Hello {name}!"
381
- case DeclinedElicitation():
382
- return "No name provided"
383
- case CancelledElicitation():
384
- return "Operation cancelled"
385
- ```
386
 
387
- Elicitation requires the client to provide an elicitation handler. If the client doesn't support elicitation, the request will fail. See [Client Elicitation](/clients/elicitation) for details on implementing client-side handlers.
388
 
389
- ### Component Changes
390
 
391
  <VersionBadge version="2.9.1" />
392
 
@@ -405,7 +205,19 @@ async def custom_tool_management(ctx: Context) -> str:
405
 
406
  These methods are primarily used internally by FastMCP's automatic notification system and most users will not need to invoke them directly.
407
 
408
- ### Request Information
 
 
 
 
 
 
 
 
 
 
 
 
409
 
410
  Access metadata about the current request and client.
411
 
@@ -425,60 +237,6 @@ async def request_info(ctx: Context) -> dict:
425
  - **`ctx.client_id -> str | None`**: Get the ID of the client making the request, if provided during initialization
426
  - **`ctx.session_id -> str | None`**: Get the MCP session ID for session-based data sharing (HTTP transports only)
427
 
428
- ### Advanced Access
429
-
430
-
431
- #### FastMCP Server and Sessions
432
-
433
- ```python
434
- @mcp.tool
435
- async def advanced_tool(ctx: Context) -> str:
436
- """Demonstrate advanced context access."""
437
- # Access the FastMCP server instance
438
- server_name = ctx.fastmcp.name
439
-
440
- # Low-level session access (rarely needed)
441
- session = ctx.session
442
- request_context = ctx.request_context
443
-
444
- return f"Server: {server_name}"
445
- ```
446
-
447
- #### HTTP Requests
448
-
449
- <VersionBadge version="2.2.7" />
450
-
451
- <Warning>
452
- The `ctx.get_http_request()` method is deprecated and will be removed in a future version.
453
- Please use the `get_http_request()` dependency function instead.
454
- See the [HTTP Requests pattern](/patterns/http-requests) for more details.
455
- </Warning>
456
-
457
- For web applications, you can access the underlying HTTP request:
458
-
459
- ```python
460
- @mcp.tool
461
- async def handle_web_request(ctx: Context) -> dict:
462
- """Access HTTP request information from the Starlette request."""
463
- request = ctx.get_http_request()
464
-
465
- # Access HTTP headers, query parameters, etc.
466
- user_agent = request.headers.get("user-agent", "Unknown")
467
- client_ip = request.client.host if request.client else "Unknown"
468
-
469
- return {
470
- "user_agent": user_agent,
471
- "client_ip": client_ip,
472
- "path": request.url.path,
473
- }
474
- ```
475
-
476
- #### Advanced Properties Reference
477
-
478
- - **`ctx.fastmcp -> FastMCP`**: Access the server instance the context belongs to
479
- - **`ctx.session`**: Access the raw `mcp.server.session.ServerSession` object
480
- - **`ctx.request_context`**: Access the raw `mcp.shared.context.RequestContext` object
481
-
482
  <Warning>
483
- Direct use of `session` or `request_context` requires understanding the low-level MCP Python SDK and may be less stable than using the methods provided directly on the `Context` object.
484
- </Warning>
 
6
  ---
7
  import { VersionBadge } from '/snippets/version-badge.mdx'
8
 
9
+ When defining FastMCP [tools](/servers/tools), [resources](/servers/resources), resource templates, or [prompts](/servers/prompts), your functions might need to interact with the underlying MCP session or access advanced server capabilities. FastMCP provides the `Context` object for this purpose.
10
 
11
  ## What Is Context?
12
 
 
37
 
38
  #### Tools
39
 
40
+ ```python {1, 6}
41
  from fastmcp import FastMCP, Context
42
 
43
+ mcp = FastMCP(name="Context Demo")
44
 
45
  @mcp.tool
46
  async def process_file(file_uri: str, ctx: Context) -> str:
 
53
 
54
  <VersionBadge version="2.2.5" />
55
 
56
+ ```python {1, 6, 12}
57
+ from fastmcp import FastMCP, Context
58
+
59
+ mcp = FastMCP(name="Context Demo")
60
+
61
  @mcp.resource("resource://user-data")
62
  async def get_user_data(ctx: Context) -> dict:
63
  """Fetch personalized user data based on the request context."""
 
75
 
76
  <VersionBadge version="2.2.5" />
77
 
78
+ ```python {1, 6}
79
+ from fastmcp import FastMCP, Context
80
+
81
+ mcp = FastMCP(name="Context Demo")
82
+
83
  @mcp.prompt
84
  async def data_analysis_request(dataset: str, ctx: Context) -> str:
85
  """Generate a request to analyze data with contextual information."""
 
97
  FastMCP provides dependency functions that allow you to retrieve the active context from anywhere within a server request's execution flow:
98
 
99
  ```python {2,9}
100
+ from fastmcp import FastMCP
101
  from fastmcp.server.dependencies import get_context
102
 
103
+ mcp = FastMCP(name="Dependency Demo")
104
 
105
  # Utility function that needs context but doesn't receive it as a parameter
106
  async def process_data(data: list[float]) -> dict:
 
122
 
123
  ## Context Capabilities
124
 
125
+ FastMCP provides several advanced capabilities through the context object. Each capability has dedicated documentation with comprehensive examples and best practices:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
+ ### Logging
 
 
 
 
 
 
128
 
129
+ Send debug, info, warning, and error messages back to the MCP client for visibility into function execution.
130
 
131
  ```python
132
+ await ctx.debug("Starting analysis")
133
+ await ctx.info(f"Processing {len(data)} items")
134
+ await ctx.warning("Deprecated parameter used")
135
+ await ctx.error("Processing failed")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  ```
137
 
138
+ See [Server Logging](/servers/logging) for complete documentation and examples.
139
+ ### Client Elicitation
 
 
 
 
 
140
 
141
+ <VersionBadge version="2.10.0" />
142
 
143
+ Request structured input from clients during tool execution, enabling interactive workflows and progressive disclosure. This is a new feature in the 6/18/2025 MCP spec.
144
 
145
  ```python
146
+ result = await ctx.elicit("Enter your name:", response_type=str)
147
+ if result.action == "accept":
148
+ name = result.data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  ```
150
 
151
+ See [User Elicitation](/servers/elicitation) for detailed examples and supported response types.
 
 
 
 
 
 
152
 
153
  ### LLM Sampling
154
 
155
  <VersionBadge version="2.0.0" />
156
 
157
+ Request the client's LLM to generate text based on provided messages, useful for leveraging AI capabilities within your tools.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
  ```python
160
+ response = await ctx.sample("Analyze this data", temperature=0.7)
 
 
 
 
 
 
 
 
 
 
 
 
161
  ```
162
 
163
+ See [LLM Sampling](/servers/sampling) for comprehensive usage and advanced techniques.
164
 
 
165
 
166
+ ### Progress Reporting
167
 
168
+ Update clients on the progress of long-running operations, enabling progress indicators and better user experience.
169
 
170
  ```python
171
+ await ctx.report_progress(progress=50, total=100) # 50% complete
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  ```
173
 
174
+ See [Progress Reporting](/servers/progress) for detailed patterns and examples.
175
 
176
+ ### Resource Access
 
 
 
 
 
177
 
178
+ Read data from resources registered with your FastMCP server, allowing access to files, configuration, or dynamic content.
 
 
 
179
 
180
  ```python
181
+ content_list = await ctx.read_resource("resource://config")
182
+ content = content_list[0].content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  ```
184
 
185
+ **Method signature:**
186
+ - **`ctx.read_resource(uri: str | AnyUrl) -> list[ReadResourceContents]`**: Returns a list of resource content parts
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
 
 
188
 
189
+ ### Change Notifications
190
 
191
  <VersionBadge version="2.9.1" />
192
 
 
205
 
206
  These methods are primarily used internally by FastMCP's automatic notification system and most users will not need to invoke them directly.
207
 
208
+ ### FastMCP Server
209
+
210
+ To access the underlying FastMCP server instance, you can use the `ctx.fastmcp` property:
211
+
212
+ ```python
213
+ @mcp.tool
214
+ async def my_tool(ctx: Context) -> None:
215
+ # Access the FastMCP server instance
216
+ server_name = ctx.fastmcp.name
217
+ ...
218
+ ```
219
+
220
+ ### MCP Request
221
 
222
  Access metadata about the current request and client.
223
 
 
237
  - **`ctx.client_id -> str | None`**: Get the ID of the client making the request, if provided during initialization
238
  - **`ctx.session_id -> str | None`**: Get the MCP session ID for session-based data sharing (HTTP transports only)
239
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  <Warning>
241
+ The MCP request is part of the low-level MCP SDK and intended for advanced use cases. Most users will not need to use it directly.
242
+ </Warning>
docs/servers/elicitation.mdx ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: User Elicitation
3
+ sidebarTitle: Elicitation
4
+ description: Request structured input from users during tool execution through the MCP context.
5
+ icon: user-check
6
+ ---
7
+
8
+ import { VersionBadge } from '/snippets/version-badge.mdx'
9
+
10
+ <VersionBadge version="2.10.0" />
11
+
12
+ User elicitation allows MCP servers to request structured input from users during tool execution. Instead of requiring all inputs upfront, tools can interactively ask for missing parameters, clarification, or additional context as needed.
13
+
14
+ <Tip>
15
+ Most of the examples in this document assume you have a FastMCP server instance named `mcp` and show how to use the `ctx.elicit` method to request user input from an `@mcp.tool`-decorated function.
16
+ </Tip>
17
+
18
+ ## What is Elicitation?
19
+
20
+ Elicitation enables tools to pause execution and request specific information from users. This is particularly useful for:
21
+
22
+ - **Missing parameters**: Ask for required information not provided initially
23
+ - **Clarification requests**: Get user confirmation or choices for ambiguous scenarios
24
+ - **Progressive disclosure**: Collect complex information step-by-step
25
+ - **Dynamic workflows**: Adapt tool behavior based on user responses
26
+
27
+ For example, a file management tool might ask "Which directory should I create?" or a data analysis tool might request "What date range should I analyze?"
28
+
29
+ ### Basic Usage
30
+
31
+ Use the `ctx.elicit()` method within any tool function to request user input:
32
+
33
+ ```python {14-17}
34
+ from fastmcp import FastMCP, Context
35
+ from dataclasses import dataclass
36
+
37
+ mcp = FastMCP("Elicitation Server")
38
+
39
+ @dataclass
40
+ class UserInfo:
41
+ name: str
42
+ age: int
43
+
44
+ @mcp.tool
45
+ async def collect_user_info(ctx: Context) -> str:
46
+ """Collect user information through interactive prompts."""
47
+ result = await ctx.elicit(
48
+ message="Please provide your information",
49
+ response_type=UserInfo
50
+ )
51
+
52
+ if result.action == "accept":
53
+ user = result.data
54
+ return f"Hello {user.name}, you are {user.age} years old"
55
+ elif result.action == "decline":
56
+ return "Information not provided"
57
+ else: # cancel
58
+ return "Operation cancelled"
59
+ ```
60
+
61
+ ## Method Signature
62
+
63
+ <Card icon="code" title="Context Elicitation Method">
64
+ <ResponseField name="ctx.elicit" type="async method">
65
+ <Expandable title="Parameters">
66
+ <ResponseField name="message" type="str">
67
+ The prompt message to display to the user
68
+ </ResponseField>
69
+
70
+ <ResponseField name="response_type" type="type" default="str">
71
+ The Python type defining the expected response structure (dataclass, primitive type, etc.) Note that elicitation responses are subject to a restricted subset of JSON Schema types. See [Supported Response Types](#supported-response-types) for more details.
72
+ </ResponseField>
73
+ </Expandable>
74
+
75
+ <Expandable title="Response">
76
+ <ResponseField name="ElicitationResult" type="object">
77
+ Result object containing the user's response
78
+
79
+ <Expandable title="properties">
80
+ <ResponseField name="action" type="Literal['accept', 'decline', 'cancel']">
81
+ How the user responded to the request
82
+ </ResponseField>
83
+
84
+ <ResponseField name="data" type="response_type | None">
85
+ The user's input data (only present when action is "accept")
86
+ </ResponseField>
87
+ </Expandable>
88
+ </ResponseField>
89
+ </Expandable>
90
+ </ResponseField>
91
+ </Card>
92
+
93
+ ## Elicitation Actions
94
+
95
+ The elicitation result contains an `action` field indicating how the user responded:
96
+
97
+ - **`accept`**: User provided valid input - data is available in the `data` field
98
+ - **`decline`**: User chose not to provide the requested information and the data field is `None`
99
+ - **`cancel`**: User cancelled the entire operation and the data field is `None`
100
+
101
+ ```python {5, 7}
102
+ @mcp.tool
103
+ async def my_tool(ctx: Context) -> str:
104
+ result = await ctx.elicit("Choose an action")
105
+
106
+ if result.action == "accept":
107
+ return "Accepted!"
108
+ elif result.action == "decline":
109
+ return "Declined!"
110
+ else:
111
+ return "Cancelled!"
112
+ ```
113
+
114
+ FastMCP also provides typed result classes for pattern matching on the `action` field:
115
+
116
+ ```python {1-5, 12, 14, 16}
117
+ from fastmcp.server.elicitation import (
118
+ AcceptedElicitation,
119
+ DeclinedElicitation,
120
+ CancelledElicitation,
121
+ )
122
+
123
+ @mcp.tool
124
+ async def pattern_example(ctx: Context) -> str:
125
+ result = await ctx.elicit("Enter your name:", response_type=str)
126
+
127
+ match result:
128
+ case AcceptedElicitation(data=name):
129
+ return f"Hello {name}!"
130
+ case DeclinedElicitation():
131
+ return "No name provided"
132
+ case CancelledElicitation():
133
+ return "Operation cancelled"
134
+ ```
135
+
136
+ ## Response Types
137
+
138
+ The server must send a schema to the client indicating the type of data it expects in response to the elicitation request. If the request is `accept`-ed, the client must send a response that matches the schema.
139
+
140
+ The MCP spec only supports a limited subset of JSON Schema types for elicitation responses. Specifically, it only supports JSON **objects** with **primitive** properties including `string`, `number` (or `integer`), `boolean` and `enum` fields.
141
+
142
+ FastMCP makes it easy to request a broader range of types, including scalars (e.g. `str`), by automatically wrapping them in MCP-compatible object schemas.
143
+
144
+
145
+ ### Scalar Types
146
+
147
+ You can request simple scalar data types for basic input, such as a string, integer, or boolean.
148
+
149
+ When you request a scalar type, FastMCP automatically wraps it in an object schema for MCP spec compatibility. Clients will see a corresponding schema requesting a single "value" field of the requested type. Once clients respond, the provided object is "unwrapped" and the scalar value is returned to your tool function as the `data` field of the `ElicitationResult` object.
150
+
151
+ As a developer, this means you do not have to worry about creating or accessing a structured object when you only need a scalar value.
152
+
153
+ <CodeGroup>
154
+ ```python {4} title="Request a string"
155
+ @mcp.tool
156
+ async def get_user_name(ctx: Context) -> str:
157
+ """Get the user's name."""
158
+ result = await ctx.elicit("What's your name?", response_type=str)
159
+
160
+ if result.action == "accept":
161
+ return f"Hello, {result.data}!"
162
+ return "No name provided"
163
+ ```
164
+ ```python {4} title="Request an integer"
165
+ @mcp.tool
166
+ async def pick_a_number(ctx: Context) -> str:
167
+ """Pick a number."""
168
+ result = await ctx.elicit("Pick a number!", response_type=int)
169
+
170
+ if result.action == "accept":
171
+ return f"You picked {result.data}"
172
+ return "No number provided"
173
+ ```
174
+ ```python {4} title="Request a boolean"
175
+ @mcp.tool
176
+ async def pick_a_boolean(ctx: Context) -> str:
177
+ """Pick a boolean."""
178
+ result = await ctx.elicit("True or false?", response_type=bool)
179
+
180
+ if result.action == "accept":
181
+ return f"You picked {result.data}"
182
+ return "No boolean provided"
183
+ ```
184
+ </CodeGroup>
185
+
186
+ ### Constrained Options
187
+
188
+ Often you'll want to constrain the user's response to a specific set of values. You can do this by using a `Literal` type or a Python enum as the response type, or by passing a list of strings to the `response_type` parameter as a convenient shortcut.
189
+
190
+ <CodeGroup>
191
+ ```python {6} title="Using a list of strings"
192
+ @mcp.tool
193
+ async def set_priority(ctx: Context) -> str:
194
+ """Set task priority level."""
195
+ result = await ctx.elicit(
196
+ "What priority level?",
197
+ response_type=["low", "medium", "high"],
198
+ )
199
+
200
+ if result.action == "accept":
201
+ return f"Priority set to: {result.data}"
202
+ ```
203
+ ```python {1, 8} title="Using a Literal type"
204
+ from typing import Literal
205
+
206
+ @mcp.tool
207
+ async def set_priority(ctx: Context) -> str:
208
+ """Set task priority level."""
209
+ result = await ctx.elicit(
210
+ "What priority level?",
211
+ response_type=Literal["low", "medium", "high"]
212
+ )
213
+
214
+ if result.action == "accept":
215
+ return f"Priority set to: {result.data}"
216
+ return "No priority set"
217
+ ```
218
+ ```python {1, 11} title="Using a Python enum"
219
+ from enum import Enum
220
+
221
+ class Priority(Enum):
222
+ LOW = "low"
223
+ MEDIUM = "medium"
224
+ HIGH = "high"
225
+
226
+ @mcp.tool
227
+ async def set_priority(ctx: Context) -> str:
228
+ """Set task priority level."""
229
+ result = await ctx.elicit("What priority level?", response_type=Priority)
230
+
231
+ if result.action == "accept":
232
+ return f"Priority set to: {result.data.value}"
233
+ return "No priority set"
234
+ ```
235
+ </CodeGroup>
236
+
237
+
238
+ ### Structured Responses
239
+
240
+ You can request structured data with multiple fields by using a dataclass, typed dict, or Pydantic model as the response type. Note that the MCP spec only supports shallow objects with scalar (string, number, boolean) or enum properties.
241
+
242
+ ```python {1, 16, 20}
243
+ from dataclasses import dataclass
244
+ from typing import Literal
245
+
246
+ @dataclass
247
+ class TaskDetails:
248
+ title: str
249
+ description: str
250
+ priority: Literal["low", "medium", "high"]
251
+ due_date: str
252
+
253
+ @mcp.tool
254
+ async def create_task(ctx: Context) -> str:
255
+ """Create a new task with user-provided details."""
256
+ result = await ctx.elicit(
257
+ "Please provide task details",
258
+ response_type=TaskDetails
259
+ )
260
+
261
+ if result.action == "accept":
262
+ task = result.data
263
+ return f"Created task: {task.title} (Priority: {task.priority})"
264
+ return "Task creation cancelled"
265
+ ```
266
+
267
+ ## Multi-Turn Elicitation
268
+
269
+ Tools can make multiple elicitation calls to gather information progressively:
270
+
271
+ ```python {6, 11, 16-19}
272
+ @mcp.tool
273
+ async def plan_meeting(ctx: Context) -> str:
274
+ """Plan a meeting by gathering details step by step."""
275
+
276
+ # Get meeting title
277
+ title_result = await ctx.elicit("What's the meeting title?", response_type=str)
278
+ if title_result.action != "accept":
279
+ return "Meeting planning cancelled"
280
+
281
+ # Get duration
282
+ duration_result = await ctx.elicit("Duration in minutes?", response_type=int)
283
+ if duration_result.action != "accept":
284
+ return "Meeting planning cancelled"
285
+
286
+ # Get priority
287
+ priority_result = await ctx.elicit(
288
+ "Is this urgent?",
289
+ response_type=Literal["yes", "no"]
290
+ )
291
+ if priority_result.action != "accept":
292
+ return "Meeting planning cancelled"
293
+
294
+ urgent = priority_result.data == "yes"
295
+ return f"Meeting '{title_result.data}' planned for {duration_result.data} minutes (Urgent: {urgent})"
296
+ ```
297
+
298
+
299
+ ## Client Requirements
300
+
301
+ Elicitation requires the client to implement an elicitation handler. See [Client Elicitation](/clients/elicitation) for details on how clients can handle these requests.
302
+
303
+ If a client doesn't support elicitation, calls to `ctx.elicit()` will raise an error indicating that elicitation is not supported.
docs/servers/logging.mdx ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Server Logging
3
+ sidebarTitle: Logging
4
+ description: Send log messages back to MCP clients through the context.
5
+ icon: receipt
6
+ ---
7
+
8
+ import { VersionBadge } from '/snippets/version-badge.mdx'
9
+
10
+ <Tip>
11
+ This documentation covers **MCP client logging** - sending messages from your server to MCP clients. For standard server-side logging (e.g., writing to files, console), use `fastmcp.utilities.logging.get_logger()` or Python's built-in `logging` module.
12
+ </Tip>
13
+
14
+ Server logging allows MCP tools to send debug, info, warning, and error messages back to the client. This provides visibility into function execution and helps with debugging during development and operation.
15
+
16
+ ## Why Use Server Logging?
17
+
18
+ Server logging is essential for:
19
+
20
+ - **Debugging**: Send detailed execution information to help diagnose issues
21
+ - **Progress visibility**: Keep users informed about what the tool is doing
22
+ - **Error reporting**: Communicate problems and their context to clients
23
+ - **Audit trails**: Create records of tool execution for compliance or analysis
24
+
25
+ Unlike standard Python logging, MCP server logging sends messages directly to the client, making them visible in the client's interface or logs.
26
+
27
+ ### Basic Usage
28
+
29
+ Use the context logging methods within any tool function:
30
+
31
+ ```python {8-9, 13, 17, 21}
32
+ from fastmcp import FastMCP, Context
33
+
34
+ mcp = FastMCP("LoggingDemo")
35
+
36
+ @mcp.tool
37
+ async def analyze_data(data: list[float], ctx: Context) -> dict:
38
+ """Analyze numerical data with comprehensive logging."""
39
+ await ctx.debug("Starting analysis of numerical data")
40
+ await ctx.info(f"Analyzing {len(data)} data points")
41
+
42
+ try:
43
+ if not data:
44
+ await ctx.warning("Empty data list provided")
45
+ return {"error": "Empty data list"}
46
+
47
+ result = sum(data) / len(data)
48
+ await ctx.info(f"Analysis complete, average: {result}")
49
+ return {"average": result, "count": len(data)}
50
+
51
+ except Exception as e:
52
+ await ctx.error(f"Analysis failed: {str(e)}")
53
+ raise
54
+ ```
55
+
56
+ ## Logging Methods
57
+
58
+ <Card icon="code" title="Context Logging Methods">
59
+ <ResponseField name="ctx.debug" type="async method">
60
+ Send debug-level messages for detailed execution information
61
+
62
+ <Expandable title="parameters">
63
+ <ResponseField name="message" type="str">
64
+ The debug message to send to the client
65
+ </ResponseField>
66
+ </Expandable>
67
+ </ResponseField>
68
+
69
+ <ResponseField name="ctx.info" type="async method">
70
+ Send informational messages about normal execution
71
+
72
+ <Expandable title="parameters">
73
+ <ResponseField name="message" type="str">
74
+ The information message to send to the client
75
+ </ResponseField>
76
+ </Expandable>
77
+ </ResponseField>
78
+
79
+ <ResponseField name="ctx.warning" type="async method">
80
+ Send warning messages for potential issues that didn't prevent execution
81
+
82
+ <Expandable title="parameters">
83
+ <ResponseField name="message" type="str">
84
+ The warning message to send to the client
85
+ </ResponseField>
86
+ </Expandable>
87
+ </ResponseField>
88
+
89
+ <ResponseField name="ctx.error" type="async method">
90
+ Send error messages for problems that occurred during execution
91
+
92
+ <Expandable title="parameters">
93
+ <ResponseField name="message" type="str">
94
+ The error message to send to the client
95
+ </ResponseField>
96
+ </Expandable>
97
+ </ResponseField>
98
+
99
+ <ResponseField name="ctx.log" type="async method">
100
+ Generic logging method with custom level and logger name
101
+
102
+ <Expandable title="parameters">
103
+ <ResponseField name="level" type="Literal['debug', 'info', 'warning', 'error']">
104
+ The log level for the message
105
+ </ResponseField>
106
+
107
+ <ResponseField name="message" type="str">
108
+ The message to send to the client
109
+ </ResponseField>
110
+
111
+ <ResponseField name="logger_name" type="str | None" default="None">
112
+ Optional custom logger name for categorizing messages
113
+ </ResponseField>
114
+ </Expandable>
115
+ </ResponseField>
116
+ </Card>
117
+
118
+ ## Log Levels
119
+
120
+ ### Debug
121
+ Use for detailed information that's typically only useful when diagnosing problems:
122
+
123
+ ```python
124
+ @mcp.tool
125
+ async def process_file(file_path: str, ctx: Context) -> str:
126
+ """Process a file with detailed debug logging."""
127
+ await ctx.debug(f"Starting to process file: {file_path}")
128
+ await ctx.debug("Checking file permissions")
129
+
130
+ # File processing logic
131
+ await ctx.debug("File processing completed successfully")
132
+ return "File processed"
133
+ ```
134
+
135
+ ### Info
136
+ Use for general information about normal program execution:
137
+
138
+ ```python
139
+ @mcp.tool
140
+ async def backup_database(ctx: Context) -> str:
141
+ """Backup database with progress information."""
142
+ await ctx.info("Starting database backup")
143
+ await ctx.info("Connecting to database")
144
+ await ctx.info("Backup completed successfully")
145
+ return "Database backed up"
146
+ ```
147
+
148
+ ### Warning
149
+ Use for potentially harmful situations that don't prevent execution:
150
+
151
+ ```python
152
+ @mcp.tool
153
+ async def validate_config(config: dict, ctx: Context) -> dict:
154
+ """Validate configuration with warnings for deprecated options."""
155
+ if "old_api_key" in config:
156
+ await ctx.warning("Using deprecated 'old_api_key' field. Please use 'api_key' instead")
157
+
158
+ if config.get("timeout", 30) > 300:
159
+ await ctx.warning("Timeout value is very high (>5 minutes), this may cause issues")
160
+
161
+ return {"status": "valid", "warnings": "see logs"}
162
+ ```
163
+
164
+ ### Error
165
+ Use for error events that might still allow the application to continue:
166
+
167
+ ```python
168
+ @mcp.tool
169
+ async def batch_process(items: list[str], ctx: Context) -> dict:
170
+ """Process multiple items, logging errors for failed items."""
171
+ successful = 0
172
+ failed = 0
173
+
174
+ for item in items:
175
+ try:
176
+ # Process item
177
+ successful += 1
178
+ except Exception as e:
179
+ await ctx.error(f"Failed to process item '{item}': {str(e)}")
180
+ failed += 1
181
+
182
+ return {"successful": successful, "failed": failed}
183
+ ```
184
+
185
+
186
+ ## Client Handling
187
+
188
+ Log messages are sent to the client through the MCP protocol. How clients handle these messages depends on their implementation:
189
+
190
+ - **Development clients**: May display logs in real-time for debugging
191
+ - **Production clients**: May store logs for later analysis or display to users
192
+ - **Integration clients**: May forward logs to external logging systems
193
+
194
+ See [Client Logging](/clients/logging) for details on how clients can handle server log messages.
docs/servers/progress.mdx ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Progress Reporting
3
+ sidebarTitle: Progress
4
+ description: Update clients on the progress of long-running operations through the MCP context.
5
+ icon: chart-line
6
+ ---
7
+
8
+ import { VersionBadge } from '/snippets/version-badge.mdx'
9
+
10
+ Progress reporting allows MCP tools to notify clients about the progress of long-running operations. This enables clients to display progress indicators and provide better user experience during time-consuming tasks.
11
+
12
+ ## Why Use Progress Reporting?
13
+
14
+ Progress reporting is valuable for:
15
+
16
+ - **User experience**: Keep users informed about long-running operations
17
+ - **Progress indicators**: Enable clients to show progress bars or percentages
18
+ - **Timeout prevention**: Demonstrate that operations are actively progressing
19
+ - **Debugging**: Track execution progress for performance analysis
20
+
21
+ ### Basic Usage
22
+
23
+ Use `ctx.report_progress()` to send progress updates to the client:
24
+
25
+ ```python {14, 21}
26
+ from fastmcp import FastMCP, Context
27
+ import asyncio
28
+
29
+ mcp = FastMCP("ProgressDemo")
30
+
31
+ @mcp.tool
32
+ async def process_items(items: list[str], ctx: Context) -> dict:
33
+ """Process a list of items with progress updates."""
34
+ total = len(items)
35
+ results = []
36
+
37
+ for i, item in enumerate(items):
38
+ # Report progress as we process each item
39
+ await ctx.report_progress(progress=i, total=total)
40
+
41
+ # Simulate processing time
42
+ await asyncio.sleep(0.1)
43
+ results.append(item.upper())
44
+
45
+ # Report 100% completion
46
+ await ctx.report_progress(progress=total, total=total)
47
+
48
+ return {"processed": len(results), "results": results}
49
+ ```
50
+
51
+ ## Method Signature
52
+
53
+ <Card icon="code" title="Context Progress Method">
54
+ <ResponseField name="ctx.report_progress" type="async method">
55
+ Report progress to the client for long-running operations
56
+
57
+ <Expandable title="parameters">
58
+ <ResponseField name="progress" type="float">
59
+ Current progress value (e.g., 24, 0.75, 1500)
60
+ </ResponseField>
61
+
62
+ <ResponseField name="total" type="float | None" default="None">
63
+ Optional total value (e.g., 100, 1.0, 2000). When provided, clients may interpret this as enabling percentage calculation.
64
+ </ResponseField>
65
+ </Expandable>
66
+ </ResponseField>
67
+ </Card>
68
+
69
+ ## Progress Patterns
70
+
71
+ ### Percentage-Based Progress
72
+
73
+ Report progress as a percentage (0-100):
74
+
75
+ ```python {13-14}
76
+ @mcp.tool
77
+ async def download_file(url: str, ctx: Context) -> str:
78
+ """Download a file with percentage progress."""
79
+ total_size = 1000 # KB
80
+ downloaded = 0
81
+
82
+ while downloaded < total_size:
83
+ # Download chunk
84
+ chunk_size = min(50, total_size - downloaded)
85
+ downloaded += chunk_size
86
+
87
+ # Report percentage progress
88
+ percentage = (downloaded / total_size) * 100
89
+ await ctx.report_progress(progress=percentage, total=100)
90
+
91
+ await asyncio.sleep(0.1) # Simulate download time
92
+
93
+ return f"Downloaded file from {url}"
94
+ ```
95
+
96
+ ### Absolute Progress
97
+
98
+ Report progress with absolute values:
99
+
100
+ ```python {10}
101
+ @mcp.tool
102
+ async def backup_database(ctx: Context) -> str:
103
+ """Backup database tables with absolute progress."""
104
+ tables = ["users", "orders", "products", "inventory", "logs"]
105
+
106
+ for i, table in enumerate(tables):
107
+ await ctx.info(f"Backing up table: {table}")
108
+
109
+ # Report absolute progress
110
+ await ctx.report_progress(progress=i + 1, total=len(tables))
111
+
112
+ # Simulate backup time
113
+ await asyncio.sleep(0.5)
114
+
115
+ return "Database backup completed"
116
+ ```
117
+
118
+ ### Indeterminate Progress
119
+
120
+ Report progress without a known total for operations where the endpoint is unknown:
121
+
122
+ ```python {11}
123
+ @mcp.tool
124
+ async def scan_directory(directory: str, ctx: Context) -> dict:
125
+ """Scan directory with indeterminate progress."""
126
+ files_found = 0
127
+
128
+ # Simulate directory scanning
129
+ for i in range(10): # Unknown number of files
130
+ files_found += 1
131
+
132
+ # Report progress without total for indeterminate operations
133
+ await ctx.report_progress(progress=files_found)
134
+
135
+ await asyncio.sleep(0.2)
136
+
137
+ return {"files_found": files_found, "directory": directory}
138
+ ```
139
+
140
+ ### Multi-Stage Operations
141
+
142
+ Break complex operations into stages with progress for each:
143
+
144
+ ```python
145
+ @mcp.tool
146
+ async def data_migration(source: str, destination: str, ctx: Context) -> str:
147
+ """Migrate data with multi-stage progress reporting."""
148
+
149
+ # Stage 1: Validation (0-25%)
150
+ await ctx.info("Validating source data")
151
+ for i in range(5):
152
+ await ctx.report_progress(progress=i * 5, total=100)
153
+ await asyncio.sleep(0.1)
154
+
155
+ # Stage 2: Export (25-60%)
156
+ await ctx.info("Exporting data from source")
157
+ for i in range(7):
158
+ progress = 25 + (i * 5)
159
+ await ctx.report_progress(progress=progress, total=100)
160
+ await asyncio.sleep(0.1)
161
+
162
+ # Stage 3: Transform (60-80%)
163
+ await ctx.info("Transforming data format")
164
+ for i in range(4):
165
+ progress = 60 + (i * 5)
166
+ await ctx.report_progress(progress=progress, total=100)
167
+ await asyncio.sleep(0.1)
168
+
169
+ # Stage 4: Import (80-100%)
170
+ await ctx.info("Importing to destination")
171
+ for i in range(4):
172
+ progress = 80 + (i * 5)
173
+ await ctx.report_progress(progress=progress, total=100)
174
+ await asyncio.sleep(0.1)
175
+
176
+ # Final completion
177
+ await ctx.report_progress(progress=100, total=100)
178
+
179
+ return f"Migration from {source} to {destination} completed"
180
+ ```
181
+
182
+
183
+ ## Client Requirements
184
+
185
+ Progress reporting requires clients to support progress handling:
186
+
187
+ - Clients must send a `progressToken` in the initial request to receive progress updates
188
+ - If no progress token is provided, progress calls will have no effect (they won't error)
189
+ - See [Client Progress](/clients/progress) for details on implementing client-side progress handling
docs/servers/sampling.mdx ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: LLM Sampling
3
+ sidebarTitle: Sampling
4
+ description: Request the client's LLM to generate text based on provided messages through the MCP context.
5
+ icon: robot
6
+ ---
7
+
8
+ import { VersionBadge } from '/snippets/version-badge.mdx'
9
+
10
+ <VersionBadge version="2.0.0" />
11
+
12
+ LLM sampling allows MCP tools to request the client's LLM to generate text based on provided messages. This is useful when tools need to leverage the LLM's capabilities to process data, generate responses, or perform text-based analysis.
13
+
14
+ ## Why Use LLM Sampling?
15
+
16
+ LLM sampling enables tools to:
17
+
18
+ - **Leverage AI capabilities**: Use the client's LLM for text generation and analysis
19
+ - **Offload complex reasoning**: Let the LLM handle tasks requiring natural language understanding
20
+ - **Generate dynamic content**: Create responses, summaries, or transformations based on data
21
+ - **Maintain context**: Use the same LLM instance that the user is already interacting with
22
+
23
+ ### Basic Usage
24
+
25
+ Use `ctx.sample()` to request text generation from the client's LLM:
26
+
27
+ ```python {14}
28
+ from fastmcp import FastMCP, Context
29
+
30
+ mcp = FastMCP("SamplingDemo")
31
+
32
+ @mcp.tool
33
+ async def analyze_sentiment(text: str, ctx: Context) -> dict:
34
+ """Analyze the sentiment of text using the client's LLM."""
35
+ prompt = f"""Analyze the sentiment of the following text as positive, negative, or neutral.
36
+ Just output a single word - 'positive', 'negative', or 'neutral'.
37
+
38
+ Text to analyze: {text}"""
39
+
40
+ # Request LLM analysis
41
+ response = await ctx.sample(prompt)
42
+
43
+ # Process the LLM's response
44
+ sentiment = response.text.strip().lower()
45
+
46
+ # Map to standard sentiment values
47
+ if "positive" in sentiment:
48
+ sentiment = "positive"
49
+ elif "negative" in sentiment:
50
+ sentiment = "negative"
51
+ else:
52
+ sentiment = "neutral"
53
+
54
+ return {"text": text, "sentiment": sentiment}
55
+ ```
56
+
57
+ ## Method Signature
58
+
59
+ <Card icon="code" title="Context Sampling Method">
60
+ <ResponseField name="ctx.sample" type="async method">
61
+ Request text generation from the client's LLM
62
+
63
+ <Expandable title="parameters">
64
+ <ResponseField name="messages" type="str | list[str | SamplingMessage]">
65
+ A string or list of strings/message objects to send to the LLM
66
+ </ResponseField>
67
+
68
+ <ResponseField name="system_prompt" type="str | None" default="None">
69
+ Optional system prompt to guide the LLM's behavior
70
+ </ResponseField>
71
+
72
+ <ResponseField name="temperature" type="float | None" default="None">
73
+ Optional sampling temperature (controls randomness, typically 0.0-1.0)
74
+ </ResponseField>
75
+
76
+ <ResponseField name="max_tokens" type="int | None" default="512">
77
+ Optional maximum number of tokens to generate
78
+ </ResponseField>
79
+
80
+ <ResponseField name="model_preferences" type="ModelPreferences | str | list[str] | None" default="None">
81
+ Optional model selection preferences (e.g., model hint string, list of hints, or ModelPreferences object)
82
+ </ResponseField>
83
+ </Expandable>
84
+
85
+ <Expandable title="returns">
86
+ <ResponseField name="response" type="TextContent | ImageContent">
87
+ The LLM's response content (typically TextContent with a .text attribute)
88
+ </ResponseField>
89
+ </Expandable>
90
+ </ResponseField>
91
+ </Card>
92
+
93
+ ## Simple Text Generation
94
+
95
+ ### Basic Prompting
96
+
97
+ Generate text with simple string prompts:
98
+
99
+ ```python {6}
100
+ @mcp.tool
101
+ async def generate_summary(content: str, ctx: Context) -> str:
102
+ """Generate a summary of the provided content."""
103
+ prompt = f"Please provide a concise summary of the following content:\n\n{content}"
104
+
105
+ response = await ctx.sample(prompt)
106
+ return response.text
107
+ ```
108
+
109
+ ### System Prompt
110
+
111
+ Use system prompts to guide the LLM's behavior:
112
+
113
+ ```python {4-9}
114
+ @mcp.tool
115
+ async def generate_code_example(concept: str, ctx: Context) -> str:
116
+ """Generate a Python code example for a given concept."""
117
+ response = await ctx.sample(
118
+ messages=f"Write a simple Python code example demonstrating '{concept}'.",
119
+ system_prompt="You are an expert Python programmer. Provide concise, working code examples without explanations.",
120
+ temperature=0.7,
121
+ max_tokens=300
122
+ )
123
+
124
+ code_example = response.text
125
+ return f"```python\n{code_example}\n```"
126
+ ```
127
+
128
+
129
+ ### Model Preferences
130
+
131
+ Specify model preferences for different use cases:
132
+
133
+ ```python {4-8, 17-22}
134
+ @mcp.tool
135
+ async def creative_writing(topic: str, ctx: Context) -> str:
136
+ """Generate creative content using a specific model."""
137
+ response = await ctx.sample(
138
+ messages=f"Write a creative short story about {topic}",
139
+ model_preferences="claude-3-sonnet", # Prefer a specific model
140
+ temperature=0.9, # High creativity
141
+ max_tokens=1000
142
+ )
143
+
144
+ return response.text
145
+
146
+ @mcp.tool
147
+ async def technical_analysis(data: str, ctx: Context) -> str:
148
+ """Perform technical analysis with a reasoning-focused model."""
149
+ response = await ctx.sample(
150
+ messages=f"Analyze this technical data and provide insights: {data}",
151
+ model_preferences=["claude-3-opus", "gpt-4"], # Prefer reasoning models
152
+ temperature=0.2, # Low randomness for consistency
153
+ max_tokens=800
154
+ )
155
+
156
+ return response.text
157
+ ```
158
+
159
+ ### Complex Message Structures
160
+
161
+ Use structured messages for more complex interactions:
162
+
163
+ ```python {1, 6-10}
164
+ from fastmcp.client.sampling import SamplingMessage
165
+
166
+ @mcp.tool
167
+ async def multi_turn_analysis(user_query: str, context_data: str, ctx: Context) -> str:
168
+ """Perform analysis using multi-turn conversation structure."""
169
+ messages = [
170
+ SamplingMessage(role="user", content=f"I have this data: {context_data}"),
171
+ SamplingMessage(role="assistant", content="I can see your data. What would you like me to analyze?"),
172
+ SamplingMessage(role="user", content=user_query)
173
+ ]
174
+
175
+ response = await ctx.sample(
176
+ messages=messages,
177
+ system_prompt="You are a data analyst. Provide detailed insights based on the conversation context.",
178
+ temperature=0.3
179
+ )
180
+
181
+ return response.text
182
+ ```
183
+
184
+ ## Client Requirements
185
+
186
+ LLM sampling requires client support:
187
+
188
+ - Clients must implement sampling handlers to process requests
189
+ - If the client doesn't support sampling, calls to `ctx.sample()` will fail
190
+ - See [Client Sampling](/clients/sampling) for details on implementing client-side sampling handlers