Confirmation
Every write except approve_transactions goes through one function,
confirm.ask, before it changes anything. This page describes exactly what it does.
What is being confirmed
Section titled “What is being confirmed”Each tool builds a subject: the exact change, as data.
| Tool | Subject |
|---|---|
apply_categories |
the list of changes (transaction, category after) |
set_category_budget |
category, month (resolved), amount in milliunits |
update_category |
category, name after, group after |
create_category |
group, name |
create_transactions |
account, the transactions, approved |
reconcile_account |
account, bank balance, difference to adjust |
split_transaction |
transaction, the lines (amount, category, memo) |
undo_operation |
the operation id |
Its fingerprint is the SHA-256 of the JSON [plan_id, subject], keys sorted; for
a list of changes, the order of the elements does not matter. An answer or a code only
ever confirms the subject whose fingerprint it carries.
Three ways to ask
Section titled “Three ways to ask”ask checks what the client declared at connection and picks one:
1. The client supports elicitation, protocol 2026-07-28
Section titled “1. The client supports elicitation, protocol 2026-07-28”In the current MCP protocol a server cannot send the client a request of its own during a call. avenir-mcp uses the input required round trip:
- The first call returns an
InputRequiredResultinstead of the tool’s answer. It holds one request,confirm, a form with a single booleanvalue, whose message is the preview (up to 20 changes, then “… and N more”), and arequest_state: the fingerprint. - The client shows the form. The user ticks, or refuses.
- The client calls the tool again with the same arguments, the answer in
input_responses, and therequest_stateit received. FastMCP seals that state on the wire and rejects tampering. - avenir-mcp recomputes the plan and its fingerprint. If they differ from
request_state— someone changed the budget in between — the call fails with “The budget changed between the preview and the answer” and nothing is written.
2. The client supports elicitation, older protocol
Section titled “2. The client supports elicitation, older protocol”The server asks during the call (ctx.elicit) with the same yes/no form, and waits for
the answer.
3. The client does not support elicitation
Section titled “3. The client does not support elicitation”The first call returns status: "confirmation_required", the preview, and a
confirmation code. The agent shows the preview to the user and, only if they agree,
calls again with the same arguments plus confirmation.
How answers are read
Section titled “How answers are read”| Answer | Result |
|---|---|
| form accepted, box ticked | applied |
| form accepted, box not ticked | declined |
decline |
declined — nothing written |
cancel (dismissed, or a client that cannot show it) |
falls back to a confirmation code |
A dismissal is not a refusal: headless clients dismiss every question, and treating that as no would make every write impossible — the defect avenir-mcp’s evaluation found on its first run.
Confirmation codes
Section titled “Confirmation codes”| Property | Value |
|---|---|
| Format | 11 URL-safe characters (secrets.token_urlsafe(8)) |
| Lifetime | 600 seconds, on a monotonic clock |
| Use | once: a code is spent on its first use, even if it does not match |
| Scope | the fingerprint of one subject in one budget |
| Storage | the server’s memory only: a restart forgets every code |
A code that is unknown, expired, already used, or issued for other changes is refused with: “This confirmation code is unknown, expired, already used, or was issued for different changes. Call again without confirmation to get a new preview.”
Requiring the user’s own answer
Section titled “Requiring the user’s own answer”A confirmation code proves that a preview existed, not that you read it. With
AVENIR_MCP_REQUIRE_ELICITATION=1:
| Situation | Without the variable | With it |
|---|---|---|
| client can ask, you say yes | applied |
applied |
| question dismissed | a confirmation code | declined |
| client cannot ask | a confirmation code | error: nothing changed |
a confirmation code is passed |
checked, then applied | error: codes are disabled |
Only clients that support MCP elicitation can then write.
What the user sees
Section titled “What the user sees”| Tool | Question |
|---|---|
apply_categories |
Recategorise 2 transaction(s)? then one line per change: date, payee, amount, before → after |
set_category_budget |
Budget Restaurants for 2026-09-01: 120.00 → 150.00? |
update_category |
Change category ‘Tennis’ (Fun) to ‘Sport’ (Fun)? |
create_category |
Create category ‘Pets’ in Everyday? |
create_transactions |
Create 1 transaction(s) on Checking? then one line per transaction |
split_transaction |
Split 2026-09-12 Market 86.40 into 2 lines? then one line per category, and a warning that only YNAB can undo it |
reconcile_account |
Reconcile Checking: mark 49 cleared transaction(s) reconciled? |
undo_operation |
Undo: recategorise 2 transaction(s)?, or the kind of undo |
Unofficial project. We are not affiliated, associated, or in any way officially connected with YNAB or any of its subsidiaries or affiliates. YNAB and You Need A Budget are registered trademarks of YNAB. avenir-mcp is provided as is, without warranty, and is not financial advice. Legal notice