MCP has three primitives that most developers learn first: tools, resources, and prompts. But the specification includes a fourth capability that flips the direction of control. It is called sampling.
Tools let agents call into servers. Resources let agents read from servers. Sampling flips the arrow. It lets a server ask the client’s language model to generate a completion — without the server needing its own API key, its own model, or any direct access to an LLM.
Why sampling exists
Most MCP servers are stateless utilities. A file server reads files. A database server runs queries. They do not need to think. But some tasks require reasoning in the middle of a workflow, and building that reasoning into the server itself creates problems.
Consider a code review server. It can pull diffs, parse ASTs, and check linting rules. But generating a natural-language summary of what changed and why it matters requires a language model. Without sampling, the server has two bad options: bundle its own LLM integration (adding cost, latency, and key management) or punt the reasoning back to the client through a multi-step tool call chain that the model has to orchestrate.
Sampling gives the server a third option. It sends a sampling/createMessage request to the client, includes the context it wants the model to consider, and gets back a completion. The client handles model selection, API keys, rate limits, and user approval. The server stays focused on its domain.
How it works
The flow has four steps.
1. The server sends a sampling request. It constructs a sampling/createMessage call with a list of messages (the context it wants the model to see), an optional system prompt, and parameters like maxTokens and temperature. It can also specify model preferences — whether it needs a model that handles images, or one that prioritizes speed over capability.
{
"method": "sampling/createMessage",
"params": {
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Summarize the following diff and flag any breaking changes:\n\n```diff\n- function getUser(id: string)\n+ function getUser(id: string, opts?: GetUserOptions)\n```"
}
}
],
"maxTokens": 500,
"temperature": 0.3
}
}
2. The client intercepts the request. This is where sampling differs from a direct API call. The client is in control. It can show the request to the user for approval before sending anything to a model. It can modify the messages, strip sensitive content, or reject the request entirely.
3. The client sends the completion to its configured model. The server never knows which model runs. It could be Claude, GPT, a local model, or anything the client supports. The server does not need an API key. The client uses its own.
4. The client returns the result. The server receives the model’s response and continues its workflow. The response includes the generated text, the role, and a model field telling the server which model actually produced the result.
What makes this different from tool calls
The confusion between sampling and tools is common. Here is the difference.
With tools, the model is in the driver’s seat. The model decides to call a tool, the server executes it, and the result flows back to the model. The model reasons, the server acts.
With sampling, the server is in the driver’s seat. The server decides it needs a completion, asks the client for one, and uses the result to continue its own workflow. The server acts, then the model reasons, then the server acts again.
This inversion matters because it lets servers build multi-step workflows where LLM reasoning happens at specific points without handing control back to the outer conversation. A data analysis server could pull a dataset, ask the model to identify anomalies, filter the results based on the model’s response, and return a final report — all within a single tool call from the user’s perspective.
The human-in-the-loop requirement
The MCP specification is explicit about this: clients should not send sampling requests to a model without user awareness. The recommended pattern is to show the user what the server wants to send, let them approve or modify it, and only then forward it to the model.
This matters for two reasons. First, it prevents servers from using sampling to exfiltrate data through carefully crafted prompts. The user can see exactly what context the server is sending. Second, it keeps the user in control of model costs. Every sampling request consumes tokens, and a poorly written server could generate hundreds of requests in a single workflow.
In practice, clients handle this differently. Some show every request. Some batch approvals. Some let users set per-server policies. The specification leaves the implementation details to the client but makes the intent clear: sampling must not be a backdoor.
Model preferences
Servers can express preferences about which model handles their sampling request without naming a specific model. The specification defines a modelPreferences object with three priority fields:
- costPriority — how much the server cares about keeping costs low
- speedPriority — how much the server cares about response latency
- intelligencePriority — how much the server cares about reasoning quality
Each field takes a value between 0 and 1. A code review server that needs accurate analysis might set intelligencePriority: 0.9 and costPriority: 0.2. A logging server that just needs to format messages might flip those values.
The server can also specify capability hints — whether it needs a model that handles images, or one with a particular context window. The client maps these preferences to its available models. The server never picks the model directly.
When to use sampling in your server
Sampling fits a specific set of problems. Not every server needs it.
Good fits: Servers that need mid-workflow reasoning. A server that pulls raw data and needs to summarize, classify, or explain it before returning results. A server that orchestrates multi-step processes where some steps require natural language generation.
Bad fits: Servers that just read or write data. If your server’s job is to query a database and return rows, adding sampling creates unnecessary latency and cost. Let the client’s model handle interpretation on its own.
Watch out for: Recursive patterns. If a sampling response triggers another tool call that triggers another sampling request, you can end up in a loop. The specification warns against this, and good clients will cap the depth. But server authors should design their workflows to avoid deep chains.
The current state of sampling support
Sampling is part of the MCP specification but not universally supported across clients. Claude Desktop and several other MCP clients implement it, though the approval UX varies. If you are building a server that depends on sampling, check that your target clients support it and document the dependency clearly.
For servers listed in the AgentNDX directory, you can check the capabilities field to see whether a server uses sampling. Servers that do are a small minority today, but the number is growing as developers realize they can offload reasoning to the client instead of bundling their own model integrations.
FAQ
Q: Does sampling cost the server anything? A: No. The client pays for the model tokens. The server sends a request and gets a response. This is one of the main advantages — servers can use LLM capabilities without managing API keys or billing.
Q: Can a server use sampling without the user knowing? A: The specification says no. Clients should surface sampling requests to the user for review. In practice, enforcement depends on the client implementation. As a server author, assume the user will see your sampling prompts.
Q: What happens if the client does not support sampling?
A: The server’s sampling/createMessage request will fail. If your server requires sampling to function, declare it in your server capabilities and handle the unsupported case gracefully — return an error message explaining that the client needs to support sampling for this feature to work.