diff --git a/CLAUDE.md b/CLAUDE.md
index 186a040cc..c18ead0a5 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -13,7 +13,7 @@ This document contains critical information about working with this codebase. Fo
2. Code Quality
- Type hints required for all code
- - Public APIs must have docstrings
+ - All public members MUST have Google Python Style Guide-compliant docstrings
- Functions must be focused and small
- Follow existing patterns exactly
- Line length: 120 chars maximum
@@ -24,6 +24,10 @@ This document contains critical information about working with this codebase. Fo
- Coverage: test edge cases and errors
- New features require tests
- Bug fixes require regression tests
+ - Documentation
+ - Test changes in docs/ and Python docstrings: `uv run mkdocs build`
+ - On macOS: `export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib & uv run mkdocs build`
+ - Fix WARNING and ERROR issues and re-run build until clean
- For commits fixing bugs or adding features based on user reports add:
@@ -132,3 +136,46 @@ This document contains critical information about working with this codebase. Fo
- **Only catch `Exception` for**:
- Top-level handlers that must not crash
- Cleanup blocks (log at debug level)
+
+## Docstring best practices for SDK documentation
+
+The following guidance ensures docstrings are genuinely helpful for new SDK users by providing navigation, context, and accurate examples.
+
+### Structure and formatting
+
+- Follow Google Python Style Guide for docstrings
+- Format docstrings in Markdown compatible with mkdocs-material and mkdocstrings
+- Always surround lists with blank lines (before and after) - also applies to Markdown (.md) files
+- Always surround headings with blank lines - also applies to Markdown (.md) files
+- Always surround fenced code blocks with blank lines - also applies to Markdown (.md) files
+- Use sentence case for all headings and heading-like text - also applies to Markdown (.md) files
+
+### Content requirements
+
+- Access patterns: Explicitly state how users typically access the method/class with phrases like "You typically access this
+method through..." or "You typically call this method by..."
+- Cross-references: Use extensive cross-references to related members to help SDK users navigate:
+ - Format: [`displayed_text`][module.path.to.Member]
+ - Include backticks around the displayed text
+ - Link to types, related methods, and alternative approaches
+- Parameter descriptions:
+ - Document all valid values for enums/literals
+ - Explain what each parameter does and when to use it
+ - Cross-reference parameter types where helpful
+- Real-world examples:
+ - Show actual usage patterns from the SDK, not theoretical code
+ - Include imports and proper module paths
+ - Verify examples against source code for accuracy
+ - Show multiple approaches (e.g., low-level SDK vs FastMCP)
+ - Add comments explaining what's happening
+ - Examples should be concise and only as complex as needed to clearly demonstrate real-world usage
+- Context and purpose:
+ - Explain not just what the method does, but why and when to use it
+ - Include notes about important considerations (e.g., client filtering, performance)
+ - Mention alternative approaches where applicable
+
+### Verification
+
+ - All code examples MUST be 100% accurate to the actual SDK implementation
+ - Verify imports, class names, method signatures against source code
+ - You MUST NOT rely on existing documentation as authoritative - you MUST check the source
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c18937f5b..a29f3faaa 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,15 +10,15 @@ Thank you for your interest in contributing to the MCP Python SDK! This document
4. Clone your fork: `git clone https://github.com/YOUR-USERNAME/python-sdk.git`
5. Install dependencies:
-```bash
-uv sync --frozen --all-extras --dev
-```
+ ```bash
+ uv sync --frozen --all-extras --dev
+ ```
6. Set up pre-commit hooks:
-```bash
-uv tool install pre-commit --with pre-commit-uv --force-reinstall
-```
+ ```bash
+ uv tool install pre-commit --with pre-commit-uv --force-reinstall
+ ```
## Development Workflow
@@ -33,37 +33,64 @@ uv tool install pre-commit --with pre-commit-uv --force-reinstall
4. Ensure tests pass:
-```bash
-uv run pytest
-```
+ ```bash
+ uv run pytest
+ ```
5. Run type checking:
-```bash
-uv run pyright
-```
+ ```bash
+ uv run pyright
+ ```
6. Run linting:
-```bash
-uv run ruff check .
-uv run ruff format .
-```
+ ```bash
+ uv run ruff check .
+ uv run ruff format .
+ ```
7. Update README snippets if you modified example code:
-```bash
-uv run scripts/update_readme_snippets.py
-```
+ ```bash
+ uv run scripts/update_readme_snippets.py
+ ```
8. (Optional) Run pre-commit hooks on all files:
-```bash
-pre-commit run --all-files
-```
+ ```bash
+ pre-commit run --all-files
+ ```
9. Submit a pull request to the same branch you branched from
+## Building and viewing documentation
+
+To build and view the documentation locally:
+
+1. Install documentation dependencies (included with `--dev` flag above):
+
+ ```bash
+ uv sync --frozen --group docs
+ ```
+
+2. Serve the documentation locally:
+
+ ```bash
+ uv run mkdocs serve
+ ```
+
+ **Note for macOS users**: If you encounter a [Cairo library error](https://squidfunk.github.io/mkdocs-material/plugins/requirements/image-processing/#cairo-library-was-not-found), set the library path before running mkdocs:
+
+ ```bash
+ export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib
+ uv run mkdocs serve
+ ```
+
+3. Open your browser to `http://127.0.0.1:8000/python-sdk/` to view the documentation
+
+The documentation will auto-reload when you make changes to files in `docs/`, `mkdocs.yml`, or `src/mcp/`.
+
## Code Style
- We use `ruff` for linting and formatting
diff --git a/docs/api.md b/docs/api.md
deleted file mode 100644
index 3f696af54..000000000
--- a/docs/api.md
+++ /dev/null
@@ -1 +0,0 @@
-::: mcp
diff --git a/docs/examples-authentication.md b/docs/examples-authentication.md
new file mode 100644
index 000000000..509d3d7cf
--- /dev/null
+++ b/docs/examples-authentication.md
@@ -0,0 +1,144 @@
+# Authentication examples
+
+MCP supports OAuth 2.1 authentication for protecting server resources. This section demonstrates both server-side token verification and client-side authentication flows.
+
+## Security considerations
+
+When implementing authentication:
+
+- **Use HTTPS**: All OAuth flows must use HTTPS in production
+- **Token validation**: Always validate tokens on the resource server side
+- **Scope checking**: Verify that tokens have required scopes
+- **Introspection**: Use token introspection for distributed validation
+- **RFC compliance**: Follow RFC 9728 for proper authoriazation server (AS) discovery
+
+## OAuth architecture
+
+The MCP OAuth implementation follows the OAuth 2.1 authorization code flow with token introspection:
+
+```mermaid
+sequenceDiagram
+ participant C as Client
+ participant AS as Authorization Server
+ participant RS as Resource Server
(MCP Server)
+ participant U as User
+
+ Note over C,RS: 1. Discovery Phase (RFC 9728)
+ C->>RS: GET /.well-known/oauth-protected-resource
+ RS->>C: Protected Resource Metadata
(issuer, scopes, etc.)
+
+ Note over C,AS: 2. Authorization Phase
+ C->>AS: GET /authorize?response_type=code&client_id=...
+ AS->>U: Redirect to login/consent
+ U->>AS: User authenticates and consents
+ AS->>C: Authorization code (via redirect)
+
+ Note over C,AS: 3. Token Exchange
+ C->>AS: POST /token
(authorization_code grant)
+ AS->>C: Access token + refresh token
+
+ Note over C,RS: 4. Resource Access
+ C->>RS: MCP request + Authorization: Bearer
+ RS->>AS: POST /introspect
(validate token)
+ AS->>RS: Token info (active, scopes, user)
+ RS->>C: MCP response (if authorized)
+
+ Note over C,AS: 5. Token Refresh (when needed)
+ C->>AS: POST /token
(refresh_token grant)
+ AS->>C: New access token
+```
+
+**Components:**
+
+- **Authorization Server (AS)**: Handles OAuth flows, issues and validates tokens
+- **Resource Server (RS)**: Your MCP server that validates tokens and serves protected resources
+- **Client**: Discovers AS through RFC 9728, obtains tokens, and uses them with MCP server
+- **User**: Resource owner who authorizes access
+
+## OAuth server implementation
+
+FastMCP server with OAuth token verification:
+
+```python
+--8<-- "examples/snippets/servers/oauth_server.py"
+```
+
+This example shows:
+
+- Implementing the `TokenVerifier` protocol for token validation
+- Using `AuthSettings` for RFC 9728 Protected Resource Metadata
+- Resource server configuration with authorization server discovery
+- Protected tools that require authentication
+
+## Complete authentication server
+
+Full Authorization Server implementation with token introspection:
+
+```python
+--8<-- "examples/servers/simple-auth/mcp_simple_auth/auth_server.py"
+```
+
+This comprehensive example includes:
+
+- OAuth 2.1 authorization flows (authorization code, refresh token)
+- Token introspection endpoint for resource servers
+- Client registration and metadata management
+- RFC 9728 protected resource metadata endpoint
+
+## Resource server with introspection
+
+MCP Resource Server that validates tokens via Authorization Server introspection:
+
+```python
+--8<-- "examples/servers/simple-auth/mcp_simple_auth/server.py"
+```
+
+This demonstrates:
+
+- Token introspection for validation instead of local token verification
+- Separation of Authorization Server (AS) and Resource Server (RS)
+- Protected MCP tools and resources
+- Production-ready server patterns
+
+## Token verification implementation
+
+Custom token verification logic:
+
+```python
+--8<-- "examples/servers/simple-auth/mcp_simple_auth/token_verifier.py"
+```
+
+This component handles:
+
+- HTTP token introspection requests
+- Token validation with scope checking
+- RFC 8707 resource parameter validation
+- Error handling and logging
+
+## Simple authentication provider
+
+Authentication provider for development and testing:
+
+```python
+--8<-- "examples/servers/simple-auth/mcp_simple_auth/simple_auth_provider.py"
+```
+
+This utility provides:
+
+- Simplified token generation for testing
+- Development authentication flows
+- Testing utilities for protected resources
+
+## Legacy Authorization Server
+
+Backward compatibility with older OAuth implementations:
+
+```python
+--8<-- "examples/servers/simple-auth/mcp_simple_auth/legacy_as_server.py"
+```
+
+This example shows:
+
+- Support for non-RFC 9728 compliant clients
+- Legacy endpoint compatibility
+- Migration patterns for existing systems
diff --git a/docs/examples-clients.md b/docs/examples-clients.md
new file mode 100644
index 000000000..5fa7a2992
--- /dev/null
+++ b/docs/examples-clients.md
@@ -0,0 +1,127 @@
+# Client examples
+
+MCP clients connect to servers to access tools, resources, and prompts. This section demonstrates various client patterns and connection types.
+
+These examples provide comprehensive patterns for building MCP clients that can handle various server types, authentication methods, and interaction patterns.
+
+## Basic stdio client
+
+Connecting to MCP servers over stdio transport:
+
+```python
+--8<-- "examples/snippets/clients/stdio_client.py"
+```
+
+This fundamental example demonstrates:
+
+- Creating `StdioServerParameters` for server connection
+- Using `ClientSession` for MCP communication
+- Listing and calling tools, reading resources, getting prompts
+- Handling both structured and unstructured tool results
+- Sampling callback implementation for LLM integration
+
+## Streamable HTTP client
+
+Connecting to HTTP-based MCP servers:
+
+```python
+--8<-- "examples/snippets/clients/streamable_basic.py"
+```
+
+This example shows:
+
+- Using `streamablehttp_client` for HTTP connections
+- Simpler connection setup for web-deployed servers
+- Basic tool listing and execution over HTTP
+
+## Display utilities
+
+Helper utilities for client user interfaces:
+
+```python
+--8<-- "examples/snippets/clients/display_utilities.py"
+```
+
+This practical example covers:
+
+- Using `get_display_name()` for human-readable names
+- Proper precedence rules for tool/resource titles
+- Building user-friendly client interfaces
+- Consistent naming across different MCP objects
+
+## OAuth authentication client
+
+Client-side OAuth 2.1 authentication flow:
+
+```python
+--8<-- "examples/snippets/clients/oauth_client.py"
+```
+
+This comprehensive example demonstrates:
+
+- `OAuthClientProvider` setup and configuration
+- Token storage with custom `TokenStorage` implementation
+- Authorization flow handling (redirect and callback)
+- Authenticated requests to protected MCP servers
+
+## Completion client
+
+Using completion suggestions for better user experience:
+
+```python
+--8<-- "examples/snippets/clients/completion_client.py"
+```
+
+This advanced example shows:
+
+- Resource template argument completion
+- Context-aware completions (e.g., repository suggestions based on owner)
+- Prompt argument completion
+- Dynamic suggestion generation
+
+## Tool result parsing
+
+Understanding and processing tool results:
+
+```python
+--8<-- "examples/snippets/clients/parsing_tool_results.py"
+```
+
+This detailed example covers:
+
+- Parsing different content types (`TextContent`, `ImageContent`, `EmbeddedResource`)
+- Handling structured output data
+- Processing embedded resources
+- Error handling for failed tool executions
+
+## Complete chatbot client
+
+A full-featured chatbot that integrates with multiple MCP servers:
+
+```python
+--8<-- "examples/clients/simple-chatbot/mcp_simple_chatbot/main.py"
+```
+
+This production-ready example includes:
+
+- **Multi-server management**: Connect to multiple MCP servers simultaneously
+- **LLM integration**: Use Groq API for natural language processing
+- **Tool orchestration**: Automatic tool selection and execution
+- **Error handling**: Retry mechanisms and graceful failure handling
+- **Configuration management**: JSON-based server configuration
+- **Session management**: Persistent conversation context
+
+## Authentication client
+
+Complete OAuth client implementation:
+
+```python
+--8<-- "examples/clients/simple-auth-client/mcp_simple_auth_client/main.py"
+```
+
+This example demonstrates:
+
+- Full OAuth 2.1 client implementation
+- Token management and refresh
+- Protected resource access
+- Integration with authenticated MCP servers
diff --git a/docs/examples-echo-servers.md b/docs/examples-echo-servers.md
new file mode 100644
index 000000000..0da3d1a41
--- /dev/null
+++ b/docs/examples-echo-servers.md
@@ -0,0 +1,76 @@
+# Echo server examples
+
+Echo servers provide a foundation for understanding MCP patterns before building more complex functionality.
+
+Echo servers are useful for:
+
+- **Testing client connections**: Verify that your client can connect and call tools
+- **Understanding MCP basics**: Learn the fundamental request/response patterns
+- **Development and debugging**: Simple, predictable behavior for testing
+- **Protocol verification**: Ensure transport layers work correctly
+
+The following servers are minimal examples that demonstrate basic MCP functionality by echoing input back to clients.
+
+## Simple echo server
+
+The most basic echo implementation:
+
+```python
+--8<-- "examples/fastmcp/simple_echo.py"
+```
+
+This minimal example shows:
+
+- Single tool implementation with string input/output
+- Basic parameter handling
+- Simple string manipulation and return
+
+## Enhanced echo server
+
+More sophisticated echo patterns:
+
+```python
+--8<-- "examples/fastmcp/echo.py"
+```
+
+This enhanced version demonstrates:
+
+- Multiple echo variants (basic echo, uppercase, reverse)
+- Different parameter types and patterns
+- Tool naming and description best practices
+
+## Usage
+
+These echo servers can be used to test different aspects of MCP:
+
+```bash
+# Test with MCP Inspector
+uv run mcp dev echo.py
+
+# Test direct execution
+python echo.py
+
+# Test with custom clients
+# (Use the client examples to connect to these echo servers)
+```
+
+## Testing tool calls
+
+Example tool calls you can make to echo servers:
+
+```json
+{
+ "tool": "echo",
+ "arguments": {
+ "message": "Hello, MCP!"
+ }
+}
+```
+
+Expected response:
+
+```json
+{
+ "result": "Echo: Hello, MCP!"
+}
+```
diff --git a/docs/examples-lowlevel-servers.md b/docs/examples-lowlevel-servers.md
new file mode 100644
index 000000000..e440cf2a4
--- /dev/null
+++ b/docs/examples-lowlevel-servers.md
@@ -0,0 +1,95 @@
+# Low-level server examples
+
+The [low-level server API](reference/mcp/server/lowlevel/server.md) provides maximum control over MCP protocol implementation. Use these patterns when you need fine-grained control or when [`FastMCP`][mcp.server.fastmcp.FastMCP] doesn't meet your requirements.
+
+The low-level API provides the foundation that FastMCP is built upon, giving you access to all MCP protocol features with complete control over implementation details.
+
+## When to use low-level API
+
+Choose the low-level API when you need:
+
+- Custom protocol message handling
+- Complex initialization sequences
+- Fine-grained control over capabilities
+- Integration with existing server infrastructure
+- Performance optimization at the protocol level
+- Custom authentication or authorization logic
+
+Key differences between the low-level server API and FastMCP are:
+
+| | Low-level API | FastMCP |
+| --------------- | ------------------------ | ----------------------------- |
+| **Control** | Maximum control | Convention over configuration |
+| **Boilerplate** | More verbose | Minimal setup |
+| **Decorators** | Server method decorators | Simple function decorators |
+| **Schema** | Manual definition | Automatic from type hints |
+| **Lifecycle** | Manual management | Automatic handling |
+| **Best for** | Complex custom logic | Rapid development |
+
+## Basic low-level server
+
+Fundamental low-level server patterns:
+
+```python
+--8<-- "examples/snippets/servers/lowlevel/basic.py"
+```
+
+This example demonstrates:
+
+- Creating a [`Server`][mcp.server.lowlevel.Server] instance directly
+- Manual handler registration with decorators
+- Prompt management with `@server.list_prompts()` and `@server.get_prompt()`
+- Manual capability declaration
+- Explicit initialization and connection handling
+
+## Low-level server with lifespan
+
+Resource management and lifecycle control:
+
+```python
+--8<-- "examples/snippets/servers/lowlevel/lifespan.py"
+```
+
+This advanced pattern shows:
+
+- Custom lifespan context manager for resource initialization
+- Database connection management example
+- Accessing lifespan context through `server.request_context`
+- Tool implementation with resource access
+- Proper cleanup and connection management
+
+## Structured output with low-level API
+
+Manual structured output control:
+
+```python
+--8<-- "examples/snippets/servers/lowlevel/structured_output.py"
+```
+
+And a standalone implementation:
+
+```python
+--8<-- "examples/servers/structured_output_lowlevel.py"
+```
+
+These examples cover:
+
+- Manual `outputSchema` definition in tool specifications
+- Direct dictionary return for structured data
+- Automatic validation against defined schemas
+- Backward compatibility with text content
+
+## Simple tool server
+
+Complete low-level server focused on tools:
+
+```python
+--8<-- "examples/servers/simple-tool/mcp_simple_tool/server.py"
+```
+
+This production-ready example includes:
+
+- Full tool lifecycle management
+- Input validation and error handling
+- Proper MCP protocol compliance
+- Tool execution with structured responses
diff --git a/docs/examples-quickstart.md b/docs/examples-quickstart.md
new file mode 100644
index 000000000..20d76ae08
--- /dev/null
+++ b/docs/examples-quickstart.md
@@ -0,0 +1,52 @@
+# Getting started
+
+This section provides quick and simple examples to get you started with the MCP Python SDK.
+
+These examples can be run directly with:
+
+```bash
+python server.py
+```
+
+Or test with the MCP Inspector:
+
+```bash
+uv run mcp dev server.py
+```
+
+## FastMCP quickstart
+
+The easiest way to create an MCP server is with [`FastMCP`][mcp.server.fastmcp.FastMCP]. This example demonstrates the core concepts: tools, resources, and prompts.
+
+```python
+--8<-- "examples/snippets/servers/fastmcp_quickstart.py"
+```
+
+This example shows how to:
+
+- Create a FastMCP server instance
+- Add a tool that performs computation (`add`)
+- Add a dynamic resource that provides data (`greeting://`)
+- Add a prompt template for LLM interactions (`greet_user`)
+
+## Basic server
+
+An even simpler starting point:
+
+```python
+--8<-- "examples/fastmcp/readme-quickstart.py"
+```
+
+## Direct execution
+
+For the simplest possible server deployment:
+
+```python
+--8<-- "examples/snippets/servers/direct_execution.py"
+```
+
+This example demonstrates:
+
+- Minimal server setup with just a greeting tool
+- Direct execution without additional configuration
+- Entry point setup for standalone running
diff --git a/docs/examples-server-advanced.md b/docs/examples-server-advanced.md
new file mode 100644
index 000000000..5c7912c2d
--- /dev/null
+++ b/docs/examples-server-advanced.md
@@ -0,0 +1,95 @@
+# Advanced server examples
+
+This section covers advanced server patterns including lifecycle management, context handling, and interactive capabilities.
+
+These advanced patterns enable rich, interactive server implementations that go beyond simple request-response workflows.
+
+## Lifespan management
+
+Managing server lifecycle with resource initialization and cleanup:
+
+```python
+--8<-- "examples/snippets/servers/lifespan_example.py"
+```
+
+This example demonstrates:
+
+- Type-safe lifespan context management
+- Resource initialization on startup (database connections, etc.)
+- Automatic cleanup on shutdown
+- Accessing lifespan context from tools via [`ctx.request_context.lifespan_context`][mcp.server.fastmcp.Context.request_context]
+
+## User interaction and elicitation
+
+Tools that can request additional information from users:
+
+```python
+--8<-- "examples/snippets/servers/elicitation.py"
+```
+
+This example shows:
+
+- Using [`ctx.elicit()`][mcp.server.fastmcp.Context.elicit] to request user input
+- Pydantic schemas for validating user responses
+- Handling user acceptance, decline, or cancellation
+- Interactive booking workflow patterns
+
+## LLM sampling and integration
+
+Tools that interact with LLMs through sampling:
+
+```python
+--8<-- "examples/snippets/servers/sampling.py"
+```
+
+This demonstrates:
+
+- Using [`ctx.session.create_message()`][mcp.server.session.ServerSession.create_message] for LLM interaction
+- Structured message creation with [`SamplingMessage`][mcp.types.SamplingMessage] and [`TextContent`][mcp.types.TextContent]
+- Processing LLM responses within tools
+- Chaining LLM interactions for complex workflows
+
+## Logging and notifications
+
+Advanced logging and client notification patterns:
+
+```python
+--8<-- "examples/snippets/servers/notifications.py"
+```
+
+This example covers:
+
+- Multiple log levels (debug, info, warning, error)
+- Resource change notifications via [`ctx.session.send_resource_list_changed()`][mcp.server.session.ServerSession.send_resource_list_changed]
+- Contextual logging within tool execution
+- Client communication patterns
+
+## Image handling
+
+Working with images in MCP servers:
+
+```python
+--8<-- "examples/snippets/servers/images.py"
+```
+
+This shows:
+
+- Using FastMCP's [`Image`][mcp.server.fastmcp.Image] class for automatic image handling
+- PIL integration for image processing with [`PIL.Image.open()`][PIL.Image.open]
+- Returning images from tools
+- Image format conversion and optimization
+
+## Completion support
+
+Providing argument completion for enhanced user experience:
+
+```python
+--8<-- "examples/snippets/servers/completion.py"
+```
+
+This advanced pattern demonstrates:
+
+- Dynamic completion based on partial input
+- Context-aware suggestions (repository suggestions based on owner)
+- Resource template parameter completion
+- Prompt argument completion
diff --git a/docs/examples-server-prompts.md b/docs/examples-server-prompts.md
new file mode 100644
index 000000000..b9d7f20e4
--- /dev/null
+++ b/docs/examples-server-prompts.md
@@ -0,0 +1,42 @@
+# Server prompts examples
+
+Prompts are reusable templates that help structure LLM interactions. They provide a way to define consistent interaction patterns that users can invoke.
+
+Prompts are user-controlled primitives and are particularly useful for:
+
+- Code review templates
+- Debugging assistance workflows
+- Content generation patterns
+- Structured analysis requests
+
+Unlike tools (which are model-controlled) and resources (which are application-controlled), prompts are invoked directly by users to initiate specific types of interactions with the LLM.
+
+## Basic prompts
+
+Simple prompt templates for common scenarios:
+
+```python
+--8<-- "examples/snippets/servers/basic_prompt.py"
+```
+
+This example demonstrates:
+
+- Simple string prompts (`review_code`)
+- Multi-message prompt conversations (`debug_error`)
+- Using different message types (User and Assistant messages)
+- Prompt titles for better user experience
+
+## Simple prompt server
+
+A complete server focused on prompt management:
+
+```python
+--8<-- "examples/servers/simple-prompt/mcp_simple_prompt/server.py"
+```
+
+This low-level server example shows:
+
+- Prompt listing and retrieval
+- Argument handling and validation
+- Dynamic prompt generation based on parameters
+- Production-ready prompt patterns using the low-level API
diff --git a/docs/examples-server-resources.md b/docs/examples-server-resources.md
new file mode 100644
index 000000000..b3a5cfa33
--- /dev/null
+++ b/docs/examples-server-resources.md
@@ -0,0 +1,51 @@
+# Server resources examples
+
+Resources provide data to LLMs without side effects. They're similar to GET endpoints in REST APIs and should be used for exposing information rather than performing actions.
+
+Resources are essential for providing contextual information to LLMs, whether it's configuration data, file contents, or dynamic information that changes over time.
+
+## Basic resources
+
+Simple resource patterns for exposing data:
+
+```python
+--8<-- "examples/snippets/servers/basic_resource.py"
+```
+
+This example demonstrates:
+
+- Static resources with fixed URIs (`config://settings`)
+- Dynamic resources with URI templates (`file://documents/{name}`)
+- Simple string data return
+- JSON configuration data
+
+## Simple resource server
+
+A complete server focused on resource management:
+
+```python
+--8<-- "examples/servers/simple-resource/mcp_simple_resource/server.py"
+```
+
+This is an example of a low-level server that:
+
+- Uses the low-level server API for maximum control
+- Implements resource listing and reading
+- Handles URI templates and parameter extraction
+- Demonstrates production-ready resource patterns
+
+
+## Memory and state management
+
+Resources that manage server memory and state:
+
+```python
+--8<-- "examples/fastmcp/memory.py"
+```
+
+This example shows how to:
+
+- Implement persistent memory across requests
+- Store and retrieve conversational context
+- Handle memory cleanup and management
+- Provide memory resources to LLMs
diff --git a/docs/examples-server-tools.md b/docs/examples-server-tools.md
new file mode 100644
index 000000000..7662f5b5e
--- /dev/null
+++ b/docs/examples-server-tools.md
@@ -0,0 +1,74 @@
+# Server tools examples
+
+Tools are functions that LLMs can call to perform actions or computations. This section demonstrates various tool patterns and capabilities.
+
+## Basic tools
+
+Simple tools that perform computations and return results:
+
+```python
+--8<-- "examples/snippets/servers/basic_tool.py"
+```
+
+## Tools with context and progress reporting
+
+Tools can access MCP context for logging, progress reporting, and other capabilities:
+
+```python
+--8<-- "examples/snippets/servers/tool_progress.py"
+```
+
+This example shows:
+
+- Using the `Context` parameter for MCP capabilities
+- Progress reporting during long-running operations
+- Structured logging at different levels
+- Async tool functions
+
+## Complex input handling
+
+Tools can handle complex data structures and validation:
+
+```python
+--8<-- "examples/fastmcp/complex_inputs.py"
+```
+
+## Parameter descriptions
+
+Tools with detailed parameter descriptions and validation:
+
+```python
+--8<-- "examples/fastmcp/parameter_descriptions.py"
+```
+
+## Unicode and internationalization
+
+Handling Unicode and international text in tools:
+
+```python
+--8<-- "examples/fastmcp/unicode_example.py"
+```
+
+## Desktop integration
+
+Tools that interact with the desktop environment:
+
+```python
+--8<-- "examples/fastmcp/desktop.py"
+```
+
+## Screenshot tools
+
+Tools for taking and processing screenshots:
+
+```python
+--8<-- "examples/fastmcp/screenshot.py"
+```
+
+## Text messaging tool
+
+Tool to send a text message.
+
+```python
+--8<-- "examples/fastmcp/text_me.py"
+```
diff --git a/docs/examples-structured-output.md b/docs/examples-structured-output.md
new file mode 100644
index 000000000..55bf27c10
--- /dev/null
+++ b/docs/examples-structured-output.md
@@ -0,0 +1,67 @@
+# Structured output examples
+
+Structured output allows tools to return well-typed, validated data that clients can easily process. This section covers various approaches to structured data.
+
+Structured output provides several advantages:
+
+- **Type Safety**: Automatic validation ensures data integrity
+- **Documentation**: Schemas serve as API documentation
+- **Client Integration**: Easier processing by client applications
+- **Backward Compatibility**: Still provides unstructured text content
+- **IDE Support**: Better development experience with type hints
+
+Choose structured output when you need reliable, processable data from your tools.
+
+## FastMCP structured output
+
+Using FastMCP's automatic structured output capabilities:
+
+```python
+--8<-- "examples/snippets/servers/structured_output.py"
+```
+
+This comprehensive example demonstrates:
+
+- **Pydantic models**: Rich validation and documentation (`WeatherData`)
+- **TypedDict**: Simpler structures (`LocationInfo`)
+- **Dictionary types**: Flexible schemas (`dict[str, float]`)
+- **Regular classes**: With type hints for structured output (`UserProfile`)
+- **Untyped classes**: Fall back to unstructured output (`UntypedConfig`)
+- **Primitive wrapping**: Simple types wrapped in `{"result": value}`
+
+## Weather service with structured output
+
+A complete weather service demonstrating real-world structured output patterns:
+
+```python
+--8<-- "examples/fastmcp/weather_structured.py"
+```
+
+This extensive example shows:
+
+- **Nested Pydantic models**: Complex data structures with validation
+- **Multiple output formats**: Different approaches for different use cases
+- **Dataclass support**: Using dataclasses for structured output
+- **Production patterns**: Realistic data structures for weather APIs
+- **Testing integration**: Built-in testing via MCP protocol
+
+## Low-level structured output
+
+Using the low-level server API for maximum control:
+
+```python
+--8<-- "examples/snippets/servers/lowlevel/structured_output.py"
+```
+
+And a standalone low-level example:
+
+```python
+--8<-- "examples/servers/structured_output_lowlevel.py"
+```
+
+These examples demonstrate:
+
+- Manual schema definition with `outputSchema`
+- Validation against defined schemas
+- Returning structured data directly from tools
+- Backward compatibility with unstructured content
diff --git a/docs/examples-transport-http.md b/docs/examples-transport-http.md
new file mode 100644
index 000000000..54e45640e
--- /dev/null
+++ b/docs/examples-transport-http.md
@@ -0,0 +1,91 @@
+# HTTP transport examples
+
+HTTP transports enable web-based MCP server deployment with support for multiple clients and scalable architectures.
+
+Choose HTTP transports for production deployments that need to serve multiple clients or integrate with web infrastructure.
+
+## Transport comparison
+
+| Feature | Streamable HTTP | SSE | stdio |
+| ---------------- | ------------------ | ----------------- | ---------------- |
+| **Resumability** | ✅ With event store | ❌ | ❌ |
+| **Scalability** | ✅ Multi-client | ✅ Multi-client | ❌ Single process |
+| **State** | Configurable | Session-based | Process-based |
+| **Deployment** | Web servers | Web servers | Local execution |
+| **Best for** | Production APIs | Real-time updates | Development/CLI |
+
+## Streamable HTTP configuration
+
+Basic streamable HTTP server setup with different configurations:
+
+```python
+--8<-- "examples/snippets/servers/streamable_config.py"
+```
+
+This example demonstrates:
+
+- **Stateful servers**: Maintain session state (default)
+- **Stateless servers**: No session persistence (`stateless_http=True`)
+- **JSON responses**: Disable SSE streaming (`json_response=True`)
+- Transport selection at runtime
+
+## Mounting multiple servers
+
+Deploying multiple MCP servers in a single Starlette application:
+
+```python
+--8<-- "examples/snippets/servers/streamable_starlette_mount.py"
+```
+
+This pattern shows:
+
+- Creating multiple FastMCP server instances
+- Mounting servers at different paths (`/echo`, `/math`)
+- Shared lifespan management across servers
+- Combined session manager lifecycle
+
+## Stateful HTTP server
+
+Full low-level implementation of a stateful HTTP server:
+
+```python
+--8<-- "examples/servers/simple-streamablehttp/mcp_simple_streamablehttp/server.py"
+```
+
+This comprehensive example includes:
+
+- Event store for resumability (reconnection support)
+- Progress notifications and logging
+- Resource change notifications
+- Streaming tool execution with progress updates
+- Production-ready ASGI integration
+
+## Stateless HTTP server
+
+Low-level stateless server for high-scale deployments:
+
+```python
+--8<-- "examples/servers/simple-streamablehttp-stateless/mcp_simple_streamablehttp_stateless/server.py"
+```
+
+Features of stateless design:
+
+- No session state persistence
+- Simplified architecture for load balancing
+- Better horizontal scaling capabilities
+- Each request is independent
+
+## Event store implementation
+
+Supporting resumable connections with event storage:
+
+```python
+--8<-- "examples/servers/simple-streamablehttp/mcp_simple_streamablehttp/event_store.py"
+```
+
+This component enables:
+
+- Client reconnection with `Last-Event-ID` headers
+- Event replay for missed messages
+- Persistent streaming across connection interruptions
+- Production-ready resumability patterns
diff --git a/docs/index.md b/docs/index.md
index 42ad9ca0c..885cf858d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,5 +1,85 @@
-# MCP Server
+# MCP Python SDK
-This is the MCP Server implementation in Python.
+A Python implementation of the Model Context Protocol (MCP) that enables applications to provide context for LLMs in a standardized way.
-It only contains the [API Reference](api.md) for the time being.
+## Examples
+
+The [Examples](examples-quickstart.md) section provides working code examples covering many aspects of MCP development. Each code example in these documents corresponds to an example .py file in the examples/ directory in the repository.
+
+- [Getting started](examples-quickstart.md): Quick introduction to FastMCP and basic server patterns
+- [Server development](examples-server-tools.md): Tools, resources, prompts, and structured output examples
+- [Transport protocols](examples-transport-http.md): HTTP and streamable transport implementations
+- [Low-level servers](examples-lowlevel-servers.md): Advanced patterns using the low-level server API
+- [Authentication](examples-authentication.md): OAuth 2.1 server and client implementations
+- [Client development](examples-clients.md): Complete client examples with various connection types
+
+## API Reference
+
+Complete API documentation is auto-generated from the source code and available in the [API Reference](reference/mcp/index.md) section.
+
+## Code example index
+
+### Servers
+
+| File | Transport | Resources | Prompts | Tools | Completions | Sampling | Elicitation | Progress | Logging | Authentication | Configuration |
+|---|---|---|---|---|---|---|---|---|---|---|---|
+| [Complex input handling](examples-server-tools.md#complex-input-handling) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Desktop integration](examples-server-tools.md#desktop-integration) | stdio | ✅ | — | ✅ | — | — | — | — | — | — | — |
+| [Enhanced echo server](examples-echo-servers.md#enhanced-echo-server) | stdio | ✅ | ✅ | ✅ | — | — | — | — | — | — | — |
+| [Memory and state management](examples-server-resources.md#memory-and-state-management) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Parameter descriptions](examples-server-tools.md#parameter-descriptions) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Basic server](examples-quickstart.md#basic-server) | stdio | ✅ | — | ✅ | — | — | — | — | — | — | — |
+| [Screenshot tools](examples-server-tools.md#screenshot-tools) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Simple echo server](examples-echo-servers.md#simple-echo-server) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Text messaging tool](examples-server-tools.md#text-messaging-tool) | stdio | — | — | ✅ | — | — | — | — | — | — | ✅ |
+| [Unicode and internationalization](examples-server-tools.md#unicode-and-internationalization) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Weather service with structured output](examples-structured-output.md#weather-service-with-structured-output) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Complete authentication server](examples-authentication.md#complete-authentication-server) | streamable-http | — | — | — | — | — | — | — | — | ✅ | — |
+| [Legacy Authorization Server](examples-authentication.md#legacy-authorization-server) | streamable-http | — | — | ✅ | — | — | — | — | — | ✅ | ✅ |
+| [Resource server with introspection](examples-authentication.md#resource-server-with-introspection) | streamable-http | — | — | ✅ | — | — | — | — | — | ✅ | ✅ |
+| [Simple prompt server](examples-server-prompts.md#simple-prompt-server) | stdio | — | ✅ | — | — | — | — | — | — | — | — |
+| [Simple resource server](examples-server-resources.md#simple-resource-server) | stdio | ✅ | — | — | — | — | — | — | — | — | — |
+| [Stateless HTTP server](examples-transport-http.md#stateless-http-server) | streamable-http | — | — | ✅ | — | — | — | — | ✅ | — | ✅ |
+| [Stateful HTTP server](examples-transport-http.md#stateful-http-server) | streamable-http | — | — | ✅ | — | — | — | — | ✅ | — | ✅ |
+| [Simple tool server](examples-lowlevel-servers.md#simple-tool-server) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Low-level structured output](examples-structured-output.md#low-level-structured-output) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Basic prompts](examples-server-prompts.md#basic-prompts) | stdio | — | ✅ | — | — | — | — | — | — | — | — |
+| [Basic resources](examples-server-resources.md#basic-resources) | stdio | ✅ | — | — | — | — | — | — | — | — | ✅ |
+| [Basic tools](examples-server-tools.md#basic-tools) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Completion support](examples-server-advanced.md#completion-support) | stdio | ✅ | ✅ | — | ✅ | — | — | — | — | — | — |
+| [Direct execution](examples-quickstart.md#direct-execution) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [User interaction and elicitation](examples-server-advanced.md#user-interaction-and-elicitation) | stdio | — | — | ✅ | — | — | ✅ | — | — | — | — |
+| [FastMCP quickstart](examples-quickstart.md#fastmcp-quickstart) | stdio | ✅ | ✅ | ✅ | — | — | — | — | — | — | — |
+| [Image handling](examples-server-advanced.md#image-handling) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Lifespan management](examples-server-advanced.md#lifespan-management) | stdio | — | — | ✅ | — | — | — | — | — | — | ✅ |
+| [Basic low-level server](examples-lowlevel-servers.md#basic-low-level-server) | stdio | — | ✅ | — | — | — | — | — | — | — | — |
+| [Low-level server with lifespan](examples-lowlevel-servers.md#low-level-server-with-lifespan) | stdio | — | — | ✅ | — | — | — | — | — | — | ✅ |
+| [Low-level structured output](examples-structured-output.md#low-level-structured-output) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Logging and notifications](examples-server-advanced.md#logging-and-notifications) | stdio | — | — | ✅ | — | — | — | — | ✅ | — | — |
+| [OAuth server implementation](examples-authentication.md#oauth-server-implementation) | streamable-http | — | — | ✅ | — | — | — | — | — | ✅ | — |
+| [LLM sampling and integration](examples-server-advanced.md#llm-sampling-and-integration) | stdio | — | — | ✅ | — | ✅ | — | — | — | — | — |
+| [Streamable HTTP configuration](examples-transport-http.md#streamable-http-configuration) | streamable-http | — | — | ✅ | — | — | — | — | — | — | ✅ |
+| [Mounting multiple servers](examples-transport-http.md#mounting-multiple-servers) | streamable-http | — | — | ✅ | — | — | — | — | — | — | ✅ |
+| [FastMCP structured output](examples-structured-output.md#fastmcp-structured-output) | stdio | — | — | ✅ | — | — | — | — | — | — | — |
+| [Tools with context and progress reporting](examples-server-tools.md#tools-with-context-and-progress-reporting) | stdio | — | — | ✅ | — | — | — | ✅ | ✅ | — | — |
+
+### Clients
+
+| File | Transport | Resources | Prompts | Tools | Completions | Sampling | Authentication |
+|---|---|---|---|---|---|---|---|
+| [Authentication client](examples-clients.md#authentication-client) | streamable-http | — | — | ✅ | — | — | ✅ |
+| [Complete chatbot client](examples-clients.md#complete-chatbot-client) | stdio | — | — | ✅ | — | — | — |
+| [Completion client](examples-clients.md#completion-client) | stdio | ✅ | ✅ | — | ✅ | — | — |
+| [Display utilities](examples-clients.md#display-utilities) | stdio | ✅ | — | ✅ | — | — | — |
+| [OAuth authentication client](examples-clients.md#oauth-authentication-client) | streamable-http | ✅ | — | ✅ | — | — | ✅ |
+| [Tool result parsing](examples-clients.md#tool-result-parsing) | stdio | — | — | ✅ | — | — | — |
+| [Basic stdio client](examples-clients.md#basic-stdio-client) | stdio | ✅ | ✅ | ✅ | — | ✅ | — |
+| [Streamable HTTP client](examples-clients.md#streamable-http-client) | streamable-http | — | — | ✅ | — | — | — |
+
+Notes:
+
+- **Resources** for clients indicates the example uses the Resources API (reading resources or listing resource templates).
+- **Completions** refers to the completion/complete API for argument autocompletion.
+- **Sampling** indicates the example exercises the sampling/createMessage flow (server-initiated in server examples; client-provided callback in stdio_client).
+- **Authentication** indicates OAuth support is implemented in the example.
+- Em dash (—) indicates **not demonstrated** in the example.
diff --git a/mkdocs.yml b/mkdocs.yml
index b907cb873..7d03fea64 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -1,18 +1,31 @@
-site_name: MCP Server
-site_description: MCP Server
-strict: true
+site_name: MCP Python SDK
+site_description: Python implementation of the Model Context Protocol (MCP)
+strict: false
-repo_name: modelcontextprotocol/python-sdk
-repo_url: https://github.com/modelcontextprotocol/python-sdk
+repo_name: mmacy/python-sdk
+repo_url: https://github.com/mmacy/python-sdk
edit_uri: edit/main/docs/
-site_url: https://modelcontextprotocol.github.io/python-sdk
+site_url: https://mmacy.github.io/python-sdk
# TODO(Marcelo): Add Anthropic copyright?
# copyright: © Model Context Protocol 2025 to present
nav:
- Home: index.md
- - API Reference: api.md
+ - Code examples:
+ - Getting started: examples-quickstart.md
+ - Echo servers: examples-echo-servers.md
+ - Server development:
+ - Tools: examples-server-tools.md
+ - Resources: examples-server-resources.md
+ - Prompts: examples-server-prompts.md
+ - Structured output: examples-structured-output.md
+ - Advanced patterns: examples-server-advanced.md
+ - Transport protocols:
+ - HTTP transport: examples-transport-http.md
+ - Low-level servers: examples-lowlevel-servers.md
+ - Authentication: examples-authentication.md
+ - Client development: examples-clients.md
theme:
name: "material"
@@ -99,22 +112,24 @@ watch:
plugins:
- search
- - social
+ # - social # Disabled due to Cairo dependency issues
- glightbox
- mkdocstrings:
handlers:
python:
paths: [src/mcp]
options:
+ group_by_category: false
+ # members_order: source
relative_crossrefs: true
- members_order: source
separate_signature: true
show_signature_annotations: true
+ show_source: false
signature_crossrefs: true
- group_by_category: false
- # 3 because docs are in pages with an H2 just above them
- heading_level: 3
import:
- url: https://docs.python.org/3/objects.inv
- url: https://docs.pydantic.dev/latest/objects.inv
- url: https://typing-extensions.readthedocs.io/en/latest/objects.inv
+ - url: https://pillow.readthedocs.io/en/stable/objects.inv
+ - api-autonav:
+ modules: ["src/mcp"]
diff --git a/pyproject.toml b/pyproject.toml
index 9b84c5815..73c6f6148 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -62,6 +62,7 @@ dev = [
]
docs = [
"mkdocs>=1.6.1",
+ "mkdocs-api-autonav>=0.3.1",
"mkdocs-glightbox>=0.4.0",
"mkdocs-material[imaging]>=9.5.45",
"mkdocstrings-python>=1.12.2",
diff --git a/src/mcp/__init__.py b/src/mcp/__init__.py
index e93b95c90..8de2e4e47 100644
--- a/src/mcp/__init__.py
+++ b/src/mcp/__init__.py
@@ -1,3 +1,47 @@
+"""An implementation of the [Model Context Protocol (MCP) specification](https://modelcontextprotocol.io/specification/latest) in Python.
+
+Use the MCP Python SDK to:
+
+- Build MCP clients that can connect to any MCP server
+- Build MCP servers that expose resources, prompts, and tools
+- Use standard transports like stdio, SSE, and Streamable HTTP
+- Handle MCP protocol messages and lifecycle events
+
+## Example - create a [`FastMCP`][mcp.server.fastmcp.FastMCP] server
+
+```python
+from mcp.server.fastmcp import FastMCP
+
+# Create an MCP server
+mcp = FastMCP("Demo")
+
+@mcp.tool()
+def add(a: int, b: int) -> int:
+ \"\"\"Add two numbers\"\"\"
+ return a + b
+
+if __name__ == "__main__":
+ mcp.run()
+```
+
+## Example - create a client
+
+```python
+from mcp import ClientSession, StdioServerParameters, stdio_client
+
+server_params = StdioServerParameters(
+ command="python", args=["server.py"]
+)
+
+async with stdio_client(server_params) as (read, write):
+ async with ClientSession(read, write) as session:
+ await session.initialize()
+ tools = await session.list_tools()
+ result = await session.call_tool("add", {"a": 5, "b": 3})
+```
+
+"""
+
from .client.session import ClientSession
from .client.session_group import ClientSessionGroup
from .client.stdio import StdioServerParameters, stdio_client
diff --git a/src/mcp/client/session.py b/src/mcp/client/session.py
index 1853ce7c1..4f0d0bbec 100644
--- a/src/mcp/client/session.py
+++ b/src/mcp/client/session.py
@@ -107,6 +107,46 @@ class ClientSession(
types.ServerNotification,
]
):
+ """A client session for communicating with an MCP server.
+
+ This class provides a high-level interface for MCP client operations, including
+ tool calling, resource management, prompt handling, and protocol initialization.
+ It manages the bidirectional communication channel with an MCP server and handles
+ protocol-level concerns like message validation and capability negotiation.
+
+ The session supports various MCP capabilities:
+
+ - Tool execution with structured output validation
+ - Resource access and subscription management
+ - Prompt template retrieval and completion
+ - Progress notifications and logging
+ - Custom sampling, elicitation, and root listing callbacks
+
+ Args:
+ read_stream: Stream for receiving messages from the server.
+ write_stream: Stream for sending messages to the server.
+ read_timeout_seconds: Optional timeout for read operations.
+ sampling_callback: Optional callback for handling sampling requests from the server.
+ elicitation_callback: Optional callback for handling elicitation requests from the server.
+ list_roots_callback: Optional callback for handling root listing requests from the server.
+ logging_callback: Optional callback for handling log messages from the server.
+ message_handler: Optional custom handler for incoming messages and exceptions.
+ client_info: Optional client implementation information.
+
+ Example:
+ ```python
+ async with create_client_session() as session:
+ # Initialize the session
+ await session.initialize()
+
+ # List available tools
+ tools = await session.list_tools()
+
+ # Call a tool
+ result = await session.call_tool("my_tool", {"arg": "value"})
+ ```
+ """
+
def __init__(
self,
read_stream: MemoryObjectReceiveStream[SessionMessage | Exception],
@@ -135,6 +175,17 @@ def __init__(
self._tool_output_schemas: dict[str, dict[str, Any] | None] = {}
async def initialize(self) -> types.InitializeResult:
+ """Initialize the MCP session with the server.
+
+ Sends an initialization request to establish capabilities and protocol version.
+ This must be called before any other operations can be performed.
+
+ Returns:
+ Server's initialization response containing capabilities and metadata
+
+ Raises:
+ McpError: If initialization fails or protocol version is unsupported
+ """
sampling = types.SamplingCapability() if self._sampling_callback is not _default_sampling_callback else None
elicitation = (
types.ElicitationCapability() if self._elicitation_callback is not _default_elicitation_callback else None
@@ -288,7 +339,81 @@ async def call_tool(
read_timeout_seconds: timedelta | None = None,
progress_callback: ProgressFnT | None = None,
) -> types.CallToolResult:
- """Send a tools/call request with optional progress callback support."""
+ """Execute a tool on the connected MCP server.
+
+ This method sends a tools/call request to execute a specific tool with provided
+ arguments. The server will validate the arguments against the tool's input schema
+ and return structured or unstructured content based on the tool's configuration.
+
+ For tools that return structured output, the result will be automatically validated
+ against the tool's output schema if one is defined. Tools may also return various
+ content types including text, images, and embedded resources.
+
+ Args:
+ name: The name of the tool to execute. Must match a tool exposed by the server.
+ arguments: Optional dictionary of arguments to pass to the tool. The structure
+ must match the tool's input schema. Defaults to None for tools that don't
+ require arguments.
+ read_timeout_seconds: Optional timeout for the tool execution. If not specified,
+ uses the session's default read timeout. Useful for long-running tools.
+ progress_callback: Optional callback function to receive progress updates during
+ tool execution. The callback receives progress notifications as they're sent
+ by the server.
+
+ Returns:
+ CallToolResult containing the tool's response. The result includes:
+ - content: List of content blocks (text, images, embedded resources)
+ - structuredContent: Validated structured data if the tool has an output schema
+ - isError: Boolean indicating if the tool execution failed
+
+ Raises:
+ RuntimeError: If the tool returns structured content that doesn't match its
+ output schema, or if the tool name is not found on the server.
+ ValidationError: If the provided arguments don't match the tool's input schema.
+ TimeoutError: If the tool execution exceeds the specified timeout.
+
+ Example:
+ ```python
+ # Simple tool call without arguments
+ result = await session.call_tool("ping")
+
+ # Tool call with arguments
+ result = await session.call_tool("add", {"a": 5, "b": 3})
+
+ # Access text content
+ for content in result.content:
+ if isinstance(content, types.TextContent):
+ print(content.text)
+
+ # Access structured output (if available)
+ if result.structuredContent:
+ user_data = result.structuredContent
+ print(f"Result: {user_data}")
+
+ # Handle tool execution errors
+ if result.isError:
+ print("Tool execution failed")
+
+ # Long-running tool with progress tracking
+ def on_progress(progress_token, progress, total, message):
+ percent = (progress / total) * 100 if total else 0
+ print(f"Progress: {percent:.1f}% - {message}")
+
+ result = await session.call_tool(
+ "long_task",
+ {"steps": 10},
+ read_timeout_seconds=timedelta(minutes=5),
+ progress_callback=on_progress
+ )
+ ```
+
+ Note:
+ Tools may return different content types:
+ - TextContent: Plain text responses
+ - ImageContent: Generated images with MIME type and binary data
+ - EmbeddedResource: File contents or external resources
+ - Structured data via structuredContent when output schema is defined
+ """
result = await self.send_request(
types.ClientRequest(
diff --git a/src/mcp/client/session_group.py b/src/mcp/client/session_group.py
index 700b5417f..79fb702d6 100644
--- a/src/mcp/client/session_group.py
+++ b/src/mcp/client/session_group.py
@@ -75,12 +75,14 @@ class ClientSessionGroup:
the client and can be accessed via the session.
Example Usage:
- name_fn = lambda name, server_info: f"{(server_info.name)}_{name}"
- async with ClientSessionGroup(component_name_hook=name_fn) as group:
- for server_params in server_params:
- await group.connect_to_server(server_param)
- ...
+ ```python
+ name_fn = lambda name, server_info: f"{(server_info.name)}_{name}"
+ async with ClientSessionGroup(component_name_hook=name_fn) as group:
+ for server_params in server_params:
+ await group.connect_to_server(server_param)
+ # ...
+ ```
"""
class _ComponentNames(BaseModel):
diff --git a/src/mcp/server/fastmcp/exceptions.py b/src/mcp/server/fastmcp/exceptions.py
index fb5bda106..2a9edefa0 100644
--- a/src/mcp/server/fastmcp/exceptions.py
+++ b/src/mcp/server/fastmcp/exceptions.py
@@ -2,20 +2,52 @@
class FastMCPError(Exception):
- """Base error for FastMCP."""
+ """Base exception class for all FastMCP-related errors.
+
+ This is the root exception type for all errors that can occur within
+ the FastMCP framework. Specific error types inherit from this class.
+ """
class ValidationError(FastMCPError):
- """Error in validating parameters or return values."""
+ """Raised when parameter or return value validation fails.
+
+ This exception is raised when input arguments don't match a tool's
+ input schema, or when output values fail validation against output schemas.
+ It typically indicates incorrect data types, missing required fields,
+ or values that don't meet schema constraints.
+ """
class ResourceError(FastMCPError):
- """Error in resource operations."""
+ """Raised when resource operations fail.
+
+ This exception is raised for resource-related errors such as:
+
+ - Resource not found for a given URI
+ - Resource content cannot be read or generated
+ - Resource template parameter validation failures
+ - Resource access permission errors
+ """
class ToolError(FastMCPError):
- """Error in tool operations."""
+ """Raised when tool operations fail.
+
+ This exception is raised for tool-related errors such as:
+
+ - Tool not found for a given name
+ - Tool execution failures or unhandled exceptions
+ - Tool registration conflicts or validation errors
+ - Tool parameter or result processing errors
+ """
class InvalidSignature(Exception):
- """Invalid signature for use with FastMCP."""
+ """Raised when a function signature is incompatible with FastMCP.
+
+ This exception is raised when trying to register a function as a tool,
+ resource, or prompt that has an incompatible signature. This can occur
+ when functions have unsupported parameter types, complex annotations
+ that cannot be converted to JSON schema, or other signature issues.
+ """
diff --git a/src/mcp/server/fastmcp/prompts/base.py b/src/mcp/server/fastmcp/prompts/base.py
index b45cfc917..cf5d2b0cf 100644
--- a/src/mcp/server/fastmcp/prompts/base.py
+++ b/src/mcp/server/fastmcp/prompts/base.py
@@ -74,6 +74,7 @@ def from_function(
"""Create a Prompt from a function.
The function can return:
+
- A string (converted to a message)
- A Message object
- A dict (converted to a message)
diff --git a/src/mcp/server/fastmcp/prompts/manager.py b/src/mcp/server/fastmcp/prompts/manager.py
index 6b01d91cd..1aa949b59 100644
--- a/src/mcp/server/fastmcp/prompts/manager.py
+++ b/src/mcp/server/fastmcp/prompts/manager.py
@@ -1,4 +1,70 @@
-"""Prompt management functionality."""
+"""Prompt management functionality for FastMCP servers.
+
+This module provides the PromptManager class, which serves as the central registry
+for managing prompts in FastMCP servers. Prompts are reusable templates that generate
+structured messages for AI model interactions, enabling consistent and parameterized
+communication patterns.
+
+The PromptManager handles the complete lifecycle of prompts:
+
+- Registration and storage of prompt templates
+- Retrieval by name for use in MCP protocol handlers
+- Rendering with arguments to produce message sequences
+- Duplicate detection and management
+
+Key concepts:
+
+- Prompts are created from functions using Prompt.from_function()
+- Each prompt has a unique name used for registration and retrieval
+- Prompts can accept typed arguments for dynamic content generation
+- Rendered prompts return Message objects ready for AI model consumption
+
+Examples:
+ Basic prompt management workflow:
+
+ ```python
+ from mcp.server.fastmcp.prompts import PromptManager, Prompt
+
+ # Initialize the manager
+ manager = PromptManager()
+
+ # Create a prompt from a function
+ def analysis_prompt(topic: str, context: str) -> list[str]:
+ return [
+ f"Please analyze the following topic: {topic}",
+ f"Additional context: {context}",
+ "Provide a detailed analysis with key insights."
+ ]
+
+ # Register the prompt
+ prompt = Prompt.from_function(analysis_prompt)
+ manager.add_prompt(prompt)
+
+ # Render the prompt with arguments
+ messages = await manager.render_prompt(
+ "analysis_prompt",
+ {"topic": "AI Safety", "context": "Enterprise deployment"}
+ )
+ ```
+
+ Integration with FastMCP servers:
+
+ ```python
+ from mcp.server.fastmcp import FastMCP
+
+ mcp = FastMCP("My Server")
+
+ @mcp.prompt()
+ def code_review(language: str, code: str) -> str:
+ return f"Review this {language} code for best practices:\\n\\n{code}"
+
+ # The prompt is automatically registered with the server's PromptManager
+ ```
+
+Note:
+ This module is primarily used internally by FastMCP servers, but can be used
+ directly for advanced prompt management scenarios or custom MCP implementations.
+"""
from typing import Any
@@ -9,25 +75,91 @@
class PromptManager:
- """Manages FastMCP prompts."""
+ """Manages prompt registration, storage, and rendering for FastMCP servers.
+
+ The PromptManager is the central registry for all prompts in a FastMCP server. It handles
+ prompt registration, retrieval by name, listing all available prompts, and rendering
+ prompts with provided arguments. Prompts are templates that can generate structured
+ messages for AI model interactions.
+
+ This class is typically used internally by FastMCP servers but can be used directly
+ for advanced prompt management scenarios.
+
+ Args:
+ warn_on_duplicate_prompts: Whether to log warnings when attempting to register
+ a prompt with a name that already exists. Defaults to True.
+
+ Attributes:
+ warn_on_duplicate_prompts: Whether duplicate prompt warnings are enabled.
+
+ Examples:
+ Basic usage:
+
+ ```python
+ from mcp.server.fastmcp.prompts import PromptManager, Prompt
+
+ # Create a manager
+ manager = PromptManager()
+
+ # Create and add a prompt
+ def greeting_prompt(name: str) -> str:
+ return f"Hello, {name}! How can I help you today?"
+
+ prompt = Prompt.from_function(greeting_prompt)
+ manager.add_prompt(prompt)
+
+ # Render the prompt
+ messages = await manager.render_prompt("greeting_prompt", {"name": "Alice"})
+ ```
+
+ Disabling duplicate warnings:
+
+ ```python
+ # Useful in testing scenarios or when you need to replace prompts
+ manager = PromptManager(warn_on_duplicate_prompts=False)
+ ```
+ """
def __init__(self, warn_on_duplicate_prompts: bool = True):
self._prompts: dict[str, Prompt] = {}
self.warn_on_duplicate_prompts = warn_on_duplicate_prompts
def get_prompt(self, name: str) -> Prompt | None:
- """Get prompt by name."""
+ """Retrieve a registered prompt by its name.
+
+ Args:
+ name: The name of the prompt to retrieve.
+
+ Returns:
+ The Prompt object if found, None if no prompt exists with the given name.
+ """
return self._prompts.get(name)
def list_prompts(self) -> list[Prompt]:
- """List all registered prompts."""
+ """Get a list of all registered prompts.
+
+ Returns:
+ A list containing all Prompt objects currently registered with this manager.
+ Returns an empty list if no prompts are registered.
+ """
return list(self._prompts.values())
def add_prompt(
self,
prompt: Prompt,
) -> Prompt:
- """Add a prompt to the manager."""
+ """Register a prompt with the manager.
+
+ If a prompt with the same name already exists, the existing prompt is returned
+ without modification. A warning is logged if warn_on_duplicate_prompts is True.
+
+ Args:
+ prompt: The Prompt object to register.
+
+ Returns:
+ The registered Prompt object. If a prompt with the same name already exists,
+ returns the existing prompt instead of the new one.
+ """
# Check for duplicates
existing = self._prompts.get(prompt.name)
@@ -40,7 +172,40 @@ def add_prompt(
return prompt
async def render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> list[Message]:
- """Render a prompt by name with arguments."""
+ """Render a prompt into a list of messages ready for AI model consumption.
+
+ This method looks up the prompt by name, validates that all required arguments
+ are provided, executes the prompt function with the given arguments, and converts
+ the result into a standardized list of Message objects.
+
+ Args:
+ name: The name of the prompt to render.
+ arguments: Optional dictionary of arguments to pass to the prompt function.
+ Must include all required arguments defined by the prompt.
+
+ Returns:
+ A list of Message objects containing the rendered prompt content.
+ Each Message has a role ("user" or "assistant") and content.
+
+ Raises:
+ ValueError: If the prompt name is not found or if required arguments are missing.
+
+ Examples:
+ Simple prompt without arguments:
+
+ ```python
+ messages = await manager.render_prompt("welcome")
+ ```
+
+ Prompt with arguments:
+
+ ```python
+ messages = await manager.render_prompt(
+ "greeting",
+ {"name": "Alice", "language": "en"}
+ )
+ ```
+ """
prompt = self.get_prompt(name)
if not prompt:
raise ValueError(f"Unknown prompt: {name}")
diff --git a/src/mcp/server/fastmcp/resources/base.py b/src/mcp/server/fastmcp/resources/base.py
index f57631cc1..660436ab6 100644
--- a/src/mcp/server/fastmcp/resources/base.py
+++ b/src/mcp/server/fastmcp/resources/base.py
@@ -15,7 +15,19 @@
class Resource(BaseModel, abc.ABC):
- """Base class for all resources."""
+ """Base class for all MCP resources.
+
+ Resources provide contextual data that can be read by LLMs. Each resource
+ has a URI, optional metadata like name and description, and content that
+ can be retrieved via the read() method.
+
+ Attributes:
+ uri: Unique identifier for the resource
+ name: Optional name for the resource (defaults to URI if not provided)
+ title: Optional human-readable title
+ description: Optional description of the resource content
+ mime_type: MIME type of the resource content (defaults to text/plain)
+ """
model_config = ConfigDict(validate_default=True)
@@ -32,7 +44,18 @@ class Resource(BaseModel, abc.ABC):
@field_validator("name", mode="before")
@classmethod
def set_default_name(cls, name: str | None, info: ValidationInfo) -> str:
- """Set default name from URI if not provided."""
+ """Set default name from URI if not provided.
+
+ Args:
+ name: The provided name value
+ info: Pydantic validation info containing other field values
+
+ Returns:
+ The name to use for the resource
+
+ Raises:
+ ValueError: If neither name nor uri is provided
+ """
if name:
return name
if uri := info.data.get("uri"):
@@ -41,5 +64,12 @@ def set_default_name(cls, name: str | None, info: ValidationInfo) -> str:
@abc.abstractmethod
async def read(self) -> str | bytes:
- """Read the resource content."""
+ """Read the resource content.
+
+ Returns:
+ The resource content as either a string or bytes
+
+ Raises:
+ ResourceError: If the resource cannot be read
+ """
pass
diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py
index 924baaa9b..fb884efb8 100644
--- a/src/mcp/server/fastmcp/server.py
+++ b/src/mcp/server/fastmcp/server.py
@@ -119,6 +119,84 @@ async def wrap(_: MCPServer[LifespanResultT, Request]) -> AsyncIterator[Lifespan
class FastMCP(Generic[LifespanResultT]):
+ """A high-level ergonomic interface for creating MCP servers.
+
+ FastMCP provides a decorator-based API for building MCP servers with automatic
+ parameter validation, structured output support, and built-in transport handling.
+ It supports stdio, SSE, and Streamable HTTP transports out of the box.
+
+ Features include automatic validation using Pydantic, structured output conversion,
+ context injection for MCP capabilities, lifespan management, multiple transport
+ support, and built-in OAuth 2.1 authentication.
+
+ Args:
+ name: Human-readable name for the server. If None, defaults to "FastMCP"
+ instructions: Optional instructions/description for the server
+ auth_server_provider: OAuth authorization server provider for authentication
+ token_verifier: Token verifier for validating OAuth tokens
+ event_store: Event store for Streamable HTTP transport persistence
+ tools: Pre-configured tools to register with the server
+ debug: Enable debug mode for additional logging
+ log_level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
+ host: Host address for HTTP transports
+ port: Port number for HTTP transports
+ mount_path: Base mount path for SSE transport
+ sse_path: Path for SSE endpoint
+ message_path: Path for message endpoint
+ streamable_http_path: Path for Streamable HTTP endpoint
+ json_response: Whether to use JSON responses instead of SSE for Streamable HTTP
+ stateless_http: Whether to operate in stateless mode for Streamable HTTP
+ warn_on_duplicate_resources: Whether to warn when duplicate resources are registered
+ warn_on_duplicate_tools: Whether to warn when duplicate tools are registered
+ warn_on_duplicate_prompts: Whether to warn when duplicate prompts are registered
+ dependencies: List of package dependencies (currently unused)
+ lifespan: Async context manager for server startup/shutdown lifecycle
+ auth: Authentication settings for OAuth 2.1 support
+ transport_security: Transport security settings
+
+ Examples:
+ Basic server creation:
+
+ ```python
+ from mcp.server.fastmcp import FastMCP
+
+ # Create a server
+ mcp = FastMCP("My Server")
+
+ # Add a tool
+ @mcp.tool()
+ def add_numbers(a: int, b: int) -> int:
+ \"\"\"Add two numbers together.\"\"\"
+ return a + b
+
+ # Add a resource
+ @mcp.resource("greeting://{name}")
+ def get_greeting(name: str) -> str:
+ \"\"\"Get a personalized greeting.\"\"\"
+ return f"Hello, {name}!"
+
+ # Run the server
+ if __name__ == "__main__":
+ mcp.run()
+ ```
+
+ Server with authentication:
+
+ ```python
+ from mcp.server.auth.settings import AuthSettings
+ from pydantic import AnyHttpUrl
+
+ mcp = FastMCP(
+ "Protected Server",
+ auth=AuthSettings(
+ issuer_url=AnyHttpUrl("https://auth.example.com"),
+ resource_server_url=AnyHttpUrl("http://localhost:8000"),
+ required_scopes=["read", "write"]
+ )
+ )
+ ```
+ """
+
def __init__(
self,
name: str | None = None,
@@ -235,7 +313,7 @@ def run(
transport: Literal["stdio", "sse", "streamable-http"] = "stdio",
mount_path: str | None = None,
) -> None:
- """Run the FastMCP server. Note this is a synchronous function.
+ """Run the FastMCP server. This is a synchronous function.
Args:
transport: Transport protocol to use ("stdio", "sse", or "streamable-http")
@@ -282,9 +360,91 @@ async def list_tools(self) -> list[MCPTool]:
]
def get_context(self) -> Context[ServerSession, LifespanResultT, Request]:
- """
- Returns a Context object. Note that the context will only be valid
- during a request; outside a request, most methods will error.
+ """Get the current request context when automatic injection isn't available.
+
+ This method provides access to the current [`Context`][mcp.server.fastmcp.Context]
+ object when you can't rely on FastMCP's automatic parameter injection. It's
+ primarily useful in helper functions, callbacks, or other scenarios where
+ the context isn't automatically provided via function parameters.
+
+ In most cases, you should prefer automatic context injection by declaring
+ a Context parameter in your tool/resource functions. Use this method only
+ when you need context access from code that isn't directly called by FastMCP.
+
+ You might call this method directly in:
+
+ - **Helper functions**
+
+ ```python
+ mcp = FastMCP(name="example")
+
+ async def log_operation(operation: str):
+ # Get context when it's not injected
+ ctx = mcp.get_context()
+ await ctx.info(f"Performing operation: {operation}")
+
+ @mcp.tool()
+ async def main_tool(data: str) -> str:
+ await log_operation("data_processing") # Helper needs context
+ return process_data(data)
+ ```
+
+ - **Callbacks** and **event handlers** when context is needed in async callbacks
+
+ ```python
+ async def progress_callback(current: int, total: int):
+ ctx = mcp.get_context() # Access context in callback
+ await ctx.report_progress(current, total)
+
+ @mcp.tool()
+ async def long_operation(data: str) -> str:
+ return await process_with_callback(data, progress_callback)
+ ```
+
+ - **Class methods** when context is needed in class-based code
+
+ ```python
+ class DataProcessor:
+ def __init__(self, mcp_server: FastMCP):
+ self.mcp = mcp_server
+
+ async def process_chunk(self, chunk: str) -> str:
+ ctx = self.mcp.get_context() # Get context in method
+ await ctx.debug(f"Processing chunk of size {len(chunk)}")
+ return processed_chunk
+
+ processor = DataProcessor(mcp)
+
+ @mcp.tool()
+ async def process_data(data: str) -> str:
+ return await processor.process_chunk(data)
+ ```
+
+ Returns:
+ [`Context`][mcp.server.fastmcp.Context] object for the current request
+ with access to all MCP capabilities including logging, progress reporting,
+ user interaction, and session access.
+
+ Raises:
+ LookupError: If called outside of a request context (e.g., during server
+ initialization, shutdown, or from code not handling a client request).
+
+ Note:
+ **Prefer automatic injection**: In most cases, declare a Context parameter
+ in your function signature instead of calling this method:
+
+ ```python
+ # Preferred approach
+ @mcp.tool()
+ async def my_tool(data: str, ctx: Context) -> str:
+ await ctx.info("Processing data")
+ return result
+
+ # Only use get_context() when injection isn't available
+ async def helper_function():
+ ctx = mcp.get_context()
+ await ctx.info("Helper called")
+ ```
"""
try:
request_context = self._mcp_server.request_context
@@ -293,12 +453,29 @@ def get_context(self) -> Context[ServerSession, LifespanResultT, Request]:
return Context(request_context=request_context, fastmcp=self)
async def call_tool(self, name: str, arguments: dict[str, Any]) -> Sequence[ContentBlock] | dict[str, Any]:
- """Call a tool by name with arguments."""
+ """Call a registered tool by name with the provided arguments.
+
+ Args:
+ name: Name of the tool to call
+ arguments: Dictionary of arguments to pass to the tool
+
+ Returns:
+ Tool execution result, either as content blocks or structured data
+
+ Raises:
+ ToolError: If the tool is not found or execution fails
+ ValidationError: If the arguments don't match the tool's schema
+ """
context = self.get_context()
return await self._tool_manager.call_tool(name, arguments, context=context, convert_result=True)
async def list_resources(self) -> list[MCPResource]:
- """List all available resources."""
+ """List all available resources registered with this server.
+
+ Returns:
+ List of MCP Resource objects containing URI, name, description, and MIME type
+ information for each registered resource.
+ """
resources = self._resource_manager.list_resources()
return [
@@ -313,6 +490,15 @@ async def list_resources(self) -> list[MCPResource]:
]
async def list_resource_templates(self) -> list[MCPResourceTemplate]:
+ """List all available resource templates registered with this server.
+
+ Resource templates define URI patterns that can be dynamically resolved
+ with different parameters to access multiple related resources.
+
+ Returns:
+ List of MCP ResourceTemplate objects containing URI templates, names,
+ and descriptions for each registered resource template.
+ """
templates = self._resource_manager.list_templates()
return [
MCPResourceTemplate(
@@ -325,7 +511,17 @@ async def list_resource_templates(self) -> list[MCPResourceTemplate]:
]
async def read_resource(self, uri: AnyUrl | str) -> Iterable[ReadResourceContents]:
- """Read a resource by URI."""
+ """Read the contents of a resource by its URI.
+
+ Args:
+ uri: The URI of the resource to read
+
+ Returns:
+ Iterable of ReadResourceContents containing the resource data
+
+ Raises:
+ ResourceError: If the resource is not found or cannot be read
+ """
resource = await self._resource_manager.get_resource(uri)
if not resource:
@@ -397,19 +593,22 @@ def tool(
- If False, unconditionally creates an unstructured tool
Example:
- @server.tool()
- def my_tool(x: int) -> str:
- return str(x)
-
- @server.tool()
- def tool_with_context(x: int, ctx: Context) -> str:
- ctx.info(f"Processing {x}")
- return str(x)
-
- @server.tool()
- async def async_tool(x: int, context: Context) -> str:
- await context.report_progress(50, 100)
- return str(x)
+
+ ```python
+ @server.tool()
+ def my_tool(x: int) -> str:
+ return str(x)
+
+ @server.tool()
+ def tool_with_context(x: int, ctx: Context) -> str:
+ ctx.info(f"Processing {x}")
+ return str(x)
+
+ @server.tool()
+ async def async_tool(x: int, context: Context) -> str:
+ await context.report_progress(50, 100)
+ return str(x)
+ ```
"""
# Check if user passed function directly instead of calling decorator
if callable(name):
@@ -439,12 +638,15 @@ def completion(self):
- context: Optional CompletionContext with previously resolved arguments
Example:
- @mcp.completion()
- async def handle_completion(ref, argument, context):
- if isinstance(ref, ResourceTemplateReference):
- # Return completions based on ref, argument, and context
- return Completion(values=["option1", "option2"])
- return None
+
+ ```python
+ @mcp.completion()
+ async def handle_completion(ref, argument, context):
+ if isinstance(ref, ResourceTemplateReference):
+ # Return completions based on ref, argument, and context
+ return Completion(values=["option1", "option2"])
+ return None
+ ```
"""
return self._mcp_server.completion()
@@ -484,23 +686,26 @@ def resource(
mime_type: Optional MIME type for the resource
Example:
- @server.resource("resource://my-resource")
- def get_data() -> str:
- return "Hello, world!"
-
- @server.resource("resource://my-resource")
- async get_data() -> str:
- data = await fetch_data()
- return f"Hello, world! {data}"
-
- @server.resource("resource://{city}/weather")
- def get_weather(city: str) -> str:
- return f"Weather for {city}"
-
- @server.resource("resource://{city}/weather")
- async def get_weather(city: str) -> str:
- data = await fetch_weather(city)
- return f"Weather for {city}: {data}"
+
+ ```python
+ @server.resource("resource://my-resource")
+ def get_data() -> str:
+ return "Hello, world!"
+
+ @server.resource("resource://my-resource")
+ async get_data() -> str:
+ data = await fetch_data()
+ return f"Hello, world! {data}"
+
+ @server.resource("resource://{city}/weather")
+ def get_weather(city: str) -> str:
+ return f"Weather for {city}"
+
+ @server.resource("resource://{city}/weather")
+ async def get_weather(city: str) -> str:
+ data = await fetch_weather(city)
+ return f"Weather for {city}: {data}"
+ ```
"""
# Check if user passed function directly instead of calling decorator
if callable(uri):
@@ -566,32 +771,35 @@ def prompt(
title: Optional human-readable title for the prompt
description: Optional description of what the prompt does
- Example:
- @server.prompt()
- def analyze_table(table_name: str) -> list[Message]:
- schema = read_table_schema(table_name)
- return [
- {
- "role": "user",
- "content": f"Analyze this schema:\n{schema}"
- }
- ]
-
- @server.prompt()
- async def analyze_file(path: str) -> list[Message]:
- content = await read_file(path)
- return [
- {
- "role": "user",
- "content": {
- "type": "resource",
- "resource": {
- "uri": f"file://{path}",
- "text": content
- }
+ Examples:
+
+ ```python
+ @server.prompt()
+ def analyze_table(table_name: str) -> list[Message]:
+ schema = read_table_schema(table_name)
+ return [
+ {
+ "role": "user",
+ "content": f"Analyze this schema: {schema}"
+ }
+ ]
+
+ @server.prompt()
+ async def analyze_file(path: str) -> list[Message]:
+ content = await read_file(path)
+ return [
+ {
+ "role": "user",
+ "content": {
+ "type": "resource",
+ "resource": {
+ "uri": f"file://{path}",
+ "text": content
}
}
- ]
+ }
+ ]
+ ```
"""
# Check if user passed function directly instead of calling decorator
if callable(name):
@@ -630,9 +838,12 @@ def custom_route(
include_in_schema: Whether to include in OpenAPI schema, defaults to True
Example:
- @server.custom_route("/health", methods=["GET"])
- async def health_check(request: Request) -> Response:
- return JSONResponse({"status": "ok"})
+
+ ```python
+ @server.custom_route("/health", methods=["GET"])
+ async def health_check(request: Request) -> Response:
+ return JSONResponse({"status": "ok"})
+ ```
"""
def decorator(
@@ -1007,37 +1218,156 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
class Context(BaseModel, Generic[ServerSessionT, LifespanContextT, RequestT]):
- """Context object providing access to MCP capabilities.
+ """High-level context object providing convenient access to MCP capabilities.
- This provides a cleaner interface to MCP's RequestContext functionality.
- It gets injected into tool and resource functions that request it via type hints.
+ This is FastMCP's user-friendly wrapper around the underlying [`RequestContext`][mcp.shared.context.RequestContext]
+ that provides the same functionality with additional convenience methods and better
+ ergonomics. It gets automatically injected into FastMCP tool and resource functions
+ that declare it in their type hints, eliminating the need to manually access the
+ request context.
- To use context in a tool function, add a parameter with the Context type annotation:
+ The Context object provides access to all MCP capabilities including logging,
+ progress reporting, resource reading, user interaction, capability checking, and
+ access to the underlying session and request metadata. It's the recommended way
+ to interact with MCP functionality in FastMCP applications.
+
+ ## Automatic injection
+
+ Context is automatically injected into functions based on type hints. The parameter
+ name can be anything as long as it's annotated with `Context`. The context parameter
+ is optional - tools that don't need it can omit it entirely.
```python
- @server.tool()
- def my_tool(x: int, ctx: Context) -> str:
- # Log messages to the client
- ctx.info(f"Processing {x}")
- ctx.debug("Debug info")
- ctx.warning("Warning message")
- ctx.error("Error message")
+ from mcp.server.fastmcp import FastMCP, Context
+
+ mcp = FastMCP(name="example")
+
+ @mcp.tool()
+ async def simple_tool(data: str) -> str:
+ # No context needed
+ return f"Processed: {data}"
+
+ @mcp.tool()
+ async def advanced_tool(data: str, ctx: Context) -> str:
+ # Context automatically injected
+ await ctx.info("Starting processing")
+ return f"Processed: {data}"
+ ```
+
+ ## Relationship to RequestContext
+
+ Context is a thin wrapper around [`RequestContext`][mcp.shared.context.RequestContext]
+ that provides the same underlying functionality with additional convenience methods:
- # Report progress
- ctx.report_progress(50, 100)
+ - **Context convenience methods**: `ctx.info()`, `ctx.error()`, `ctx.elicit()`, etc.
+ - **Direct RequestContext access**: `ctx.request_context` for low-level operations
+ - **Session access**: `ctx.session` for advanced ServerSession functionality
+ - **Request metadata**: `ctx.request_id`, access to lifespan context, etc.
- # Access resources
- data = ctx.read_resource("resource://data")
+ ## Capabilities provided
- # Get request info
- request_id = ctx.request_id
- client_id = ctx.client_id
+ **Logging**: Send structured log messages to the client with automatic request linking:
- return str(x)
+ ```python
+ await ctx.debug("Detailed debug information")
+ await ctx.info("General status updates")
+ await ctx.warning("Important warnings")
+ await ctx.error("Error conditions")
+ ```
+
+ **Progress reporting**: Keep users informed during long operations:
+
+ ```python
+ for i in range(100):
+ await ctx.report_progress(i, 100, f"Processing item {i}")
+ # ... do work
+ ```
+
+ **User interaction**: Collect additional information during tool execution:
+
+ ```python
+ class UserPrefs(BaseModel):
+ format: str
+ detailed: bool
+
+ result = await ctx.elicit("How should I format the output?", UserPrefs)
+ if result.action == "accept":
+ format_data(data, result.data.format)
+ ```
+
+ **Resource access**: Read MCP resources during tool execution:
+
+ ```python
+ content = await ctx.read_resource("file://data/config.json")
+ ```
+
+ **Capability checking**: Verify client support before using advanced features:
+
+ ```python
+ if ctx.session.check_client_capability(types.ClientCapabilities(sampling=...)):
+ # Use advanced features
+ pass
+ ```
+
+ ## Examples
+
+ Complete tool with context usage:
+
+ ```python
+ from pydantic import BaseModel
+ from mcp.server.fastmcp import FastMCP, Context
+
+ class ProcessingOptions(BaseModel):
+ format: str
+ include_metadata: bool
+
+ mcp = FastMCP(name="processor")
+
+ @mcp.tool()
+ async def process_data(
+ data: str,
+ ctx: Context,
+ auto_format: bool = False
+ ) -> str:
+ await ctx.info(f"Starting to process {len(data)} characters")
+
+ # Get user preferences if not auto-formatting
+ if not auto_format:
+ if ctx.session.check_client_capability(
+ types.ClientCapabilities(elicitation=types.ElicitationCapability())
+ ):
+ prefs_result = await ctx.elicit(
+ "How would you like the data processed?",
+ ProcessingOptions
+ )
+ if prefs_result.action == "accept":
+ format_type = prefs_result.data.format
+ include_meta = prefs_result.data.include_metadata
+ else:
+ await ctx.warning("Using default format")
+ format_type = "standard"
+ include_meta = False
+ else:
+ format_type = "standard"
+ include_meta = False
+ else:
+ format_type = "auto"
+ include_meta = True
+
+ # Process with progress updates
+ for i in range(0, len(data), 100):
+ chunk = data[i:i+100]
+ await ctx.report_progress(i, len(data), f"Processing chunk {i//100 + 1}")
+ # ... process chunk
+
+ await ctx.info(f"Processing complete with format: {format_type}")
+ return processed_data
```
- The context parameter name can be anything as long as it's annotated with Context.
- The context is optional - tools that don't need it can omit the parameter.
+ Note:
+ Context objects are request-scoped and automatically managed by FastMCP.
+ Don't store references to them beyond the request lifecycle. Each tool
+ invocation gets a fresh Context instance tied to that specific request.
"""
_request_context: RequestContext[ServerSessionT, LifespanContextT, RequestT] | None
@@ -1065,7 +1395,36 @@ def fastmcp(self) -> FastMCP:
def request_context(
self,
) -> RequestContext[ServerSessionT, LifespanContextT, RequestT]:
- """Access to the underlying request context."""
+ """Access to the underlying RequestContext for low-level operations.
+
+ This property provides direct access to the [`RequestContext`][mcp.shared.context.RequestContext]
+ that this Context wraps. Use this when you need low-level access to request
+ metadata, lifespan context, or other features not exposed by Context's
+ convenience methods.
+
+ Most users should prefer Context's convenience methods like `info()`, `elicit()`,
+ etc. rather than accessing the underlying RequestContext directly.
+
+ Returns:
+ The underlying [`RequestContext`][mcp.shared.context.RequestContext] containing
+ session, metadata, and lifespan context.
+
+ Raises:
+ ValueError: If called outside of a request context.
+
+ Example:
+ ```python
+ @mcp.tool()
+ async def advanced_tool(data: str, ctx: Context) -> str:
+ # Access lifespan context directly
+ db = ctx.request_context.lifespan_context["database"]
+
+ # Access request metadata
+ progress_token = ctx.request_context.meta.progressToken if ctx.request_context.meta else None
+
+ return processed_data
+ ```
+ """
if self._request_context is None:
raise ValueError("Context is not available outside of a request")
return self._request_context
@@ -1107,26 +1466,132 @@ async def elicit(
message: str,
schema: type[ElicitSchemaModelT],
) -> ElicitationResult[ElicitSchemaModelT]:
- """Elicit information from the client/user.
+ """Elicit structured information from the client or user during tool execution.
+
+ This method enables interactive data collection from clients during tool processing.
+ The client may display the message to the user and collect a response according to
+ the provided Pydantic schema, or if the client is an agent, it may automatically
+ generate an appropriate response. This is useful for gathering additional parameters,
+ user preferences, or confirmation before proceeding with operations.
- This method can be used to interactively ask for additional information from the
- client within a tool's execution. The client might display the message to the
- user and collect a response according to the provided schema. Or in case a
- client is an agent, it might decide how to handle the elicitation -- either by asking
- the user or automatically generating a response.
+ You typically access this method through the [`Context`][mcp.server.fastmcp.Context]
+ object injected into your FastMCP tool functions. Always check that the client
+ supports elicitation using [`check_client_capability`][mcp.server.session.ServerSession.check_client_capability]
+ before calling this method.
Args:
- schema: A Pydantic model class defining the expected response structure, according to the specification,
- only primive types are allowed.
- message: Optional message to present to the user. If not provided, will use
- a default message based on the schema
+ message: The prompt or question to present to the user. Should clearly explain
+ what information is being requested and why it's needed.
+ schema: A Pydantic model class defining the expected response structure.
+ According to the MCP specification, only primitive types (str, int, float, bool)
+ and simple containers (list, dict) are allowed - no complex nested objects.
Returns:
- An ElicitationResult containing the action taken and the data if accepted
+ `ElicitationResult` containing:
+
+ - `action`: One of "accept", "decline", or "cancel" indicating user response
+ - `data`: The structured response data (only populated if action is "accept")
+
+ Raises:
+ RuntimeError: If called before session initialization is complete.
+ ValidationError: If the client response doesn't match the provided schema.
+ Various exceptions: Depending on client implementation and user interaction.
+
+ Examples:
+ Collect user preferences before processing:
+
+ ```python
+ from pydantic import BaseModel
+ from mcp.server.fastmcp import FastMCP, Context
+
+ class ProcessingOptions(BaseModel):
+ format: str
+ include_metadata: bool
+ max_items: int
+
+ mcp = FastMCP(name="example-server")
+
+ @mcp.tool()
+ async def process_data(data: str, ctx: Context) -> str:
+ # Check if client supports elicitation
+ if not ctx.session.check_client_capability(
+ types.ClientCapabilities(elicitation=types.ElicitationCapability())
+ ):
+ # Fall back to default processing
+ return process_with_defaults(data)
+
+ # Ask user for processing preferences
+ result = await ctx.elicit(
+ "How would you like me to process this data?",
+ ProcessingOptions
+ )
+
+ if result.action == "accept":
+ options = result.data
+ await ctx.info(f"Processing with format: {options.format}")
+ return process_with_options(data, options)
+ elif result.action == "decline":
+ return process_with_defaults(data)
+ else: # cancel
+ return "Processing cancelled by user"
+ ```
+
+ Confirm before destructive operations:
+
+ ```python
+ class ConfirmDelete(BaseModel):
+ confirm: bool
+ reason: str
+
+ @mcp.tool()
+ async def delete_files(pattern: str, ctx: Context) -> str:
+ files = find_matching_files(pattern)
+
+ result = await ctx.elicit(
+ f"About to delete {len(files)} files matching '{pattern}'. Continue?",
+ ConfirmDelete
+ )
+
+ if result.action == "accept" and result.data.confirm:
+ await ctx.info(f"Deletion confirmed: {result.data.reason}")
+ return delete_files(files)
+ else:
+ return "Deletion cancelled"
+ ```
+
+ Handle different response types:
+
+ ```python
+ class UserChoice(BaseModel):
+ option: str # "auto", "manual", "skip"
+ details: str
+
+ @mcp.tool()
+ async def configure_system(ctx: Context) -> str:
+ result = await ctx.elicit(
+ "How should I configure the system?",
+ UserChoice
+ )
+
+ match result.action:
+ case "accept":
+ choice = result.data
+ await ctx.info(f"User selected: {choice.option}")
+ return configure_with_choice(choice)
+ case "decline":
+ await ctx.warning("User declined configuration")
+ return "Configuration skipped by user"
+ case "cancel":
+ await ctx.info("Configuration cancelled")
+ return "Operation cancelled"
+ ```
Note:
- Check the result.action to determine if the user accepted, declined, or cancelled.
- The result.data will only be populated if action is "accept" and validation succeeded.
+ The client determines how to handle elicitation requests. Some clients may
+ show interactive forms to users, while others may automatically generate
+ responses based on context. Always handle all possible action values
+ ("accept", "decline", "cancel") in your code and provide appropriate
+ fallbacks for clients that don't support elicitation.
"""
return await elicit_with_validation(
@@ -1146,7 +1611,6 @@ async def log(
level: Log level (debug, info, warning, error)
message: Log message
logger_name: Optional logger name
- **extra: Additional structured data to include
"""
await self.request_context.session.send_log_message(
level=level,
@@ -1162,12 +1626,79 @@ def client_id(self) -> str | None:
@property
def request_id(self) -> str:
- """Get the unique ID for this request."""
+ """Get the unique identifier for the current request.
+
+ This ID uniquely identifies the current client request and is useful for
+ logging, tracing, error reporting, and linking related operations. It's
+ automatically used by Context's convenience methods when sending notifications
+ or responses to ensure they're associated with the correct request.
+
+ Returns:
+ str: Unique request identifier that can be used for tracing and logging.
+
+ Example:
+ ```python
+ @mcp.tool()
+ async def traceable_tool(data: str, ctx: Context) -> str:
+ # Log with request ID for traceability
+ print(f"Processing request {ctx.request_id}")
+
+ # Request ID is automatically included in Context methods
+ await ctx.info("Starting processing") # Links to this request
+
+ return processed_data
+ ```
+ """
return str(self.request_context.request_id)
@property
- def session(self):
- """Access to the underlying session for advanced usage."""
+ def session(self) -> ServerSession:
+ """Access to the underlying ServerSession for advanced MCP operations.
+
+ This property provides direct access to the [`ServerSession`][mcp.server.session.ServerSession]
+ for advanced operations not covered by Context's convenience methods. Use this
+ when you need direct session control, capability checking, or low-level MCP
+ protocol operations.
+
+ Most users should prefer Context's convenience methods (`info()`, `elicit()`, etc.)
+ which internally use this session with appropriate request linking.
+
+ Returns:
+ [`ServerSession`][mcp.server.session.ServerSession]: The session for
+ communicating with the client and accessing advanced MCP features.
+
+ Examples:
+ Capability checking before using advanced features:
+
+ ```python
+ @mcp.tool()
+ async def advanced_tool(data: str, ctx: Context) -> str:
+ # Check client capabilities
+ if ctx.session.check_client_capability(
+ types.ClientCapabilities(sampling=types.SamplingCapability())
+ ):
+ # Use LLM sampling
+ response = await ctx.session.create_message(
+ messages=[types.SamplingMessage(...)],
+ max_tokens=100
+ )
+ return response.content.text
+ else:
+ return "Client doesn't support LLM sampling"
+ ```
+
+ Direct resource notifications:
+
+ ```python
+ @mcp.tool()
+ async def update_resource(uri: str, ctx: Context) -> str:
+ # ... update the resource ...
+
+ # Notify client of resource changes
+ await ctx.session.send_resource_updated(AnyUrl(uri))
+ return "Resource updated"
+ ```
+ """
return self.request_context.session
# Convenience methods for common log levels
diff --git a/src/mcp/server/fastmcp/tools/base.py b/src/mcp/server/fastmcp/tools/base.py
index f50126081..24716a21f 100644
--- a/src/mcp/server/fastmcp/tools/base.py
+++ b/src/mcp/server/fastmcp/tools/base.py
@@ -48,7 +48,23 @@ def from_function(
annotations: ToolAnnotations | None = None,
structured_output: bool | None = None,
) -> Tool:
- """Create a Tool from a function."""
+ """Create a Tool from a function.
+
+ Args:
+ fn: The function to wrap as a tool
+ name: Optional name for the tool (defaults to function name)
+ title: Optional human-readable title for the tool
+ description: Optional description (defaults to function docstring)
+ context_kwarg: Name of parameter that should receive the Context object
+ annotations: Optional tool annotations for additional metadata
+ structured_output: Whether to enable structured output for this tool
+
+ Returns:
+ Tool instance configured from the function
+
+ Raises:
+ ValueError: If the function is a lambda without a provided name
+ """
from mcp.server.fastmcp.server import Context
func_name = name or fn.__name__
@@ -93,7 +109,19 @@ async def run(
context: Context[ServerSessionT, LifespanContextT, RequestT] | None = None,
convert_result: bool = False,
) -> Any:
- """Run the tool with arguments."""
+ """Run the tool with the provided arguments.
+
+ Args:
+ arguments: Dictionary of arguments to pass to the tool function
+ context: Optional MCP context for accessing capabilities
+ convert_result: Whether to convert the result using the function metadata
+
+ Returns:
+ The tool's execution result, potentially converted based on convert_result
+
+ Raises:
+ ToolError: If tool execution fails or validation errors occur
+ """
try:
result = await self.fn_metadata.call_fn_with_arg_validation(
self.fn,
diff --git a/src/mcp/server/fastmcp/tools/tool_manager.py b/src/mcp/server/fastmcp/tools/tool_manager.py
index bfa8b2382..e0c6901a9 100644
--- a/src/mcp/server/fastmcp/tools/tool_manager.py
+++ b/src/mcp/server/fastmcp/tools/tool_manager.py
@@ -17,7 +17,15 @@
class ToolManager:
- """Manages FastMCP tools."""
+ """Manages registration and execution of FastMCP tools.
+
+ The ToolManager handles tool registration, validation, and execution.
+ It maintains a registry of tools and provides methods for adding,
+ retrieving, and calling tools.
+
+ Attributes:
+ warn_on_duplicate_tools: Whether to warn when duplicate tools are registered
+ """
def __init__(
self,
@@ -35,11 +43,22 @@ def __init__(
self.warn_on_duplicate_tools = warn_on_duplicate_tools
def get_tool(self, name: str) -> Tool | None:
- """Get tool by name."""
+ """Get a registered tool by name.
+
+ Args:
+ name: Name of the tool to retrieve
+
+ Returns:
+ Tool instance if found, None otherwise
+ """
return self._tools.get(name)
def list_tools(self) -> list[Tool]:
- """List all registered tools."""
+ """List all registered tools.
+
+ Returns:
+ List of all Tool instances registered with this manager
+ """
return list(self._tools.values())
def add_tool(
diff --git a/src/mcp/server/fastmcp/utilities/func_metadata.py b/src/mcp/server/fastmcp/utilities/func_metadata.py
index 70be8796d..256f63749 100644
--- a/src/mcp/server/fastmcp/utilities/func_metadata.py
+++ b/src/mcp/server/fastmcp/utilities/func_metadata.py
@@ -185,11 +185,12 @@ def func_metadata(
func: The function to convert to a pydantic model
skip_names: A list of parameter names to skip. These will not be included in
the model.
- structured_output: Controls whether the tool's output is structured or unstructured
- - If None, auto-detects based on the function's return type annotation
- - If True, unconditionally creates a structured tool (return type annotation permitting)
- - If False, unconditionally creates an unstructured tool
+ structured_output: Controls whether the tool's output is structured or unstructured.
+ If None, auto-detects based on the function's return type annotation.
+ If True, unconditionally creates a structured tool (return type annotation permitting).
+ If False, unconditionally creates an unstructured tool.
+ Note:
If structured, creates a Pydantic model for the function's result based on its annotation.
Supports various return types:
- BaseModel subclasses (used directly)
diff --git a/src/mcp/server/lowlevel/server.py b/src/mcp/server/lowlevel/server.py
index 8c459383c..b65279b23 100644
--- a/src/mcp/server/lowlevel/server.py
+++ b/src/mcp/server/lowlevel/server.py
@@ -5,64 +5,83 @@
It allows you to easily define and handle various types of requests and notifications
in an asynchronous manner.
-Usage:
-1. Create a Server instance:
- server = Server("your_server_name")
-
-2. Define request handlers using decorators:
- @server.list_prompts()
- async def handle_list_prompts() -> list[types.Prompt]:
- # Implementation
-
- @server.get_prompt()
- async def handle_get_prompt(
- name: str, arguments: dict[str, str] | None
- ) -> types.GetPromptResult:
- # Implementation
-
- @server.list_tools()
- async def handle_list_tools() -> list[types.Tool]:
- # Implementation
-
- @server.call_tool()
- async def handle_call_tool(
- name: str, arguments: dict | None
- ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
- # Implementation
-
- @server.list_resource_templates()
- async def handle_list_resource_templates() -> list[types.ResourceTemplate]:
- # Implementation
+The [`Server`][mcp.server.lowlevel.server.Server] class provides methods to register handlers for various MCP requests and
+notifications. It automatically manages the request context and handles incoming
+messages from the client.
+
+## Usage example
+
+1. Create a [`Server`][mcp.server.lowlevel.server.Server] instance:
+
+ ```python
+ server = Server("your_server_name")
+ ```
+
+ 2. Define request handlers using decorators:
+
+ ```python
+ @server.list_prompts()
+ async def handle_list_prompts() -> list[types.Prompt]:
+ # Implementation
+ ...
+
+ @server.get_prompt()
+ async def handle_get_prompt(
+ name: str, arguments: dict[str, str] | None
+ ) -> types.GetPromptResult:
+ # Implementation
+ ...
+
+ @server.list_tools()
+ async def handle_list_tools() -> list[types.Tool]:
+ # Implementation
+ ...
+
+ @server.call_tool()
+ async def handle_call_tool(
+ name: str, arguments: dict | None
+ ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
+ # Implementation
+ ...
+
+ @server.list_resource_templates()
+ async def handle_list_resource_templates() -> list[types.ResourceTemplate]:
+ # Implementation
+ ...
+ ```
3. Define notification handlers if needed:
- @server.progress_notification()
- async def handle_progress(
- progress_token: str | int, progress: float, total: float | None,
- message: str | None
- ) -> None:
- # Implementation
+
+ ```python
+ @server.progress_notification()
+ async def handle_progress(
+ progress_token: str | int, progress: float, total: float | None,
+ message: str | None
+ ) -> None:
+ # Implementation
+ ...
+ ```
4. Run the server:
- async def main():
- async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
- await server.run(
- read_stream,
- write_stream,
- InitializationOptions(
- server_name="your_server_name",
- server_version="your_version",
- capabilities=server.get_capabilities(
- notification_options=NotificationOptions(),
- experimental_capabilities={},
- ),
- ),
- )
-
- asyncio.run(main())
-
-The Server class provides methods to register handlers for various MCP requests and
-notifications. It automatically manages the request context and handles incoming
-messages from the client.
+
+ ```python
+ async def main():
+ async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
+ await server.run(
+ read_stream,
+ write_stream,
+ InitializationOptions(
+ server_name="your_server_name",
+ server_version="your_version",
+ capabilities=server.get_capabilities(
+ notification_options=NotificationOptions(),
+ experimental_capabilities={},
+ ),
+ ),
+ )
+
+ asyncio.run(main())
+ ```
"""
from __future__ import annotations as _annotations
@@ -122,7 +141,7 @@ async def lifespan(_: Server[LifespanResultT, RequestT]) -> AsyncIterator[dict[s
"""Default lifespan context manager that does nothing.
Args:
- server: The server instance this lifespan is managing
+ _: The server instance this lifespan is managing
Returns:
An empty context object
@@ -226,7 +245,73 @@ def get_capabilities(
def request_context(
self,
) -> RequestContext[ServerSession, LifespanResultT, RequestT]:
- """If called outside of a request context, this will raise a LookupError."""
+ """Access the current request context for low-level MCP server operations.
+
+ This property provides access to the [`RequestContext`][mcp.shared.context.RequestContext]
+ for the current request, which contains the session, request metadata, lifespan
+ context, and other request-scoped information. This is the primary way to access
+ MCP capabilities when using the low-level SDK.
+
+ You typically access this property from within handler functions (tool handlers,
+ resource handlers, prompt handlers, etc.) to get the context for the current
+ client request. The context is automatically managed by the server and is only
+ available during request processing.
+
+ Examples:
+
+ **Logging and communication**:
+
+ ```python
+ @app.call_tool()
+ async def my_tool(name: str, arguments: dict[str, Any]) -> list[types.ContentBlock]:
+ ctx = app.request_context
+ await ctx.session.send_log_message(
+ level="info",
+ data="Starting tool processing",
+ related_request_id=ctx.request_id
+ )
+ ```
+
+ **Capability checking**:
+
+ ```python
+ @app.call_tool()
+ async def advanced_tool(name: str, arguments: dict[str, Any]) -> list[types.ContentBlock]:
+ ctx = app.request_context
+ if ctx.session.check_client_capability(
+ types.ClientCapabilities(sampling=types.SamplingCapability())
+ ):
+ # Use advanced features
+ response = await ctx.session.create_message(messages, max_tokens=100)
+ else:
+ # Fall back to basic functionality
+ pass
+ ```
+
+ **Accessing lifespan resources**:
+
+ ```python
+ @app.call_tool()
+ async def database_query(name: str, arguments: dict[str, Any]) -> list[types.ContentBlock]:
+ ctx = app.request_context
+ db = ctx.lifespan_context["database"] # Access startup resource
+ results = await db.query(arguments["sql"])
+ return [types.TextContent(type="text", text=str(results))]
+ ```
+
+ Returns:
+ [`RequestContext`][mcp.shared.context.RequestContext] for the current request,
+ containing session, metadata, and lifespan context.
+
+ Raises:
+ LookupError: If called outside of a request context (e.g., during server
+ initialization, shutdown, or from code not handling a client request).
+
+ Note:
+ For FastMCP applications, consider using the injected [`Context`][mcp.server.fastmcp.Context]
+ parameter instead, which provides the same functionality with additional
+ convenience methods and better ergonomics.
+ """
return request_ctx.get()
def list_prompts(self):
@@ -430,6 +515,7 @@ def call_tool(self, *, validate_input: bool = True):
The handler validates input against inputSchema (if validate_input=True), calls the tool function,
and builds a CallToolResult with the results:
+
- Unstructured content (iterable of ContentBlock): returned in content
- Structured content (dict): returned in structuredContent, serialized JSON text returned in content
- Both: returned in content and structuredContent
diff --git a/src/mcp/server/session.py b/src/mcp/server/session.py
index 5c696b136..edc42fd31 100644
--- a/src/mcp/server/session.py
+++ b/src/mcp/server/session.py
@@ -6,31 +6,30 @@
used in MCP servers to interact with the client.
Common usage pattern:
-```
- server = Server(name)
-
- @server.call_tool()
- async def handle_tool_call(ctx: RequestContext, arguments: dict[str, Any]) -> Any:
- # Check client capabilities before proceeding
- if ctx.session.check_client_capability(
- types.ClientCapabilities(experimental={"advanced_tools": dict()})
- ):
- # Perform advanced tool operations
- result = await perform_advanced_tool_operation(arguments)
- else:
- # Fall back to basic tool operations
- result = await perform_basic_tool_operation(arguments)
-
- return result
-
- @server.list_prompts()
- async def handle_list_prompts(ctx: RequestContext) -> list[types.Prompt]:
- # Access session for any necessary checks or operations
- if ctx.session.client_params:
- # Customize prompts based on client initialization parameters
- return generate_custom_prompts(ctx.session.client_params)
- else:
- return default_prompts
+```python
+server = Server(name)
+
+@server.call_tool()
+async def handle_tool_call(ctx: RequestContext, arguments: dict[str, Any]) -> Any:
+ # Check client capabilities before proceeding
+ if ctx.session.check_client_capability(
+ types.ClientCapabilities(experimental={"advanced_tools": dict()})
+ ):
+ # Perform advanced tool operations
+ result = await perform_advanced_tool_operation(arguments)
+ else:
+ # Fall back to basic tool operations
+ result = await perform_basic_tool_operation(arguments)
+ return result
+
+@server.list_prompts()
+async def handle_list_prompts(ctx: RequestContext) -> list[types.Prompt]:
+ # Access session for any necessary checks or operations
+ if ctx.session.client_params:
+ # Customize prompts based on client initialization parameters
+ return generate_custom_prompts(ctx.session.client_params)
+ else:
+ return default_prompts
```
The ServerSession class is typically used internally by the Server class and should not
@@ -103,7 +102,102 @@ def client_params(self) -> types.InitializeRequestParams | None:
return self._client_params
def check_client_capability(self, capability: types.ClientCapabilities) -> bool:
- """Check if the client supports a specific capability."""
+ """Check if the client supports specific capabilities before using advanced MCP features.
+
+ This method allows MCP servers to verify that the connected client supports
+ required capabilities before calling methods that depend on them. It performs
+ an AND operation - the client must support ALL capabilities specified in the
+ request, not just some of them.
+
+ You typically access this method through the session available in your request
+ context via [`app.request_context.session`][mcp.shared.context.RequestContext]
+ within handler functions. Always check capabilities before using features like
+ sampling, elicitation, or experimental functionality.
+
+ Args:
+ capability: A [`types.ClientCapabilities`][mcp.types.ClientCapabilities] object
+ specifying which capabilities to check. Can include:
+
+ - `roots`: Check if client supports root listing operations
+ - `sampling`: Check if client supports LLM sampling via [`create_message`][mcp.server.session.ServerSession.create_message]
+ - `elicitation`: Check if client supports user interaction via [`elicit`][mcp.server.session.ServerSession.elicit]
+ - `experimental`: Check for non-standard experimental capabilities
+
+ Returns:
+ bool: `True` if the client supports ALL requested capabilities, `False` if
+ the client hasn't been initialized yet or lacks any of the requested
+ capabilities.
+
+ Examples:
+ Check sampling capability before creating LLM messages:
+
+ ```python
+ from typing import Any
+ from mcp.server.lowlevel import Server
+ import mcp.types as types
+
+ app = Server("example-server")
+
+ @app.call_tool()
+ async def call_tool(name: str, arguments: dict[str, Any]) -> list[types.ContentBlock]:
+ ctx = app.request_context
+
+ # Check if client supports LLM sampling
+ if ctx.session.check_client_capability(
+ types.ClientCapabilities(sampling=types.SamplingCapability())
+ ):
+ # Safe to use create_message
+ response = await ctx.session.create_message(
+ messages=[types.SamplingMessage(
+ role="user",
+ content=types.TextContent(type="text", text="Help me analyze this data")
+ )],
+ max_tokens=100
+ )
+ return [types.TextContent(type="text", text=response.content.text)]
+ else:
+ return [types.TextContent(type="text", text="Client doesn't support LLM sampling")]
+ ```
+
+ Check experimental capabilities:
+
+ ```python
+ @app.call_tool()
+ async def call_tool(name: str, arguments: dict[str, Any]) -> list[types.ContentBlock]:
+ ctx = app.request_context
+
+ # Check for experimental advanced tools capability
+ if ctx.session.check_client_capability(
+ types.ClientCapabilities(experimental={"advanced_tools": {}})
+ ):
+ # Use experimental features
+ return await use_advanced_tool_features(arguments)
+ else:
+ # Fall back to basic functionality
+ return await use_basic_tool_features(arguments)
+ ```
+
+ Check multiple capabilities at once:
+
+ ```python
+ # Client must support BOTH sampling AND elicitation
+ if ctx.session.check_client_capability(
+ types.ClientCapabilities(
+ sampling=types.SamplingCapability(),
+ elicitation=types.ElicitationCapability()
+ )
+ ):
+ # Safe to use both features
+ user_input = await ctx.session.elicit("What would you like to analyze?", schema)
+ llm_response = await ctx.session.create_message(messages, max_tokens=100)
+ ```
+
+ Note:
+ This method returns `False` if the session hasn't been initialized yet
+ (before the client sends the initialization request). It also returns
+ `False` if the client lacks ANY of the requested capabilities - all
+ specified capabilities must be supported for this method to return `True`.
+ """
if self._client_params is None:
return False
@@ -182,7 +276,157 @@ async def send_log_message(
logger: str | None = None,
related_request_id: types.RequestId | None = None,
) -> None:
- """Send a log message notification."""
+ """Send a log message notification from the server to the client.
+
+ This method allows MCP servers to send log messages to the connected client for
+ debugging, monitoring, and error reporting purposes. The client can filter these
+ messages based on the logging level it has configured via the logging/setLevel
+ request. Check client capabilities using [`check_client_capability`][mcp.server.session.ServerSession.check_client_capability]
+ if you need to verify logging support.
+
+ You typically access this method through the session available in your request
+ context. When using the low-level SDK, access it via
+ [`app.request_context.session`][mcp.shared.context.RequestContext] within handler
+ functions. With FastMCP, use the convenience logging methods on the
+ [`Context`][mcp.server.fastmcp.Context] object instead, like
+ [`ctx.info()`][mcp.server.fastmcp.Context.info] or
+ [`ctx.error()`][mcp.server.fastmcp.Context.error].
+
+ Log messages are one-way notifications and do not expect a response from the client.
+ They are useful for providing visibility into server operations, debugging issues,
+ and tracking the flow of request processing.
+
+ Args:
+ level: The severity level of the log message as a `types.LoggingLevel`. Must be one of:
+
+ - `debug`: Detailed information for debugging
+ - `info`: General informational messages
+ - `notice`: Normal but significant conditions
+ - `warning`: Warning conditions that should be addressed
+ - `error`: Error conditions that don't prevent operation
+ - `critical`: Critical conditions requiring immediate attention
+ - `alert`: Action must be taken immediately
+ - `emergency`: System is unusable
+
+ data: The data to log. Can be any JSON-serializable value including:
+
+ - Simple strings for text messages
+ - Objects/dictionaries for structured logging
+ - Lists for multiple related items
+ - Numbers, booleans, or null values
+
+ logger: Optional name to identify the source of the log message.
+ Useful for categorizing logs from different components or modules
+ within your server (e.g., "database", "auth", "tool_handler").
+ related_request_id: Optional `types.RequestId` linking this log to a specific client request.
+ Use this to associate log messages with the request they relate to,
+ making it easier to trace request processing and debug issues.
+
+ Returns:
+ None
+
+ Raises:
+ RuntimeError: If called before session initialization is complete.
+ Various exceptions: Depending on serialization or transport errors.
+
+ Examples:
+ In a tool handler using the low-level SDK:
+
+ ```python
+ from typing import Any
+ from mcp.server.lowlevel import Server
+ import mcp.types as types
+
+ app = Server("example-server")
+
+ @app.call_tool()
+ async def call_tool(name: str, arguments: dict[str, Any]) -> list[types.ContentBlock]:
+ # Access the request context to get the session
+ ctx = app.request_context
+
+ # Log the start of processing
+ await ctx.session.send_log_message(
+ level="info",
+ data=f"Processing tool call: {name}",
+ logger="tool_handler",
+ related_request_id=ctx.request_id
+ )
+
+ # Process and log any issues
+ try:
+ result = perform_operation(arguments)
+ except Exception as e:
+ await ctx.session.send_log_message(
+ level="error",
+ data={"error": str(e), "tool": name, "args": arguments},
+ logger="tool_handler",
+ related_request_id=ctx.request_id
+ )
+ raise
+
+ return [types.TextContent(type="text", text=str(result))]
+ ```
+
+ Using FastMCP's [`Context`][mcp.server.fastmcp.Context] helper for cleaner logging:
+
+ ```python
+ from mcp.server.fastmcp import FastMCP, Context
+
+ mcp = FastMCP(name="example-server")
+
+ @mcp.tool()
+ async def fetch_data(url: str, ctx: Context) -> str:
+ # FastMCP's Context provides convenience methods that internally
+ # call send_log_message with the appropriate parameters
+ await ctx.info(f"Fetching data from {url}")
+ await ctx.debug("Starting request")
+
+ try:
+ data = await fetch(url)
+ await ctx.info("Data fetched successfully")
+ return data
+ except Exception as e:
+ await ctx.error(f"Failed to fetch: {e}")
+ raise
+ ```
+
+ Streaming notifications with progress updates:
+
+ ```python
+ import anyio
+ from typing import Any
+ from mcp.server.lowlevel import Server
+ import mcp.types as types
+
+ app = Server("example-server")
+
+ @app.call_tool()
+ async def call_tool(name: str, arguments: dict[str, Any]) -> list[types.ContentBlock]:
+ ctx = app.request_context
+ count = arguments.get("count", 5)
+
+ for i in range(count):
+ # Send progress updates to the client
+ await ctx.session.send_log_message(
+ level="info",
+ data=f"[{i + 1}/{count}] Processing item",
+ logger="progress_stream",
+ related_request_id=ctx.request_id
+ )
+ if i < count - 1:
+ await anyio.sleep(1)
+
+ return [types.TextContent(type="text", text="Operation complete")]
+ ```
+
+ Note:
+ Log messages are only delivered to the client if the client's configured
+ logging level permits it. For example, if the client has set its level to
+ "warning", it will not receive "debug" or "info" messages. Consider this
+ when deciding what level to use for your log messages. This method internally
+ uses [`send_notification`][mcp.shared.session.BaseSession.send_notification] to
+ deliver the log message to the client.
+ """
await self.send_notification(
types.ServerNotification(
types.LoggingMessageNotification(
@@ -221,7 +465,86 @@ async def create_message(
model_preferences: types.ModelPreferences | None = None,
related_request_id: types.RequestId | None = None,
) -> types.CreateMessageResult:
- """Send a sampling/create_message request."""
+ """Send a message to an LLM through the MCP client for processing.
+
+ This method enables MCP servers to request LLM sampling from the connected client.
+ The client forwards the request to its configured LLM provider (OpenAI, Anthropic, etc.)
+ and returns the generated response. This is useful for tools that need LLM assistance
+ to process user requests or generate content.
+
+ The client must support the sampling capability for this method to work. Check
+ client capabilities using [`check_client_capability`][mcp.server.session.ServerSession.check_client_capability] before calling this method.
+
+ Args:
+ messages: List of [`SamplingMessage`][mcp.types.SamplingMessage] objects representing the conversation history.
+ Each message has a role ("user" or "assistant") and content (text, image, or audio).
+ max_tokens: Maximum number of tokens the LLM should generate in the response.
+ system_prompt: Optional system message to set the LLM's behavior and context.
+ include_context: Optional context inclusion preferences for the LLM request.
+ temperature: Optional sampling temperature (0.0-1.0) controlling response randomness.
+ Lower values make responses more deterministic.
+ stop_sequences: Optional list of strings that will cause the LLM to stop generating
+ when encountered in the response.
+ metadata: Optional arbitrary metadata to include with the request.
+ model_preferences: Optional preferences for which model the client should use.
+ related_request_id: Optional ID linking this request to a parent request for tracing.
+
+ Returns:
+ CreateMessageResult containing the LLM's response with role, content, model name,
+ and stop reason information.
+
+ Raises:
+ RuntimeError: If called before session initialization is complete.
+ Various exceptions: Depending on client implementation and LLM provider errors.
+
+ Examples:
+ Basic text generation:
+
+ ```python
+ from mcp.types import SamplingMessage, TextContent
+
+ result = await session.create_message(
+ messages=[
+ SamplingMessage(
+ role="user",
+ content=TextContent(type="text", text="Explain quantum computing")
+ )
+ ],
+ max_tokens=150
+ )
+ print(result.content.text) # Generated explanation
+ ```
+
+ Multi-turn conversation with system prompt:
+
+ ```python
+ from mcp.types import SamplingMessage, TextContent
+
+ result = await session.create_message(
+ messages=[
+ SamplingMessage(
+ role="user",
+ content=TextContent(type="text", text="What's the weather like?")
+ ),
+ SamplingMessage(
+ role="assistant",
+ content=TextContent(type="text", text="I don't have access to weather data.")
+ ),
+ SamplingMessage(
+ role="user",
+ content=TextContent(type="text", text="Then help me write a poem about rain")
+ )
+ ],
+ max_tokens=100,
+ system_prompt="You are a helpful poetry assistant.",
+ temperature=0.8
+ )
+ ```
+
+ Note:
+ This method requires the client to have sampling capability enabled. Most modern
+ MCP clients support this, but always check capabilities before use in production code.
+ """
return await self.send_request(
request=types.ServerRequest(
types.CreateMessageRequest(
@@ -261,14 +584,36 @@ async def elicit(
requestedSchema: types.ElicitRequestedSchema,
related_request_id: types.RequestId | None = None,
) -> types.ElicitResult:
- """Send an elicitation/create request.
+ """Send an elicitation request to collect structured information from the client.
+
+ This is the low-level method for client elicitation. For most use cases, prefer
+ the higher-level [`Context.elicit`][mcp.server.fastmcp.Context.elicit] method
+ which provides automatic Pydantic validation and a more convenient interface.
+
+ You typically access this method through the session available in your request
+ context via [`app.request_context.session`][mcp.shared.context.RequestContext]
+ within handler functions. Always check that the client supports elicitation using
+ [`check_client_capability`][mcp.server.session.ServerSession.check_client_capability]
+ before calling this method.
Args:
- message: The message to present to the user
- requestedSchema: Schema defining the expected response structure
+ message: The prompt or question to present to the user.
+ requestedSchema: A [`types.ElicitRequestedSchema`][mcp.types.ElicitRequestedSchema]
+ defining the expected response structure according to JSON Schema.
+ related_request_id: Optional `types.RequestId` linking
+ this elicitation to a specific client request for tracing.
Returns:
- The client's response
+ [`types.ElicitResult`][mcp.types.ElicitResult] containing the client's response
+ and action taken (accept, decline, or cancel).
+
+ Raises:
+ RuntimeError: If called before session initialization is complete.
+ Various exceptions: Depending on client implementation and user interaction.
+
+ Note:
+ Most developers should use [`Context.elicit`][mcp.server.fastmcp.Context.elicit]
+ instead, which provides Pydantic model validation and better error handling.
"""
return await self.send_request(
types.ServerRequest(
diff --git a/src/mcp/shared/context.py b/src/mcp/shared/context.py
index f3006e7d5..2e35935aa 100644
--- a/src/mcp/shared/context.py
+++ b/src/mcp/shared/context.py
@@ -13,6 +13,87 @@
@dataclass
class RequestContext(Generic[SessionT, LifespanContextT, RequestT]):
+ """Context object containing information about the current MCP request.
+
+ This is the fundamental context object in the MCP Python SDK that provides access
+ to request-scoped information and capabilities. It's created automatically for each
+ incoming client request and contains everything needed to process that request,
+ including the session for client communication, request metadata, and any resources
+ initialized during server startup.
+
+ The RequestContext is available throughout the request lifecycle and provides the
+ foundation for both low-level and high-level SDK usage patterns. In the low-level
+ SDK, you access it via [`Server.request_context`][mcp.server.lowlevel.server.Server.request_context].
+ In FastMCP, it's wrapped by the more convenient [`Context`][mcp.server.fastmcp.Context]
+ class that provides the same functionality with additional helper methods.
+
+ ## Request lifecycle
+
+ The RequestContext is created when a client request arrives and destroyed when the
+ request completes. It's only available during request processing - attempting to
+ access it outside of a request handler will raise a `LookupError`.
+
+ ## Access patterns
+
+ **Low-level SDK**: Access directly via the server's request_context property:
+
+ ```python
+ @app.call_tool()
+ async def my_tool(name: str, arguments: dict[str, Any]) -> list[types.ContentBlock]:
+ ctx = app.request_context # Get the RequestContext
+ await ctx.session.send_log_message(level="info", data="Processing...")
+ ```
+
+ **FastMCP**: Use the injected Context wrapper instead:
+
+ ```python
+ @mcp.tool()
+ async def my_tool(data: str, ctx: Context) -> str:
+ await ctx.info("Processing...") # Context provides convenience methods
+ ```
+
+ ## Lifespan context integration
+
+ Resources initialized during server startup (databases, connections, etc.) are
+ accessible through the `lifespan_context` attribute, enabling request handlers
+ to use shared resources safely:
+
+ ```python
+ # Server startup - initialize shared resources
+ @asynccontextmanager
+ async def server_lifespan(server):
+ db = await Database.connect()
+ try:
+ yield {"db": db}
+ finally:
+ await db.disconnect()
+
+ # Request handling - access shared resources
+ @server.call_tool()
+ async def query_data(name: str, arguments: dict[str, Any]):
+ ctx = server.request_context
+ db = ctx.lifespan_context["db"] # Access startup resource
+ results = await db.query(arguments["query"])
+ ```
+
+ Attributes:
+ request_id: Unique identifier for the current request as a `RequestId`.
+ Use this for logging, tracing, or linking related operations.
+ meta: Optional request metadata including progress tokens and other client-provided
+ information. May be `None` if no metadata was provided.
+ session: The [`ServerSession`][mcp.server.session.ServerSession] for communicating
+ with the client. Use this to send responses, log messages, or check capabilities.
+ lifespan_context: Application-specific resources initialized during server startup.
+ Contains any objects yielded by the server's lifespan function.
+ request: The original request object from the client, if available. May be `None`
+ for some request types.
+
+ Note:
+ This object is request-scoped and thread-safe within that scope. Each request
+ gets its own RequestContext instance. Don't store references to it beyond the
+ request lifecycle, as it becomes invalid when the request completes.
+ """
+
request_id: RequestId
meta: RequestParams.Meta | None
session: SessionT
diff --git a/src/mcp/shared/exceptions.py b/src/mcp/shared/exceptions.py
index 97a1c09a9..eb8999a92 100644
--- a/src/mcp/shared/exceptions.py
+++ b/src/mcp/shared/exceptions.py
@@ -2,13 +2,24 @@
class McpError(Exception):
- """
- Exception type raised when an error arrives over an MCP connection.
+ """Exception raised when an MCP protocol error is received from a peer.
+
+ This exception is raised when the remote MCP peer returns an error response
+ instead of a successful result. It wraps the ErrorData received from the peer
+ and provides access to the error code, message, and any additional data.
+
+ Attributes:
+ error: The ErrorData object received from the MCP peer containing
+ error code, message, and optional additional data
"""
error: ErrorData
def __init__(self, error: ErrorData):
- """Initialize McpError."""
+ """Initialize McpError with error data from the MCP peer.
+
+ Args:
+ error: ErrorData object containing the error details from the peer
+ """
super().__init__(error.message)
self.error = error
diff --git a/src/mcp/types.py b/src/mcp/types.py
index 98fefa080..51bd9c91c 100644
--- a/src/mcp/types.py
+++ b/src/mcp/types.py
@@ -715,7 +715,90 @@ class AudioContent(BaseModel):
class SamplingMessage(BaseModel):
- """Describes a message issued to or received from an LLM API."""
+ """Represents a message in an LLM conversation for sampling/generation requests.
+
+ SamplingMessage is used to structure conversation history when requesting LLM
+ text generation through the MCP sampling protocol. Each message represents a
+ single turn in the conversation with a specific role and content.
+
+ This class is primarily used with [`ServerSession.create_message`][mcp.server.session.ServerSession.create_message] to send
+ conversation context to LLMs via MCP clients. The message format follows
+ standard LLM conversation patterns with distinct roles for users and assistants.
+
+ Attributes:
+ role: The speaker role, either "user" for human input or "assistant" for AI responses.
+ content: The message content, which can be [`TextContent`][mcp.types.TextContent],
+ [`ImageContent`][mcp.types.ImageContent], or [`AudioContent`][mcp.types.AudioContent].
+
+ Examples:
+ Creating a simple text message:
+
+ ```python
+ from mcp.types import SamplingMessage, TextContent
+
+ user_msg = SamplingMessage(
+ role="user",
+ content=TextContent(type="text", text="Hello, how are you?")
+ )
+ ```
+
+ Creating an assistant response:
+
+ ```python
+ assistant_msg = SamplingMessage(
+ role="assistant",
+ content=TextContent(type="text", text="I'm doing well, thank you!")
+ )
+ ```
+
+ Creating a message with image content:
+
+ ```python
+ import base64
+
+ # Assuming you have image_bytes containing image data
+ image_data = base64.b64encode(image_bytes).decode()
+
+ image_msg = SamplingMessage(
+ role="user",
+ content=ImageContent(
+ type="image",
+ data=image_data,
+ mimeType="image/jpeg"
+ )
+ )
+ ```
+
+ Building a conversation history:
+
+ ```python
+ conversation = [
+ SamplingMessage(
+ role="user",
+ content=TextContent(type="text", text="What's 2+2?")
+ ),
+ SamplingMessage(
+ role="assistant",
+ content=TextContent(type="text", text="2+2 equals 4.")
+ ),
+ SamplingMessage(
+ role="user",
+ content=TextContent(type="text", text="Now what's 4+4?")
+ )
+ ]
+
+ # Use in create_message call
+ result = await session.create_message(
+ messages=conversation,
+ max_tokens=50
+ )
+ ```
+
+ Note:
+ The role field is constrained to "user" or "assistant" only. The content
+ supports multiple media types, but actual support depends on the LLM provider
+ and client implementation.
+ """
role: Role
content: TextContent | ImageContent | AudioContent
diff --git a/uv.lock b/uv.lock
index 59192bee0..b8802eeba 100644
--- a/uv.lock
+++ b/uv.lock
@@ -619,6 +619,7 @@ dev = [
]
docs = [
{ name = "mkdocs" },
+ { name = "mkdocs-api-autonav" },
{ name = "mkdocs-glightbox" },
{ name = "mkdocs-material", extra = ["imaging"] },
{ name = "mkdocstrings-python" },
@@ -659,6 +660,7 @@ dev = [
]
docs = [
{ name = "mkdocs", specifier = ">=1.6.1" },
+ { name = "mkdocs-api-autonav", specifier = ">=0.3.1" },
{ name = "mkdocs-glightbox", specifier = ">=0.4.0" },
{ name = "mkdocs-material", extras = ["imaging"], specifier = ">=9.5.45" },
{ name = "mkdocstrings-python", specifier = ">=1.12.2" },
@@ -931,6 +933,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" },
]
+[[package]]
+name = "mkdocs-api-autonav"
+version = "0.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mkdocs" },
+ { name = "mkdocstrings-python" },
+ { name = "pyyaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/dd/9f/c73e0b79b9be34f3dd975e7ba175ef6397a986f470f9aafac491d53699f8/mkdocs_api_autonav-0.3.1.tar.gz", hash = "sha256:5d37ad53a03600acff0f7d67fad122a38800d172777d3c4f8c0dfbb9b58e8c29", size = 15980, upload-time = "2025-08-08T04:08:50.167Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cd/60/5acc016c75cac9758eff0cbf032d2504c8baca701d5ea4a784932e4764af/mkdocs_api_autonav-0.3.1-py3-none-any.whl", hash = "sha256:363cdf24ec12670971049291b72806ee55ae6560611ffd6ed2fdeb69c43e6d4f", size = 12033, upload-time = "2025-08-08T04:08:48.349Z" },
+]
+
[[package]]
name = "mkdocs-autorefs"
version = "1.4.2"