Architecture
avenir-mcp is a Python package built on FastMCP. Its design keeps three things apart: what talks to YNAB, what computes, and what the agent sees.
Modules
Section titled “Modules”Directoryavenir_mcp/
- server.py — entry point: transport, log level, read-only policy
- app.py — the FastMCP instance, the
writetag, month validation, log filter - tools_budget.py — read tools close to YNAB, and
approve_transactions - tools_classify.py —
suggest_categories,apply_categories - tools_accounts.py —
reconcile_account,forecast_balance,create_transactions - tools_categories.py —
create_category,update_category,set_category_budget - tools_undo.py —
undo_operation, for every kind of operation - context.py — resources and prompts
- confirm.py — preview, confirmation, apply, journal
- client.py — the only code that talks to YNAB
- journal.py — the only code that writes to disk
- triage.py — pending transactions and suggestions, pure
- classifier.py — payee normalisation and scoring, pure
- writes.py — plans, undo plans, confirmation codes, pure
- reconcile.py — account analysis, pure
- forecast.py — recurring charges and projection, pure
- analytics.py — month overview, balances, trends, pure
| Layer | Modules | Rule |
|---|---|---|
| Tools | tools_*.py, context.py |
declare what the agent sees; orchestrate; never compute |
| Confirmation | confirm.py |
the single path every confirmed write goes through |
| Logic | triage, classifier, writes, reconcile, forecast, analytics |
pure functions: no network, no disk, fully unit-tested |
| I/O | client.py (HTTP), journal.py (disk) |
the only side effects |
Amounts cross the I/O boundary in YNAB’s milliunits and leave the logic in currency units: no tool ever returns milliunits.
A read call
Section titled “A read call”- The client calls
get_monthly_summarywith{"plan_id": "last-used", "month": "2026-09-01"}. - FastMCP validates the arguments against the tool’s input schema.
- The tool checks
month(app.check_month) before any request: a malformed month is refused with a message, at no cost. client.get_monthsends oneGET /plans/last-used/months/2026-09-01to YNAB.analytics.month_overviewkeeps the totals and the overspent categories, in currency units.- FastMCP validates the answer against the output schema and returns it as
structuredContent, with its JSON text for older clients.
A write call
Section titled “A write call”- The client calls
apply_categorieswith assignments. - The tool reads the current state (
client.get_transactions,client.get_categories) and askswrites.plan_categorizationwhat would change. Invalid assignments are refused here, before anything is asked. confirm.write_planasks the user through the channel the client supports — see Confirmation. Until the answer is yes, it returns the preview.- On yes, one bulk
PATCH /plans/{id}/transactionsapplies every change. journal.Journal.recordappends the operation to the journal; its id comes back to the agent for undo.
Registration and read-only mode
Section titled “Registration and read-only mode”Every tool is registered at import time with MCP annotations (readOnlyHint,
destructiveHint, idempotentHint, openWorldHint); write tools also carry the tag
write. At start-up, server.main calls app.configure(enable_writes=...), which
disables every tool tagged write unless AVENIR_MCP_WRITE=1. A disabled tool is
neither listed nor callable. A test fails if a tool lacks annotations or if its tag and
its readOnlyHint disagree.
Dependencies
Section titled “Dependencies”| Package | Why |
|---|---|
fastmcp |
the MCP server: protocol, schemas, transports |
httpx |
HTTP requests to YNAB |
pydantic |
already required by fastmcp; turns field docstrings into schema descriptions |
Nothing else at run time. Adding a dependency needs a written reason (AGENTS.md).
Unofficial project. We are not affiliated, associated, or in any way officially connected with YNAB or any of its subsidiaries or affiliates. The official YNAB website can be found at https://www.ynab.com. The names YNAB and You Need A Budget, as well as related names, tradenames, marks, trademarks, emblems, and images are registered trademarks of YNAB. avenir-mcp is provided as is, without warranty, and is not financial advice. Legal notice · Privacy