Proposal Flow
Agents don’t just ask permission — they propose data. An enrichment skill reads Apollo and drafts a Salesforce account; a sync job reconciles two systems and proposes the merged record. A bare approve/reject is too coarse for that: the human needs to see where each value came from, fix the ones the agent got wrong, and apply only the fields they trust.
That’s what elicit_proposal is for: a diff
grid — one or more read-only Source columns, a read-only Current column,
and an editable Proposed column with a per-field Include toggle. The
reviewer corrects, includes, and submits; the result carries only the fields
they included.
The idea
Section titled “The idea”Write the proposal skill once; the server adapts. Your skill calls
elicit_proposal, then reuses the same
elicit_await →
elicit_result loop as the
Approval Flow. Underneath, the server renders the
grid as a review page and (best-effort) pushes a URL-mode elicitation prompt to
hosts that advertise support; every host reaches the same outcome through the
same long-poll. The reviewer’s decision comes back as action plus the
included-fields-only values.
The grid
Section titled “The grid”The caller supplies three things (full shapes on the
elicit_proposal reference):
columns— names the read-only columns: one or more Sources (e.g. “Apollo”, “Crunchbase”) and the Current system (e.g. “Salesforce”). For a brand-new record the Current column is simply blank.requestedSchema— the authority for the editable Proposed column, the same MCP elicitation subset aselicit_approval’s form mode. Each property’sdefaultis its initial Proposed value — the caller computes it (typically “take Current if present, else Source”).fields— each property’s Source and Current cell values.
The review page derives the rest: Source cells that differ from Current get a
highlight outline, double-clicking a Source or Current cell copies it into
Proposed, rows whose Proposed value differs from Current start included, and a
required property with no Current value locks its Include on so the reviewer
can’t build an incomplete record. Header actions — Accept All, Reject
All, Restore Proposed — handle the whole grid at once. See
Derived behavior for the exact
rules.
Submit, or cancel
Section titled “Submit, or cancel”Two outcomes, both first-class:
- Submit — the request resolves
approved;elicit_resultreturnsaction: "submit"andvaluesholding only the included fields with their final (possibly edited) Proposed values, validated against the schema subset for those keys. Unincluded fields are omitted entirely — not present, notnull— so the skill applies exactly what the human blessed and nothing more. - Cancel — the request resolves
rejected, andelicit_resultreturnsaction: "cancel"(fetchable like any other decided request, not an error). The skill reads it as “don’t touch the record.”
The tools
Section titled “The tools”Proposal reuses the approval polling tools — only the first call is new. Each has its own reference page with exact input/output shapes:
elicit_proposal— record the proposed create-or-update and (best-effort) push a URL-mode prompt; always returnsreviewUrl.elicit_await— bounded long-poll until the reviewer decides or the wait bound elapses.elicit_status— non-blocking status read.elicit_result— fetch the outcome:actionplusvalueson submit (idempotent, every fetch recorded).
How a request plays out
Section titled “How a request plays out”The skill’s column never changes from the Approval Flow — the only new step is
the first call. Both flows converge on the same elicit_await wake.
Identity passthrough
Section titled “Identity passthrough”context (object type, record id, external keys) is echoed back verbatim in
the result so the skill knows which record to apply values to — but it is
not shown to the reviewer by default. The grid is the focus; pass a
LiquidJS displayTemplate to render a header above it (see
Templates).
Expiry
Section titled “Expiry”Every request carries a TTL (timeoutSeconds, default 1 hour, clamped 60
seconds to 24 hours). Once it passes, the request is expired — a terminal
state. elicit_await and elicit_status both report "expired" at the
deadline, and the review page refuses a late submission. Ask the agent to
resend if that happens.