skip to content

If a user denies an MCP sampling request, what does the client send back?

level: middleimportance: must knowfreq 46%

answer

  1. A refusal has no message
  2. No server-initiated request, no response
  3. Deny looks exactly like a crash
  4. Retry means yes, silence means no
  5. Servers must tolerate never hearing back

basics

~20 s

Nothing at all. In MCP revision 2026-07-28 a refusal is expressed by simply not retrying the original request. There is no decline message, no error code and no reason string for the server to read.

solid answer

~50 s

A denial is silence. Since revision 2026-07-28 there are no server-initiated JSON-RPC requests in MCP, so a sampling request is not something the client answers — it arrives as an interim `InputRequiredResult` for an operation the client itself started. The client fulfils it by retrying that original request with the collected input; it declines by simply never retrying. Nothing goes back on the wire. That is a deliberate simplification, but it has a real consequence: the server cannot distinguish a user who clicked *deny* from a client that crashed, lost its network, or decided the request was too expensive. Server code therefore must not block waiting for a completion or treat one as guaranteed — it has to be safe for the operation to just stop there. Before 2026-07-28 the shape was different: the server sent a request and the client replied with a JSON-RPC error.

go deeper

for a junior

Remember that a user can always refuse a sampling request, and that refusing means the interaction just stops. Do not invent a rejection message the client sends.

for a middle

Explain that revision 2026-07-28 removed server-initiated requests, so sampling arrives as an interim result and a decline is expressed by not retrying the original request. Contrast that with the pre-2026-07-28 error response.

for a senior

Demonstrate the server-side consequence: no blocking waits, no state held across the interim result, idempotent partial work, and a timeout that treats the operation as abandoned. Explain why denial and disconnection being indistinguishable is acceptable.

for a principal

Be ready to argue the design: silence removes exchange state from a stateless protocol and denies a hostile server the signal that a human is watching, at the cost of servers never learning why an operation stopped. Say which side of that tradeoff you would take.

## The shape of the interaction determines the shape of a refusal Up to and including revision 2025-11-25, a server could initiate a JSON-RPC request. Sampling used that: the server sent `sampling/createMessage` and waited for a response. Because it was a request, a refusal had somewhere to live — the client answered with a JSON-RPC error, and the server learned that something had gone wrong. Revision 2026-07-28 removed server-initiated requests entirely; there is no `ServerRequest` union any more. What replaced them is a pattern in which the server returns an interim result, `InputRequiredResult`, from a `tools/call`, `prompts/get` or `resources/read` that the **client** issued. A sampling request is one entry in that result's `inputRequests` map. The client, having asked the question in the first place, decides whether to continue the exchange. Continuing means re-issuing the original request with the responses supplied. Not continuing means doing nothing. There is no third option, and therefore no decline message to design. ## What the user experience maps onto When the client shows the user "this server wants to run a completion using your model" and the user clicks *deny*, the client drops the interim result on the floor. No frame is written to stdout, no HTTP request is made. From the server's side, the story is: it returned an `input_required` result, and then nothing ever came back for it. This is worth saying explicitly in an interview because it is genuinely counter-intuitive to anyone used to request/response APIs, where every refusal has a status code. Here, refusal is the absence of a message. ## What the server can and cannot infer The server cannot tell why the exchange stopped. All of these look identical to it: - The user reviewed the prompt and refused. - The user never saw the dialog because the host was in an unattended batch mode with sampling switched off. - The client process exited. - The network dropped, or the user closed the tab. - The client's own policy engine blocked the request before a human ever saw it — for example a per-server spend cap that had already been hit. That ambiguity is a design constraint on servers, not a defect to work around. A server must never write logic like "wait for the completion, then finish the tool call", because the completion may never arrive. It should treat the operation as abandoned after a reasonable time, release whatever it was holding, and be safe to have the same tool called again from scratch. Any partial work it did before returning the interim result has to be either idempotent or cleaned up. ## Why the specification chose silence Three reasons follow from the 2026-07-28 framing. First, MCP is now a stateless protocol — every request is self-contained, and an open connection is not a conversation. Making a refusal into a message would create a piece of exchange state the server is expected to reconcile. Second, refusals carry information a privacy-minded client may not want to give up: a reason string tells the server how the user thinks, and a distinguishable "user denied" tells a hostile server that a human is watching and that it should try a subtler prompt next time. Third, silence is the failure mode you get anyway from crashes and disconnects, so servers already had to handle it; making denial look the same removes a code path rather than adding one. ## Distinguish this from elicitation A common mix-up in interviews: elicitation, which is *not* deprecated in 2026-07-28, does have explicit actions — `accept`, `decline` and `cancel` — carried in its result. But that result only ever reaches the server if the client retries the original request carrying it. So the distinction is: elicitation lets the user express "I saw this and I am saying no" as data, whereas walking away from the whole exchange still sends nothing. A user who denies the *entire* interaction produces silence either way. ## What to say about the legacy era If the interviewer is probing migration knowledge, name the change precisely. Do not say "MCP has no way to decline" as a timeless fact — say that up to revision 2025-11-25 a client answered a server-initiated `sampling/createMessage` request with a JSON-RPC error response, and that revision 2026-07-28 removed server-initiated requests, so a refusal became the absence of a retry. A candidate who describes the old error-response behaviour as current is exactly what these questions are designed to surface.

  • What does that ambiguity mean for how a server implements a tool that requests sampling?
    It has to be safe for the operation to simply end. The server must not block a thread waiting for a completion, must not hold a lock or a transaction open across the interim result, and must expect the same tool to be called again from the beginning. Any work done before returning the `InputRequiredResult` should be idempotent or cleaned up on a timeout.
  • How is a sampling denial different from an elicitation decline?
    Elicitation, which is not deprecated in 2026-07-28, carries an explicit action — accept, decline or cancel — inside its result, so the server can be told "the user said no" as data. But that only reaches the server if the client retries. Abandoning the exchange altogether sends nothing in either case.
  • How did declining work before revision 2026-07-28?
    Servers could initiate JSON-RPC requests then, so `sampling/createMessage` was a real request from the server and the client replied to it — a refusal came back as a JSON-RPC error response. Revision 2026-07-28 removed server-initiated requests entirely, which is why the refusal has no message to travel in any more.

saying these in an interview costs you the question

  • Says the client returns a JSON-RPC error to decline
  • Believes a decline reason string reaches the server
  • Thinks the server can distinguish denial from a crash
  • Has the server block waiting for a completion
  • Confuses sampling denial with elicitation's decline action

context