In plain English
Most people who learn the Model Context Protocol stop at the three things a server offers the host: tools, resources, and prompts. But MCP runs in both directions. The host (the app holding the AI model) can offer capabilities back to the server, and the conversation can pause to ask the human a question. Three of those lesser-known features are sampling, roots, and elicitation.

Here is the one-line version of each. Sampling lets a server ask the host's model to generate text — the server borrows the AI instead of bringing its own. Roots tell a server which folders or URLs it is allowed to work in — the host draws the boundary of the sandbox. Elicitation lets a server pause and ask the user for a missing piece of input — structured, mid-task, like a form popping up.
An everyday analogy: think of a contractor (the server) hired to renovate a house. Sampling is the contractor borrowing the homeowner's design software instead of buying their own copy — they use your tool to produce a sketch. Roots is the homeowner saying "you may work in the kitchen and the garage, nowhere else" — that defines the job site. Elicitation is the contractor stopping halfway to ask "what colour tiles do you want?" — a structured question they need answered before they can finish.
Why these features matter
Each feature removes a real limitation that you hit the moment you build a non-trivial MCP server.
- Sampling lets a server stay model-agnostic. Without it, if your server needs to summarize a document or classify some text, you have to ship your own API key, pick a model, and pay for inference yourself. Sampling lets the server say "host, please run this for me" — the server gets AI capability for free, using whatever model the user already configured, and never touches a secret key.
- Roots make file access safe and scoped. A filesystem or git server that could read anywhere on disk is dangerous. Roots let the host hand the server an explicit list of allowed directories (the project folder, say) so the server knows the legitimate boundaries of its work instead of guessing — or over-reaching.
- Elicitation removes guesswork from the model. Before elicitation, if a server was missing information — a target environment, a confirmation, a missing field — it had two bad options: fail, or let the model invent the value. Elicitation gives a clean third path: pause and ask the human a typed question, get a structured answer back, and continue.
Who should care? Anyone building an MCP server that does more than wrap a single API call. The moment your server wants to reason over its own data, stay inside a safe boundary, or collect a missing detail without crashing, these three features are the supported, spec-blessed way to do it — instead of hard-coding a model, hard-coding a path, or guessing.
How each feature works
All three ride on the same JSON-RPC channel MCP already uses, but they reverse or redirect the usual direction of a request. The key is who asks whom.
- Direction: server → host
- Method: sampling/createMessage
- Server asks the host's model to generate text
- Host stays in control: may edit or refuse
- Needs: a client that advertises sampling
- Direction: host → server
- Method: roots/list (+ change notification)
- Host tells the server its allowed boundaries
- Server scopes its work to those URIs
- Needs: a client that advertises roots
- Direction: server → user (via host)
- Method: elicitation/create
- Server requests structured input from the user
- User fills a form; host returns the values
- Needs: a client that advertises elicitation
Sampling: the server borrows the model
Normally the model calls the server's tools. Sampling flips that: the server sends a sampling/createMessage request to the host, containing messages and parameters, and the host runs them through whatever model it has and returns the completion. Crucially, the host is the gatekeeper. The spec recommends a human-in-the-loop design: the host may show the proposed prompt to the user, let them edit it, run it, and even let them review the result before it goes back to the server. The server never sees the user's API key and cannot silently spend their money.
// Inside a tool handler, the SERVER asks the host to sample.
const result = await server.server.createMessage({
messages: [
{
role: "user",
content: { type: "text", text: `Summarize this changelog in 3 bullets:\n${changelog}` }
}
],
maxTokens: 300
});
// result.content.text is the model's reply, produced by the HOST's model.
const summary = result.content.type === "text" ? result.content.text : "";Roots: the host draws the boundary
A root is a URI (usually a file:// directory, sometimes an HTTP base) that the host declares as an allowed working area. After connecting, the server calls roots/list to learn the boundaries, and the host can send a notifications/roots/list_changed message whenever the user opens a different project or grants a new folder. Roots are advisory — the protocol does not physically jail the server — but a well-behaved server treats them as the authoritative scope and refuses to touch anything outside them.
// The SERVER asks the host which directories it may operate in.
const { roots } = await server.server.listRoots();
// roots = [{ uri: "file:///home/me/project", name: "project" }]
function isAllowed(path: string): boolean {
return roots.some(r => path.startsWith(new URL(r.uri).pathname));
}Elicitation: the server asks the user
Elicitation lets a server pause mid-task and request input directly from the human. The server sends an elicitation/create request that carries a short message and a JSON Schema describing the fields it wants. The host renders that schema as a form, the user fills it in (or cancels), and the host returns a structured object. Because the shape is declared up front, the server gets back clean, typed data — not a free-text guess the model had to invent.
# Python SDK — pause and ask the user for typed input.
result = await ctx.elicit(
message="Which environment should I deploy to?",
schema={
"type": "object",
"properties": {
"environment": {"type": "string", "enum": ["staging", "production"]},
"confirm": {"type": "boolean"}
},
"required": ["environment", "confirm"]
}
)
if result.action == "accept" and result.content["confirm"]:
deploy(result.content["environment"])A worked example: a smart deploy server
It is easiest to see how the three fit together by imagining one server that uses all of them — a deployment helper for a coding host.
- Roots first. On connect, the server calls
roots/listand learns it may work infile:///home/me/project. It will only read deploy configs from inside that folder — never the user's whole disk. - Sampling next. The user says "ship it." The server reads the recent git commits, then uses sampling to ask the host's model to draft a release-notes summary from those commits. No separate API key, no extra model dependency — it borrows the host's model.
- Elicitation last. Before doing anything irreversible, the server elicits a structured confirmation: a dropdown for the target environment (staging or production) and a required "yes, deploy" checkbox. The user picks production and confirms. Only then does the deploy run.
How they compare to the three primitives
Beginners sometimes try to force these features into the tools/resources/prompts mental model. They are genuinely different because the initiator changes. The table below puts all six side by side.
| Feature | Who initiates | Who decides the outcome | Typical use |
|---|---|---|---|
| Tool | Model | Server runs the code | Take an action with a side effect |
| Resource | Application | Server returns data | Load read-only context |
| Prompt | User | Server returns messages | Run a structured template |
| Sampling | Server | Host's model + user | Server needs an AI completion |
| Roots | Host | Host declares scope | Tell the server its safe boundary |
| Elicitation | Server | User answers a form | Collect a missing typed input |
Client support and pitfalls
This is the single most important practical caveat: these features only work if the host (client) supports them. Tools enjoy near-universal support, but sampling, roots, and elicitation are optional capabilities that a client advertises (or does not) during the initialize handshake. A server that hard-requires sampling will simply fail on a host that never implemented it.
- Check capabilities on connect. During the handshake, inspect the client's advertised
capabilities. Only callsampling/createMessage,roots/list, orelicitation/createif the corresponding capability is present. - Always design a fallback. If sampling is unavailable, can your server skip the AI step or ask the user to paste a summary? If elicitation is unavailable, can a required value come from a tool argument instead? Never let a missing optional capability brick the whole server.
- Treat roots as advisory, not enforced. The protocol does not stop a server from reading outside its roots; your code must. A server that ignores roots is a security bug waiting to happen.
- Keep a human in the loop for sampling. The spec is explicit that hosts should let users review sampling requests. Do not design a server that assumes silent, unattended completions — many hosts will surface them to the user for approval.
Going deeper
Once the basics click, a few deeper nuances are worth knowing.
Sampling enables "agentic" servers. Because a server can request completions, it can run its own internal reasoning loop — read data, ask the host's model what to do, act, repeat — without shipping a model of its own. This is essentially a mini agent loop living inside the server, powered by the host's model. It is also why sampling is the most security-sensitive of the three: a server with sampling can drive the model, so hosts gate it carefully.
Model preferences in sampling. A sampling request can include hints about the kind of model it wants — for example, a preference for speed over maximum capability, or a named model family. These are only hints: the host maps them to whatever models it actually has and remains free to substitute. Your server should never assume it got the exact model it asked for.
Roots change during a session. Roots are not fixed at connect time. When the user opens a different project or grants access to a new folder, the host fires notifications/roots/list_changed, and a well-built server re-reads the list and updates its scope live. Treat roots as a subscription, not a one-time read.
Elicitation has three outcomes, not one. The user can accept (you get the typed object), decline (they actively said no), or cancel (they dismissed the form). Handle all three. A server that treats decline and cancel the same way, or that crashes on anything but accept, will frustrate users. And never elicit sensitive secrets like passwords through it — the spec discourages using elicitation for credentials.
Where to go next: if you are ready to build, see building MCP servers and how these features differ from plain function calling. The durable takeaway is that MCP is a two-way protocol: the most capable, best-behaved servers use the host's model (sampling), respect the host's boundaries (roots), and ask the human when they are unsure (elicitation) — rather than guessing.
FAQ
What is MCP sampling used for?
Sampling lets an MCP server ask the host's model to generate text on its behalf — for example, to summarize a document or classify some input. The server borrows the host's AI instead of shipping its own API key and model, so it stays model-agnostic and never touches the user's credentials. The host runs the request and can let the user review it first.
What does the MCP roots feature do?
Roots are a list of URIs (usually file:// directories) that the host declares as the server's allowed working area. The server calls roots/list to learn its boundaries and should refuse to operate outside them. Roots are advisory — the protocol does not physically enforce them — so the server's own code must respect the scope.
What is MCP elicitation?
Elicitation lets a server pause mid-task and request structured input from the user. It sends a short message plus a JSON Schema describing the fields it needs; the host renders a form, and the user accepts (returning typed values), declines, or cancels. It replaces guessing or failing when a server is missing a required detail.
How is elicitation different from an MCP prompt?
A prompt is a template the user chooses to start a conversation. Elicitation is the server interrupting an in-progress task to ask the user a specific, schema-defined question. The initiator and the timing are opposite: prompts begin work, elicitation pauses work to collect a missing input.
Do all MCP clients support sampling, roots, and elicitation?
No. Unlike tools, these are optional capabilities that a client advertises during the initialize handshake. Some hosts support roots but not sampling, or elicitation behind a flag. Always check the advertised capabilities on connect and design a fallback so a missing feature does not break your server.
Is MCP sampling a security risk?
It is the most security-sensitive of the three because a server can drive the host's model. That is why the spec recommends keeping a human in the loop: the host should be able to show, edit, or reject a sampling request, and the server never sees the user's API key. Design servers that expect this oversight rather than silent, unattended completions.