From 2a3fa142f88c4d234265fc62a2e9048796ff0a23 Mon Sep 17 00:00:00 2001
From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
Date: Sat, 28 Jun 2025 15:05:13 -0400
Subject: [PATCH] Update documentation
---
docs/docs.json | 14 +-
docs/servers/context.mdx | 384 +++++++----------------------------
docs/servers/elicitation.mdx | 303 +++++++++++++++++++++++++++
docs/servers/logging.mdx | 194 ++++++++++++++++++
docs/servers/progress.mdx | 189 +++++++++++++++++
docs/servers/sampling.mdx | 190 +++++++++++++++++
6 files changed, 959 insertions(+), 315 deletions(-)
create mode 100644 docs/servers/elicitation.mdx
create mode 100644 docs/servers/logging.mdx
create mode 100644 docs/servers/progress.mdx
create mode 100644 docs/servers/sampling.mdx
diff --git a/docs/docs.json b/docs/docs.json
index 852c86ea9..52fdf824f 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -69,8 +69,18 @@
"pages": [
"servers/tools",
"servers/resources",
- "servers/prompts",
- "servers/context"
+ "servers/prompts"
+ ]
+ },
+ {
+ "group": "Advanced Features",
+ "icon": "stars",
+ "pages": [
+ "servers/context",
+ "servers/elicitation",
+ "servers/logging",
+ "servers/progress",
+ "servers/sampling"
]
},
{
diff --git a/docs/servers/context.mdx b/docs/servers/context.mdx
index a29f84ddd..a44165b75 100644
--- a/docs/servers/context.mdx
+++ b/docs/servers/context.mdx
@@ -6,7 +6,7 @@ icon: rectangle-code
---
import { VersionBadge } from '/snippets/version-badge.mdx'
-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.
+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.
## What Is Context?
@@ -37,10 +37,10 @@ To use the context object within any of your functions, simply add a parameter t
#### Tools
-```python
+```python {1, 6}
from fastmcp import FastMCP, Context
-mcp = FastMCP(name="ContextDemo")
+mcp = FastMCP(name="Context Demo")
@mcp.tool
async def process_file(file_uri: str, ctx: Context) -> str:
@@ -53,7 +53,11 @@ async def process_file(file_uri: str, ctx: Context) -> str:
-```python
+```python {1, 6, 12}
+from fastmcp import FastMCP, Context
+
+mcp = FastMCP(name="Context Demo")
+
@mcp.resource("resource://user-data")
async def get_user_data(ctx: Context) -> dict:
"""Fetch personalized user data based on the request context."""
@@ -71,7 +75,11 @@ async def get_user_profile(user_id: str, ctx: Context) -> dict:
-```python
+```python {1, 6}
+from fastmcp import FastMCP, Context
+
+mcp = FastMCP(name="Context Demo")
+
@mcp.prompt
async def data_analysis_request(dataset: str, ctx: Context) -> str:
"""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
FastMCP provides dependency functions that allow you to retrieve the active context from anywhere within a server request's execution flow:
```python {2,9}
-from fastmcp import FastMCP, Context
+from fastmcp import FastMCP
from fastmcp.server.dependencies import get_context
-mcp = FastMCP(name="DependencyDemo")
+mcp = FastMCP(name="Dependency Demo")
# Utility function that needs context but doesn't receive it as a parameter
async def process_data(data: list[float]) -> dict:
@@ -114,279 +122,71 @@ async def analyze_dataset(dataset_name: str) -> dict:
## Context Capabilities
+FastMCP provides several advanced capabilities through the context object. Each capability has dedicated documentation with comprehensive examples and best practices:
+
### Logging
-Send log messages back to the MCP client. This is useful for debugging and providing visibility into function execution during a request.
+Send debug, info, warning, and error messages back to the MCP client for visibility into function execution.
```python
-@mcp.tool
-async def analyze_data(data: list[float], ctx: Context) -> dict:
- """Analyze numerical data with logging."""
- await ctx.debug("Starting analysis of numerical data")
- await ctx.info(f"Analyzing {len(data)} data points")
-
- try:
- result = sum(data) / len(data)
- await ctx.info(f"Analysis complete, average: {result}")
- return {"average": result, "count": len(data)}
- except ZeroDivisionError:
- await ctx.warning("Empty data list provided")
- return {"error": "Empty data list"}
- except Exception as e:
- await ctx.error(f"Analysis failed: {str(e)}")
- raise
+await ctx.debug("Starting analysis")
+await ctx.info(f"Processing {len(data)} items")
+await ctx.warning("Deprecated parameter used")
+await ctx.error("Processing failed")
```
-**Available Logging Methods:**
+See [Server Logging](/servers/logging) for complete documentation and examples.
+### Client Elicitation
-- **`ctx.debug(message: str)`**: Low-level details useful for debugging
-- **`ctx.info(message: str)`**: General information about execution
-- **`ctx.warning(message: str)`**: Potential issues that didn't prevent execution
-- **`ctx.error(message: str)`**: Errors that occurred during execution
-- **`ctx.log(level: Literal["debug", "info", "warning", "error"], message: str, logger_name: str | None = None)`**: Generic log method supporting custom logger names
+
-### Progress Reporting
-
-For long-running operations, notify the client about the progress. This allows clients to display progress indicators and provide a better user experience.
+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.
```python
-@mcp.tool
-async def process_items(items: list[str], ctx: Context) -> dict:
- """Process a list of items with progress updates."""
- total = len(items)
- results = []
-
- for i, item in enumerate(items):
- # Report progress as percentage
- await ctx.report_progress(progress=i, total=total)
-
- # Process the item (simulated with a sleep)
- await asyncio.sleep(0.1)
- results.append(item.upper())
-
- # Report 100% completion
- await ctx.report_progress(progress=total, total=total)
-
- return {"processed": len(results), "results": results}
+result = await ctx.elicit("Enter your name:", response_type=str)
+if result.action == "accept":
+ name = result.data
```
-**Method signature:**
-
-- **`ctx.report_progress(progress: float, total: float | None = None)`**
- - `progress`: Current progress value (e.g., 24)
- - `total`: Optional total value (e.g., 100). If provided, clients may interpret this as a percentage.
-
-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.
-
-### Resource Access
-
-Read data from resources registered with your FastMCP server. This allows functions to access files, configuration, or dynamically generated content.
-
-```python
-@mcp.tool
-async def summarize_document(document_uri: str, ctx: Context) -> str:
- """Summarize a document by its resource URI."""
- # Read the document content
- content_list = await ctx.read_resource(document_uri)
-
- if not content_list:
- return "Document is empty"
-
- document_text = content_list[0].content
-
- # Example: Generate a simple summary (length-based)
- words = document_text.split()
- total_words = len(words)
-
- await ctx.info(f"Document has {total_words} words")
-
- # Return a simple summary
- if total_words > 100:
- summary = " ".join(words[:100]) + "..."
- return f"Summary ({total_words} words total): {summary}"
- else:
- return f"Full document ({total_words} words): {document_text}"
-```
-
-**Method signature:**
-
-- **`ctx.read_resource(uri: str | AnyUrl) -> list[ReadResourceContents]`**
- - `uri`: The resource URI to read
- - Returns a list of resource content parts (usually containing just one item)
-
-The returned content is typically accessed via `content_list[0].content` and can be text or binary data depending on the resource.
+See [User Elicitation](/servers/elicitation) for detailed examples and supported response types.
### LLM Sampling
-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.
+Request the client's LLM to generate text based on provided messages, useful for leveraging AI capabilities within your tools.
```python
-@mcp.tool
-async def analyze_sentiment(text: str, ctx: Context) -> dict:
- """Analyze the sentiment of a text using the client's LLM."""
- # Create a sampling prompt asking for sentiment analysis
- 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}"
-
- # Send the sampling request to the client's LLM (provide a hint for the model you want to use)
- response = await ctx.sample(prompt, model_preferences="claude-3-sonnet")
-
- # Process the LLM's response
- sentiment = response.text.strip().lower()
-
- # Map to standard sentiment values
- if "positive" in sentiment:
- sentiment = "positive"
- elif "negative" in sentiment:
- sentiment = "negative"
- else:
- sentiment = "neutral"
-
- return {"text": text, "sentiment": sentiment}
+response = await ctx.sample("Analyze this data", temperature=0.7)
+```
+
+See [LLM Sampling](/servers/sampling) for comprehensive usage and advanced techniques.
+
+
+### Progress Reporting
+
+Update clients on the progress of long-running operations, enabling progress indicators and better user experience.
+
+```python
+await ctx.report_progress(progress=50, total=100) # 50% complete
+```
+
+See [Progress Reporting](/servers/progress) for detailed patterns and examples.
+
+### Resource Access
+
+Read data from resources registered with your FastMCP server, allowing access to files, configuration, or dynamic content.
+
+```python
+content_list = await ctx.read_resource("resource://config")
+content = content_list[0].content
```
**Method signature:**
+- **`ctx.read_resource(uri: str | AnyUrl) -> list[ReadResourceContents]`**: Returns a list of resource content parts
-- **`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`**
- - `messages`: A string or list of strings/message objects to send to the LLM
- - `system_prompt`: Optional system prompt to guide the LLM's behavior
- - `temperature`: Optional sampling temperature (controls randomness)
- - `max_tokens`: Optional maximum number of tokens to generate (defaults to 512)
- - `model_preferences`: Optional model selection preferences (e.g., a model hint string, list of hints, or a ModelPreferences object)
- - Returns the LLM's response as TextContent or ImageContent
-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.
-
-```python
-@mcp.tool
-async def generate_example(concept: str, ctx: Context) -> str:
- """Generate a Python code example for a given concept."""
- # Using a system prompt and a user message
- response = await ctx.sample(
- messages=f"Write a simple Python code example demonstrating '{concept}'.",
- system_prompt="You are an expert Python programmer. Provide concise, working code examples without explanations.",
- temperature=0.7,
- max_tokens=300
- )
-
- code_example = response.text
- return f"```python\n{code_example}\n```"
-```
-
-See [Client Sampling](/clients/client#llm-sampling) for more details on how clients handle these requests.
-
-### User Elicitation
-
-
-
-Request structured input from users during tool execution. This allows tools to interactively ask for missing parameters, clarification, or additional context as needed.
-
-```python
-from dataclasses import dataclass
-
-@dataclass
-class UserInfo:
- name: str
- age: int
-
-@mcp.tool
-async def collect_user_info(ctx: Context) -> str:
- """Collect user information through interactive prompts."""
- # Request structured user information
- result = await ctx.elicit(
- message="Please provide your information",
- response_type=UserInfo
- )
-
- if result.action == "accept":
- user = result.data
- return f"Hello {user.name}, you are {user.age} years old"
- elif result.action == "decline":
- return "Information not provided"
- else: # cancel
- return "Operation cancelled"
-```
-
-**Method signature:**
-
-- **`ctx.elicit(message: str, response_type: type = str) -> ElicitationResult`**
- - `message`: The prompt message to display to the user
- - `response_type`: The Python type defining the expected response structure (dataclass, primitive type, etc.)
- - Returns an `ElicitationResult` with `action` ("accept", "decline", "cancel") and `data` (when accepted)
-
-**Supported Response Types:**
-
-- **Primitive types**: `str`, `int`, `float`, `bool`
-- **Literal types**: `Literal["option1", "option2"]` for constrained choices
-- **Enum types**: Python enums for predefined options
-- **Dataclass types**: Custom structured data with multiple fields
-
-```python
-from typing import Literal
-from enum import Enum
-
-class Priority(Enum):
- LOW = "low"
- MEDIUM = "medium"
- HIGH = "high"
-
-@dataclass
-class TaskInfo:
- title: str
- priority: Priority
- urgent: bool
-
-@mcp.tool
-async def create_task(ctx: Context) -> str:
- """Create a task with user-provided details."""
- # Multiple elicitation calls for different information
-
- # Simple string input
- title_result = await ctx.elicit("What's the task title?", response_type=str)
- if title_result.action != "accept":
- return "Task creation cancelled"
-
- # Enum selection
- priority_result = await ctx.elicit("What's the priority?", response_type=Priority)
- if priority_result.action != "accept":
- return "Task creation cancelled"
-
- # Boolean choice
- urgent_result = await ctx.elicit("Is this urgent?", response_type=bool)
- if urgent_result.action != "accept":
- return "Task creation cancelled"
-
- return f"Created task: {title_result.data} (Priority: {priority_result.data.value}, Urgent: {urgent_result.data})"
-```
-
-**Pattern Matching Support:**
-
-FastMCP provides typed result classes for pattern matching:
-
-```python
-from fastmcp.server.elicitation import (
- AcceptedElicitation,
- DeclinedElicitation,
- CancelledElicitation
-)
-
-@mcp.tool
-async def pattern_example(ctx: Context) -> str:
- result = await ctx.elicit("Enter your name:", response_type=str)
-
- match result:
- case AcceptedElicitation(data=name):
- return f"Hello {name}!"
- case DeclinedElicitation():
- return "No name provided"
- case CancelledElicitation():
- return "Operation cancelled"
-```
-
-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.
-
-### Component Changes
+### Change Notifications
@@ -405,7 +205,19 @@ async def custom_tool_management(ctx: Context) -> str:
These methods are primarily used internally by FastMCP's automatic notification system and most users will not need to invoke them directly.
-### Request Information
+### FastMCP Server
+
+To access the underlying FastMCP server instance, you can use the `ctx.fastmcp` property:
+
+```python
+@mcp.tool
+async def my_tool(ctx: Context) -> None:
+ # Access the FastMCP server instance
+ server_name = ctx.fastmcp.name
+ ...
+```
+
+### MCP Request
Access metadata about the current request and client.
@@ -425,60 +237,6 @@ async def request_info(ctx: Context) -> dict:
- **`ctx.client_id -> str | None`**: Get the ID of the client making the request, if provided during initialization
- **`ctx.session_id -> str | None`**: Get the MCP session ID for session-based data sharing (HTTP transports only)
-### Advanced Access
-
-
-#### FastMCP Server and Sessions
-
-```python
-@mcp.tool
-async def advanced_tool(ctx: Context) -> str:
- """Demonstrate advanced context access."""
- # Access the FastMCP server instance
- server_name = ctx.fastmcp.name
-
- # Low-level session access (rarely needed)
- session = ctx.session
- request_context = ctx.request_context
-
- return f"Server: {server_name}"
-```
-
-#### HTTP Requests
-
-
-
-The `ctx.get_http_request()` method is deprecated and will be removed in a future version.
-Please use the `get_http_request()` dependency function instead.
-See the [HTTP Requests pattern](/patterns/http-requests) for more details.
-
-
-For web applications, you can access the underlying HTTP request:
-
-```python
-@mcp.tool
-async def handle_web_request(ctx: Context) -> dict:
- """Access HTTP request information from the Starlette request."""
- request = ctx.get_http_request()
-
- # Access HTTP headers, query parameters, etc.
- user_agent = request.headers.get("user-agent", "Unknown")
- client_ip = request.client.host if request.client else "Unknown"
-
- return {
- "user_agent": user_agent,
- "client_ip": client_ip,
- "path": request.url.path,
- }
-```
-
-#### Advanced Properties Reference
-
-- **`ctx.fastmcp -> FastMCP`**: Access the server instance the context belongs to
-- **`ctx.session`**: Access the raw `mcp.server.session.ServerSession` object
-- **`ctx.request_context`**: Access the raw `mcp.shared.context.RequestContext` object
-
-
-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.
-
+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.
+
\ No newline at end of file
diff --git a/docs/servers/elicitation.mdx b/docs/servers/elicitation.mdx
new file mode 100644
index 000000000..098ae9c0a
--- /dev/null
+++ b/docs/servers/elicitation.mdx
@@ -0,0 +1,303 @@
+---
+title: User Elicitation
+sidebarTitle: Elicitation
+description: Request structured input from users during tool execution through the MCP context.
+icon: user-check
+---
+
+import { VersionBadge } from '/snippets/version-badge.mdx'
+
+
+
+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.
+
+
+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.
+
+
+## What is Elicitation?
+
+Elicitation enables tools to pause execution and request specific information from users. This is particularly useful for:
+
+- **Missing parameters**: Ask for required information not provided initially
+- **Clarification requests**: Get user confirmation or choices for ambiguous scenarios
+- **Progressive disclosure**: Collect complex information step-by-step
+- **Dynamic workflows**: Adapt tool behavior based on user responses
+
+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?"
+
+### Basic Usage
+
+Use the `ctx.elicit()` method within any tool function to request user input:
+
+```python {14-17}
+from fastmcp import FastMCP, Context
+from dataclasses import dataclass
+
+mcp = FastMCP("Elicitation Server")
+
+@dataclass
+class UserInfo:
+ name: str
+ age: int
+
+@mcp.tool
+async def collect_user_info(ctx: Context) -> str:
+ """Collect user information through interactive prompts."""
+ result = await ctx.elicit(
+ message="Please provide your information",
+ response_type=UserInfo
+ )
+
+ if result.action == "accept":
+ user = result.data
+ return f"Hello {user.name}, you are {user.age} years old"
+ elif result.action == "decline":
+ return "Information not provided"
+ else: # cancel
+ return "Operation cancelled"
+```
+
+## Method Signature
+
+
+
+
+
+ The prompt message to display to the user
+
+
+
+ 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.
+
+
+
+
+
+ Result object containing the user's response
+
+
+
+ How the user responded to the request
+
+
+
+ The user's input data (only present when action is "accept")
+
+
+
+
+
+
+
+## Elicitation Actions
+
+The elicitation result contains an `action` field indicating how the user responded:
+
+- **`accept`**: User provided valid input - data is available in the `data` field
+- **`decline`**: User chose not to provide the requested information and the data field is `None`
+- **`cancel`**: User cancelled the entire operation and the data field is `None`
+
+```python {5, 7}
+@mcp.tool
+async def my_tool(ctx: Context) -> str:
+ result = await ctx.elicit("Choose an action")
+
+ if result.action == "accept":
+ return "Accepted!"
+ elif result.action == "decline":
+ return "Declined!"
+ else:
+ return "Cancelled!"
+```
+
+FastMCP also provides typed result classes for pattern matching on the `action` field:
+
+```python {1-5, 12, 14, 16}
+from fastmcp.server.elicitation import (
+ AcceptedElicitation,
+ DeclinedElicitation,
+ CancelledElicitation,
+)
+
+@mcp.tool
+async def pattern_example(ctx: Context) -> str:
+ result = await ctx.elicit("Enter your name:", response_type=str)
+
+ match result:
+ case AcceptedElicitation(data=name):
+ return f"Hello {name}!"
+ case DeclinedElicitation():
+ return "No name provided"
+ case CancelledElicitation():
+ return "Operation cancelled"
+```
+
+## Response Types
+
+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.
+
+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.
+
+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.
+
+
+### Scalar Types
+
+You can request simple scalar data types for basic input, such as a string, integer, or boolean.
+
+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.
+
+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.
+
+
+```python {4} title="Request a string"
+@mcp.tool
+async def get_user_name(ctx: Context) -> str:
+ """Get the user's name."""
+ result = await ctx.elicit("What's your name?", response_type=str)
+
+ if result.action == "accept":
+ return f"Hello, {result.data}!"
+ return "No name provided"
+```
+```python {4} title="Request an integer"
+@mcp.tool
+async def pick_a_number(ctx: Context) -> str:
+ """Pick a number."""
+ result = await ctx.elicit("Pick a number!", response_type=int)
+
+ if result.action == "accept":
+ return f"You picked {result.data}"
+ return "No number provided"
+```
+```python {4} title="Request a boolean"
+@mcp.tool
+async def pick_a_boolean(ctx: Context) -> str:
+ """Pick a boolean."""
+ result = await ctx.elicit("True or false?", response_type=bool)
+
+ if result.action == "accept":
+ return f"You picked {result.data}"
+ return "No boolean provided"
+```
+
+
+### Constrained Options
+
+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.
+
+
+```python {6} title="Using a list of strings"
+@mcp.tool
+async def set_priority(ctx: Context) -> str:
+ """Set task priority level."""
+ result = await ctx.elicit(
+ "What priority level?",
+ response_type=["low", "medium", "high"],
+ )
+
+ if result.action == "accept":
+ return f"Priority set to: {result.data}"
+```
+```python {1, 8} title="Using a Literal type"
+from typing import Literal
+
+@mcp.tool
+async def set_priority(ctx: Context) -> str:
+ """Set task priority level."""
+ result = await ctx.elicit(
+ "What priority level?",
+ response_type=Literal["low", "medium", "high"]
+ )
+
+ if result.action == "accept":
+ return f"Priority set to: {result.data}"
+ return "No priority set"
+```
+```python {1, 11} title="Using a Python enum"
+from enum import Enum
+
+class Priority(Enum):
+ LOW = "low"
+ MEDIUM = "medium"
+ HIGH = "high"
+
+@mcp.tool
+async def set_priority(ctx: Context) -> str:
+ """Set task priority level."""
+ result = await ctx.elicit("What priority level?", response_type=Priority)
+
+ if result.action == "accept":
+ return f"Priority set to: {result.data.value}"
+ return "No priority set"
+```
+
+
+
+### Structured Responses
+
+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.
+
+```python {1, 16, 20}
+from dataclasses import dataclass
+from typing import Literal
+
+@dataclass
+class TaskDetails:
+ title: str
+ description: str
+ priority: Literal["low", "medium", "high"]
+ due_date: str
+
+@mcp.tool
+async def create_task(ctx: Context) -> str:
+ """Create a new task with user-provided details."""
+ result = await ctx.elicit(
+ "Please provide task details",
+ response_type=TaskDetails
+ )
+
+ if result.action == "accept":
+ task = result.data
+ return f"Created task: {task.title} (Priority: {task.priority})"
+ return "Task creation cancelled"
+```
+
+## Multi-Turn Elicitation
+
+Tools can make multiple elicitation calls to gather information progressively:
+
+```python {6, 11, 16-19}
+@mcp.tool
+async def plan_meeting(ctx: Context) -> str:
+ """Plan a meeting by gathering details step by step."""
+
+ # Get meeting title
+ title_result = await ctx.elicit("What's the meeting title?", response_type=str)
+ if title_result.action != "accept":
+ return "Meeting planning cancelled"
+
+ # Get duration
+ duration_result = await ctx.elicit("Duration in minutes?", response_type=int)
+ if duration_result.action != "accept":
+ return "Meeting planning cancelled"
+
+ # Get priority
+ priority_result = await ctx.elicit(
+ "Is this urgent?",
+ response_type=Literal["yes", "no"]
+ )
+ if priority_result.action != "accept":
+ return "Meeting planning cancelled"
+
+ urgent = priority_result.data == "yes"
+ return f"Meeting '{title_result.data}' planned for {duration_result.data} minutes (Urgent: {urgent})"
+```
+
+
+## Client Requirements
+
+Elicitation requires the client to implement an elicitation handler. See [Client Elicitation](/clients/elicitation) for details on how clients can handle these requests.
+
+If a client doesn't support elicitation, calls to `ctx.elicit()` will raise an error indicating that elicitation is not supported.
\ No newline at end of file
diff --git a/docs/servers/logging.mdx b/docs/servers/logging.mdx
new file mode 100644
index 000000000..623873bc8
--- /dev/null
+++ b/docs/servers/logging.mdx
@@ -0,0 +1,194 @@
+---
+title: Server Logging
+sidebarTitle: Logging
+description: Send log messages back to MCP clients through the context.
+icon: receipt
+---
+
+import { VersionBadge } from '/snippets/version-badge.mdx'
+
+
+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.
+
+
+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.
+
+## Why Use Server Logging?
+
+Server logging is essential for:
+
+- **Debugging**: Send detailed execution information to help diagnose issues
+- **Progress visibility**: Keep users informed about what the tool is doing
+- **Error reporting**: Communicate problems and their context to clients
+- **Audit trails**: Create records of tool execution for compliance or analysis
+
+Unlike standard Python logging, MCP server logging sends messages directly to the client, making them visible in the client's interface or logs.
+
+### Basic Usage
+
+Use the context logging methods within any tool function:
+
+```python {8-9, 13, 17, 21}
+from fastmcp import FastMCP, Context
+
+mcp = FastMCP("LoggingDemo")
+
+@mcp.tool
+async def analyze_data(data: list[float], ctx: Context) -> dict:
+ """Analyze numerical data with comprehensive logging."""
+ await ctx.debug("Starting analysis of numerical data")
+ await ctx.info(f"Analyzing {len(data)} data points")
+
+ try:
+ if not data:
+ await ctx.warning("Empty data list provided")
+ return {"error": "Empty data list"}
+
+ result = sum(data) / len(data)
+ await ctx.info(f"Analysis complete, average: {result}")
+ return {"average": result, "count": len(data)}
+
+ except Exception as e:
+ await ctx.error(f"Analysis failed: {str(e)}")
+ raise
+```
+
+## Logging Methods
+
+
+
+ Send debug-level messages for detailed execution information
+
+
+
+ The debug message to send to the client
+
+
+
+
+
+ Send informational messages about normal execution
+
+
+
+ The information message to send to the client
+
+
+
+
+
+ Send warning messages for potential issues that didn't prevent execution
+
+
+
+ The warning message to send to the client
+
+
+
+
+
+ Send error messages for problems that occurred during execution
+
+
+
+ The error message to send to the client
+
+
+
+
+
+ Generic logging method with custom level and logger name
+
+
+
+ The log level for the message
+
+
+
+ The message to send to the client
+
+
+
+ Optional custom logger name for categorizing messages
+
+
+
+
+
+## Log Levels
+
+### Debug
+Use for detailed information that's typically only useful when diagnosing problems:
+
+```python
+@mcp.tool
+async def process_file(file_path: str, ctx: Context) -> str:
+ """Process a file with detailed debug logging."""
+ await ctx.debug(f"Starting to process file: {file_path}")
+ await ctx.debug("Checking file permissions")
+
+ # File processing logic
+ await ctx.debug("File processing completed successfully")
+ return "File processed"
+```
+
+### Info
+Use for general information about normal program execution:
+
+```python
+@mcp.tool
+async def backup_database(ctx: Context) -> str:
+ """Backup database with progress information."""
+ await ctx.info("Starting database backup")
+ await ctx.info("Connecting to database")
+ await ctx.info("Backup completed successfully")
+ return "Database backed up"
+```
+
+### Warning
+Use for potentially harmful situations that don't prevent execution:
+
+```python
+@mcp.tool
+async def validate_config(config: dict, ctx: Context) -> dict:
+ """Validate configuration with warnings for deprecated options."""
+ if "old_api_key" in config:
+ await ctx.warning("Using deprecated 'old_api_key' field. Please use 'api_key' instead")
+
+ if config.get("timeout", 30) > 300:
+ await ctx.warning("Timeout value is very high (>5 minutes), this may cause issues")
+
+ return {"status": "valid", "warnings": "see logs"}
+```
+
+### Error
+Use for error events that might still allow the application to continue:
+
+```python
+@mcp.tool
+async def batch_process(items: list[str], ctx: Context) -> dict:
+ """Process multiple items, logging errors for failed items."""
+ successful = 0
+ failed = 0
+
+ for item in items:
+ try:
+ # Process item
+ successful += 1
+ except Exception as e:
+ await ctx.error(f"Failed to process item '{item}': {str(e)}")
+ failed += 1
+
+ return {"successful": successful, "failed": failed}
+```
+
+
+## Client Handling
+
+Log messages are sent to the client through the MCP protocol. How clients handle these messages depends on their implementation:
+
+- **Development clients**: May display logs in real-time for debugging
+- **Production clients**: May store logs for later analysis or display to users
+- **Integration clients**: May forward logs to external logging systems
+
+See [Client Logging](/clients/logging) for details on how clients can handle server log messages.
diff --git a/docs/servers/progress.mdx b/docs/servers/progress.mdx
new file mode 100644
index 000000000..d3705aae3
--- /dev/null
+++ b/docs/servers/progress.mdx
@@ -0,0 +1,189 @@
+---
+title: Progress Reporting
+sidebarTitle: Progress
+description: Update clients on the progress of long-running operations through the MCP context.
+icon: chart-line
+---
+
+import { VersionBadge } from '/snippets/version-badge.mdx'
+
+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.
+
+## Why Use Progress Reporting?
+
+Progress reporting is valuable for:
+
+- **User experience**: Keep users informed about long-running operations
+- **Progress indicators**: Enable clients to show progress bars or percentages
+- **Timeout prevention**: Demonstrate that operations are actively progressing
+- **Debugging**: Track execution progress for performance analysis
+
+### Basic Usage
+
+Use `ctx.report_progress()` to send progress updates to the client:
+
+```python {14, 21}
+from fastmcp import FastMCP, Context
+import asyncio
+
+mcp = FastMCP("ProgressDemo")
+
+@mcp.tool
+async def process_items(items: list[str], ctx: Context) -> dict:
+ """Process a list of items with progress updates."""
+ total = len(items)
+ results = []
+
+ for i, item in enumerate(items):
+ # Report progress as we process each item
+ await ctx.report_progress(progress=i, total=total)
+
+ # Simulate processing time
+ await asyncio.sleep(0.1)
+ results.append(item.upper())
+
+ # Report 100% completion
+ await ctx.report_progress(progress=total, total=total)
+
+ return {"processed": len(results), "results": results}
+```
+
+## Method Signature
+
+
+
+ Report progress to the client for long-running operations
+
+
+
+ Current progress value (e.g., 24, 0.75, 1500)
+
+
+
+ Optional total value (e.g., 100, 1.0, 2000). When provided, clients may interpret this as enabling percentage calculation.
+
+
+
+
+
+## Progress Patterns
+
+### Percentage-Based Progress
+
+Report progress as a percentage (0-100):
+
+```python {13-14}
+@mcp.tool
+async def download_file(url: str, ctx: Context) -> str:
+ """Download a file with percentage progress."""
+ total_size = 1000 # KB
+ downloaded = 0
+
+ while downloaded < total_size:
+ # Download chunk
+ chunk_size = min(50, total_size - downloaded)
+ downloaded += chunk_size
+
+ # Report percentage progress
+ percentage = (downloaded / total_size) * 100
+ await ctx.report_progress(progress=percentage, total=100)
+
+ await asyncio.sleep(0.1) # Simulate download time
+
+ return f"Downloaded file from {url}"
+```
+
+### Absolute Progress
+
+Report progress with absolute values:
+
+```python {10}
+@mcp.tool
+async def backup_database(ctx: Context) -> str:
+ """Backup database tables with absolute progress."""
+ tables = ["users", "orders", "products", "inventory", "logs"]
+
+ for i, table in enumerate(tables):
+ await ctx.info(f"Backing up table: {table}")
+
+ # Report absolute progress
+ await ctx.report_progress(progress=i + 1, total=len(tables))
+
+ # Simulate backup time
+ await asyncio.sleep(0.5)
+
+ return "Database backup completed"
+```
+
+### Indeterminate Progress
+
+Report progress without a known total for operations where the endpoint is unknown:
+
+```python {11}
+@mcp.tool
+async def scan_directory(directory: str, ctx: Context) -> dict:
+ """Scan directory with indeterminate progress."""
+ files_found = 0
+
+ # Simulate directory scanning
+ for i in range(10): # Unknown number of files
+ files_found += 1
+
+ # Report progress without total for indeterminate operations
+ await ctx.report_progress(progress=files_found)
+
+ await asyncio.sleep(0.2)
+
+ return {"files_found": files_found, "directory": directory}
+```
+
+### Multi-Stage Operations
+
+Break complex operations into stages with progress for each:
+
+```python
+@mcp.tool
+async def data_migration(source: str, destination: str, ctx: Context) -> str:
+ """Migrate data with multi-stage progress reporting."""
+
+ # Stage 1: Validation (0-25%)
+ await ctx.info("Validating source data")
+ for i in range(5):
+ await ctx.report_progress(progress=i * 5, total=100)
+ await asyncio.sleep(0.1)
+
+ # Stage 2: Export (25-60%)
+ await ctx.info("Exporting data from source")
+ for i in range(7):
+ progress = 25 + (i * 5)
+ await ctx.report_progress(progress=progress, total=100)
+ await asyncio.sleep(0.1)
+
+ # Stage 3: Transform (60-80%)
+ await ctx.info("Transforming data format")
+ for i in range(4):
+ progress = 60 + (i * 5)
+ await ctx.report_progress(progress=progress, total=100)
+ await asyncio.sleep(0.1)
+
+ # Stage 4: Import (80-100%)
+ await ctx.info("Importing to destination")
+ for i in range(4):
+ progress = 80 + (i * 5)
+ await ctx.report_progress(progress=progress, total=100)
+ await asyncio.sleep(0.1)
+
+ # Final completion
+ await ctx.report_progress(progress=100, total=100)
+
+ return f"Migration from {source} to {destination} completed"
+```
+
+
+## Client Requirements
+
+Progress reporting requires clients to support progress handling:
+
+- Clients must send a `progressToken` in the initial request to receive progress updates
+- If no progress token is provided, progress calls will have no effect (they won't error)
+- See [Client Progress](/clients/progress) for details on implementing client-side progress handling
diff --git a/docs/servers/sampling.mdx b/docs/servers/sampling.mdx
new file mode 100644
index 000000000..b037bdad9
--- /dev/null
+++ b/docs/servers/sampling.mdx
@@ -0,0 +1,190 @@
+---
+title: LLM Sampling
+sidebarTitle: Sampling
+description: Request the client's LLM to generate text based on provided messages through the MCP context.
+icon: robot
+---
+
+import { VersionBadge } from '/snippets/version-badge.mdx'
+
+
+
+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.
+
+## Why Use LLM Sampling?
+
+LLM sampling enables tools to:
+
+- **Leverage AI capabilities**: Use the client's LLM for text generation and analysis
+- **Offload complex reasoning**: Let the LLM handle tasks requiring natural language understanding
+- **Generate dynamic content**: Create responses, summaries, or transformations based on data
+- **Maintain context**: Use the same LLM instance that the user is already interacting with
+
+### Basic Usage
+
+Use `ctx.sample()` to request text generation from the client's LLM:
+
+```python {14}
+from fastmcp import FastMCP, Context
+
+mcp = FastMCP("SamplingDemo")
+
+@mcp.tool
+async def analyze_sentiment(text: str, ctx: Context) -> dict:
+ """Analyze the sentiment of text using the client's LLM."""
+ 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}"""
+
+ # Request LLM analysis
+ response = await ctx.sample(prompt)
+
+ # Process the LLM's response
+ sentiment = response.text.strip().lower()
+
+ # Map to standard sentiment values
+ if "positive" in sentiment:
+ sentiment = "positive"
+ elif "negative" in sentiment:
+ sentiment = "negative"
+ else:
+ sentiment = "neutral"
+
+ return {"text": text, "sentiment": sentiment}
+```
+
+## Method Signature
+
+
+
+ Request text generation from the client's LLM
+
+
+
+ A string or list of strings/message objects to send to the LLM
+
+
+
+ Optional system prompt to guide the LLM's behavior
+
+
+
+ Optional sampling temperature (controls randomness, typically 0.0-1.0)
+
+
+
+ Optional maximum number of tokens to generate
+
+
+
+ Optional model selection preferences (e.g., model hint string, list of hints, or ModelPreferences object)
+
+
+
+
+
+ The LLM's response content (typically TextContent with a .text attribute)
+
+
+
+
+
+## Simple Text Generation
+
+### Basic Prompting
+
+Generate text with simple string prompts:
+
+```python {6}
+@mcp.tool
+async def generate_summary(content: str, ctx: Context) -> str:
+ """Generate a summary of the provided content."""
+ prompt = f"Please provide a concise summary of the following content:\n\n{content}"
+
+ response = await ctx.sample(prompt)
+ return response.text
+```
+
+### System Prompt
+
+Use system prompts to guide the LLM's behavior:
+
+```python {4-9}
+@mcp.tool
+async def generate_code_example(concept: str, ctx: Context) -> str:
+ """Generate a Python code example for a given concept."""
+ response = await ctx.sample(
+ messages=f"Write a simple Python code example demonstrating '{concept}'.",
+ system_prompt="You are an expert Python programmer. Provide concise, working code examples without explanations.",
+ temperature=0.7,
+ max_tokens=300
+ )
+
+ code_example = response.text
+ return f"```python\n{code_example}\n```"
+```
+
+
+### Model Preferences
+
+Specify model preferences for different use cases:
+
+```python {4-8, 17-22}
+@mcp.tool
+async def creative_writing(topic: str, ctx: Context) -> str:
+ """Generate creative content using a specific model."""
+ response = await ctx.sample(
+ messages=f"Write a creative short story about {topic}",
+ model_preferences="claude-3-sonnet", # Prefer a specific model
+ temperature=0.9, # High creativity
+ max_tokens=1000
+ )
+
+ return response.text
+
+@mcp.tool
+async def technical_analysis(data: str, ctx: Context) -> str:
+ """Perform technical analysis with a reasoning-focused model."""
+ response = await ctx.sample(
+ messages=f"Analyze this technical data and provide insights: {data}",
+ model_preferences=["claude-3-opus", "gpt-4"], # Prefer reasoning models
+ temperature=0.2, # Low randomness for consistency
+ max_tokens=800
+ )
+
+ return response.text
+```
+
+### Complex Message Structures
+
+Use structured messages for more complex interactions:
+
+```python {1, 6-10}
+from fastmcp.client.sampling import SamplingMessage
+
+@mcp.tool
+async def multi_turn_analysis(user_query: str, context_data: str, ctx: Context) -> str:
+ """Perform analysis using multi-turn conversation structure."""
+ messages = [
+ SamplingMessage(role="user", content=f"I have this data: {context_data}"),
+ SamplingMessage(role="assistant", content="I can see your data. What would you like me to analyze?"),
+ SamplingMessage(role="user", content=user_query)
+ ]
+
+ response = await ctx.sample(
+ messages=messages,
+ system_prompt="You are a data analyst. Provide detailed insights based on the conversation context.",
+ temperature=0.3
+ )
+
+ return response.text
+```
+
+## Client Requirements
+
+LLM sampling requires client support:
+
+- Clients must implement sampling handlers to process requests
+- If the client doesn't support sampling, calls to `ctx.sample()` will fail
+- See [Client Sampling](/clients/sampling) for details on implementing client-side sampling handlers