Halfway through a tool call, an MCP server discovers it needs something only a human can supply: which of three matching records you meant, whether to really overwrite the production config, a project ID nobody ever set. It cannot just ask. A server is a process on the other end of a JSON-RPC pipe — no window, no keyboard, no relationship with the person whose work it is doing. Elicitation is the protocol answer: the server sends a structured request, the host renders it in its own trusted interface, and the user decides. What follows is the mechanism and its edges: the schema that bounds the question, the three answers a server must handle, the one thing never to elicit, and how to survive nobody answering.

Why a server cannot simply prompt

Everything about elicitation follows from one fact: an MCP server has no user interface. It may be a subprocess speaking over stdio or a remote service answering HTTP from a datacenter; whatever it writes goes to a log or a JSON-RPC frame, not to a screen. Nor does it have any trust relationship with the human — the person authorized a host application, which chose to connect to this server. The human’s attention belongs to the host.

Without a way to ask, a server hitting a missing value has three bad options. It can fail, turning a recoverable gap into an error the model must interpret. It can guess, which is how tools delete the wrong record. Or it can demand the value up front as configuration, forcing it to hold information it has no business storing. Elicitation exists because all three are worse than asking.

Advertisement

The inversion, and where it is not sampling

MCP’s usual direction of travel is client-to-server: the host’s client calls tools/call, the server answers. Elicitation reverses it: mid-request, the server issues a request to the client and waits, suspending the original call inside its own handler. MCP has two such inversions and they are easy to confuse: sampling asks the host to run a model completion on the server’s behalf, while elicitation asks the host to obtain input from the human. That is the whole boundary; the rest of this article stays on the human side.

The request carries two things: a message explaining what is needed and why, and a schema describing the shape of an acceptable answer. The message is what the user reads; the schema is what the host renders and what the server may assume about whatever comes back.

The schema is a constraint, not a hint

The schema reads like documentation but works like a fence. Current revisions deliberately restrict elicitation schemas to a flat object of primitive fields — strings, numbers, booleans and enumerated choices, with titles and descriptions for display — rather than the full generality of JSON Schema. No nesting, no arrays of objects, no arbitrary composition.

That restriction is doing real work. It guarantees a host can mechanically render any valid request as an ordinary form: a checkbox for a boolean, a dropdown for an enumeration, a labelled field for a string — without interpreting a schema language it did not expect. And a server cannot smuggle a data-extraction interrogation into what the user reads as a simple question. The schema also validates: what the user submits is checked against it before it reaches the server, which therefore receives typed values rather than free text to parse.

The host as mediator

The host is not a pass-through; it is where every guarantee lives. It attributes the request — the user must see which named server is asking, not a floating dialog that could have come from the application itself. It renders the request in its own chrome, so a server cannot forge the look of a system prompt or a login screen. And it enforces policy: a host may cap how often a server asks, suppress a muted server, or reject a request without ever showing it, answering on the user’s behalf.

This is the trust posture the protocol takes everywhere — the server proposes, the host disposes — and it is why elicitation can be offered to servers of unknown provenance at all. A server can request; it cannot compel, cannot see what it was not given, and cannot tell a human’s refusal from a policy’s.

MCP elicitation — servers requesting input from usersstructured prompts mid-operation, safelyServer needs inputmissing parameter, confirmationElicitation requestschema + promptClient mediatespresents to userUser respondsstructured inputSchema validationtyped responsesUser controlapprove, decline, cancelTrust boundaryserver can't forcevs samplinginput vs generationUse casesconfig, confirmation, disambiguationSecurityno sensitive-data coercionOps — schema design + UX + consent flowsvalidatecontrolboundcompareapplysecuredesignoperateoperate
Elicitation: a server requests structured input via a schema; the host mediates, presenting it to the user who controls whether and how to respond.

Three outcomes: accept, decline, cancel

A server that treats elicitation as a function returning a value has already misunderstood it. There are three outcomes, kept distinct on purpose.

OutcomeWhat the user didWhat it means
AcceptFilled the form and submittedYou get schema-valid content; proceed
DeclineExplicitly refused this requestA deliberate “no” — do not re-ask
CancelDismissed without decidingNo answer at all — intent unknown

The difference between the last two is the one teams collapse and then regret. Decline is information: the user considered the question and said no, so a well-behaved server records that and stops asking. Cancel is the absence of information — the dialog dismissed, the window closed, the user gone. Treating cancel as “no” silently converts an interruption into a decision; treating decline as cancel produces a server that asks the same question forever. And accept only promises the fields were supplied and valid, not that the values are right.

Handling all three in the tool that asked

The elicitation sits inside a suspended tool call, so each outcome must become a tool result. The common anti-pattern is letting anything other than accept raise, so a user politely declining produces a stack trace and a protocol-level failure. That is wrong on both counts: a declined confirmation is a successful execution of a tool whose correct behaviour was to do nothing.

Return it as a normal tool result whose content says so — “deployment not performed; the user declined confirmation” — not as an error. The model reads that sentence and can explain it, instead of retrying a call it thinks crashed. Reserve the error channel for elicitation that actually broke, and never loop to re-ask on decline; a server that pesters until the user gives in is the coercion this design exists to prevent.

Advertisement

What must never be elicited

There is a hard line, and it is credentials. Passwords, API keys, one-time codes, card numbers: a server must never ask for them through elicitation, and a host should treat a request fishing for them as hostile. The reason is precisely the mechanism’s strength: elicitation renders inside the interface the user already trusts, so a request for a password arrives wearing the host’s clothes — a confused-deputy setup, borrowing the host’s credibility to make a third party’s phishing prompt look official.

Legitimate secrets have their own path: an authorization flow the user completes with the identity provider directly, or configuration the host holds and the server never sees. A mid-tool prompt improves on neither. So: servers should elicit only choices, confirmations and non-secret values; hosts should scan messages and field metadata for credential-shaped asks, warn loudly, and offer a one-click way to cut that server off.

The operation is blocked behind the request

An elicitation is the slowest call your server will ever make: a network hop is milliseconds, a human is seconds, minutes, or never. Meanwhile the handler sits parked mid-execution and whatever it holds stays held.

That makes resource discipline non-negotiable. Do not elicit with an open database transaction, a held lock, or a rented connection — gather the input first, then start the work that needs those things. Set a timeout you can defend and treat expiry as cancel: no decision was made, so undo cleanly and report. Expect the host to have its own timeout and to answer for the user if theirs fires first, and expect the operation to be cancellable while the question is pending, so your cleanup path must survive that. Batch related questions: three fields in one form is one interruption, three elicitations is three chances to lose the user.

Hosts with nobody to ask

Elicitation is a client capability, declared during the initialize handshake. Plenty of hosts will not declare it: a headless CI runner, a batch pipeline, an agent with no human attached. They are not broken; they have nobody to ask.

So the check comes before the ask: inspect what the connected client advertised and choose a path, rather than assuming the facility exists and erroring out when the request bounces. Three good fallbacks, in order of preference — use a safe default and say in the result that you did; expose the same value as an optional tool parameter so the model can supply it from context it already has; or fail with a message naming exactly what is missing, so the model can ask the user in conversation and call again. That last fallback is the point — the model is a fine channel to the human, just a less structured one.

Designing a flow that survives refusal

Add it up and elicitation stops looking like a feature to sprinkle on and starts looking like a design constraint. Three rules earn their keep. Ask rarely: every question is an interruption, and a tool that asks twice per invocation gets uninstalled. Ask early, before expensive or partially committed work. Ask answerably — the user sees a form, not your call stack, so the message must be self-contained and the options must mean something without protocol context.

And assume no answer. The unsupported path, the declined path and the timed-out path are not exotic; on a busy day they are the majority. Each should end in a clean, side-effect-free state that tells the model what happened and what to try instead. A server built that way degrades into a well-behaved non-interactive server when nobody is home — which is what you want, because the interactive version is the optimistic case.

Elicitation exists because an MCP server has no screen and no standing with the human — so instead of failing, guessing, or hoarding configuration, it sends a schema-bound request and lets the host put the question to the user in trusted chrome, with attribution and policy attached. Design to three answers, not one: accept gives you validated values, decline is a decision to record, cancel is the absence of a decision — collapsing the last two yields silent assumptions or a server that nags. Never elicit credentials; that is phishing with the host’s credibility borrowed. The operation is parked behind a human, so hold no locks, set a timeout, batch your questions. And check the client capability first: the flow that degrades gracefully when nobody answers is the flow that works everywhere.