Skip to content

Repository files navigation

workbench-ynab

Tax-aware YNAB budget review and approval-gated write-back for Claude Code. Part of the claude-workbench marketplace.

Status: pre-release / under construction. This repo is being built sprint-by-sprint by the workbench dev team (Lestrade triage → Watson development → Holmes review). Some commands and the MCP launcher referenced below ship in later sprints — each is marked with the sprint it lands in. See docs/ROADMAP.md for the full plan and issue backlog.

What this is

A productized weekly financial review for YNAB. It speaks as your own Claude agent by default — or as Hobbes, the shipped default persona, when no agent is configured (the name is configurable — see docs/persona.md). Each run reads your budget, categorizes transactions, flags duplicates, surfaces tax-aware insights, and proposes ledger-only fixes inside YNAB for your approval.

It productizes a proven prototype: a hand-run, deeply tax-aware review that has run as an ad-hoc scheduled task since April 2026. This plugin turns that into a first-class, shareable tool — a formalized persona, a reusable tax-aware methodology, a frozen HTML report template, and approval-gated write-back.

Two things make this plugin unusual among workbench plugins, and both are front and center — not buried:

  • It writes back to YNAB. Beyond reading, it can categorize, allocate, fix duplicates, and reconcile inside your budget. Every write waits for your explicit approval, and the plugin never moves real money — see The read / propose / approve loop.
  • It handles a financial access token. Your YNAB Personal Access Token is stored only in the macOS Keychain — never in the repo, never in a config file, never logged. See Privacy / where the token lives.

⚠️ Estimates only — not tax advice. Consult a qualified professional before filing or paying.

This tool produces estimates for organizational purposes only. It is not tax, legal, or financial advice, it makes simplifying assumptions, and its figures may be incomplete or wrong for your situation. Consult a qualified tax professional before you file or pay. (Canonical wording: skills/shared/disclaimer.md.)

Currency & tax scope (v1). The review reads your budget's currency_format and renders every amount in that currency — correct symbol, symbol placement, separators, and decimal digits — so a non-USD budget (EUR 1.234,56 €, JPY ¥1,234, …) displays correctly via one shared money helper (assets/format-money.js). This is a presentation capability only: the tax engine is US-only and is not extended to any other jurisdiction. A non-USD budget gets correct currency display but the same US-only tax logic — the tax sections assume US federal rules regardless of budget currency.

Architecture

Mirrors the sibling workbench plugins: a single entry point dispatches a read-only orchestrator that plans the review, the main conversation drives the interactive review + propose/approve protocol via skills, and a vendored YNAB MCP is the only thing that ever talks to the YNAB API.

                      ┌──────────────────────────────┐
                      │  /workbench-ynab:ynab-review  │  ← entry point
                      │   (or the scheduled task)     │     (Sprint 3)
                      └───────────────┬──────────────┘
                                      │ dispatches
                                      ▼
                      ┌──────────────────────────────┐
                      │  ynab-orchestrator           │  ← read-only sub-agent
                      │  • inspects budget state     │     returns a structured
                      │  • plans which analyses run  │     review PLAN — it never
                      │  • sizes the data pull       │     writes to YNAB
                      └───────────────┬──────────────┘
                                      │ plan
                                      ▼
   You ↔ assistant (main)  ◁───────── drives the interactive review protocol
            │                         (12-section methodology, change-set,
            │                          propose → approve → apply)
            │
            │  reads config           ┌────────────────────────────────────┐
            ├───────────────────────▶ │  budget · tax-profile · persona    │
            │  (skills ONLY)          │  config — lives OUTSIDE the repo    │
            │                         └────────────────────────────────────┘
            │
            │  dispatches YNAB verbs
            ▼
   ┌──────────────────────────────┐
   │  vendored YNAB MCP           │   the ONLY thing that talks to the
   │  bin/launcher.sh → node      │   YNAB API. Receives the token +
   │  @dizzlkheinz/ynab-mcpb      │   its package-native env — never
   │  (frozen bundle)             │   this plugin's config.
   └───────────────┬──────────────┘
                   │ HTTPS
                   ▼
              💰  YNAB API

The config split is deliberate and load-bearing:

Layer Reads Never sees
🧠 Skills (main conversation) budget id, tax profile, persona config the YNAB token
🔌 Vendored YNAB MCP (tool server) the YNAB token (YNAB_ACCESS_TOKEN) + its package-native env budget / tax / persona config

The skills do all the tax and persona reasoning from config; the MCP does all the API talking from the token. Neither crosses into the other's lane — the token never reaches a skill, and the config never reaches the MCP.

Prerequisites

  • macOS (darwin) — setup stores the YNAB token in the macOS Keychain via security(1), and the launcher + vendored MCP run on macOS.
  • System node — Node >= 24 (the latest LTS — by policy the supported floor is the newest Node LTS major at the last bundle vendoring) — the vendored YNAB MCP is a self-contained Node bundle launched by bin/launcher.sh; a system Node runtime at or above the pinned floor must be on PATH. The bundle runs on node directly — there is no node_modules install step and no npx-on-demand. The floor is pinned in vendor/ynab-mcp/NODE_VERSION and enforced by both setup and the launcher; it is re-checked against the current LTS line on every bundle bump by the re-vendor flow (see docs/vendoring.md, which governs the pinned bundle version).
  • jq — used by the launcher and setup tooling to read and validate JSON config.
  • security(1) — the macOS Keychain CLI; stores and retrieves the YNAB Personal Access Token.
  • workbench-core@claude-workbench — shared memory vault, session lifecycle, and plugin infrastructure (also the source of the agent name the persona falls back to).

Installation

From the marketplace

claude plugin marketplace add mike-bronner/claude-workbench
claude plugin install workbench-ynab@claude-workbench

Local checkout (development)

Link your clone into the skills directory, which Claude Code loads local plugin checkouts from:

git clone https://github.com/mike-bronner/workbench-ynab
mkdir -p ~/.claude/skills
ln -s "$PWD/workbench-ynab" ~/.claude/skills/workbench-ynab

The link name must match the plugin name in .claude-plugin/plugin.json. Claude Code loads the checkout as workbench-ynab@skills-dir; confirm it with claude plugin list.

claude plugin install takes a plugin name resolved against a registered marketplace, not a filesystem path, and claude plugin marketplace add takes a marketplace — this repo ships a plugin manifest, not a marketplace manifest. Neither command accepts this checkout. See docs/fresh-install-test.md for the verification.

After installing either way, restart Claude Code so the plugin's agents, skills, commands, and the vendored MCP server are picked up.

Setup

First-run configuration is a one-time step. Run the setup command (ships in Sprint 1):

/workbench-ynab:setup

It:

  1. Seeds the Keychain token — prompts for your YNAB Personal Access Token and stores it in the macOS Keychain (never in the repo or a config file).
  2. Writes config — creates config.json in the plugin data directory (outside the repo) with your budget, tax profile, and persona settings.
  3. Pre-approves the tool glob — pre-approves the namespaced mcp__plugin_workbench-ynab_ynab__* read tools so reviews run without per-call prompts. Write verbs stay behind the approval gate.
  4. Offers legacy migration — detects and offers to retire the old hand-run prototype and its scheduled task.

Get a YNAB Personal Access Token from YNAB → Account Settings → Developer Settings.

What it does

Each review reads your budget (read-only) and produces a tax-aware report organized into twelve analysis sections: transaction hygiene (tax-aware classification, duplicate detection, uncategorized, stale uncleared), spending and budget health (cost-cutting, budget health, unusual/large, reconciliation status), then a six-sub-score financial health score, a forecast, a prioritized action list, and a year-to-date tax summary.

The tax-aware sections are driven entirely by a data-driven, shareable tax profile — never hard-coded owner detail. The section-by-section table — what each analysis surfaces, which tier runs it, and how it diverges from the prototype — is docs/methodology.md; for the tax model and profile schema, see docs/tax-mapping.md and assets/tax/README.md.

The read / propose / approve loop

This is the core safety story. Write-back never happens silently.

  1. Read — the plugin reads your YNAB budget, accounts, categories, and transactions.
  2. Propose — it surfaces the fixes it found (categorizations, Ready-to-Assign allocations, duplicate fixes, reconciliations) as a single proposed change-set. Nothing has touched the ledger yet.
  3. Approve — it waits for your explicit approval before any write. One approval covers one batch — approving one change-set never pre-approves the next.
  4. Apply — only after approval, the batch is applied. Apply defaults to a dry-run that reports exactly what would change.

Every write is ledger-only, strictly limited to:

  • Categorize — assign a transaction to a category.
  • Allocate — move money from Ready-to-Assign into a category.
  • Fix duplicates — delete a double-entered transaction.
  • Reconcile — bring an account's cleared/reconciled balance into line.

The plugin NEVER moves real money. It initiates no transfers and no payments to the outside world — no money ever leaves or moves between your real accounts. This is enforced structurally: every change-set carries a money_movement: false invariant that cannot be set otherwise, and a runtime guardrail hard-blocks any apply that maps to a money-moving operation. The full safety model — allowed vs. forbidden operations, the batch-approval gate, and the exact write tools — is docs/write-back-safety.md; the machine contract is assets/changeset-contract.md.

Privacy / where the token lives

Your YNAB Personal Access Token is stored ONLY in the macOS Keychain. It is never committed to the repo, never written to a config file, never logged, and is injected into the vendored MCP at launch time as the YNAB_ACCESS_TOKEN environment variable — read fresh from the Keychain on every launch, never persisted elsewhere.

The token lives under the Keychain service ynab-mcp, account access-token.

Store it (setup does this for you):

security add-generic-password -s "ynab-mcp" -a "access-token" -w "$TOKEN" -U

Read it back (the launcher does this at MCP start):

security find-generic-password -s "ynab-mcp" -a "access-token" -w

Configuration lives outside the repo. Your budget id, tax profile, and persona settings live at:

~/.claude/plugins/data/workbench-ynab-claude-workbench/config.json

This path is deliberately outside the installed plugin tree, so plugin updates never clobber it — re-installing or upgrading workbench-ynab leaves your config and tax profile untouched. The config never holds the token (that's Keychain-only), and it is never committed.

Generated reports & data are unencrypted, plaintext financial records. Every review run writes files to your local disk — the HTML report (default ~/Documents/Claude/Reports/) plus the audit log, monitor state, and estimated-tax tracker under the data directory above. Together they hold your complete transaction history, balances, payees, and tax detail in cleartext. The plugin creates them owner-only (mode 0600, directories 0700) at write time, but does not encrypt them. Two things to know:

  • ⚠️ ~/Documents may sync to iCloud Drive. With macOS Desktop & Documents syncing enabled, your financial reports can be silently uploaded to iCloud. Keep them on local, disk-encrypted storage (enable FileVault) and don't point .report.output_dir at a shared or cloud-synced folder unless you intend those records to travel there.
  • Prune old reports. Reports accumulate one file per run. bin/ynab-prune.sh removes reports older than a retention threshold (default 30 days, dry-run by default). See the Generated Artifacts section of SECURITY.md for the full artifact inventory, locations, and retention policy.
  • Removing the plugin leaves all of this behind. Uninstalling workbench-ynab does not remove the scheduled tasks, the Keychain token, the settings.json pre-approvals, or the data directory. Run /workbench-ynab:uninstall before you remove the plugin — it tears down each one, asks before touching your financial records, and keeps them by default. If the plugin is already gone, follow the by-hand checklist in docs/uninstall.md. Either way, revoke the token at YNAB — deleting the Keychain entry does not revoke server-side access.

Commands

Every command is namespaced under /workbench-ynab:. The plugin is mid-build; the Ships in column marks the sprint each command lands in (see docs/ROADMAP.md).

Command Description Ships in
/workbench-ynab:setup First-run setup: seed the YNAB token into the Keychain, write config, pre-approve the read-tool glob, offer legacy migration. Sprint 1
/workbench-ynab:ynab-review Run a tax-aware review for a tier (weekly / monthly / quarterly-tax / annual); produces the report and the proposed change-set. Sprint 3
/workbench-ynab:ynab-weekly-review Run the weekly review ad-hoc — plans via the orchestrator, then forces the weekly tier only. Sprint 3
/workbench-ynab:ynab-monthly-review Run the monthly review ad-hoc — plans via the orchestrator, then forces the monthly tier only. Sprint 3
/workbench-ynab:ynab-quarterly-tax-review Run the quarterly estimated-tax review ad-hoc — plans via the orchestrator, then forces the quarterly-tax tier only. Sprint 3
/workbench-ynab:ynab-annual-review Run the annual review ad-hoc — plans via the orchestrator, then forces the annual tier only. Sprint 3
/workbench-ynab:ynab-apply Review a proposed change-set and, on explicit approval, apply the ledger-only writes (dry-run by default). Sprint 4
/workbench-ynab:ynab-migrate Retire the legacy hand-run prototype: the old Desktop connector, its token, and the prototype scheduled tasks/directories. Sprint 5
/workbench-ynab:ynab-prune Prune old generated reports under the retention policy — previews by default, deletes only with --apply. Keeps unencrypted financial history from accumulating unbounded. Sprint 5
/workbench-ynab:uninstall Tear down every piece of system state setup created — the two scheduled tasks, the Keychain token, the settings.json pre-approvals, and (only on explicit confirmation) the plaintext data directory. Idempotent. By-hand equivalent: docs/uninstall.md. Sprint 5
/workbench-ynab:ynab-monitor Run one proactive between-run monitoring pass: advance the monitor state store from fresh YNAB data, run the four alert detectors (overdrawn, large/unusual transaction, budget overrun, bill due), dispatch any new finding, and exit silently when nothing is alert-worthy. Sprint 6 (v-Next)
/workbench-ynab:ynab-portfolio Run the cross-budget portfolio rollup: one consolidated report across every configured budget — combined net worth, aggregate income vs spending, cross-budget Ready-to-Assign, a unified health score, and a single YTD tax picture across the business-tagged budgets. Read-only. See docs/portfolio-rollup.md. Sprint 6 (v-Next)

Versioning

The plugin and its vendored YNAB MCP bundle are pinned together in git. The bundle is @dizzlkheinz/ynab-mcpb, version-frozen at 0.27.1 and vendored as a self-contained vendor/ynab-mcp/index.cjs — no npx-on-demand, no floating dependency. The pinned version, tarball hash, and provenance are recorded in vendor/ynab-mcp/vendored.json; the bundle is only ever updated via the re-vendor script (bin/revendor.sh), never by hand. See docs/vendoring.md for how to update the bundle, verify the result, and the version-marker format.

Pinning both versions in git means a given workbench-ynab commit always runs against the exact MCP bundle it was tested with — boot is offline, frozen, and reproducible.

Two version numbers live in this repo. They track different things, are deliberately independent, and are never co-bumped.

  • The plugin's own version lives in .claude-plugin/plugin.json (currently 0.1.1). This is the only version release automation bumps — the release workflow's sole bump target is .claude-plugin/plugin.json, and no other manifest, JSON, or config file in the repo carries a release version. It starts at 0.1.0 and is cut to 1.0.0 at first release.
  • The vendored YNAB MCP version is recorded in vendor/ynab-mcp/vendored.json (@dizzlkheinz/ynab-mcpb@0.27.1). It is frozen, provenance-only — a record of exactly which upstream bundle is checked into git, not a number this plugin releases against. Release automation never touches it; it changes only when the bundle is deliberately re-vendored.

The two schemes do not move together: bumping the plugin version leaves the vendored bundle version untouched, and re-vendoring the bundle leaves the plugin version untouched.

License

This project is licensed under the MIT License — see the LICENSE file for details.

About

Tax-aware YNAB budget review and approval-gated write-back for Claude Code. Vendored YNAB MCP, zero-config setup. Part of the claude-workbench marketplace.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages