Self-hosting

One server for the whole household

Run ynab-mcp on a server you control and everyone can use it from claude.ai, the Claude apps or any MCP client, each with their own YNAB account.

When to self-host

For one person using Claude Desktop or Claude Code on one computer, the local install is simpler and you can stop there. Self-host when you want any of these:

  • to use your budget from claude.ai or the Claude mobile apps, which connect to remote servers;
  • to share one instance with a partner, family or small team, with nobody seeing anyone else's budget;
  • to keep tokens off individual laptops.

There is no central ynab-mcp service. You run the instance: it fetches each user's YNAB data from YNAB and sends tool results only to that user's MCP client, which passes them to Claude as part of the conversation. See Where your data goes.

How it works

In HTTP mode with OAuth configured, the server signs people in with YNAB itself. There are no separate accounts or passwords: a person's identity is their YNAB user.

  1. MCP clientclaude.ai, Claude Desktop, Claude Code
  2. Your ynab-mcp serverShows a consent screen, stores each user's YNAB grant encrypted
  3. YNABThe user approves access to their budget
  1. A person adds your server's /mcp address to their client.
  2. The client registers with your server and opens its consent page, which names the client and where it will send them back.
  3. They choose read-only or read-write and continue to YNAB to approve.
  4. Your server stores their YNAB grant, encrypted, and gives the client its own short-lived token. From then on, every request runs as that person, with their own cache, rate limit and audit log.

Setup at a glance

  1. Register a YNAB OAuth application in YNAB's Developer Settings, with the redirect URI https://YOUR_PUBLIC_URL/oauth/ynab/callback.
  2. Generate an encryption key with openssl rand -base64 32 and keep it somewhere safe. It must stay the same across restarts.
  3. Set the environment: MCP_TRANSPORT=http, PUBLIC_URL, YNAB_OAUTH_CLIENT_ID, YNAB_OAUTH_CLIENT_SECRET, ENCRYPTION_KEY, and a durable STORAGE_DRIVER. OAuth mode turns on when the four OAuth settings are all present.
  4. Put it behind TLS. OAuth needs HTTPS. A reverse proxy such as Caddy, Traefik or nginx in front of the container is the usual way. Set TRUST_PROXY=1 for a single proxy hop, but only when the server is reachable solely through the proxy (publish no ports). Otherwise clients can spoof X-Forwarded-For and dodge the per-IP rate limits.
  5. Connect a client to https://YOUR_PUBLIC_URL/mcp.

A minimal Docker Compose service looks like this (the guide has the full version with Caddy):

services:
  ynab-mcp:
    image: ghcr.io/auzroz/ynab-mcp:latest
    environment:
      MCP_TRANSPORT: http
      PUBLIC_URL: https://ynab-mcp.example.com
      YNAB_OAUTH_CLIENT_ID: ${YNAB_OAUTH_CLIENT_ID}
      YNAB_OAUTH_CLIENT_SECRET: ${YNAB_OAUTH_CLIENT_SECRET}
      ENCRYPTION_KEY: ${ENCRYPTION_KEY}
      STORAGE_DRIVER: sqlite
      SQLITE_PATH: /data/ynab-mcp.db
      # Only with a proxy in front and no published ports.
      TRUST_PROXY: "1"
    volumes: [ "ynab-data:/data" ]
volumes: { ynab-data: {} }

Every setting is listed in the configuration reference.

Storage

DriverGood for
memoryTrying it out. The default. Everything is lost on restart, so everyone has to reconnect.
sqliteA single instance: a household or a small team. Set SQLITE_PATH to a file on a persistent volume.
postgresWhen you already run Postgres, or want managed backups. Set DATABASE_URL.

Stored YNAB tokens are encrypted with AES-256-GCM using your ENCRYPTION_KEY. Back the database up like anything else that matters.

Access levels

Read-only or read-write is chosen per connection at the consent screen. Someone can connect their phone read-only and their laptop read-write, and each keeps its own level; a token refresh never widens it.

As the operator you can narrow it further:

  • YNAB_OAUTH_ALLOW_WRITE=false offers only read-only at consent.
  • YNAB_READ_ONLY=true (the default) keeps the whole server read-only, whatever people choose. Set it to false to allow read-write connections at all.

Connecting clients

  • claude.ai: add a custom connector with the URL https://YOUR_PUBLIC_URL/mcp. Claude discovers your server's OAuth settings and walks you through consent.
  • Claude Code: claude mcp add --transport http ynab https://YOUR_PUBLIC_URL/mcp, then run /mcp in a session to sign in.
  • Claude Desktop and stdio-only clients: use the mcp-remote shim, which runs the same sign-in flow.
npx mcp-remote https://YOUR_PUBLIC_URL/mcp
Single-user header mode

Without the OAuth settings, HTTP mode takes a YNAB token per request in an X-YNAB-Token header. It is meant for a personal instance on a private network behind TLS. For anything shared or public, use OAuth mode.

Upgrading

Back up the database first. Copy the SQLite file while the server is stopped, or pg_dump the Postgres database. On startup the server updates its schema in place. Read the release notes before upgrading, as some releases ask users to reconnect once.

The full guide

The deployer guide on GitHub has every detail: registering the YNAB app, each setting, the TLS and Compose setup, host and origin checks behind proxies, sessions, token rotation, and upgrades.

Read the remote hosting guide