Ynab MCP README

The full upstream README, mirrored for reference. Install config, tool schemas, adoption signals and our own overview live on the Ynab MCP listing page.

YNAB MCP Server

npm version Docker License: MIT Node.js 22.14+ MCP Compatible TypeScript

A comprehensive Model Context Protocol (MCP) server for YNAB (You Need A Budget) that enables AI assistants like Claude Desktop to read your budget, analyze spending patterns, detect subscriptions, and provide personalized financial insights—all through natural conversation.

Documentation site: https://auzroz.github.io/ynab-mcp/, with installation, example conversations and the full tool reference.

Why YNAB MCP?

Traditional YNAB integrations require manual API calls or custom scripting. This MCP server lets you:

  • Talk to your budget naturally — Ask Claude "Am I overspending on dining out?" and get instant insights
  • Get AI-powered financial analysis — Subscription detection, spending trends, savings recommendations, and budget health scores
  • Stay safe by default — Read-only mode protects against accidental changes
  • Access everything — 69 tools total: comprehensive YNAB API coverage (43 tools), 23 custom analytics tools, and 3 system tools

Features

FeatureDescription
Broad API Coverage69 MCP tools spanning the YNAB API endpoints
Advanced Analytics23 tools for spending analysis, trend detection, and financial insights
Read-Only by DefaultWrite operations require explicit opt-in (YNAB_READ_ONLY=false)
Smart Rate LimitingToken bucket algorithm with 180 req/hour budget (10% safety margin)
Intelligent CachingTTL cache for slow-changing data plus delta sync (last_knowledge_of_server), so repeat reads fetch only what changed (see Caching)
Type-SafeFull TypeScript implementation with Zod schema validation

Quick Start

Prerequisites

  • A YNAB account with API access
  • A YNAB Personal Access Token (get one here)

Installation Options

Option 1: npx (Easiest)

No installation required—run directly:

Terminal
npx -y ynab-mcp

Note: The npm package ynab-mcp-server is an unrelated project. This server is published as ynab-mcp.

Option 2: Docker

Terminal
docker run -e YNAB_ACCESS_TOKEN=your_token ghcr.io/auzroz/ynab-mcp:latest

Option 3: Install from Source

bash
# Clone the repository
git clone https://github.com/auzroz/ynab-mcp.git
cd ynab-mcp

# Install dependencies and build
npm install
npm run build

# Run the server
npm start

Configuration

The server is configured via environment variables:

VariableRequiredDescription
YNAB_ACCESS_TOKENYesYour YNAB Personal Access Token
YNAB_BUDGET_IDNoDefault budget UUID (uses "last-used" if not set)
YNAB_READ_ONLYNoSet to false to enable write operations (default: true)

Remote / HTTP mode

By default the server speaks stdio (local). Set MCP_TRANSPORT=http to run it as a remote server over the MCP Streamable HTTP transport at POST /mcp, with a plain GET /health for load balancers. Each session gets its own client; all sessions of one user share one cache, rate limiter and audit log, never shared between users.

VariableDescription
MCP_TRANSPORTstdio (default) or http
PORTHTTP port (default 3000)
ALLOWED_HOSTSComma-separated Host allowlist for DNS-rebinding protection (default: the PUBLIC_URL host, plus localhost when PUBLIC_URL is unset or loopback). An explicit list replaces the defaults
ALLOWED_ORIGINSComma-separated Origin allowlist for /mcp (default: the PUBLIC_URL origin; without PUBLIC_URL, https:// for each allowed host, plus http:// for localhost). Requests with no Origin header aren't checked
ENABLE_DNS_REBINDING_PROTECTIONOrigin/Host checks, on by default (0.4.0 breaking default; behind a proxy forward Host or set ALLOWED_HOSTS); false disables them (unsafe). See docs/REMOTE_HOSTING.md
MCP_RESOURCE_URLResource URL (default PUBLIC_URL/mcp)
MCP_MAX_SESSIONS / MCP_MAX_SESSIONS_PER_USERGlobal cap on concurrent sessions (default 100; at the cap initialize gets 503 + Retry-After) and per-user cap (default 10; past it the user's least recently used session is closed). See docs/REMOTE_HOSTING.md
MCP_SESSION_IDLE_TIMEOUT_MSIdle time before a session is closed (default 30 min); a session with an open request or SSE stream is not idle

Multi-user (YNAB OAuth). Configure a YNAB OAuth app + ENCRYPTION_KEY + PUBLIC_URL and the server becomes an OAuth 2.1 Authorization Server federated to YNAB: many users connect their own YNAB accounts to one instance, each isolated, choosing read-only or read-write for each connection at consent. See docs/REMOTE_HOSTING.md for the full deployer guide (registering the YNAB app, storage, TLS). Tokens are bound to the MCP resource URL (RFC 8707). Back up your SQLite/Postgres database before upgrading: the schema is migrated in place on startup (see the guide's Upgrading section).

Single-user (header auth). If the OAuth variables aren't all set, HTTP mode takes the YNAB token per request via the X-YNAB-Token header (falling back to YNAB_ACCESS_TOKEN for one user). Serve behind TLS.

bash
MCP_TRANSPORT=http PORT=3000 YNAB_ACCESS_TOKEN=… npm start
# connect an MCP client via the mcp-remote shim:
npx mcp-remote http://localhost:3000/mcp --header "X-YNAB-Token: <your-token>"

Claude Desktop Integration

Add to your Claude Desktop configuration:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

Using npx (Recommended)

config.json
{
  "mcpServers": {
    "ynab": {
      "command": "npx",
      "args": ["-y", "ynab-mcp"],
      "env": {
        "YNAB_ACCESS_TOKEN": "your_token_here"
      }
    }
  }
}

Using Docker

config.json
{
  "mcpServers": {
    "ynab": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "YNAB_ACCESS_TOKEN",
        "ghcr.io/auzroz/ynab-mcp:latest"
      ],
      "env": {
        "YNAB_ACCESS_TOKEN": "your_token_here"
      }
    }
  }
}

Using Local Build

config.json
{
  "mcpServers": {
    "ynab": {
      "command": "node",
      "args": ["/path/to/ynab-mcp/dist/index.js"],
      "env": {
        "YNAB_ACCESS_TOKEN": "your_token_here"
      }
    }
  }
}

Example Conversations

Once connected, try asking Claude:

QuestionTool Used
"What's my current net worth?"ynab_net_worth
"How much did I spend on dining out this month?"ynab_list_category_transactions
"What subscriptions do I have?"ynab_detect_recurring
"Is my budget in good shape?"ynab_budget_health
"Where can I cut back on spending?"ynab_savings_opportunities
"Am I on track this month?"ynab_spending_pace
"Compare this month to last month"ynab_monthly_comparison

Available Tools (69 Total)

Analytics Tools (23)

These tools provide AI-powered financial insights beyond basic YNAB functionality:

ToolDescription
ynab_detect_recurringFind subscription patterns and recurring payments
ynab_spending_analysisAnalyze spending trends by category
ynab_budget_healthAssess overall budget health with score and alerts
ynab_savings_opportunitiesIdentify potential areas to save money
ynab_budget_vs_actualsCompare budgeted vs actual spending
ynab_quick_summaryAt-a-glance budget status overview
ynab_income_expenseIncome vs expense breakdown and trends
ynab_net_worthCalculate total net worth from all accounts
ynab_goal_progressTrack goal funding progress with projections
ynab_spending_by_payeeAnalyze spending by merchant/payee
ynab_unused_categoriesFind inactive or unused categories
ynab_monthly_comparisonMonth-over-month spending comparison
ynab_spending_trendsMulti-month trend analysis with projections
ynab_cash_flow_forecastProject future cash flow based on scheduled transactions
ynab_reconciliation_helperHelp with account reconciliation
ynab_budget_suggestionsGet budget suggestions based on history
ynab_overspending_alertsQuick check for overspent categories
ynab_transaction_searchPowerful multi-filter transaction search
ynab_spending_paceTrack daily spending rate vs target
ynab_category_balancesQuick category balance lookup
ynab_credit_card_statusCredit card balances vs payment categories
ynab_age_of_moneyAge of money metric with explanation
ynab_preview_scheduled_transactionPreview the budget impact of a scheduled transaction

Core YNAB API Tools (43)

Coverage of the YNAB API endpoints:

User Tools (1)
  • ynab_get_user — Get authenticated user information
Budget Tools (3)
  • ynab_list_budgets — List all accessible budgets
  • ynab_get_budget — Get detailed budget information
  • ynab_get_budget_settings — Get budget settings (currency format, etc.)
Account Tools (3)
  • ynab_list_accounts — List all accounts with balances, on-budget/closed status, notes, direct-import status, transfer payee ids, and debt details
  • ynab_get_account — Get specific account details
  • ynab_create_account — Create a new account (write mode)
Category Tools (9)
  • ynab_list_categories — List all category groups and categories
  • ynab_get_category — Get category details
  • ynab_get_month_category — Get category budget for specific month
  • ynab_update_category — Set a category's budgeted amount for one month (write mode)
  • ynab_move_money — Move budgeted money between two categories in one month (changes budgeted amounts; creates no YNAB money-movement record) (write mode)
  • ynab_update_category_details — Rename a category, change its note, move it to another group, or set/clear its target (including recurring targets via goal_frequency) (write mode)
  • ynab_create_category — Create a new category in a group, optionally with a target (goal_target in dollars, goal_target_date, goal_needs_whole_amount, goal_frequency for recurring targets like $50 weekly) (write mode)
  • ynab_create_category_group — Create a new category group (write mode)
  • ynab_update_category_group — Rename a category group (write mode)
Payee Tools (7)
  • ynab_list_payees — List all payees (merchants/vendors)
  • ynab_get_payee — Get specific payee details
  • ynab_create_payee — Create a new payee (write mode)
  • ynab_update_payee — Rename a payee (write mode)
  • ynab_list_payee_locations — List all payee locations for mapping
  • ynab_get_payee_location — Get specific payee location
  • ynab_list_payee_locations_by_payee — List locations for a specific payee
Month Tools (2)
  • ynab_list_months — List all budget months
  • ynab_get_month — Get detailed month summary with categories
Transaction Tools (11)
  • ynab_list_transactions — List transactions with filters
  • ynab_get_transaction — Get transaction details
  • ynab_create_transaction — Create a new transaction (write mode)
  • ynab_create_transactions — Bulk create transactions (write mode)
  • ynab_update_transaction — Update a transaction (write mode)
  • ynab_update_transactions — Bulk update transactions (write mode)
  • ynab_delete_transaction — Delete a transaction (write mode)
  • ynab_list_account_transactions — List transactions for a specific account
  • ynab_list_category_transactions — List transactions for a specific category
  • ynab_list_payee_transactions — List transactions for a specific payee
  • ynab_import_transactions — Deprecated (YNAB API 1.88): no longer imports anything; linked accounts sync automatically (write mode)
Scheduled Transaction Tools (5)
  • ynab_list_scheduled_transactions — List scheduled/recurring transactions
  • ynab_get_scheduled_transaction — Get scheduled transaction details
  • ynab_create_scheduled_transaction — Create a scheduled transaction (write mode)
  • ynab_update_scheduled_transaction — Update a scheduled transaction (write mode)
  • ynab_delete_scheduled_transaction — Delete a scheduled transaction (write mode)
Money Movement Tools (2)
  • ynab_list_money_movements — List moves of funds between categories (optional month)
  • ynab_list_money_movement_groups — List groups of money moves (optional month)
System Tools (3)
  • ynab_rate_limit_status — Check API rate limit status
  • ynab_audit_log — View write operation audit log
  • ynab_health_check — Test API connectivity

Tips and Common Recipes

Recording a transfer between accounts. A transfer is a transaction whose payee is the other account's transfer payee. Look up the target account's transfer_payee_id (returned by ynab_list_accounts and ynab_get_account, or find the payee named "Transfer : " in ynab_list_payees under transfer_payees), then call ynab_create_transaction on the source account with that value as payee_id. Leave category_id empty for transfers between on-budget accounts; YNAB creates the matching transaction in the other account itself.

Budgeting amounts vs. category details. Two tools update a category, and they do different things:

ToolChanges
ynab_update_categoryThe budgeted amount for one category in one month (e.g. "budget $500 for Groceries in January")
ynab_update_category_detailsThe category itself: name, note, category group, and target (goal)

To shift money between categories in one step, use ynab_move_money. It subtracts from one category's budgeted amount and adds to another's for the same month. It edits budgeted amounts only: YNAB's API has no money-movement write, so the move does not appear in ynab_list_money_movements, which lists moves made in YNAB's own apps. The two updates are not atomic; if the second fails the server tries to restore the first and reports whether that worked.

Budgets are "plans" in YNAB's API. YNAB has renamed budgets to "plans" in its API and SDK. This server keeps the familiar budget naming for tools and parameters (ynab_list_budgets, budget_id), and it maps to YNAB's plan endpoints internally. Wherever YNAB documentation says "plan" or plan_id, read it as budget and budget_id.

Available Prompts

Beyond raw tool access, the server exposes 7 MCP Prompts — guided workflows grounded in YNAB's own budgeting methodology (the Four Rules, Age of Money, True Expenses) that orchestrate several tools into a single review. Any MCP client that supports the prompts capability can invoke them directly.

PromptWhat it does
ynab-pulse-checkFast daily-style status check
ynab-monthly-reviewFull end-of-month budget review and next month's suggested targets
ynab-assign-moneyGive Every Dollar a Job — allocate unassigned funds
ynab-true-expensesEmbrace Your True Expenses — plan for irregular/annual costs
ynab-overspending-triageRoll With the Punches — find and fix overspending
ynab-age-of-moneyAge Your Money — check cash cushion and credit card float
ynab-reconcile-cleanupWeekly hygiene: reconciliation and category cleanup

See docs/REMOTE_HOSTING.md for details.

Skills

Our MCP server gives Claude access to your YNAB budget. Our skills teach Claude YNAB's own best practices — some work standalone, some get sharper when connected. Together, they turn raw budget data into an actual coach.

The skills/ directory has 15 Claude Agent Skills: 7 that orchestrate this MCP server's tools into guided reviews, and 8 standalone coaching skills (getting started, debt payoff, irregular income, windfalls, emergency funds, and more) that work with or without this server connected. Every GitHub Release attaches them as downloadable zips — see skills/README.md for the full list and install instructions.

Development

bash
# Run with hot reload
npm run dev

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Lint code
npm run lint

# Fix lint issues
npm run lint:fix

# Type check only
npm run typecheck

Testing with MCP Inspector

Terminal
npx @modelcontextprotocol/inspector node dist/index.js

Architecture

Code
src/
├── index.ts              # Entry point (selects stdio or http transport)
├── server.ts             # Per-user server + tool registration
├── http.ts               # Express app for remote (Streamable HTTP) mode
├── crypto.ts             # AES-256-GCM at-rest token encryption
├── config/               # Environment configuration
├── auth/                 # Remote OAuth: MCP AS provider, YNAB client, sessions
├── storage/              # Pluggable storage (memory / sqlite / postgres)
├── services/             # Core services
│   ├── ynab-client.ts    # YNAB API wrapper with rate limiting
│   ├── rate-limiter.ts   # Token bucket rate limiter
│   ├── audit-log.ts      # Per-user write audit log
│   ├── cache.ts          # In-memory TTL cache
│   ├── delta-cache.ts    # Delta-sync snapshots (per user, bounded)
│   └── delta-merge.ts    # Applies YNAB delta responses to snapshots
├── tools/                # MCP tool implementations
│   ├── user/
│   ├── budgets/
│   ├── accounts/
│   ├── categories/
│   ├── payees/
│   ├── months/
│   ├── transactions/
│   ├── scheduled-transactions/
│   └── analytics/
└── utils/                # Helpers (currency, dates, errors)

Caching

Reads go through two layers, both in memory and per user:

  1. TTL cache. Accounts, categories, payees and the budget list are served from a cache for CACHE_TTL_MS (default 5 minutes). A write through this server drops the matching entry (a category write the category list, a payee write the payee list, and so on).

  2. Delta sync. For every list read (accounts, categories, months, payees, scheduled transactions, and every transaction list, whatever its filters) the server keeps a snapshot of the last full result and its YNAB server_knowledge. When the TTL entry has expired or a write dropped it (transactions, months and scheduled transactions have no TTL entry, so every read), it asks YNAB only for what changed since then and merges that in. Tools always get the complete list a full fetch would return. The saving is in payload size and latency: a refresh is one request, like the full fetch it replaces, except that a category or payee transaction list (or an uncategorized query) that a change touches costs the change-feed request plus a full re-read, so two.

    • Transaction lists are refreshed from the budget's change feed (all transactions changed since the snapshot, from a far-past since_date, so it never depends on a cached first month), not from their own filtered endpoint, so a transaction approved, re-categorized, re-dated or moved out of a filtered list doesn't linger in it. When a change can't be placed exactly (the uncategorized type, a date at the edge of YNAB's default 12-month window, a change touching a category or payee list), that list is re-read in full instead.
    • Transaction and scheduled transaction rows carry payee, category and account names, and a rename doesn't change those rows, so no delta reports it. Their snapshots are dropped when this server renames a payee or category, and whenever a read of the account, category or payee list shows a name changed elsewhere. A rename made in another app and not revealed by such a read shows up within 15 minutes, as these snapshots are re-read in full at least that often.
    • Category amounts (assigned, activity, available, goals) are for the current month, and a new month changes them with no change on the server, so category and month snapshots start over each UTC month.
    • Anything inconsistent (no or decreasing server_knowledge, a delta that doesn't apply, a failed request) drops the snapshot and fetches in full.
    • ynab_get_budget, budget aliases (last-used, default) and the current month are always fetched in full.
    • Per user at most 32 snapshots and 20,000 entities are kept (least recently used first out); a snapshot unused for 30 minutes is dropped, and each is re-read in full at least hourly (transaction lists every 15 minutes). These are constants in src/services/delta-cache.ts.
    • Merged lists keep YNAB's order: transactions by date, months by month; new accounts, payees and categories are appended (a moved category goes last in its new group); within one date, transaction order may differ from YNAB's.

Cursor for clients. Every list tool response includes server_knowledge. Pass it back as since_server_knowledge (same budget and filters) to get only what changed since: the response has delta: true, the changed entities in the usual fields, deleted ones by id in deleted_ids (deleted_group_ids for category groups, deleted_months for months), and counts instead of totals.

  • Transaction tools still apply limit to a delta. When more transactions changed than limit, the response hands back your own cursor as server_knowledge (with cursor_withheld: true), so the changes it left out aren't skipped: raise limit, narrow the filters, or re-read in full.
  • A delta for a filtered transaction query comes from YNAB's filtered endpoint, which may leave out a transaction that changed so it no longer matches (approved, re-categorized, re-dated, moved); it is not in deleted_ids either. Re-read filtered lists in full now and then to keep them exact.
  • Category deltas don't show the change of month (see above); re-read in full after a month begins.

Security

FeatureDescription
Read-Only DefaultWrite operations disabled unless YNAB_READ_ONLY=false; in read-only mode (including read-only OAuth sessions) write tools are hidden from the tool list and still rejected if called
Token SecurityNever commit your YNAB_ACCESS_TOKEN; use environment variables
Input ValidationAll tool parameters validated with Zod schemas
Rate LimitingBuilt-in protection against API quota exhaustion
Audit LoggingAll write operations logged for review

Contributing

Contributions are welcome: bug reports, fixes, new tools, docs and skills. CONTRIBUTING.md covers:

  • development setup;
  • the standards every change must meet (money math, validation, sanitizing, secrets, tests);
  • how to add a tool;
  • running the Postgres storage tests;
  • commit and PR conventions, and how review and releases work.

Please report security issues privately, as described there, rather than in a public issue.

License

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

Acknowledgments

Related Projects


Built with ❤️ for the YNAB community