{
  "question": "How do I ask the user to confirm an action from inside an MCP server (elicitation)?",
  "slug": "how-to-ask-the-user-to-confirm-an-action-from-an-mcp-server-with-elicitation",
  "url": "https://www.rightshold.com/answers/how-to-ask-the-user-to-confirm-an-action-from-an-mcp-server-with-elicitation",
  "short_answer": "Use elicitation. In the current MCP spec (2026-07-28), a tool that needs a yes or more details doesn't call the client back: it answers the tools/call with an InputRequiredResult (resultType \"input_required\") holding an elicitation/create request, the client asks the user, and then retries the same tool call with the user's answer in inputResponses plus the requestState you sent. Proceed only on action \"accept\" with the content you asked for; treat \"decline\" and \"cancel\" as no. Form mode is fine for a confirmation or a choice, but it may never ask for passwords, API keys, tokens or payment details; those need URL mode, which sends the user to a page on your own domain. Only send elicitation to clients that declared the capability, and keep a fallback for those that didn't. The answer comes from the client, so for actions that really matter, check the user on your own page (URL mode) or require an approval the client can't fake.",
  "checklist": [
    "Check the client's capabilities first: elicitation is declared per request in _meta under io.modelcontextprotocol/clientCapabilities. An empty elicitation object means form mode only; servers must not send a mode the client didn't declare.",
    "Return the question from the tool call itself: an InputRequiredResult with an inputRequests map (your own key, for example \"confirm_delete\", mapped to an elicitation/create request with a message and a flat requestedSchema).",
    "Write the message so the user can decide from it alone: what will happen, to what, and the exact values (for example the record ids and the count), not the model's paraphrase.",
    "Put what you need to finish the job in requestState and protect it: the spec says to treat it as attacker-controlled, use HMAC or AEAD when it affects authorization or business logic, and bind it to the user, a short expiry and the original request.",
    "On the retry, act only if inputResponses carries action \"accept\" for your key and the content matches your schema. Decline and cancel mean stop; if the response is missing, ask again rather than guess.",
    "Make the action single-use on your side: the spec notes that an integrity-protected requestState limits replay but does not by itself make it one-time, so record that you've done it before you report success.",
    "Never collect secrets or payment details in form mode. Switch to URL mode, point it at a page on your domain, and check there that the person who opens it is the same user who made the tool call.",
    "If the client doesn't support elicitation, fail safe: return a plain result that says confirmation is needed and do nothing, instead of carrying on."
  ],
  "sections": [
    {
      "heading": "What changed in the 2026-07-28 spec",
      "points": [
        "Multi Round-Trip Requests (MRTR) replaces server-initiated requests. Servers must now send elicitation/create, sampling/createMessage and roots/list inside an InputRequiredResult; the spec calls this a breaking change, and the old pattern is no longer supported in this version.",
        "Only prompts/get, resources/read and tools/call may return an InputRequiredResult. The client must retry with a new JSON-RPC id, echo requestState unchanged, and use those fields only for that retry.",
        "The notifications/elicitation/complete notification and the elicitationId field from 2025-11-25 are gone. After a URL-mode step, the client retries the original request and your server decides from requestState (or its own stored state) whether the out-of-band step is done.",
        "Sessions and the initialize handshake were removed too, so don't keep a pending confirmation in connection memory: everything the retry needs comes from the retry itself or from your own store."
      ]
    },
    {
      "heading": "Form mode or URL mode?",
      "points": [
        "Form mode: in-band, structured, and the data is visible to the client. Schemas are flat objects with string, number, integer, boolean and enum fields (string formats email, uri, date, date-time). Good for \"Delete these 42 rows? yes/no\" or picking one of several targets.",
        "URL mode: the client shows the full URL, asks for consent and opens it in a way the client and model can't inspect; data other than the URL isn't exposed to the client. Required for passwords, API keys, access tokens and payment credentials, and the right choice for third-party OAuth.",
        "An accept on URL mode only means the user agreed to open the page, not that they finished. Check completion on your side when the retry arrives.",
        "URL mode isn't for authorizing the client to your server (that's MCP authorization), and the URL must not carry personal data or be pre-authenticated."
      ]
    },
    {
      "heading": "How much does an accept prove?",
      "points": [
        "The spec requires clients to show which server is asking and to offer clear decline and cancel options, and says hosts SHOULD keep a human in the loop for tool calls. It doesn't prove to your server that a person clicked: the accept arrives from the client, and the spec tells servers not to trust client-provided identity without verifying it.",
        "For a confirmation that guards money, deletion or anything you'd need to show later, have the user confirm on a page you control (URL mode, where you check their session against the authenticated user of the tool call) or record a separate, signed approval that your executor checks before it acts.",
        "Elicitation is the server asking. Client-side confirmation prompts for tool calls are a separate safeguard; don't remove one because you have the other."
      ]
    }
  ],
  "checks": [],
  "ask_rightshold": {
    "start": "https://www.rightshold.com/start?ref=seo",
    "mcp_endpoint": "https://www.rightshold.com/mcp",
    "ask": "Before my MCP tool deletes the 42 stale customer records, ask me to approve exactly that action, and give me a receipt the tool can check before it runs.",
    "mcp_call": {
      "tool": "request_human_approval",
      "arguments": {
        "summary": "Delete 42 stale customer records (ids in the action) from the production CRM.",
        "action": {
          "type": "delete_records",
          "system": "crm-prod",
          "table": "customers",
          "record_count": 42,
          "ids_sha256": "SHA256_OF_SORTED_ID_LIST"
        },
        "expires_in_minutes": 30
      }
    },
    "also": [
      "Needs your API key; it's in the trust toolset (connect to /mcp?toolset=trust). The action is hashed, and the account owner decides once with decide_human_approval using their account key (delegated agent keys are refused). The token works once.",
      "verify_human_approval (free) checks the signed decision and that the action details match what was approved, so your tool can refuse a changed action on the same yes.",
      "REST: POST /v1/approvals to request, POST /v1/approvals/{id}/decide to decide, POST /v1/approvals/verify (free) to check."
    ],
    "scope": "Rightshold doesn't run your MCP server, render elicitation prompts or stop a tool that never checks. It records approval requests and signed decisions that your tool can verify before acting."
  },
  "sources": [
    {
      "title": "Model Context Protocol specification 2026-07-28: Elicitation",
      "url": "https://modelcontextprotocol.io/specification/2026-07-28/client/elicitation"
    },
    {
      "title": "Model Context Protocol specification 2026-07-28: Multi Round-Trip Requests",
      "url": "https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr"
    },
    {
      "title": "Model Context Protocol specification 2026-07-28: Key changes since 2025-11-25",
      "url": "https://modelcontextprotocol.io/specification/2026-07-28/changelog"
    },
    {
      "title": "Model Context Protocol specification 2026-07-28: Tools (human in the loop)",
      "url": "https://modelcontextprotocol.io/specification/2026-07-28/server/tools"
    }
  ],
  "related": [
    {
      "title": "How to show a human the actual change before approving an AI agent action",
      "url": "https://www.rightshold.com/answers/how-to-show-a-human-the-actual-change-before-approving-an-ai-agent-action"
    },
    {
      "title": "Are MCP tool annotations like readOnlyHint a security guarantee?",
      "url": "https://www.rightshold.com/answers/are-mcp-tool-annotations-like-readonlyhint-a-security-guarantee"
    },
    {
      "title": "How to require human approval before an AI agent spends money",
      "url": "https://www.rightshold.com/answers/how-to-require-human-approval-before-an-ai-agent-spends-money"
    },
    {
      "title": "Human approvals",
      "url": "https://www.rightshold.com/human-approvals"
    }
  ],
  "computed_from": "Curated guidance; license verdicts computed from Rightshold's license profiles at render time. Not legal advice.",
  "updated": "2026-10-30"
}