Sampling
Purpose and Scope
Sampling is the server-side pattern where an MCP tool handler asks the connected client to run an LLM completion and then resumes with the client’s answer. In the TypeScript SDK guide, that request is made with ctx.mcpReq.requestSampling, which sends sampling/createMessage during an in-flight handler. The important constraint is that the model is controlled by the host client, not by the MCP server. That lets a 2025-era server use the host’s model without holding the host’s provider key, while still returning an ordinary tool result after the model response arrives.
Sources: docs/servers/sampling.md
Sampling is also a migration-sensitive feature. The repository documentation marks it deprecated under SEP-2577 as of protocol revision 2026-07-28. On that revision, the server-to-client request channel that carried requestSampling is removed, so requestSampling throws instead of silently downgrading. For new 2026-07-28-facing server code, the preferred replacement is a direct call to the server operator’s LLM provider SDK from the handler. If the interaction still needs to be client-mediated, the newer embedded-request workflow belongs to input_required, where a handler returns input requests and the client retries with responses.
Sources: docs/servers/sampling.md, docs/servers/input-required.md
Use this page when you maintain server code that still runs against 2025-era clients, when you need to understand why a sampling call fails after protocol negotiation, or when you are planning a migration away from server-pushed client requests. The page treats sampling as a compatibility feature rather than a recommended architecture for new servers. It also explains the neighboring server patterns because sampling shares the same request-scoped context surface as elicitation, progress, logging, cancellation, and error reporting.
Sources: docs/servers/sampling.md, docs/servers/elicitation.md, docs/servers/logging-progress-cancellation.md, docs/servers/errors.md
Relevant Source Files
docs/servers/sampling.md— primary server guide for sampling, including deprecation status,ctx.mcpReq.requestSampling, response shape, capability enforcement, and recap guidance.docs/servers/input-required.md— explains the 2026-07-28 embedded-request replacement model and shows how handlers returninput_requiredinstead of pushing server-to-client requests.docs/servers/elicitation.md— documents another server-initiated request helper,ctx.mcpReq.elicitInput, which has the same era boundary and helps distinguish sampling from user-input collection.docs/servers/logging-progress-cancellation.md— describes other request-scoped helpers onctx.mcpReq, including notification and cancellation patterns relevant to long-running sampling-adjacent handlers.docs/servers/errors.md— defines the difference between model-visible tool errors and JSON-RPC protocol errors, which matters when sampling cannot be performed.docs/servers/completion.md— documents server-side autocomplete, a separate capability often confused with model completion but implemented ascompletion/completesuggestions rather than LLM sampling.
Core Primitives
The main server primitive is the handler context passed as the second argument to a registered handler. In the sampling guide, a tool registered with server.registerTool receives input arguments and ctx; the handler calls ctx.mcpReq.requestSampling({ messages, maxTokens }). messages is the chat-style input sent to the client’s model, and maxTokens bounds the requested completion. The promise resolves only after the client answers, so the handler’s execution flow is sequential: receive tool call, ask the client for a model completion, read the result, and return content to the original tool caller.
Sources: docs/servers/sampling.md
The client response is documented as a CreateMessageResult. The client chooses which model satisfies the request and reports that model name in model. The result also includes the assistant role and a single content block. The server guide’s example folds those fields into a text block such as Model (host-model): ..., which is then returned as the tool’s normal content. Because the client selects the model, server code should not assume a particular provider, model family, tokenizer, or provider-specific response format unless its host integration contract says so.
Sources: docs/servers/sampling.md
Capability declaration is part of the public contract. requestSampling works only when the connected client declared the sampling capability and registered a sampling/createMessage request handler. The server can opt into stricter enforcement with enforceStrictCapabilities: true on the McpServer constructor. With strict enforcement enabled, attempting to sample against a client that did not declare sampling fails inside the handler before the server sends an unsupported request. The documented result becomes an ordinary tool error result with isError: true, which means the model can see and recover from the message.
Sources: docs/servers/sampling.md, docs/servers/errors.md
Server-Side Execution Flow
A typical sampling-enabled tool begins like any other tool: define a name, description, and input schema, then implement an asynchronous handler. Inside that handler, build the message list from validated tool input and call ctx.mcpReq.requestSampling. The sampling guide’s summarize example sends a single user message asking for a one-sentence summary and a maxTokens value of 500. After the client returns the model completion, the handler wraps the response in MCP tool content. To the original client call, this still looks like one tools/call request that eventually resolves.
Sources: docs/servers/sampling.md
server.registerTool(
'summarize',
{
description: 'Summarize text using the client LLM',
inputSchema: z.object({ text: z.string() })
},
async ({ text }, ctx) => {
const response = await ctx.mcpReq.requestSampling({
messages: [{ role: 'user', content: { type: 'text', text: `Summarize in one sentence: ${text}` } }],
maxTokens: 500
});
return { content: [{ type: 'text', text: `Model (${response.model}): ${JSON.stringify(response.content)}` }] };
}
);Because the handler blocks while the host runs the model call, design the surrounding operation as a potentially long-running request. The logging, progress, and cancellation guide shows that request-scoped helpers live on ctx.mcpReq; for long work, handlers can inspect _meta.progressToken, send notifications/progress, and observe cancellation through the request context patterns described there. Sampling itself does not replace those operational signals. If a tool prepares data, asks the client model to reason over it, and then performs more work, it should still provide progress only when the client asked for it and should avoid sending progress notifications without a token.
Sources: docs/servers/sampling.md, docs/servers/logging-progress-cancellation.md
Protocol Era and Migration Guidance
For new server implementations, the documented first recommendation is not to add sampling, but to replace it with a direct provider call. That means importing the LLM provider’s SDK into the server, using the server operator’s API key, and calling the provider from the same tool handler where requestSampling previously lived. The handler shape remains familiar: validate input, prepare a prompt, wait for a completion, and return MCP content. The difference is ownership. The server now owns model selection, credentials, failure handling, rate limits, and provider-specific observability rather than delegating those decisions to the connected host.
Sources: docs/servers/sampling.md
The 2026-07-28 behavior is intentionally explicit: requestSampling throws on a 2026-07-28 connection. The sampling guide points readers to input_required for the replacement form when the server must ask the client for something mid-call. In that model, a handler returns an input_required result with embedded requests, the client fulfills them, and the client retries the original tools/call, prompts/get, or resources/read with inputResponses. The handler then reads accepted content on re-entry. This retry-based flow is different from sampling’s push request, and migration should account for that control-flow change.
Sources: docs/servers/sampling.md, docs/servers/input-required.md
Sampling is separate from completion, even though both words sound model-related. The server completion guide defines completion as autocomplete for prompt arguments and resource template variables. It is implemented through completable fields and completion/complete, and the server returns matching suggestions such as repository names or languages. Sampling, by contrast, asks the client’s model to generate assistant content from messages. When documenting or implementing server behavior, keep those capabilities distinct: completion improves user input UX, while sampling delegates an LLM inference step to the host.
Sources: docs/servers/sampling.md, docs/servers/completion.md
Error Handling and Capability Failures
Sampling failures should be reported according to the same error model as other server handlers. The errors guide distinguishes a tool error from a protocol error. A tool error is a successful JSON-RPC result with isError: true; the model receives the content and can respond or retry. A protocol error is a JSON-RPC error response and is appropriate when the request itself is invalid. The sampling guide’s strict-capability example uses the tool-error shape when a client lacks sampling: the call returns content explaining that the client does not support sampling and marks isError: true.
Sources: docs/servers/sampling.md, docs/servers/errors.md
Prefer model-readable recovery hints in tool errors. If a summarization tool can fall back to a deterministic local summary, tell the model that sampling is unavailable and what alternative behavior was used. If no fallback exists, say which capability is missing and whether the user should connect through a host that supports 2025-era sampling. For server bugs or invalid handler parameters unrelated to the model’s next action, use the protocol-error patterns described in the errors guide. Avoid swallowing requestSampling failures and returning empty content, because that leaves the caller unable to distinguish an empty completion from an unsupported capability.
Sources: docs/servers/sampling.md, docs/servers/errors.md
Compact Reference
| Name | Where it appears | Behavior | Notes |
|---|---|---|---|
ctx.mcpReq.requestSampling | Tool handler context | Sends sampling/createMessage to the connected client and waits for the result | Deprecated for 2026-07-28; throws on that connection era |
sampling/createMessage | Client request method | Server-to-client request asking the host model for a message | Requires client sampling capability and handler |
messages | Sampling request parameter | Chat-style messages used as model input | Example uses a user text message built from tool input |
maxTokens | Sampling request parameter | Upper bound for completion length | Example sets 500 |
CreateMessageResult.model | Sampling response field | Name of the model chosen by the client | Do not assume the server selected it |
CreateMessageResult.role | Sampling response field | Assistant role returned by the client | Returned with the content block |
CreateMessageResult.content | Sampling response field | One content block containing the model output | Commonly folded into tool result content |
enforceStrictCapabilities | McpServer constructor option | Checks declared client capabilities before server-initiated requests | Missing sampling becomes a tool error in the documented example |
Practical Next Steps
If you are writing new server code, start by deciding whether the server should own the LLM call. For 2026-07-28-oriented deployments, implement that direct provider call or use the input_required pattern where client participation is truly needed. If you maintain an older integration, keep requestSampling isolated behind a small helper so the migration path is a one-line replacement rather than a cross-codebase rewrite. In tests, connect to a client that explicitly declares sampling and one that does not, and assert both the successful CreateMessageResult path and the strict-capability isError path.
Sources: docs/servers/sampling.md, docs/servers/input-required.md, docs/servers/errors.md
Read the related server pages next based on the behavior you need: use input_required for 2026-era client interaction, Elicitation for 2025-era user input requests, Logging, Progress, and Cancellation for long-running handler ergonomics, Errors for model-visible failure design, and Completion for autocomplete rather than LLM inference. Together those pages define the request-scoped server patterns that surround sampling and help prevent accidental dependence on a deprecated server-to-client request channel.
Sources: docs/servers/sampling.md, docs/servers/input-required.md, docs/servers/elicitation.md, docs/servers/logging-progress-cancellation.md, docs/servers/errors.md, docs/servers/completion.md