---
title: "Client features, multi round-trip requests and extensions"
description: "Servers sometimes need something from the client A tool may need a missing parameter, a user confirmation, a payment, or an OAuth sign-in to a…"
url: https://optimizeall.com/learn/model-context-protocol-mcp/client-features-elicitation-mrtr-extensions
updated: 2026-10-05
---

Model Context Protocol (MCP): Connect AI to Your Tools and Data · Primitives, client features and transports · lesson 4 of 18 · 15 min

# Client features, multi round-trip requests and extensions

## Servers sometimes need something from the client

A tool may need a missing parameter, a user confirmation, a payment, or an OAuth sign-in to a third-party service mid-call. MCP defines **client features** for these cases. They evolved quickly, so this lesson separates what is current from what is deprecated.

## Elicitation (current)

**Elicitation** lets a server ask the user for input through the host.

- **Form mode**: the server sends a message and a restricted JSON Schema (flat object with primitive fields, which may include defaults); the host renders a form; the user can **accept** (with data), **decline**, or **cancel**.
- **URL mode** (added in 2025-11-25): the server gives a URL for an out-of-band interaction that must **not** pass through the client, such as an OAuth consent screen, a payment page or entering an API key into the provider's own site. This keeps secrets out of the model's context and the host's logs.

Rules of thumb: never use form elicitation to collect passwords, API keys or payment card data; use URL mode for sensitive flows. Hosts must make clear which server is asking and let users refuse.

## Multi Round-Trip Requests (new in 2026-07-28)

Older specs implemented elicitation (and sampling, and roots) as **server-initiated requests** sent back to the client over a held-open stream. That fought against stateless, load-balanced deployments.

2026-07-28 introduces **Multi Round-Trip Requests (MRTR)**. Instead of calling the client, the server **returns** an interim result:

```json
{"jsonrpc": "2.0", "id": 12, "result": {
  "resultType": "input_required",
  "inputRequests": {"confirm": {"method": "elicitation/create", "params": {
     "message": "Pause campaign 'Summer Sale' for all regions?",
     "requestedSchema": {"type": "object", "properties": {"ok": {"type": "boolean"}}, "required": ["ok"]}}}},
  "requestState": "opaque-server-token"}}
```

The client gathers the answers (for example by showing a form) and **retries the original request** with `inputResponses` attached (and the `requestState` echoed back). Every result now carries `resultType`: `"complete"` or `"input_required"`. Because each round trip is an ordinary request, any server instance can handle it. The high-level SDKs hide most of this: in the Python v2 SDK you still write `await ctx.elicit(...)` inside a tool, and the SDK maps it to MRTR or legacy behavior depending on the protocol version.

## Deprecated in 2026-07-28: sampling, roots and logging

- **Sampling** let servers ask the client's model to generate text. Deprecated; the suggested migration is to integrate directly with an LLM provider API if your server needs generation.
- **Roots** let clients tell servers which directories or files were in scope. Deprecated; pass directories or files via tool parameters, resource URIs or server configuration instead.
- **Logging** via MCP notifications is deprecated; log to stderr (stdio) or use OpenTelemetry.

Under the new deprecation policy these features keep working for at least twelve months, but new implementations should not adopt them. You will still meet them in older servers and clients.

## Utilities you will use

- **Progress** notifications for long operations (sent on the response stream of the related request).
- **Cancellation** of in-flight requests.
- **Completions** for argument autocompletion in prompts and resource templates.
- **Pagination** with cursors for long lists.

## Extensions

2026-07-28 formalized an **extensions** framework: optional capabilities negotiated through an `extensions` field in client and server capabilities, identified like `io.modelcontextprotocol/ui`. Official extensions include:

- **Tasks** (`io.modelcontextprotocol/tasks`): long-running work that returns a task handle; the client polls with `tasks/get` and can send input with `tasks/update`. Useful for report generation, bulk imports and anything taking minutes.
- **MCP Apps** (`io.modelcontextprotocol/ui`): servers provide interactive HTML interfaces (charts, forms, dashboards) rendered inline in the conversation inside a sandbox. Community-maintained support lists include Claude (web and desktop), ChatGPT, VS Code GitHub Copilot, Microsoft 365 Copilot, Cursor and goose; verify with each client.
- **Authorization extensions**: OAuth client credentials for machine-to-machine access, and Enterprise-Managed Authorization for centralized control through a company identity provider.

Extensions are always opt-in: both sides must declare support.

## Worked example: a Riyadh events company booking assistant

A server exposes `book_venue(date, guests)`.

- Venue unavailable → **form elicitation**: "Try another date?" with a date field and a default.
- Deposit required → **URL elicitation** to the payment provider's hosted page; the card number never touches the model or host.
- Contract generation takes minutes → the tool returns a **task** handle; the host shows progress and fetches the PDF when done.
- The server previously used sampling to summarize venue reviews; after upgrading, it calls an LLM API directly with its own key and cost controls.

## Hands-on: elicitation in a Python tool

```python
from pydantic import BaseModel, Field
from mcp.server import MCPServer
from mcp.server.mcpserver import Context

mcp = MCPServer("venues")

class AltDate(BaseModel):
    accept_alternative: bool = Field(description="Try another date?")
    date: str = Field(default="2026-12-18", description="Alternative date (YYYY-MM-DD)")

BOOKED = {"2026-12-17"}

@mcp.tool()
async def book_venue(date: str, guests: int, ctx: Context) -> str:
    """Book the main hall for a date and number of guests."""
    if date not in BOOKED:
        return f"Provisionally booked {date} for {guests} guests."
    result = await ctx.elicit(message=f"{date} is taken. Would you like another date?", schema=AltDate)
    if result.action == "accept" and result.data.accept_alternative:
        return await book_venue(result.data.date, guests, ctx)
    return "No booking made."
```

Test it in the Inspector; if your host does not support elicitation, design a fallback (return a clear message asking the user to choose a date).

## Pitfalls

- Collecting secrets through form elicitation.
- Building new features on deprecated sampling or roots.
- Assuming every host supports elicitation or MCP Apps; check and provide fallbacks.
- Long-running tools that block without progress or tasks.

## Measuring success

Elicitation completion and decline rates, time-to-complete for URL flows, task completion times, and the share of your servers still depending on deprecated features (target zero for new work).

## Video lecture: Client features, multi round-trip requests and extensions

Lecture coming soon · 14 chapters · about 9 minutes. Read the full transcript below.

1. Client features and extensions
2. Why it matters
3. Elicitation
4. Elicitation rules
5. Multi Round-Trip Requests
6. Simple example: create_invoice
7. Deprecated in 2026-07-28
8. Utilities and extensions
9. Example: Riyadh venue booking
10. Hands-on: form elicitation
11. Deeper: the Riyadh booking flow (illustrative)
12. Watch me do it: book_venue()
13. Try this now
14. Recap

## Lecture transcript

### Client features and extensions

Sometimes a tool can't finish without the human. It needs a missing detail, a confirmation, a payment or a sign in. MCP has features for exactly this, and they changed a lot in twenty twenty six. In this lesson you'll learn elicitation, the new multi round trip requests, what's been deprecated, and the extensions that add tasks and interactive apps.

### Why it matters

Why does this matter? Because the best tools don't fail when information is missing; they ask. Imagine ordering at a café. If the barista just guesses your milk preference, you get the wrong drink. If they ask, oat or regular, it takes three seconds and everyone's happy. But if they ask for your bank PIN at the counter, something is very wrong. Elicitation is the polite question, URL mode is paying on the card machine instead of saying your PIN out loud, and the new round trip design makes asking work at scale.

### Elicitation

Elicitation lets a server ask the user for input through the host. In form mode, the server sends a message and a simple schema, the host shows a form, and the user can accept with data, decline or cancel. In URL mode, added in late twenty twenty five, the server sends a link for something that must not pass through the client at all, like an OAuth consent screen, a payment page, or entering an API key on the provider's own site. That keeps secrets out of the model's context and the host's logs.

### Elicitation rules

Two firm rules. Never use form elicitation to collect passwords, API keys or card numbers. Use URL mode for anything sensitive. And hosts must make it clear which server is asking, and let users refuse. If a server can't say which company it belongs to, or pushes users to hurry, treat that as a red flag.

### Multi Round-Trip Requests

Now the big change. In older versions, elicitation worked by the server sending a request back to the client over a stream held open for the purpose. That fights against stateless, load balanced servers. The twenty twenty six spec introduces multi round trip requests. Instead of calling the client, the server returns an interim result that says input required, listing what it needs, plus an opaque state token. The client gathers the answers and retries the original request with them attached. Every round trip is an ordinary request, so any server instance can handle it.

### Simple example: create_invoice

A simple example of multi round trip requests. A tool called create invoice gets a client name but no currency. Old style: the server sends a request back to the client over an open stream and waits. New style: the server immediately returns a result that says input required, with one question, which currency, and a small state token. The host shows a dropdown, the user picks dirhams, and the client simply calls create invoice again with the answer attached. The second call might land on a different server instance, and that's fine.

### Deprecated in 2026-07-28

The good news: SDKs hide most of this. In the Python SDK version two, you still write await context elicit inside your tool, and the SDK handles the protocol differences. Meanwhile, three older features are deprecated. Sampling, where servers borrowed the client's model: instead, call an LLM provider directly. Roots, where clients told servers which folders were in scope: instead, pass paths as tool parameters or configuration. And MCP logging: instead, log to standard error or use OpenTelemetry. They still work for at least twelve months, but don't build new things on them.

### Utilities and extensions

There are handy utilities too. Progress notifications for long operations, cancellation of in flight requests, completions for autocompleting arguments, and cursor based pagination for long lists. And then there are extensions, which are optional capabilities both sides must declare. Tasks handle long running work: the tool returns a handle, and the client polls for status and results. MCP Apps let servers show interactive interfaces like charts, forms and dashboards inside the conversation, in a sandbox. And authorization extensions cover machine to machine access and enterprise controlled sign in.

### Example: Riyadh venue booking

A worked example. An events company in Riyadh exposes a book venue tool. If the date is taken, form elicitation asks whether to try another date, with a sensible default. If a deposit is needed, URL elicitation opens the payment provider's hosted page, so the card never touches the model. Contract generation takes minutes, so the tool returns a task handle and the host shows progress. And a review summary feature that used sampling now calls an LLM API directly, with its own key and cost controls.

### Hands-on: form elicitation

The hands on code shows form elicitation in Python. If the requested date is booked, the tool asks whether to try another date, with a date field and a default. If the user accepts, it retries the booking. Test it in the Inspector, and remember: not every host supports elicitation, or MCP Apps, so design a fallback, like returning a clear message asking the user to pick another date.

### Deeper: the Riyadh booking flow (illustrative)

Let's deepen the Riyadh events company example. On a busy December weekend, illustrative again, many booking requests hit fully booked dates. With form elicitation, the assistant offered an alternative date inline, and a good share of those customers accepted instead of abandoning. Deposits moved to URL mode, so card details went straight to the payment provider, which also satisfied the company's payment security requirements. Contract generation became a task, so the chat stayed responsive while documents rendered. And removing sampling meant the server's own LLM calls were logged and budgeted like any other API usage, which the finance team appreciated.

### Watch me do it: book_venue()

Watch me do it. Let's walk through the book venue tool. The alternative date model has two fields: accept alternative, a yes or no question, and a date with a sensible default. The tool takes a date, a number of guests and the context. If the date isn't booked, it returns a provisional booking straight away. If it is booked, it calls context elicit with a friendly message and the schema. The host renders a small form. The result has an action: accept, decline or cancel. If the user accepted and wants an alternative, the tool calls itself again with the new date. Otherwise it returns, no booking made. In the Inspector, I book December seventeenth, the form appears with December eighteenth prefilled, I accept, and the result says provisionally booked for the eighteenth. On a twenty twenty six connection, the SDK turns this into an input required result and a retry, but my code doesn't change.

### Try this now

Try this now. Find one tool in your planned server that sometimes can't finish without the human. Decide whether it needs form mode, for simple, non sensitive answers, or URL mode, for anything involving credentials, payments or consent. Write the schema or the URL flow. Then write the fallback message for hosts that don't support elicitation, like, please choose a date between these options and ask again. Finally, list any older features you rely on, like sampling or roots, and plan their replacements.

### Recap

To recap: elicitation gets input from users, with URL mode for anything sensitive. Multi round trip requests make it work statelessly. Sampling, roots and MCP logging are deprecated. And extensions like Tasks and MCP Apps are opt in, so always have a fallback. Your next step: find one tool that needs user input mid call, choose form or URL mode, write the schema or flow, and describe the fallback.

## Key takeaways

- Elicitation asks users for input: form mode for simple data, URL mode for sensitive out-of-band flows.
- 2026-07-28 replaces server-initiated requests with Multi Round-Trip Requests (input_required results and retries).
- Sampling, roots and MCP logging are deprecated: use direct LLM APIs, tool parameters and stderr or OpenTelemetry.
- Extensions such as Tasks, MCP Apps and enterprise authorization are opt-in and negotiated via capabilities.
- Always design fallbacks for hosts that lack a feature.

## Try it

Identify one tool in your planned server that needs user input mid-call. Decide form versus URL mode, write the schema or URL flow, and describe the fallback for hosts without elicitation.

- [Previous: Server primitives: tools, resources and prompts](https://optimizeall.com/learn/model-context-protocol-mcp/server-primitives-tools-resources-prompts)
- [Next: Transports: stdio, Streamable HTTP and running at scale](https://optimizeall.com/learn/model-context-protocol-mcp/transports-stdio-streamable-http)
- [All lessons of Model Context Protocol (MCP): Connect AI to Your Tools and Data](https://optimizeall.com/learn/model-context-protocol-mcp)
