Module 05

Client Primitives: Roots, Sampling, Elicitation

Building Systems Model Context Protocol (MCP)

Module 5 — Client Primitives: Roots, Sampling, Elicitation

Phase 2 · The Primitives & Building. Module 4 covered what servers expose. This module covers the three capabilities that flow the other direction — things a server can ask of a client. They invert the usual picture and, done right, are what make servers powerful without making them dangerous: the host always stays in control of the model, the user, and consent. These primitives are also where the modern UX of 2025-11-25 lives (sampling-with-tools, URL-mode elicitation).

By the end you can:

  1. Explain roots as both a scoping mechanism and a security boundary.
  2. Explain sampling — why a server borrows the host’s model, and why the host stays in the loop — including the 2025-11-25 sampling-with-tools change.
  3. Explain elicitation and the 2025-11-25 URL mode, and why it matters for auth/payment UX.
  4. Design a workflow that uses all three responsibly.

1. The inversion — and why it’s safe by design

So far: client calls server. These three flip it — the server initiates a request back toward the client. But notice the careful framing in each case: the server never gets direct power. It asks; the client/host mediates and the user can refuse. That mediation is the entire safety story. Keep it front of mind:

  • Roots — the client tells the server where it’s allowed to operate. (Server receives a boundary.)
  • Sampling — the server asks the client to run a model completion. (Host controls the model, cost, and approval.)
  • Elicitation — the server asks the user, via the client, for structured input. (User sees and approves.)

All three are client capabilities: they only exist if the client advertised them in the initialize handshake (Module 3). A server must check before using them.


2. Roots — client-declared operating boundaries

A root is a URI (often a file:// directory) that the client exposes to tell the server: “these are the boundaries you may operate within.” For a filesystem server, roots are the folders the host has authorized — e.g. the currently open project. The server is expected to confine its operations to declared roots.

  • Capability: client advertises roots (optionally roots: { listChanged: true }).
  • Method: server calls roots/list to learn the current roots.
  • Notification: if roots change (user opens a different folder), the client emits notifications/roots/list_changed, and the server re-fetches.

Two jobs at once:

  1. Scoping/UX — the server knows where to work without guessing or asking repeatedly.
  2. Security boundary — roots express the intended sandbox. But note the same caution as Module 4’s annotations: roots are a declared boundary the well-behaved server respects; they are not a substitute for OS-level sandboxing or host-side enforcement. A hostile or buggy server could ignore them, so a careful host also restricts what the server process can actually reach. Roots say “stay in here”; real enforcement keeps it in here.

3. Sampling — the server borrows the host’s brain

Sampling is the most conceptually important inversion. Recall Module 1’s asterisk: servers have no model of their own. Sampling is the sanctioned way for a server to get an LLM completion — by asking the client to run it.

Why this exists (the elegant part)

A server author wants “intelligence” inside their server — to summarize, classify, extract, or drive an agentic loop — but:

  • they shouldn’t bundle their own model or API keys (cost, secrets, lock-in), and
  • the host must keep control of which model runs, what it costs, and whether a human approves.

Sampling solves both: the server describes the completion it wants; the host chooses the model, can show the request to the user for approval, runs it, and returns the result. The server gets intelligence; the host keeps the keys and the consent gate.

The flow

  • Capability: client advertises sampling.
  • Method: server → client sampling/createMessage with:
    • messages — the conversation to complete,
    • modelPreferenceshints (e.g. cost vs intelligence vs speed priorities, model name hints) the host may honor or override,
    • systemPrompt, maxTokens, temperature, etc.,
    • includeContext — how much surrounding MCP context to include (none / thisServer / allServers), subject to host policy.
  • Human-in-the-loop: the spec expects hosts to let users review/modify/reject both the outgoing request and the returned completion. This is a guardrail, not optional decoration.
  • Result: the model’s completion (role, content, the model actually used, stop reason) flows back to the server.

2025-11-25 change — sampling with tools (SEP-1577)

Previously sampling couldn’t include tool definitions, which blocked server-driven agent loops. Now a server can include tool definitions in its sampling request, so the borrowed completion can itself decide to call tools — enabling server-initiated agentic workflows while still routing model execution and consent through the host. Powerful, and a likely area of growth in 2026-07-28.

Security note: sampling is a capability a malicious server could try to abuse (e.g. exfiltrate context via includeContext, or burn tokens). That’s exactly why the host mediates model choice, context scope, cost, and user approval. Treat sampling requests as untrusted input to a host-enforced policy.


4. Elicitation — the server asks the user (safely)

Elicitation lets a server pause mid-operation and request structured input from the user, through the client. Example: a booking server has everything except the date → it elicits a date rather than failing or hallucinating one.

The flow

  • Capability: client advertises elicitation.
  • Method: server → client elicitation/create with a message (what/why) and a requestedSchema — a restricted, flat JSON Schema (primitive fields only: strings, numbers, booleans, enums — no deep nesting). The restriction keeps the client UI simple and the request auditable.
  • User actions: the client presents UI and returns one of three outcomes — accept (with the data), decline (user says no), or cancel (dismissed). Servers must handle all three and must not treat decline/cancel as accept.
  • Privacy rule: servers should not use elicitation to request secrets/credentials directly — which leads to the 2025-11-25 upgrade below.

2025-11-25 change — URL-mode elicitation (SEP-1036)

Instead of collecting sensitive data inside the client UI, a server can return a URL and have the user complete the sensitive flow in a browser — OAuth consent, a payment, entering an API key on the provider’s own page. The credential never passes through the model context or the MCP message stream. This is a big deal for auth and payment UX and connects directly to Phase 3: it’s how an MCP auth flow can hand the user off to an authorization server cleanly. Pair this mentally with the OAuth discovery dance you’ll learn in Module 9.


5. How the three fit together

A well-built server uses these to be capable and well-behaved:

  • Roots keep it operating only where it’s allowed.
  • Elicitation fills gaps by asking the user instead of guessing — and hands off secrets via URL mode.
  • Sampling gives it intelligence using the host’s model, under the host’s control.

And the through-line for your architect goal: every one of these keeps the host as the control/consent point. The same principle scales up in Phase 3 — the host (and later the gateway) is where identity, consent, and entitlements are enforced. Client primitives are that principle expressed at the protocol level.


6. Milestone exercise

Design a workflow for a travel-booking server that uses all three primitives:

  1. Roots: what boundary would the client declare, and how does the server use it? (If filesystem isn’t natural here, state what scoping role roots play or why they don’t apply — reasoning matters more than forcing it.)
  2. Elicitation: identify one missing parameter the server must ask the user for. Write the requestedSchema (flat, primitives only) and name the three outcomes you must handle. Then identify one sensitive step (payment) and explain how URL-mode elicitation handles it differently.
  3. Sampling: identify one step where the server borrows the host’s model (e.g. summarize options into a recommendation). Note which modelPreferences you’d hint and why the host might override them. Bonus: where could sampling-with-tools let the server run a small agent loop?
  4. In two sentences, explain why none of these three lets the server bypass the user or the host.

Self-check:

  • Your elicitation schema is flat (no nested objects) and you handle accept / decline / cancel distinctly.
  • You did not elicit the payment details inline — you used URL mode.
  • Your sampling step explains that the host picks the model and can require user approval; modelPreferences are hints.
  • Your closing two sentences correctly locate control at the host/client, not the server.

Get this and you understand both directions of MCP. Next: Module 6 — Build a server (stdio) and a client, where you stop designing on paper and ship something real.


Quick reference — Module 5 glossary

  • Client primitives — capabilities a server requests of a client; exist only if the client advertised them.
  • Roots — client-declared URI boundaries (roots/list, notifications/roots/list_changed); scoping + declared security boundary (not a substitute for real sandboxing).
  • Sampling — server asks client to run an LLM completion (sampling/createMessage); host controls model, context scope, cost, and approval; modelPreferences are hints. 2025-11-25: can include tool definitions (server-initiated agent loops).
  • Elicitation — server asks the user for structured input (elicitation/create) via a flat requestedSchema; outcomes accept / decline / cancel; never for secrets directly. 2025-11-25: URL mode hands sensitive flows (OAuth, payment) to the browser.
  • Through-line — all three keep the host as the control/consent point — the same principle Phase 3 enforces with identity and entitlements.