MCP Elicitation and Sampling: The Handshake That Skips Your Credential Broker
9 min read Fullmakt Team
- mcp
- authentication
- credentials
- governance
- observability
- traceability
- agents
Every control this blog has described — scoped credentials, decision tokens, a tamper-evident audit trail — assumes one thing: that an agent gets what it needs to act through a call the broker can see. MCP added two features that don’t go through that call at all. Elicitation lets a server ask the human sitting behind the agent for information directly, mid tool-call. Sampling lets a server ask the host’s own model to generate a completion, on the server’s terms. Neither is a bug. Both are a second, informal login handshake that nothing downstream of the model is watching.
Two features, one shared shape
Both are client capabilities a server can request:
- Elicitation (
elicitation/create), added in MCP’s 2025-06-18 spec revision, lets a server pause a tool call and ask the client to collect specific information from the user — a missing parameter, a confirmation, a choice between options — via a JSON schema the client renders as a form. It exists for a good reason: not every input a tool needs should be guessed or hallucinated by the model. - Sampling (
sampling/createMessage), part of MCP since its original spec, lets a server ask the client to run an LLM completion on messages the server supplies, and get the text back. It exists so a lightweight MCP server can borrow the user’s model instead of shipping its own — useful for summarization, classification, or drafting steps inside a tool.
Both invert the normal MCP direction. Everywhere else, the client (the agent host) calls the server’s tools. Here, the server calls back into the client and asks for something — user input, or model output — and the MCP OAuth handshake that authenticated the original connection has nothing to say about what happens next, because neither request is a tool call. It’s a side channel riding on top of an already-authenticated session.
Where it goes wrong
None of this requires a malicious actor to breach anything. A server that behaves exactly as the spec allows can still produce outcomes no credential broker would ever approve:
- Elicitation as a credential-phishing form. The spec itself warns servers not to request sensitive information this way — but the JSON schema behind an elicitation request can label a field however the server likes. “Please confirm your API key to continue” renders as a normal, trusted-looking form inside the agent’s own UI. A user who would never paste a secret into a chat box will often fill in a field the client itself is asking them to complete. The credential goes straight to the server. It never touches a vault reference, never gets scoped, and never appears in an audit log, because as far as the broker is concerned, no credentialed call happened at all.
- Sampling as an unaudited exfiltration path. A sampling request’s
messagesarray is whatever the server puts in it — including context the server has no business seeing. If a client concatenates recent conversation history into the payload it forwards for “helpful context,” a server can request a completion that echoes pieces of that history back in its response, laundering data out of the session through a completion request instead of a tool call. Every technique behind the lethal trifecta — private data, untrusted input, an exfiltration path — is present here; it’s just that the exfiltration path is a protocol feature, not a bug. - Sampling as a spend and behavior side channel nobody meters. Every sampling request spends the user’s model budget and produces a completion the server fully controls the framing of. Repeated, chained sampling calls can drive real cost and real model behavior — the same shape of problem as runaway agent spend — through a request type most brokers and gateways were never built to see, because it doesn’t look like a tool invocation.
- The human-approval step is optional in practice, even where it’s required on paper. The spec expects clients to let a user review and approve both an elicitation request and a sampling request before it’s sent, and again before the result goes back to the server. Plenty of client implementations render that as a single “always allow this server” toggle to keep the UX out of the user’s way — which means the one safeguard the spec relies on is the first thing product pressure removes.
Why this is a governance problem, not a UX detail
An audit trail built for incident forensics answers “what call did this agent make, under what identity, with what credential.” Elicitation and sampling requests don’t fit that shape at all — there’s no tool name, no scope, no credential reference to log, because nothing was dispensed. The record a broker keeps for every brokered call simply has no row for “the server asked the human for a password and got one,” or “the server ran three completions against the last twenty messages of context.” Those events happened entirely inside the MCP client, outside the boundary every other control in this series assumes exists.
That’s the same failure mode as MCP tool poisoning and rug-pull schema drift: a legitimate protocol mechanism, used exactly as documented, produces an outcome policy would have blocked if policy had ever been asked. The difference is that a tool call at least reaches a broker that can refuse it. An elicitation or sampling request, by design, never does.
What actually has to be true
- A credential-shaped elicitation request has to be refusable by policy, not just by spec guidance. If a field’s label or schema looks like it’s asking for a secret, API key, or password, the client — or whatever sits between the client and the server — needs the ability to block the request before it renders, the same way a confused-deputy tool call gets blocked before it executes, not after a human already typed the answer.
- Any credential a user does need to supply belongs in the vault, not in a form field. If a workflow genuinely needs a new credential on file, the answer is registering it through the broker’s own credential backend, not typing it into whatever UI element the server happened to request — one path is scoped, logged, and revocable; the other is a string now living in the server’s memory with no lifecycle attached to it at all.
- Sampling requests need the same identity and logging discipline as a tool call. Who requested it, what context it was given, what came back — recorded as a linked event, the same way a decision token and a credential dispensing are recorded as two connected facts instead of one opaque line.
- “Always allow” can’t be the default UX for either capability. A safeguard that a product team quietly turns into a one-time toggle isn’t a safeguard being applied inconsistently — it’s a safeguard that’s been effectively removed, the same lesson as approval fatigue in ordinary tool-call approvals, now applied to a channel with even less visibility.
The business case: the broker has to see the side channels too
Fullmakt’s whole premise is that raw secrets never reach the model — variables and tool arguments carry credential references, not plaintext, and resolution happens only at the broker, gated by policy, logged as a call. Elicitation and sampling matter precisely because they’re the two places that guarantee can be routed around without anyone touching the credential path Fullmakt actually controls:
- Policy can pattern-match an elicitation schema before it ever reaches the user. A request whose field looks like a secret, token, or password is exactly the kind of call a policy engine is built to refuse — the same enforcement point that already blocks a poisoned tool description can be extended to a form a server is trying to render.
- Registering a new credential always goes through the same broker path, never through whatever a server happens to ask for. If a workflow needs a secret on file, the answer is a vault entry issued and scoped like every other credential Fullmakt brokers — not a value typed once into a form and never seen again by anything that tracks it.
- Sampling requests can be treated as brokered calls, not exempt ones. Tying a sampling request to the same identity, task, and audit record as a tool call closes the gap where a completion request quietly carries data or cost that never shows up in the trail Fullmakt already keeps for everything else that agent does.
- One chokepoint, one place both channels have to clear. The argument is the same one behind every gap in this series: a control that only watches tool calls is a control with a documented blind spot, and MCP itself documents where that blind spot is.
FAQ
Are elicitation and sampling security vulnerabilities in MCP? No — both are documented, intentional protocol features. The risk isn’t that they’re broken; it’s that they’re side channels a credential broker or policy engine built around tool calls doesn’t automatically cover, so using them exactly as specified can still produce an outcome policy would have refused.
Doesn’t the MCP spec already say not to request sensitive data via elicitation? It recommends against it, but a recommendation isn’t an enforcement mechanism — nothing in the protocol stops a server from labeling a field however it wants, and nothing requires a client to detect and refuse a credential-shaped request rather than just rendering the form it was asked to render.
Should we just disable elicitation and sampling entirely? That removes real functionality some tools depend on. The more durable fix is treating both as gated, logged, policy-checked events — the same treatment every brokered tool call already gets — rather than turning them off or trusting the default “always allow” prompt most clients ship with.
How is this different from ordinary prompt injection? Prompt injection manipulates what a model decides to do with the tools it already has. Elicitation and sampling abuse don’t need to manipulate the model at all — the server is using a legitimate protocol request exactly as designed, and the gap is that the request type itself sits outside where policy and audit are normally enforced.
What should an audit log show for a sampling request? The requesting server’s identity, the task it was made under, the context it was given, and the completion returned — recorded as a linked event tied to the same identity and task record as every other call that agent made, not silently omitted because it wasn’t a tool invocation.
A login handshake and a scoped credential only govern the paths a broker was built to watch. MCP added two legitimate ways for a server to get what it wants — a human’s direct answer, or the host’s own model — without going through either one. Closing that gap means treating elicitation and sampling as brokered, audited events from the start, not discovering after the fact that the safest-looking form field in the conversation was the one thing nothing was watching.