C.W.K.
Stream
Lesson 05 of 05 · published

Sampling, Roots, Elicitation

~22 min · sampling, roots, elicitation, client-features

Level 0Curious Reader
0 XP0/48 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

The client-side trio is what makes MCP feel conversational. Each capability lets the server reach back through the host to do something only the host can do.

Sampling lets a server ask the host's LLM to do inference on its behalf. The classic case: a code-analysis server wants to reason about three possible refactors before picking one; instead of shipping its own LLM, it sends a sampling request and the host runs the inference. The host MUST ask the user before forwarding the request — sampling is "the server is asking to use my brain" and that needs consent. When approved, the response flows back to the server as a regular sampling result.

Roots let a server ask the host which directories or URIs are in scope for the current session. A filesystem server should not assume access to $HOME; it should ask the host for roots, get back something like file:///Users/me/project, and operate inside that. The host typically derives roots from the current workspace or a user pick; the protocol just defines how the question is asked.

Elicitation is the most human of the three: a server can request additional input from the user mid-workflow without restarting. "Which environment do you want to deploy to?" "Should I include the test files?" The server sends an elicitation/create request describing a small JSON Schema for the input it needs; the host renders a form, collects the response, and returns it. Elicitation is what turns a one-shot tool call into an interactive workflow without breaking the protocol's structure.

Code

Sampling — server asks the host to think·python
# Inside an MCP server (Python SDK)
from mcp.types import CreateMessageRequest, SamplingMessage, TextContent

@app.tool()
async def analyze_diff(diff: str) -> list:
    # Ask the host to run inference for us
    completion = await app.request_context.session.create_message(
        CreateMessageRequest(
            messages=[SamplingMessage(role="user", content=TextContent(
                type="text",
                text=f"Pick the riskiest hunk in this diff. One paragraph.\n\n{diff}",
            ))],
            max_tokens=400,
        )
    )
    return [TextContent(type="text", text=completion.content.text)]
Roots — server asks for filesystem scope·python
roots = await app.request_context.session.list_roots()
# roots = [{"uri": "file:///Users/me/project", "name": "project"}]
allowed = [r.uri for r in roots]
Elicitation — server asks the user a structured question·python
from mcp.types import ElicitRequest

answer = await app.request_context.session.elicit(ElicitRequest(
    message="Which environment should I deploy to?",
    requestedSchema={
        "type": "object",
        "properties": {"env": {"type": "string", "enum": ["staging","production"]}},
        "required": ["env"],
    },
))
target = answer.content["env"]

External links

Exercise

Pick whichever of the three feels least familiar (probably elicitation) and prototype it: a server tool that, on call, elicits a single piece of structured input from the user before completing. Run it through Claude Desktop and notice how the elicitation form appears mid-tool-call. That is the protocol turning a one-shot into a conversation.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.