Your MCP Server Borrowed the Host's Model. That Is Ending.
Sampling let an MCP server ask the host's model to do a little reasoning without holding any credentials, paying for any tokens, or choosing a model. The 2026-07-28 spec deprecated it.
A team I was helping ran a small MCP server that enriched support tickets. When a ticket arrived, one of its tools asked the host's model to read the body and return a severity and a suggested queue. The server itself held no model credentials, ran on a tiny box, and cost almost nothing beyond hosting, because the inference was not its inference. It borrowed the model it was already talking to. Then someone read the 2026-07-28 changelog and found the feature that made this possible, sampling, marked Deprecated. The suggested migration was a single clause: integrate directly with LLM provider APIs instead. That one clause turns a zero-credential, zero-token-cost tool into a service that needs its own API key, its own billing account, and a decision about which model to call.
What sampling actually gave you
Sampling was the one place an MCP server could do a little reasoning without becoming an AI product itself. The mechanism was a server-initiated request: mid tool call, your server sent a sampling/createMessage back to the client, the client ran a completion on its model, and handed you the result. You wrote a prompt. Somebody else ran it.
That arrangement handed you four things, and it is worth naming them before they go away.
// The old world: the server asks the HOST to run inference.
// No provider key here. The client owns the model and the bill.
async function classifyTicket(server: McpServer, ticketBody: string) {
const result = await server.createMessage({
messages: [
{ role: "user", content: { type: "text", text:
`Classify this ticket. Return JSON {severity, queue}.\n\n${ticketBody}` } },
],
maxTokens: 200,
// The host picks the actual model from its own config and keys.
modelPreferences: { hints: [{ name: "a small fast model" }] },
});
return JSON.parse(result.content.text); // host paid for these tokens
}
First, you held no model credentials. Nothing to store, scope, or rotate. Second, the host paid for the tokens, so your running cost did not scale with how much reasoning your tools did. Third, the host chose the model, which meant you inherited whatever the user had already configured and trusted. Fourth, and most quietly, the host saw the prompt and the completion, so whatever guardrails, logging, and redaction the host applied covered your inference too. Sampling let a server punch above its weight: a tiny process that could still think, on someone else's model, under someone else's controls.
What the deprecation actually says
The 2026-07-28 revision deprecated Roots, Sampling, and Logging together, under SEP-2577. Read the wording carefully, because the word Deprecated is doing specific work here. These features remain fully functional. The PR is advisory and makes no wire-level change. Your sampling calls work today and will keep working for a defined window. What changed is a status and a recommendation: new implementations should not adopt sampling, and existing ones should plan to migrate.
How long is the window? The feature lifecycle and deprecation policy sets a minimum of twelve months, measured from the release of the revision that marked the feature Deprecated, not from any later date. That release was 2026-07-28. The earliest a feature can actually be removed is the first specification revision released as Current on or after the window elapses, so mid-2027 at the soonest, and the policy is explicit that features may stay Deprecated for much longer. The only accelerant is an expedited removal for an active security risk, and even that keeps a ninety-day floor. So this is not a fire drill. It is a clock, and the sensible move is to migrate on your own schedule before someone else's.
The migration the changelog suggests is one clause: integrate directly with LLM provider APIs instead of sampling. There is a second path. Because the stateless redesign also replaced server-initiated requests with Multi Round-Trip Requests, you can technically still ask the client to run a completion by returning an input_required result, the same mechanism used to move held-open elicitation to a signed resume token. But that couples you to the client's model and swims against the direction the spec is pointing. Treat direct integration as the destination and MRTR as a stopgap you use only to buy time.
The four things that move onto your server
The migration looks like a small code change: swap one function call for another. The reason it is not small is that all four things sampling gave you move onto your server at once, and three of them are production concerns you have to design for, not just port.
The provider credential is the first. Your server now holds an API key to a model, which it never did before. That is a new secret with a full lifecycle: where it is stored, which code paths can read it, how it is scoped, how it rotates. If you have a gateway in front of your models, this is exactly the place to pin the provider, isolate the key, and rotate it on a schedule rather than dropping a long-lived key in an environment variable and forgetting it.
The cost is the second. Tokens the host used to pay for now land on your bill, and they scale with how much your tools reason. A tool that classifies every inbound ticket now runs a completion per ticket on your account. Without a budget guard, a traffic spike or a retry storm becomes a surprise invoice. This is the moment to put per-tenant budgets and cost guardrails around your own inference, because nobody upstream is metering it for you anymore.
The model choice is the third, and it brings version skew with it. Under sampling, you ran on whatever model the host configured, so you drifted with the host. Now you pin a model, and that pin can diverge from what the host expects, with a different tokenizer, a different output shape, a different refusal behavior. The failure mode is the same one you get when a client and server negotiate different protocol revisions: two components that each think they agree, quietly disagreeing. Pin deliberately and gate changes behind an eval before you upgrade the model.
The fourth is the one teams miss: data egress. Under sampling, the content you sent to a model passed through the host, under the host's visibility, logging, and redaction. Now it leaves through a connection the host cannot see. That is a new egress path carrying whatever your tool feeds the model, and it is yours to control.
Doing the direct integration without a footgun
Here is the same ticket classifier, migrated. The shape is not just "call the provider." It is "call the provider with the key held safely, the model pinned, a timeout, and a budget check," because every one of those was previously someone else's job.
import Anthropic from "@anthropic-ai/sdk";
// The key is read from a secret manager, not inlined. Scope it to this service.
const client = new Anthropic({ apiKey: await secrets.get("mcp-ticket-model-key") });
const MODEL = "claude-haiku-5-5"; // pinned; changing it goes through an eval gate
async function classifyTicket(ticketBody: string, tenant: string) {
// Budget check BEFORE spending: inference is now on our bill, per call.
if (!(await budget.tryReserve(tenant, { estTokens: 400 }))) {
throw new Error("tenant inference budget exhausted");
}
const res = await client.messages.create({
model: MODEL,
max_tokens: 200,
messages: [{ role: "user", content:
`Classify this ticket. Return JSON {severity, queue}.\n\n${redact(ticketBody)}` }],
}, { timeout: 10_000 }); // our latency and our failure to handle now
return JSON.parse(res.content[0].text);
}
Four details carry the weight. The key comes from a secret store and is scoped to this one service, so a leak is contained and rotation is a config change, not a redeploy. The model is a named constant, not a floating alias, so an upstream model refresh cannot silently change your tool's behavior overnight. There is a timeout, because the host used to own the inference call's failure handling and now you do, and a hung provider should fail your tool cleanly rather than holding a request open. And there is a budget reservation before the spend, not after, so the cost is bounded per tenant and a retry loop cannot run up a bill while you are asleep.
Notice also the redact call wrapping the ticket body. That is not decoration. It is the hinge of the next section.
Keeping the data egress honest
The quiet cost of leaving the host's model is that you also leave the host's guardrails. When your server asked for a sampling completion, the host saw exactly what you were about to send to a model and could redact, block, or log it under its own policy. Now that content goes out through your connection, and the host has no idea what you forwarded. If your ticket bodies contain personal data, that data now flows to a provider the host never chose, with no host-side control in the path.
So you inherit the egress discipline too. Redact before you send, apply the same output and egress guardrails you would put on any agent that can send data outward, and keep a record of what left and where it went. The pattern below is deliberately small: a single chokepoint that every provider call passes through, so there is one place to enforce redaction and one place that logs the egress.
// One chokepoint for every outbound model call. Redact, cap, record.
async function callModel(tenant: string, prompt: string) {
const cleaned = redact(prompt); // strip PII before it leaves
if (cleaned.length > MAX_PROMPT_CHARS) { // bound what we ship
throw new Error("prompt exceeds egress size cap");
}
const res = await client.messages.create({
model: MODEL, max_tokens: 200,
messages: [{ role: "user", content: cleaned }],
}, { timeout: 10_000 });
// Record that data left, to which provider and model, for this tenant.
audit.log({ tenant, provider: "anthropic", model: MODEL, bytes: cleaned.length });
return res.content[0].text;
}
The point is not that redaction is hard. It is that sampling let you skip owning it, and direct integration does not. The honest version of this migration includes telling the host, in your server's documentation or capability description, that your tools now send data to an external provider you control. A user who granted your server access expecting it to use their model deserves to know when that stopped being true.
The parts that bite
The migration is not where the risk is. The silence after it is. Swapping the call is an afternoon. The risk is a server that ran for a year on borrowed inference and now quietly runs on a key nobody is watching, a bill nobody is capping, and an egress path nobody is logging. Treat the four handovers as the real work and the code swap as the easy part.
A floating model alias will bite you on a Tuesday. Pinning to something like a "latest fast model" alias feels convenient until the provider rolls the alias forward and your classifier starts returning a slightly different JSON shape. Pin to an explicit version and move it on purpose, behind an eval, never by surprise.
MRTR looks like a free pass, and it is a loan. Routing inference back through the client with a Multi Round-Trip Request lets you avoid holding a key today. But you are then depending on a feature built for collecting missing input, running on a model you do not control, inside a protocol direction that is deprecating exactly this kind of server-initiated reasoning. It is a fine bridge for a few weeks while you provision a provider account. It is a bad foundation.
Cost scales with reasoning now, and reasoning is easy to add. When inference was free to you, nobody counted how many tool calls quietly ran a completion. Now each one is a line item. Before you migrate, count how often your tools actually sample, because that number is about to become a monthly number. Some of those calls may not need a model at all once you can see the bill.
Not every sampling call should become a model call. The deprecation is a good moment to ask whether a given tool needed the host's model or just needed a cheap classifier. Some reasoning that was worth doing when it was free is not worth a per-call provider charge, and a small local heuristic or a rules table may do the job the host's model used to do for nothing.
The takeaway
Sampling was a quiet gift: an MCP server could reason on the host's model without holding a key, paying for tokens, picking a model, or owning the data it sent out. The 2026-07-28 spec deprecated that gift, and the one-clause migration, integrate with a provider directly, hands all four of those responsibilities back to you at once. The code change is trivial. The design change is not, because you are standing up a new credential, a new cost center, a new model-version dependency, and a new egress path, each of which used to be someone else's problem.
You have until mid-2027 at the earliest, so there is no reason to rush and every reason to do it deliberately: hold the key in a secret manager, pin the model behind an eval, cap the spend per tenant, and redact and log what leaves. If your server calls sampling and you are not sure which of those four handovers will surprise you first, book a consultation call and we can map the migration before the deprecation clock, not a load balancer, forces the timing.
Questions this essay answers
01What is MCP sampling and why was it deprecated?
Sampling let an MCP server ask the connected host to run a model completion on its behalf, through a server-initiated sampling/createMessage request. The server did a little reasoning without holding any model credentials of its own. The 2026-07-28 revision deprecated it under SEP-2577, alongside Roots and Logging, because the stateless redesign removed server-initiated requests and the maintainers judged that model invocation may sit outside MCP's scope. It still works today, but new servers should not adopt it.
02When will MCP sampling actually be removed?
Not soon, and not on a fixed date. The feature lifecycle policy sets a minimum deprecation window of twelve months, measured from the release of the revision that marked the feature Deprecated, which was 2026-07-28. The earliest removal is the first specification revision released as Current on or after that window elapses, so roughly mid-2027 at the soonest. A feature can stay Deprecated for far longer. The only way it goes faster is an expedited removal for an active security risk, which still keeps a ninety-day floor.
03What is the recommended replacement for MCP sampling?
The changelog suggests integrating directly with an LLM provider API instead of sampling. That means your server opens its own connection to a model, with its own API key, its own token budget, and its own model choice. The alternative, routing the inference back through the client with a Multi Round-Trip Request, is possible but couples you to the client's model and is swimming against the direction of the spec. Treat the direct provider call as the destination and MRTR as a stopgap.
04Does deprecating sampling change my server's security posture?
Yes, in two ways. First, your server now holds a model provider credential it never had before, which is a new secret to store, scope, and rotate. Second, data your tool used to hand to the host's model, under the host's visibility and guardrails, now leaves through a connection the host cannot see. You become responsible for redacting what you send and disclosing where it goes. Both are ordinary production concerns, but they are new concerns for a server that used to borrow the model.
