MCP Sampling Explained: How to Build AI-Powered MCP Servers Without API Keys
TL;DR: Most developers assume Model Context Protocol (MCP) servers are strictly passive conduits that execute deterministic code when called by a client. But MCP is bidirectional: the protocol defines a capability called sampling (
sampling/createMessage), allowing a server to ask the client to run an LLM completion. This lets you build servers that perform internal reasoning, summarize vast datasets down to concise payloads, and translate natural language into structured API calls — all without requiring end users to provide OpenAI or Anthropic API keys or manage separate billing.
When developers first start building MCP servers, they typically follow a predictable pattern: an LLM decides to call a tool, the server executes a local function or REST request, and the server returns the raw output back into the conversation context.
For straightforward tasks like checking the weather or looking up a GitHub commit, this deterministic model works well. But for complex workflows — such as parsing gigabytes of server logs, extracting insights from unstructured documents, or synthesizing SQL queries from messy inputs — the passive approach breaks down fast.
Historically, server authors solved this in one of two suboptimal ways:
- Context Window Dumping: Returning megabytes of raw text directly into the chat session, triggering MCP tool overload and burning thousands of tokens.
- Hardcoded API Keys: Requiring every user who installs the server to provide an
OPENAI_API_KEYorANTHROPIC_API_KEYin theirclaude_desktop_config.json, turning installation into a credential management headache.
MCP sampling eliminates both compromises. By tapping into the host client's existing model connection, your server can run intermediate AI reasoning inside its tool handlers cleanly and securely.
How MCP Sampling Works Under the Hood
To understand sampling, you have to look at the flow of control in standard MCP communication versus sampled communication.
In standard MCP tool execution:
With MCP sampling, the server temporarily reverses the relationship mid-execution:
During step 2, the server sends a standard JSON-RPC request back across the transport (whether stdio, SSE, or Streamable HTTP) asking the client to generate a completion based on messages and parameters supplied by the server.
1. Capability Negotiation
Sampling is an optional client-side capability. When a client establishes an MCP connection, it sends an initialize request detailing what it supports:
If capabilities.sampling is absent, the server knows the client cannot process LLM completion requests.
2. The Wire Format: sampling/createMessage
When the server wants an LLM completion, it dispatches sampling/createMessage:
The client responds with the generated completion:
Model Preferences: Hints Over Hardcoded Models
One of the cleanest design choices in the Model Context Protocol specification is how model selection is decoupled.
An MCP server should never assume a specific backend provider or hardcode model identifiers like gpt-4o-2024-08-06. The user might be running Claude Desktop with Anthropic models, Cursor with custom OpenAI endpoints, or a local instance of Ollama/vLLM through a private client.
Instead, the protocol provides modelPreferences:
| Property | Type | Description |
|---|---|---|
hints | Array<{ name: string }> | Optional strings suggesting model families (e.g. claude-3-5-sonnet, gemini-1.5-flash). Clients can match or ignore them. |
costPriority | number (0.0 to 1.0) | How strongly the server prefers a cheap model over an expensive one. |
speedPriority | number (0.0 to 1.0) | How strongly the server prioritizes low latency / fast time-to-first-token. |
intelligencePriority | number (0.0 to 1.0) | How strongly the server requires advanced reasoning or deep comprehension capabilities. |
For example, if your server is performing routine regex-like string extraction from Markdown tables, you configure high speedPriority and high costPriority:
The client can then route the request to a fast, cost-effective model (like Haiku, Flash, or an 8B open-weights model) rather than spending expensive frontier-tier tokens.
TypeScript Implementation Guide
Let's build a practical TypeScript MCP server that fetches a massive remote document, uses client sampling to extract key metrics, and returns a lean response.
Using the modern @modelcontextprotocol/server package:
Python FastMCP Implementation Guide
In the Python ecosystem, the FastMCP framework provides an ergonomic @mcp.tool interface with a first-class Context object that handles sampling requests seamlessly.
FastMCP abstracts the JSON-RPC negotiation and schema validation into ctx.sample(), making intelligent tool execution as simple as calling an internal async function.
Four Real-World Architectural Patterns for Sampling
Why bother doing LLM generation inside an MCP server instead of letting the primary client model do everything? Here are four high-impact architectural patterns:
1. Token Budget Compression (Preventing Tool Overload)
Suppose you are building an MCP server that searches Jira, Slack, or GitHub. A single search query often returns 20 results totaling 40,000 tokens of raw JSON.
If you return that full payload in the tool result, you eat 20% to 50% of the active context window. Subsequent turns in the conversation will continue paying for those 40,000 tokens over and over.
With sampling, the server can internally request a fast, cheap model (e.g. gpt-4o-mini or claude-3-5-haiku) to filter out irrelevant fields, compress the 20 results down to the 3 most relevant snippets, and return only 400 tokens to the main agent.
2. Natural Language to Structured Query Translation
If your MCP server interfaces with a complex SQL database, GraphQL backend, or Elasticsearch cluster, expecting the primary agent to get every column name and filter syntax right on the first try often results in tool invocation errors.
With sampling, your server can expose a simple tool like query_analytics(natural_language_goal: string). Inside the tool handler:
- The server reads its own database schema.
- The server calls
sampling/createMessageto generate the exact SQL query based on the schema and the user's intent. - The server executes the SQL safely against the read-only replica.
- The server returns the final table to the client.
The main client model never has to wrestle with raw table definitions or dialect quirks.
3. Self-Correcting Internal Execution Loops
When an MCP tool fails (for example, an API returns a schema validation error or an HTTP 400 Bad Request), standard servers simply return the error string and make the outer agent retry on the next turn.
With sampling, the tool handler can implement an internal retry loop:
The tool heals itself before ever returning to the host, keeping the outer conversation clean and reducing end-user latency.
4. Zero-Key Multi-Tenant Plugins
If you distribute a public MCP server (for example, via npm, PyPI, or the AllMCPS Registry), requiring users to supply OpenAI or Anthropic API keys creates friction, credential exposure risks, and support tickets.
By relying on sampling, your server becomes a zero-dependency, zero-key plugin. Anyone running an MCP-compliant client can install and run your AI-augmented server out of the box with zero configuration.
Security, Privacy, and Human-in-the-Loop Boundaries
Because sampling allows a server to request computation from the client, MCP clients implement explicit security boundaries to protect users.
The Human-in-the-Loop Approval Modal
When an MCP server issues sampling/createMessage, most modern clients do not silently execute the request. Depending on the client's security settings, the UI may present a notification or confirmation dialog:
"The MCP Server 'log-triage-server' is requesting an LLM completion using Claude 3.5 Haiku. Allow this request?"
This prevents malicious or poorly designed servers from running infinite loops that drain user credits or burn API rate limits.
Context Leakage and includeContext
The includeContext parameter in sampling/createMessage controls what context the client provides to the sampled model:
"none": Only the exact messages passed in the sampling request are visible. (Recommended default)"thisServer": The client attaches MCP resources exposed by the requesting server."allServers": The client attaches resources from all active servers connected to the session.
Setting includeContext: "allServers" should be done with caution. If your server is querying an external or third-party service, you do not want it to accidentally ingest sensitive data from other connected servers (such as local filesystem or password manager MCPs).
Preventing Infinite Recursion
Consider what happens if an MCP server's sampling request prompts the client, and the client's internal reasoning decides to call the same MCP tool again. Without safeguards, this can trigger an infinite recursive loop.
To prevent this:
- Clients disable tool use during sampling: When a client resolves
sampling/createMessage, it typically disables tool calling for that sub-completion, treating it strictly as a text/image generation step. - Servers enforce depth caps: Servers should maintain internal execution counters and abort if an internal loop exceeds 2–3 iterations.
Sampling vs. Tools vs. Prompts vs. Resources
To see where sampling fits into the broader protocol, compare it to the other primitives covered in our MCP Resources and Prompts Guide:
| Primitive | Who Initiates? | Who Controls Execution? | Primary Purpose |
|---|---|---|---|
| Tools | Host Model | Server | Expose deterministic actions & external side effects. |
| Resources | Host Application | Host Client | Expose read-only documents, files, and state via URIs. |
| Prompts | User (Slash Commands) | User | Reusable prompt templates & workflows triggered on demand. |
| Sampling | MCP Server | Host Client | Lets the server request LLM completions back from the host. |
Best Practices for Building Sampled MCP Servers
- Always Check Client Capabilities: Never assume the client supports sampling. Check
capabilities.samplingduring initialization or wrap sampling calls in fallback handlers. - Prioritize Speed and Cost: For most server-side tasks (summarization, extraction, parsing), set
speedPriority: 0.8andcostPriority: 0.8. Save heavy frontier intelligence for the top-level chat. - Use Strict System Prompts: Instruct the sampled model to return structured formats (e.g. JSON or Markdown) and explicitly forbid conversational filler ("Sure! Here is your summary...").
- Cap
maxTokensAggressively: Always specify a realisticmaxTokenslimit (e.g., 200–500 tokens). This prevents runaway token generation if the model hallucinates. - Keep Prompts Focused: Do not attempt to replicate the user's entire conversation history inside a sampling call. Pass only the minimal input needed to complete the task.
Summary & Next Steps
MCP sampling transforms servers from simple API wrappers into intelligent sub-agents that can process, refine, and structure data autonomously.
By leveraging the host client's model access through sampling/createMessage, you can build powerful, context-efficient tools that deliver a superior developer experience without requiring API keys or extra configuration.
To dive deeper into building production-ready servers, explore these related guides:
Frequently asked questions
What is MCP sampling (sampling/createMessage)?
MCP sampling is a bidirectional protocol capability where an MCP server requests an LLM completion back from the host client (such as Claude Desktop, Cursor, Zed, or Claude Code) via a JSON-RPC method named sampling/createMessage. This allows the server to leverage LLM intelligence — for summarizing data, structuring queries, or multi-step reasoning — using the client's existing authenticated model session without needing its own OpenAI, Anthropic, or cloud API keys.
How does an MCP server request sampling without hardcoding a specific AI model?
The MCP specification uses modelPreferences instead of hardcoded model identifiers. When calling sampling/createMessage, the server supplies hints (e.g. preferred model families) along with priority weights between 0.0 and 1.0 for costPriority, speedPriority, and intelligencePriority. The host client evaluates these weights against available models and selects the optimal engine for the job.
What is the includeContext parameter in MCP sampling?
includeContext instructs the host client whether to attach MCP resource context to the sampling prompt. It accepts three values: 'none' (sends only the messages provided in the sampling request), 'thisServer' (attaches resources managed by the requesting MCP server), or 'allServers' (attaches resources across all MCP servers currently attached to the client). In most server-side tasks, setting this to 'none' is recommended to avoid unnecessary context consumption and unexpected token costs.
Why should I use MCP sampling instead of making direct OpenAI or Anthropic API calls inside my server?
Calling external LLM APIs directly inside an MCP server forces you or your end users to supply, store, and rotate API keys, configure environment variables, and manage rate limits and billing. Sampling delegates all model access, authentication, billing, and user consent to the host client. Additionally, sampling respects user-level privacy boundaries and ensures compliance with client-level AI provider agreements.
What happens if an MCP client does not support sampling?
Sampling is an optional client capability declared during protocol initialization. If a client connects without declaring capabilities.sampling, any sampling/createMessage request will fail with a MethodNotFound or capability error. Production MCP servers must always inspect client capabilities or use try/catch blocks to provide graceful deterministic fallbacks when sampling is unavailable.