Administrator
Published on 2026-07-19 / 1 Visits
0
0

MCP Elicitation Explained: Form, URL, and Recoverable Human-in-the-Loop Workflows

An agent becomes useful when it can keep working without constant approval. It becomes trustworthy when it knows exactly where to stop. MCP Elicitation gives that stopping point a protocol shape: a server can request a human decision during an operation, receive an explicit response, and continue without pretending that every input was available at the beginning.

The important idea is larger than a form dialog. Elicitation is a control-plane primitive. It separates routine execution from real decision points, defines where sensitive data may travel, and creates observable states for refusal, cancellation, timeout, and recovery.

This guide covers the published MCP specification dated 2025-11-25. It also explains the multi round-trip request design in the draft available on July 19, 2026. The two flows should be kept separate in production plans because the draft changes how interruption and resumption are represented on the wire.

Why Tool Arguments Are Not Always Known Up Front

Most tool schemas assume that a client can supply every required argument before tools/call. Real workflows routinely violate that assumption.

A deployment tool may discover that the target environment needs approval. A travel tool may find three acceptable itineraries and require a choice. A finance integration may need the user to authorize a third-party account. A destructive maintenance action may need confirmation only after the tool calculates its actual blast radius.

Without a protocol mechanism, applications usually choose one of four weak patterns:

  1. Fail the tool call and ask the model to try again.
  2. Hide a second confirmation tool inside the server API.
  3. Keep an unresolved in-memory promise while a custom UI collects input.
  4. Put sensitive credentials into ordinary tool arguments or chat context.

Each pattern can work in a demo. Each creates avoidable ambiguity in production. The original operation, user decision, authorization state, and retry are no longer part of one explicit control flow.

The MCP 2025-11-25 Elicitation specification addresses this by allowing a server to request input through the client while another MCP feature is being processed.

Elicitation Is a Decision Point, Not a Conversation Trick

The most useful mental model is a resumable decision point:

client request
    |
    v
server executes routine work
    |
    ├─ enough information → final result
    |
    └─ human decision required
             |
             ├─ form: collect non-sensitive structured input
             |
             └─ URL: perform sensitive interaction out of band
                           |
                           v
                    resume or safely stop

This distinction matters because mature agents should advance automatically through execution steps and stop only at genuine decisions. If every step asks for approval, the user becomes part of the machine's inner loop. If no step asks for approval, the machine crosses risk boundaries without informed consent. Elicitation provides a place to encode that boundary.

It also gives both sides a contract. Clients declare whether they support form mode, URL mode, or both. Servers must check that capability before sending a mode-specific request. A client that declares an empty elicitation capability is treated as form-only for backward compatibility.

This control-flow role complements the broader interface tradeoffs discussed in MCP vs CLI for AI Agents. That article asks how agents should reach tools. Elicitation asks what happens after a tool has started and discovers that human input is genuinely required.

Form Mode and URL Mode Have Different Trust Boundaries

The two modes gather information, but they are designed for different classes of data.

Question Form mode URL mode
Primary use Non-sensitive structured input Sensitive or externally authorized interaction
Data path User to MCP client to MCP server User to a secure web flow controlled by the server or third party
Typical examples Select an environment, enter a label, confirm a scope OAuth, API keys, payment details, subscription or account connection
Schema Restricted JSON Schema URL plus elicitationId and explanatory message
Does the MCP client see submitted data? Yes No, apart from the URL itself
Completion Response carries accepted form content User consent and external completion are separate states

Form mode: structured, reviewable, non-sensitive

Form mode uses elicitation/create with a requestedSchema. The published specification intentionally limits the schema to flat objects with primitive properties. That constraint is a user-interface choice, not an arbitrary loss of expressiveness. A client can render predictable fields, let the user review them, validate values, and return a small structured result.

{
  "jsonrpc": "2.0",
  "id": 41,
  "method": "elicitation/create",
  "params": {
    "mode": "form",
    "message": "Choose the deployment environment",
    "requestedSchema": {
      "type": "object",
      "properties": {
        "environment": {
          "type": "string",
          "enum": ["staging", "production"]
        },
        "confirm": {
          "type": "boolean",
          "title": "Proceed with deployment"
        }
      },
      "required": ["environment", "confirm"]
    }
  }
}

Passwords, API keys, access tokens, and payment credentials do not belong in this form. The specification requires sensitive interactions to use URL mode.

URL mode directs the user to an external HTTPS flow. It is appropriate when the MCP server needs authorization to a third-party service or must collect data that should never enter the MCP client or model context.

The client should show the full destination and ask for consent before navigation. It must not prefetch the URL, open it automatically, or inspect the user's input. The server must bind the flow to the authenticated user and verify that the person completing it is the same person who initiated it.

One detail causes many implementation bugs: action: "accept" means the user consented to the URL interaction. It does not mean the OAuth, payment, or credential flow succeeded. External completion is a later state. A server may send notifications/elicitation/complete, and the client should still expose manual retry or cancellation in case that notification never arrives.

The URL mode proposal, SEP-1036, explains why this separation exists. Passing third-party tokens through the MCP client would turn the server into a token-passthrough system and collapse the intended trust boundary.

Accept, Decline, and Cancel Are Three Different Outcomes

The published specification defines three actions:

  • accept: the user submitted form content or consented to a URL interaction.
  • decline: the user explicitly refused the request.
  • cancel: the interaction ended without an explicit decision, for example by closing a dialog or failing to load a page.

Treating all non-accept results as one error loses information. A decline is a policy decision. The server may offer a lower-risk alternative or stop permanently. A cancellation is an incomplete interaction. Retrying later may be appropriate.

A robust handler branches on all three states:

accept  -> validate content or start tracking the external flow
decline -> record refusal, offer an allowed alternative, or stop
cancel  -> preserve a safe resumable state, then expose retry or exit

For URL mode, add another branch after acceptance:

accepted_url
    |
    ├─ external_completed → retry or resume original operation
    ├─ external_failed → show a bounded recovery path
    ├─ expired → create a fresh elicitation
    └─ no notification → allow manual status check or retry

A Production State Model

Holding a JavaScript promise in a process-local map can demonstrate the user experience. It cannot survive a restart, a load-balancer hop, or a delayed OAuth callback. Production systems need persistent correlation.

At minimum, store a record like this:

{
  "elicitation_id": "550e8400-e29b-41d4-a716-446655440000",
  "principal_id": "user_123",
  "client_id": "mcp_client_456",
  "origin_method": "tools/call",
  "origin_request_digest": "sha256:...",
  "mode": "url",
  "status": "external_pending",
  "created_at": "2026-07-19T12:00:00Z",
  "expires_at": "2026-07-19T12:10:00Z",
  "resume_policy": "manual_or_notification",
  "completion_version": 0
}

The exact database is secondary. The invariants are what matter:

  1. The record is bound to an authenticated principal, not only to a session ID.
  2. The original operation is identified without storing sensitive payloads unnecessarily.
  3. Completion is idempotent. Duplicate callbacks and duplicate notifications do not repeat the side effect.
  4. The record expires. A late result cannot silently revive an obsolete decision.
  5. Retry is explicit. The implementation knows whether to resume automatically, ask the user, or require a fresh request.
  6. State transitions are logged. Operators can reconstruct why an action continued or stopped.

These checks turn Elicitation from a UI feature into a reliability mechanism.

Seven Implementation Rules

1. Negotiate capability before the workflow starts

Read the client's elicitation capability during initialization. Design a fallback for clients that support only form or support neither mode. A server must not discover this limitation after performing an irreversible partial action.

2. Ask only after routine work has narrowed the decision

Do not ask users to choose from raw possibilities that the tool could filter itself. Run deterministic checks first, calculate the real options, then elicit the smallest decision needed to proceed.

3. Keep secrets outside form mode and model context

URL mode protects more than the password field. It keeps the full sensitive interaction outside client logs, model transcripts, intermediary servers, and generic form renderers.

4. Bind URL flows to verified identity

The URL must lead to a server-controlled connection page that verifies the current user before forwarding to a third-party authorization service. Do not trust a username supplied in form content as proof of identity. Do not put credentials, personal data, or a pre-authenticated resource URL into the elicitation URL.

5. Make completion idempotent

OAuth callbacks, payment webhooks, client retries, and completion notifications can arrive more than once or out of order. Use a compare-and-set transition or equivalent transaction. Side effects should execute once even when completion is observed many times.

6. Design the missing-notification path

notifications/elicitation/complete is optional. Networks fail, browser tabs close, and users abandon flows. The client needs a manual status check, retry, or cancellation path. The server needs TTL cleanup for abandoned records.

7. Test every terminal state

Happy-path tests cover too little. Test explicit decline, dialog cancellation, unsupported capabilities, invalid form content, expired URLs, cross-user callbacks, duplicate completion, process restart, and retry after notification loss.

The Draft Changes the Wire Pattern

The MCP multi round-trip request draft introduces a significant change. Instead of sending a server-initiated request while the original request remains active, the server returns an InputRequiredResult. The client gathers the requested inputs and retries the original operation with a new JSON-RPC id.

initial tools/call
    -> InputRequiredResult(inputRequests, requestState)
client gathers input
retry tools/call(inputResponses, same requestState, new JSON-RPC id)
    -> final result or another InputRequiredResult

The draft makes horizontal scaling easier because a server can encode continuation context in an opaque requestState rather than requiring sticky sessions. It also creates a security obligation. The server must treat returned state as attacker-controlled. If it influences authorization, resource access, or business logic, protect its integrity with HMAC or AEAD and bind it to the principal, a short TTL, and the originating request.

This is a draft direction as of July 19, 2026. The official TypeScript SDK repository labels its main v2 line as beta and keeps v1.x as the supported production release until v2 stabilizes. Teams should version-gate implementations instead of blending the published push flow and draft MRTR flow into one ambiguous handler.

Operational Checklist

Before shipping MCP Elicitation, verify the following:

  • The client declares the required mode.
  • Form schemas contain only non-sensitive, reviewable fields.
  • URL destinations use HTTPS and display a clear trusted domain.
  • The client never prefetches or automatically opens an elicitation URL.
  • The server binds state to authenticated user and client identities.
  • accept, decline, and cancel have separate tested branches.
  • URL consent and external completion are separate states.
  • Duplicate callbacks and notifications are idempotent.
  • Expiration, cancellation, and manual retry are available.
  • Logs capture state transitions without recording secrets.
  • Protocol version determines whether the published flow or draft MRTR flow is used.

FAQ

What is MCP Elicitation?

MCP Elicitation is a client capability that lets a server request additional user input during another MCP operation. It supports structured form input and out-of-band URL interactions.

When should I use form mode?

Use form mode for non-sensitive structured decisions such as choosing an environment, confirming a scope, entering a display label, or selecting among bounded options. The user should be able to review and modify the values before submission.

When is URL mode required?

Use URL mode for secrets and sensitive authorization flows, including API keys, access tokens, payment credentials, and third-party OAuth. The sensitive data should go directly to a trusted web endpoint, outside the MCP client and model context.

Does accept mean a URL flow succeeded?

No. It means the user consented to navigate to or participate in the external interaction. Completion is a later event that may be reported by a completion notification or discovered through a manual retry or status check.

What is the difference between decline and cancel?

Decline is an explicit refusal. Cancel means the interaction ended without a clear decision. Servers can stop or offer an alternative after decline, while cancellation may remain safely retryable.

What should happen if the client does not support Elicitation?

The server should avoid sending an unsupported request and use a documented fallback. Depending on the operation, that may mean returning a clear error, providing a separate web flow, or requiring the missing value in a new user-initiated request.

How does the MRTR draft change Elicitation?

The draft returns an InputRequiredResult, lets the client gather inputs, and retries the original request with inputResponses and an opaque requestState. This replaces the older server-initiated request pattern in that draft protocol version.

References

MCP Elicitation solves a specific architectural problem: an operation can advance automatically until it reaches a real human decision, pause with an explicit protocol state, and continue without smuggling secrets or inventing a private confirmation channel. That is the difference between adding a dialog and designing a recoverable agent workflow.


Comment