--- title: LLM Sampling sidebarTitle: Sampling description: Handle server-initiated LLM sampling requests. icon: robot --- import { VersionBadge } from "/snippets/version-badge.mdx"; MCP servers can request LLM completions from clients. The client handles these requests through a sampling handler callback. ## Sampling Handler Provide a `sampling_handler` function when creating the client: ```python from fastmcp import Client from fastmcp.client.sampling import ( SamplingMessage, SamplingParams, RequestContext, ) async def sampling_handler( messages: list[SamplingMessage], params: SamplingParams, context: RequestContext ) -> str: # Your LLM integration logic here # Extract text from messages and generate a response return "Generated response based on the messages" client = Client( "my_mcp_server.py", sampling_handler=sampling_handler, ) ``` ### Handler Parameters The sampling handler receives three parameters: The role of the message. The content of the message. TextContent is most common, and has a `.text` attribute. The messages to sample from The server's preferences for which model to select. The client MAY ignore these preferences. The hints to use for model selection. The cost priority for model selection. The speed priority for model selection. The intelligence priority for model selection. An optional system prompt the server wants to use for sampling. A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. The sampling temperature. The maximum number of tokens to sample. The stop sequences to use for sampling. Optional metadata to pass through to the LLM provider. Optional list of tools the LLM can use during sampling. See [Handling Tool Requests](#handling-tool-requests). Optional control over tool usage behavior (`auto`, `required`, or `none`). Unique identifier for the MCP request ## Basic Example ```python from fastmcp import Client from fastmcp.client.sampling import SamplingMessage, SamplingParams, RequestContext async def basic_sampling_handler( messages: list[SamplingMessage], params: SamplingParams, context: RequestContext ) -> str: # Extract message content conversation = [] for message in messages: content = message.content.text if hasattr(message.content, 'text') else str(message.content) conversation.append(f"{message.role}: {content}") # Use the system prompt if provided system_prompt = params.systemPrompt or "You are a helpful assistant." # Here you would integrate with your preferred LLM service # This is just a placeholder response return f"Response based on conversation: {' | '.join(conversation)}" client = Client( "my_mcp_server.py", sampling_handler=basic_sampling_handler ) ``` If the client doesn't provide a sampling handler, servers can optionally configure a fallback handler. See [Server Sampling](/servers/sampling#sampling-fallback-handler) for details. When you provide a `sampling_handler`, FastMCP automatically advertises full sampling capabilities including tool support to the server. ## Handling Tool Requests Servers may request sampling with tools, allowing the LLM to make tool calls during generation. When tools are provided in `params.tools`, your handler should return a `CreateMessageResultWithTools` object instead of a simple string. ### Checking for Tools ```python from fastmcp import Client from fastmcp.client.sampling import SamplingMessage, SamplingParams, RequestContext from mcp.types import ( CreateMessageResultWithTools, TextContent, ToolUseContent, ToolChoice, ) async def sampling_handler_with_tools( messages: list[SamplingMessage], params: SamplingParams, context: RequestContext ) -> str | CreateMessageResultWithTools: # Check if tools were provided if params.tools: # Call your LLM with tool support # Return CreateMessageResultWithTools with appropriate content return CreateMessageResultWithTools( role="assistant", content=[TextContent(type="text", text="I'll help you with that.")], model="gpt-4", stopReason="endTurn", ) # Standard text response when no tools return "Generated response" client = Client( "my_mcp_server.py", sampling_handler=sampling_handler_with_tools, ) ``` ### Returning Tool Use When the LLM wants to call a tool, return a `CreateMessageResultWithTools` with `stopReason="toolUse"` and `ToolUseContent` in the content: ```python from mcp.types import CreateMessageResultWithTools, ToolUseContent async def sampling_handler_with_tools( messages: list[SamplingMessage], params: SamplingParams, context: RequestContext ) -> str | CreateMessageResultWithTools: if params.tools: # Your LLM decided to call a tool return CreateMessageResultWithTools( role="assistant", content=[ ToolUseContent( type="toolUse", id="call_123", name="search", input={"query": "Python tutorials"}, ) ], model="gpt-4", stopReason="toolUse", # Indicates tool use ) return "Response without tools" ``` ### Tool Choice The `params.toolChoice` field indicates how the server wants tools to be used: - **`auto`**: The LLM decides whether to use tools - **`required`**: The LLM must use at least one tool - **`none`**: The LLM should not use any tools ```python async def sampling_handler_with_tools( messages: list[SamplingMessage], params: SamplingParams, context: RequestContext ) -> str | CreateMessageResultWithTools: if params.tools and params.toolChoice: if params.toolChoice.mode == "required": # Must return a tool call pass elif params.toolChoice.mode == "none": # Should not return tool calls even though tools are available pass # "auto" - let the LLM decide return "Response" ``` Tool execution happens on the server side. The client's role is to pass tools to the LLM and return the LLM's response (which may include tool use requests). The server then executes the tools and may send follow-up sampling requests with tool results. ## Pre-built Handlers FastMCP provides ready-to-use sampling handlers for Anthropic and OpenAI that handle all the complexity of message conversion, tool formatting, and response parsing. ### Anthropic Handler The Anthropic handler uses the Claude API: ```python from anthropic import Anthropic from fastmcp import Client from fastmcp.server.sampling.anthropic import AnthropicSamplingHandler handler = AnthropicSamplingHandler( default_model="claude-sonnet-4-5-20250929", client=Anthropic(), # Uses ANTHROPIC_API_KEY env var ) async with Client("server.py", sampling_handler=handler) as client: result = await client.call_tool("summarize", {"text": "..."}) ``` The handler automatically: - Converts MCP messages to Anthropic's format - Translates tool definitions to Anthropic's tool format - Maps stop reasons (`tool_use` → `toolUse`, `end_turn` → `endTurn`) - Selects models based on server preferences (any model starting with `claude` is accepted) Requires the `anthropic` package. Install with `pip install fastmcp[anthropic]` or `pip install anthropic`. ### OpenAI Handler The OpenAI handler works with the OpenAI API and compatible providers: ```python from openai import OpenAI from fastmcp import Client from fastmcp.server.sampling.openai import OpenAISamplingHandler handler = OpenAISamplingHandler( default_model="gpt-4o-mini", client=OpenAI(), # Uses OPENAI_API_KEY env var ) async with Client("server.py", sampling_handler=handler) as client: result = await client.call_tool("analyze", {"data": "..."}) ``` The handler automatically: - Converts MCP messages to OpenAI's chat format - Translates tool definitions to OpenAI's function calling format - Maps stop reasons (`tool_calls` → `toolUse`, `stop` → `endTurn`) - Selects models based on server preferences Requires the `openai` package. Install with `pip install fastmcp[openai]` or `pip install openai`. ### Using with Compatible Providers The OpenAI handler works with any OpenAI-compatible API: ```python from openai import OpenAI from fastmcp.server.sampling.openai import OpenAISamplingHandler # Azure OpenAI handler = OpenAISamplingHandler( default_model="gpt-4o", client=OpenAI( api_key=os.getenv("AZURE_OPENAI_API_KEY"), base_url="https://your-resource.openai.azure.com/openai/deployments/your-deployment", ), ) # Local models (Ollama, vLLM, etc.) handler = OpenAISamplingHandler( default_model="llama3", client=OpenAI( api_key="not-needed", base_url="http://localhost:11434/v1", ), ) ```