As AI-powered applications become more agentic and interactive, there is a growing need for a standardized way for servers to request language model responses without directly managing model APIs or API keys. This is where Model Context Protocol (MCP) Sampling comes into the picture.
MCP Sampling provides a structured mechanism that allows a server to request AI-generated responses through a client, while keeping full control of model access, permissions, and user approvals on the client side. In simple terms, the server asks for a response, but the client decides which model to use, whether tools can be invoked, and whether the user approves the request etc.,
This design is especially powerful for building intelligent workflows where AI calls can happen inside other server features, enabling multi-step reasoning, tool usage, and agentic behavior, all without exposing model credentials to the server.
In this post, we will explore how MCP Sampling works, understand its request-response flow, learn how tool calling and multi-turn loops are handled, and see why human-in-the-loop approval is a critical part of the design.
1. Purpose of Sampling
At its core, MCP Sampling defines a standardized way for MCP servers to request AI-generated responses through clients.
Traditionally, if a server wants to generate text using an LLM, it must directly integrate with providers like OpenAI, Anthropic, or Gemini. That means the server needs to manage API keys, model selection, permissions, rate limits, and security controls.
MCP changes this approach. Instead of calling the model provider directly, the server sends a sampling request to the client, and the client becomes responsible for interacting with the language model.
This design brings several important benefits:
1.1 Client Controls Model Access
The client decides:
· which model to use
· whether tool usage is allowed
· whether the user must approve the request
· which provider API to call
This keeps model access and permissions under client control, which is a major security and governance advantage.
1.2 No Server-Side API Keys Needed
One of the biggest benefits is that servers do not need to store or manage LLM API keys.
This reduces:
· security risks
· credential leakage
· provider lock-in
· infrastructure complexity
The client owns the model connection, while the server focuses only on business logic.
1.3 Supports Multiple Content Types
Sampling is not limited to plain text generation.
It supports:
· text: normal conversational responses
· image: visual inputs or image-based prompts
· audio: voice/audio interactions
· tools: tool calling workflows
· nested agentic workflows: multi-step reasoning and execution loops
This makes MCP Sampling powerful enough to support modern AI agent architectures.
2. Human-in-the-Loop Model
One of the strongest design principles in MCP Sampling is the human-in-the-loop model.
Even though the server can request AI generations, the protocol strongly recommends that the user always remains in control of what is sent to the language model and what is returned back to the server.
This is a safety-first architecture. Before a sampling request is executed, the user should be able to:
· review the request: see what the server is asking the model
· edit the prompt: modify or refine the input before sending
· approve or deny the request: explicitly allow or reject the generation
· review the generated response: inspect the output before it is returned
This control layer helps to build trust and prevents unintended actions.
2.1 Why is this important?
Imagine an MCP server handling:
· customer support automation
· internal company data
· code generation
· tool execution workflows
Without user approval, the server could automatically send sensitive prompts or trigger unwanted tool calls. The human-in-the-loop model ensures that AI actions never happen silently. The user remains the final decision maker.
Simple Flow looks like below.
3. Capabilities in MCP Sampling
Before a server can send sampling requests, the MCP client must explicitly declare what sampling features it supports during initialization. This is done through the capabilities object.
Capabilities act like a contract between the client and server. They help the server understand:
· whether sampling is supported
· whether tool usage is allowed
· whether context inclusion is available
This prevents the server from sending unsupported requests.
3.1 Basic Sampling Capability
If the client supports normal LLM generations, it must declare:
{
"capabilities": {
"sampling": {}
}
}
This means the client can handle:
· text generation
· image prompts
· audio prompts
· normal sampling requests
At a minimum, this is required for sampling/createMessage.
3.2 Tool Use Capability
If the client supports tool-enabled sampling, it must explicitly declare tool support inside sampling:
{
"capabilities": {
"sampling": {
"tools": {}
}
}
}
This tells the server that the client can process:
· tool_use
· tool_result
· multi-turn tool loops
· parallel tool calls
This capability is mandatory before the server sends a tools array in sampling requests. As per the specification, servers MUST NOT send tool-enabled sampling requests unless the client declares sampling.tools. This avoids protocol mismatch and runtime errors.
3.3 Context Capability (Soft Deprecated)
Older versions also support:
{
"capabilities": {
"sampling": {
"context": {}
}
}
}
This was used with includeContext values like:
· thisServer
· allServers
However, this feature is now soft-deprecated.
That means:
· still supported for compatibility
· should generally be avoided in new implementations
· may be removed in future versions
In most modern implementations, it is better to omit includeContext and let it default to "none".
Why Capabilities Matter
Think of capabilities as feature discovery during handshake. The server first learns what the client can do, and then sends only compatible requests. This makes MCP robust, extensible, and provider-independent.
4. Protocol Messages
4.1 Creating Messages
At the core of MCP Sampling is the sampling/createMessage request. This is the primary protocol message used by the MCP server to request an AI-generated response through the client. The flow follows standard JSON-RPC 2.0 conventions.
Request Structure
The server sends a request like this:
{
"jsonrpc" : "2.0",
"id" : 1,
"method" : "sampling/createMessage",
"params" : {
"messages" : [ {
"role" : "user",
"content" : {
"type" : "text",
"text" : "What is the capital of Indai?"
}
} ],
"modelPreferences" : {
"hints" : [ {
"name" : "claude-3-sonnet"
} ],
"intelligencePriority" : 0.8,
"speedPriority" : 0.5
},
"systemPrompt" : "You are a helpful assistant.",
"maxTokens" : 100
}
}
Response Structure
The client returns:{
"jsonrpc" : "2.0",
"id" : 1,
"result" : {
"role" : "assistant",
"content" : {
"type" : "text",
"text" : "The capital of India is Delhi."
},
"model" : "claude-3-sonnet-20240307",
"stopReason" : "endTurn"
}
}
4.2 Sampling with Tools
One of the most powerful features of MCP Sampling is tool-enabled generation. This allows the server to request an LLM response with access to tools, enabling the model to:
· fetch live data
· query external systems
· call APIs
· perform multi-step reasoning
· continue the conversation after tool results
This is what enables agentic workflows.
Request with Tools
To allow tool usage, the server includes a tools array and optionally a toolChoice configuration inside the sampling/createMessage request.
{
"jsonrpc" : "2.0",
"id" : 1,
"method" : "sampling/createMessage",
"params" : {
"messages" : [ {
"role" : "user",
"content" : {
"type" : "text",
"text" : "What's the weather like in Bangalore and Chennai?"
}
} ],
"tools" : [ {
"name" : "get_weather",
"description" : "Get current weather for a city",
"inputSchema" : {
"type" : "object",
"properties" : {
"city" : {
"type" : "string",
"description" : "City name"
}
},
"required" : [ "city" ]
}
} ],
"toolChoice" : {
"mode" : "auto"
},
"maxTokens" : 1000
}
}
Tool Use Response
Instead of returning final text, the client may return a tool request from the LLM.
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "call_abc123",
"name": "get_weather",
"input": {
"city": "Bangalore"
}
},
{
"type": "tool_use",
"id": "call_def456",
"name": "get_weather",
"input": {
"city": "Chennai"
}
}
],
"model": "claude-3-sonnet-20240307",
"stopReason": "toolUse"
}
}
4.3 Multi-turn Tool Loop
Once the client returns a response with stopReason: "toolUse", the conversation does not end there. Instead, the server enters what MCP calls a multi-turn tool loop.
This is the core mechanism that enables agentic workflows. The typical flow is:
· Execute the requested tools
· Append tool results to the conversation
· Send a follow-up sampling/createMessage request
· Receive either more tool calls or a final response
· Repeat until endTurn
This loop can continue for multiple iterations depending on the complexity of the task.
Step 1: Execute Tool Calls
From the previous response, the LLM requested:
get_weather("Bangalore") get_weather("Chennai")
The server now executes both tools, often in parallel.
Example results:
Bangalore → 18°C, partly cloudy
Chennai → 15°C, rainy
Step 2: Send Follow-up Request with Tool Results
The server sends a new sampling/createMessage request, appending:
· original user message
· assistant tool use message
· user tool result message
{
"jsonrpc" : "2.0",
"id" : 2,
"method" : "sampling/createMessage",
"params" : {
"messages" : [ {
"role" : "user",
"content" : {
"type" : "text",
"text" : "What's the weather like in Bangalore and Chennai?"
}
}, {
"role" : "assistant",
"content" : [ {
"type" : "tool_use",
"id" : "call_abc123",
"name" : "get_weather",
"input" : {
"city" : "Bangalore"
}
}, {
"type" : "tool_use",
"id" : "call_def456",
"name" : "get_weather",
"input" : {
"city" : "Chennai"
}
} ]
}, {
"role" : "user",
"content" : [ {
"type" : "tool_result",
"toolUseId" : "call_abc123",
"content" : [ {
"type" : "text",
"text" : "Weather in Bangalore: 18°C, partly cloudy"
} ]
}, {
"type" : "tool_result",
"toolUseId" : "call_def456",
"content" : [ {
"type" : "text",
"text" : "Weather in Chennai: 15°C, rainy"
} ]
} ]
} ],
"tools" : [ {
"name" : "get_weather",
"description" : "Get current weather for a city",
"inputSchema" : {
"type" : "object",
"properties" : {
"city" : {
"type" : "string"
}
},
"required" : [ "city" ]
}
} ],
"maxTokens" : 1000
}
}
Notice the structure carefully. The tool results are sent as a new user message. This is a key MCP design decision. So the conversation now becomes:
· user question
· assistant tool request
· user tool result
· assistant final response
This preserves a clean request-response pattern.
Step 3: Final Response
Once the client forwards the updated conversation to the LLM, the model now has the external data needed to answer.
It returns the final response:
{
"jsonrpc" : "2.0",
"id" : 2,
"result" : {
"role" : "assistant",
"content" : {
"type" : "text",
"text" : "Based on the current weather data:\n\n- **Bangalore**: 18°C and partly cloudy - quite pleasant!\n- **Chennai**: 15°C and rainy - you'll want an umbrella.\n\nBangalore has slightly warmer and drier conditions today."
},
"model" : "claude-3-sonnet-20240307",
"stopReason" : "endTurn"
}
}
The loop may continue multiple times.
For example:
· tool use
· tool result
· more tool use
· more tool result
· final response
The server should usually apply an iteration limit to avoid infinite loops. A common strategy is to force final completion in the last iteration using:
"toolChoice": { "mode": "none" }
This prevents further tool calls and forces a final answer.
In summary, MCP Sampling provides a standardized and secure way for servers to request AI-generated responses through clients. Instead of directly calling LLM provider APIs, the server delegates generation to the client, which retains control over:
· model selection
· permissions
· user approvals
· tool usage
· safety checks
This architecture eliminates the need for server side API keys and enables a strong human-in-the-loop safety model. MCP Sampling turns LLM interaction into a secure, client-controlled, tool-aware conversational protocol.
Previous Next Home

No comments:
Post a Comment