Skip to content

design(mcp): retire preview→confirm double-confirm gate on write tools — use destructiveHint + client approval #998

Description

@dougborg

Summary

Retire the in-band preview → confirm/apply double-confirm pattern on write tools. Gate mutations the standard MCP way instead: destructiveHint tool annotations + the client's own call-approval flow (with context.elicit() reserved for cases that genuinely need a server-driven confirmation step). Execute the write on the first call and return the result immediately.

Why

Using the double-confirm pattern in practice causes recurring friction:

  • It's not what agents are used to. Most MCP servers don't gate writes behind a preview/confirm argument, so agents don't reliably drive the two-step handshake — they call once, get a preview, and either stop or re-call inconsistently.
  • The agent doesn't get the result right away. The first call withholds the actual outcome (it returns a preview), forcing a second round-trip. That extra hop is where a lot of the inconsistency and "did it actually apply?" confusion comes from.
  • It creates duplicate/ambiguous confirmation paths. We've already hit concrete bugs from this — e.g. card-button apply vs. agent-chat confirmation diverging, and agents re-running preview after an apply already succeeded.

This is the same cleanup just done in xpo-fitness-member-mcp: booking tools now execute immediately, gated only by destructiveHint + client approval, with the confirm arg removed. It's noticeably smoother in practice.

Proposed direction

  • Remove the preview/confirm parameter as a gate from write tools; have them apply directly and return the applied result.
  • Rely on destructiveHint (+ idempotentHint/readOnlyHint as appropriate) so clients prompt for approval before the call.
  • Where a genuine server-side confirmation or quote is truly needed, use context.elicit() rather than a stateful two-call handshake.
  • If a preview/quote is still valuable for some tools, keep it as an explicit, optional read-only affordance (e.g. a separate dry-run/quote) — not a mandatory gate in front of every mutation.

Related / prior art

Scope note

This is a design/behavior change across all write tools and their Prefab UI cards; worth confirming the migration path (and whether any tool legitimately needs elicit()) before ripping the pattern out wholesale.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions