How to pause an MCP tool call, ask the user, and resume

Say your tool call is halfway done when it hits a question only the human can answer. Which email address should the invite go to? Which region should the project deploy in? Sometimes the third-party provider forces the issue with an interactive step in the middle of an otherwise automated flow. The obvious move is to fail the call and let the model retry with more arguments. But that throws away every step already completed: accounts created, resources allocated, minutes of polling. The better move is to pause, ask, and resume.
Elicitation is the protocol answer
MCP has a mechanism for exactly this. The server sends the client an elicitation request: a human-readable message plus a JSON Schema describing the fields it wants back. The client renders a form from the schema, the user fills it in, and the answer flows back to the server, which picks the tool call back up.
Quick version note, as of July 2026. That request-over-an-open-stream mechanism is how elicitation works on the 2025 spec versions deployed everywhere today. The MCP 2026-07-28 spec restructures it into a stateless shape: the server returns an input-required result, and the client re-issues the call with the answers attached, so any replica can pick up the resume. That’s the same suspend-and-resume design this article builds by hand, adopted by the protocol itself. The store-based pattern below gets you that behavior on the spec versions your clients speak today, and it survives the migration.
The schema is what makes this practical. I built a service at ZeroClick that provisioned third-party accounts from inside an agent session, and its first version asked questions as a plain string. Reshaping the request into MCP’s form, message plus requestedSchema, meant one payload drove two different renderers: an HTML form in a web view and a native form drawn by the MCP client itself. I never wrote a translation layer between them. The server describes what it needs once, and every surface that can render a JSON Schema can collect it.
Implementing the pause
Now to the implementation. The shape that works is a coroutine suspended over an external store. The executing code calls something like await prompt(message, schema)and stops. Here’s the gist of what happens underneath:
- The pending question is written to a shared store (Redis, a database row, anything both the executor and the answer endpoint can reach), keyed by the job.
- The job’s status moves to an awaiting-input state, so anything polling the job knows a question is outstanding and can surface the form.
- A poller checks the store for an answer. In my implementation it checked every second, with a bounded deadline.
- When the answer lands, the poller resolves the promise and execution resumes on the next line, with the answer in hand.
Notice that the executing code never knows it was suspended. It wrote const email = await prompt(...)and got an email back. All the machinery lives underneath that one awaited call: the store, the status transition, the polling. That’s the property to preserve when you build this. The flow author writes straight-line code, and the runtime handles the suspension.
Machine time and human time are different budgets
Long-running tool calls need execution timeouts. Everything’s fine until a naive timeout kills exactly the call elicitation exists to save. The system I built gave each execution a five-minute budget, which is plenty for API calls and polling loops and a hard stop for runaway code. Then a user takes six minutes to decide on a project name and blows the budget while the code does nothing at all.
The fix is to re-arm the execution timeout every time an elicitation answer arrives. Machine work stays bounded at five minutes per stretch; human thinking time never counts against it. The call can wait as long as the person needs, and a hung API call still dies on schedule. You’ll likely still want an outer deadline after which an abandoned job gets cleaned up, but that deadline is a product decision, and a separate knob from the execution budget.
The read-your-writes race
Here’s the bug everyone building this hits. The answer endpoint wrote the user’s response into the pending-request record. The status view the client polls was a separate projection, and it still reported the job as awaiting input until a background pass caught up. In that window the client saw an unanswered question, re-rendered the form, and asked the user a question they had just answered.
The fix is to write the answer into both places in the same operation: the request record the executor polls, and the status projection the client polls. Once those two views can never disagree, a useful invariant falls out. The client can safely treat “pending” as “has no answer content yet” and filter on that alone. Any elicitation with content is answered; any without is open. If you catch yourself adding a flag to mark answers as “really answered,” you have two sources of truth and the race is still there.
Two kinds of input
So should every question pause execution? No. Split your inputs into known-unknowns and unknown-unknowns.
Known-unknowns are fields you can name before the flow starts: an email address, a workspace name, a plan tier. Declare these on the flow itself and collect them before execution begins. Declaring them up front pays off three ways: missing input fails fast instead of minutes in, the user answers everything in one pass instead of being interrupted repeatedly, and the host application can prefill fields it already knows, skipping the question entirely. The system I built evolved this way. The first version hardcoded a single required email prompt, and later versions replaced it with a declared list of required inputs, looping over them and skipping any already present in context.
Unknown-unknowns only surface mid-execution: the provider returned three options and someone has to pick one, or a step that’s usually automatic demanded verification this time. These are what mid-call elicitation is for. If you find a flow eliciting the same field on every run, that field is a known-unknown. Move it up front.
Authenticate the answer
The answer path is a write endpoint: “here is the answer for job X.” If it accepts any caller who knows a job ID, anyone who obtains or guesses an ID can inject answers into someone else’s flow. Elicitation answers become resource names, choices, and destinations that drive real actions. They must never become secrets: the spec forbids form-mode elicitation for credentials such as passwords or API keys, and the 2025-11-25 revision added URL-mode elicitation for exactly those flows. In the system I built, responding to an elicitation required a session token proving the caller owned the job. It took two validators: one for trusted server contexts, and a separate one for browser contexts, because a form running in a user’s browser must never hold a private API key. Whatever your setup, the rule is the same: bind answers to the session that asked the question.
One question at a time
When a flow has several questions, resist answering them in parallel. Later questions often depend on earlier answers. The region choice determines which plans exist, and the account chosen determines which projects can be named. Process elicitations sequentially: ask, wait, resume, and let the next question be computed with the previous answer in scope. Parallel forms look faster and produce contradictions. The up-front declared inputs are where you batch; the mid-flight questions are where you serialize.
What this buys you
A tool call that can pause on a human stops being a gamble. The agent starts work it can’t fully specify, the person supplies the missing pieces when they matter, and nothing already done gets thrown away. The mechanics fit in an afternoon of reading: a promise suspended over a shared store, a status the client can poll, a timeout that respects human time, one atomic write for the answer, and a token proving who’s answering. Get those five right and the rest is product design.
Questions clients ask
What is MCP elicitation?
Elicitation is the Model Context Protocol feature that lets a server request structured input from the user during a tool call. The request carries a human-readable message and a JSON Schema describing the expected fields. The client renders a form from the schema, collects the answer, and returns it to the server, which picks the tool call back up where it left off.
What if the client doesn’t support elicitation?
Elicitation is an optional client capability, so design for its absence. Declare required inputs up front so the host can collect them before execution starts, and have the tool fail with a clear message naming the missing field when a mid-call question has no way to reach the user. A clear failure lets the model retry with the argument filled in; a hang teaches the user to distrust the tool.
How long can a tool call wait for a human?
The call can wait as long as your timeout design allows. The trick is separating machine time from human time: keep a bounded budget for actual execution, but re-arm it whenever an elicitation answer arrives. In the system I built, execution had a five-minute budget that reset on every answered question, so a user could think for twenty minutes without killing the call. You still want an outer deadline that abandons the job and cleans up.
How do I test flows that need human input?
Make the answer path a real API endpoint rather than something buried in the UI. If answers arrive through an endpoint that writes to the pending-request store, tests can play the human: start the call, poll until it reaches the awaiting-input state, post a canned answer, and assert the call resumes and completes. That same seam lets you test the failure cases without a browser in the loop: wrong session token, empty answers, timeouts.
More guides
- Your MCP server is stateful. Your load balancer doesn't know that.Sessions, sticky routing, proxy timeouts, and what breaks at two replicas.
- MCP went stateless. Here's how to migrate a session-era server.The 2026-07-28 spec removes sessions. The handle pattern, elicitation changes, and the order to migrate in.
- Why models ignore your MCP tools, and the design that fixes itServer instructions, filtering server-side, error results over exceptions, and tool output as prompt surface.
Does your MCP server hold up when a human enters the loop?
I audit MCP servers for the failure modes this article describes (elicitation, timeouts, races, auth on the answer path) plus tool-surface design and directory readiness. Fixed price, $2,000, one week, credited toward any follow-on work.