Elicitation Primitive: The Server Asks the User for More Info Mid-Task
~10 min read
Elicitation lets a server request structured user input mid-task — pausing a multi-step workflow to ask for a missing preference or detail, rather than guessing or failing outright.
Elicitation is the third client-side primitive, and it addresses a specific gap: what happens when a server, partway through executing a task, realizes it's missing information only the user can actually provide? Elicitation allows servers to request user input mid-task, in a structured way — rather than the server guessing at a default, failing outright, or requiring every possible input to be front-loaded before the task even starts.
This course's example, continuing the travel-booking thread used throughout this topic: a server booking travel may ask for the user's preferences on airplane seats, room type, or their contact number to finalize a booking. These aren't things the server could reasonably infer or default — a window vs. aisle seat preference, or a phone number needed for the actual reservation, genuinely require asking the specific human at the point in the workflow where that information becomes necessary, not before.
This is meaningfully different from simply requiring all inputs upfront (the traditional API pattern of collecting every field before starting) — Elicitation lets a task begin with the information available, and pause specifically at the point where a genuine gap emerges, ask a targeted, structured question for just that missing piece, and then resume with the answer folded in. This produces a much more natural, conversational workflow than a rigid, all-fields-upfront form ever could, especially for multi-step tasks where what information is actually needed only becomes clear partway through (you don't know you need a seat preference until you've actually found flight options worth booking).
Together, Sampling (borrow the client's LLM for reasoning), Roots (scope filesystem access), and Elicitation (ask the user for missing info) round out the 3 client-side primitives — completing the full 6-primitive picture alongside the server-side Tools, Resources, and Prompts covered earlier in this topic, and confirming why MCP genuinely creates two-way communication rather than being just another one-directional tool-calling standard.
💻 Code example
# Server-side: requesting structured user input mid-task via Elicitation
# — pausing a multi-step workflow at the exact point a gap emerges.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("travel-server")
@mcp.tool()
async def finalize_booking(ctx, flight_id: str) -> str:
"""The server doesn't require seat preference or phone number
upfront — it asks for them via Elicitation only once it's
actually at the point in the workflow where they're needed."""
seat_pref = await ctx.elicit(
message="What's your seat preference?",
schema={"type": "string", "enum": ["window", "aisle", "no preference"]},
)
contact_number = await ctx.elicit(
message="What's the best phone number to reach you for this booking?",
schema={"type": "string"},
)
# Now that the genuinely-needed info has been gathered exactly
# when it became necessary, the booking can actually complete
return f"Booked flight {flight_id}, seat: {seat_pref}, contact: {contact_number}"
💬 Deep Dive with AI
Key points
- •Elicitation lets a server request structured user input mid-task, rather than guessing, failing, or requiring everything upfront
- •The book's example: asking for seat preference, room type, or contact number partway through a travel booking
- •This produces a more natural, conversational workflow than a rigid all-fields-upfront form
- •Especially valuable for multi-step tasks where what's needed only becomes clear partway through the workflow
- •Together with Sampling and Roots, this completes the 3 client-side primitives, rounding out MCP's full 6-primitive, genuinely two-way protocol