ynab-mcp-bridge
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ynab-mcp-bridgeshow me my budget summaries"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ynab-mcp-bridge
ynab-mcp-bridge is a read-only Model Context Protocol (MCP) server for YNAB.
It gives MCP clients a shared YNAB backend over either:
stateless HTTP for self-hosted or remote deployments
optional OAuth-protected HTTP for remote browser-based MCP clients
stdiofor local desktop clients and debugging
What You Get
Read-only YNAB tools for plans, accounts, categories, payees, transactions, scheduled transactions, and summary views
HTTP mode by default
Stateless
POST /mcphandling that works well with clients that do not keep durable MCP sessionsOptional auth2-backed OAuth mode for remote clients such as Claude Web
Automatic plan resolution when
YNAB_PLAN_IDis not set
Choose A Mode
Mode | Use it when | Auth | Key settings |
| Local use or a trusted self-hosted setup | None |
|
| Remote client access behind an upstream IdP | MCP-side OAuth |
|
| Same as above, but fail closed unless origins are explicitly allowed | MCP-side OAuth |
|
| Local desktop clients and debugging | None |
|
authless is the default deployment mode. http is the default transport.
Quick Start
1. Install and build
npm install
npm run build
npm run pr:ciBuild artifact policy:
dist/remains tracked in this repository for now because the published package and CLI entrypoints resolve from built JavaScript underdist/.When a source change affects runtime output, keep the generated
dist/artifacts in sync with the source change rather than treating them as disposable local-only files.CI enforces that policy with
verify:build-syncafternpm run build, and it also runsverify:packto smoke-test the packed npm artifact before treating a build as release-ready.When CI passes on the exact release commit and your dependency lockfile is already installed locally, pulling the release should be a restart-first path rather than a rebuild-first path.
2. Run the default local HTTP server
export YNAB_API_TOKEN=your-token
npm startDefaults:
host:
127.0.0.1port:
3000path:
/mcp
3. Run over stdio
export YNAB_API_TOKEN=your-token
npm run start:stdioLocal CI Preflight
Run this before creating a PR when you want one command that mirrors the required CI gates instead of running individual test commands:
npm run pr:cipr:ci runs the required local checks from CI: test:ci, test:coverage, lint:deps, lint, typecheck, lint:unused, and build. It intentionally does not include the advisory-only lint:oxlint step because that CI job is non-blocking.
preflight remains available as a backward-compatible alias, but the default pre-PR workflow should be npm run pr:ci.
For advisory quality reporting outside the blocking preflight gate, you can also run:
npm run lint:duplicates
npm run tech-debt:reportlint:duplicates runs JSCPD across maintained production, tooling, and configuration files using the checked-in .jscpd.json settings. It excludes generated output, Markdown, specs, contracts, and local task docs, and it also ignores the auth2 harness so duplication reporting stays focused on the production module surface. tech-debt:report uses the same advisory boundary and prints the current duplication, dead-export, suppression, debt-marker, and dependency-update counts.
4. Expose authless HTTP intentionally
Use this only when the network path is already trusted.
MCP_TRANSPORT=http \
MCP_HOST=0.0.0.0 \
MCP_ALLOWED_ORIGINS=https://claude.ai \
YNAB_API_TOKEN=your-token \
npm run start:http5. Enable OAuth for a remote client
This is the recommended remote setup.
MCP_TRANSPORT=http \
MCP_HOST=0.0.0.0 \
MCP_ALLOWED_ORIGINS=https://claude.ai \
MCP_DEPLOYMENT_MODE=oauth-single-tenant \
MCP_PUBLIC_URL=https://mcp.example.com/mcp \
MCP_AUTH2_CONFIG_PATH=./auth2.config.example.json \
YNAB_API_TOKEN=your-token \
npm run start:httpThe checked-in auth2.config.example.json shows the canonical auth2 file shape loaded through MCP_AUTH2_CONFIG_PATH. Copy it to a private path, replace the placeholder provider values, and keep /oauth/callback as the callback path.
With an auth2 config file in place, the minimal remote OAuth env surface is:
MCP_PUBLIC_URLMCP_AUTH2_CONFIG_PATH
Legacy MCP_OAUTH_* settings are still supported as compatibility overrides, but they are no longer required for the primary auth2 HTTP path.
Environment Variables
YNAB Backend
Variable | Required | Notes |
| Yes | Shared backend YNAB token |
| No | Default plan for tools that accept |
If YNAB_PLAN_ID is unset, the bridge tries YNAB's default_plan first, then the only available plan when exactly one exists. If a configured plan becomes stale, the bridge retries once with a fresh resolution.
Transport and HTTP
Variable | Default | Notes |
|
|
|
|
| HTTP only |
|
| HTTP only |
|
| HTTP only |
| empty | Comma-separated browser origin allowlist |
| empty | Optional comma-separated |
|
|
|
| none | Legacy compatibility shim: |
Notes:
When an
Originheader is present, HTTP mode validates it.Loopback origins are allowed automatically for loopback hosts.
oauth-hardenedrefuses to start withoutMCP_ALLOWED_ORIGINS.
OAuth
Variable | Required | Notes |
| Yes in OAuth modes | Public MCP URL, for example |
| Yes in OAuth modes | Path to the auth2 JSON config file |
| No | Defaults to |
| No | Defaults to |
| No | Defaults to a stable derived secret |
| No | Defaults to |
| No | Set to |
| No | Comma-separated runtime scope override; defaults to the auth2 config scopes |
| Optional compatibility override | Shortcut for Cloudflare Access endpoint derivation |
| Optional compatibility override | Upstream issuer |
| Optional compatibility override | Upstream authorization endpoint |
| Optional compatibility override | Upstream token endpoint |
| Optional compatibility override | Upstream JWKS endpoint |
| Optional compatibility override | Upstream confidential client ID |
| Optional compatibility override | Upstream confidential client secret |
How HTTP Mode Behaves
The default HTTP transport is stateless. Clients should send
POST /mcpand should not depend on durable MCP sessions.The bridge still exposes
Mcp-Session-Idheaders for compatibility, but session continuity is not the primary path.In OAuth modes, the bridge acts as the MCP authorization server and exposes:
/.well-known/oauth-authorization-server/register/authorize/token/.well-known/oauth-protected-resource/mcp
OAuth here protects access to a shared backend YNAB token. It does not do per-user YNAB OAuth delegation.
MCP_OAUTH_SKIP_LOCAL_CONSENT=trueis intended for owner-operated deployments where you want the bridge to forward every authorization request upstream without showing the local approval page.
Cloudflare Access
For Cloudflare Access, the simplest setup is:
MCP_PUBLIC_URLMCP_AUTH2_CONFIG_PATH
Put the Cloudflare issuer, authorization endpoint, token endpoint, JWKS URI, client ID, and client secret in the auth2 config file. The legacy MCP_OAUTH_* settings remain available if you need to override those values at runtime.
Important details:
Register the callback built from
MCP_PUBLIC_URLandMCP_OAUTH_CALLBACK_PATH, for examplehttps://mcp.example.com/oauth/callbackKeep
MCP_PUBLIC_URLon the external HTTPS hostname, not the internal bind addressUse the public MCP URL as the audience unless your Access app expects a different resource identifier
Do not use the older tenant-wide
/cdn-cgi/access/sso/oauth2/*endpoints for this flow
If Cloudflare injects Cf-Access-Jwt-Assertion, the bridge can translate that assertion into a bridge-local token as an explicit compatibility path.
Tool Coverage
The server exposes a read-only YNAB toolset across:
user and plan metadata
plan settings and plan months
accounts, categories, and payees
transactions and scheduled transactions
payee locations
money movement and transfer summaries
higher-level financial summaries such as spending, cash flow, income, goal progress, obligations, and budget health
For YNAB-style summaries, treat assigned_vs_spent as a timing and buffering signal, not a score for budget discipline. In buffered budgets it often reflects paycheck timing, category staging, or money reserved for future months rather than overspending or underspending by itself.
CLI Examples
Start with the default HTTP settings:
node dist/index.jsStart over stdio:
node dist/index.js --transport stdioStart over HTTP explicitly:
node dist/index.js --transport http --host 127.0.0.1 --port 3000 --path /mcpRun the local HTTP reliability probe:
npm run reliability:http -- --requests 10 --concurrency 2The reliability command uses a bounded authless HTTP scenario that:
starts a local bridge unless you pass
--urlruns
initialize,tools/list, andynab_get_mcp_versionprints attempts, failures, error rate, and latency percentiles
exits non-zero when the error rate is above
--max-error-rate
Useful flags:
--requests <n>: number of reliability sequences to run. Each sequence performs three MCP operations.--concurrency <n>: number of sequences to run in parallel.--max-error-rate <0..1>: fail the run if the observed error rate exceeds this threshold.--url <http-url>: target an already running bridge instead of starting a local one.--host,--port,--path: override the local server bind address when--urlis not used.
Write a machine-readable smoke artifact and compare against a prior run:
npm run reliability:http -- \
--requests 10 \
--concurrency 2 \
--json-out artifacts/reliability/smoke.json \
--baseline-artifact artifacts/reliability/baseline-smoke.jsonRun the heavier reliability suite in dry-run mode:
npm run reliability:load -- \
--profile baseline \
--url http://127.0.0.1:3000/mcp \
--json-out artifacts/reliability/baseline.json \
--dry-runThe dedicated load suite is designed for named profiles instead of ad hoc request counts:
smoke: fast local regression check using the built-in Node probebaseline: repeatable average-load run for comparisonsstress: higher sustained load to expose overload behaviorspike: sudden burst behaviorsoak: longer steady-state run to catch degradation over time
Recommended workflow:
run
smokeon local changesrecord
baselineartifacts on a stable environmentrun
stressandspikebefore higher-risk releasesrun
soakon a scheduled cadence or before major rollout events
Thresholds should be evaluated with error rate and latency percentiles such as p95 and p99, not averages alone. The smoke command and artifact comparison flow already follow that model, and the load-suite dry run prints the exact thresholds that would be enforced by the heavier external runner.
Allow specific browser origins:
node dist/index.js \
--transport http \
--host 0.0.0.0 \
--port 3000 \
--path /mcp \
--allowed-origins https://claude.ai,https://chat.openai.comLock down accepted host headers too:
node dist/index.js \
--transport http \
--host 0.0.0.0 \
--port 3000 \
--path /mcp \
--allowed-origins https://claude.ai \
--allowed-hosts mcp.example.comEnable OAuth with explicit upstream endpoints:
node dist/index.js \
--transport http \
--host 0.0.0.0 \
--port 3000 \
--path /mcp \
--allowed-origins https://claude.ai \
--deployment-mode oauth-single-tenant \
--public-url https://mcp.example.com/mcp \
--oauth-issuer https://example.cloudflareaccess.com/cdn-cgi/access/sso/oidc/client-123 \
--oauth-authorization-url https://example.cloudflareaccess.com/cdn-cgi/access/sso/oidc/client-123/authorization \
--oauth-token-url https://example.cloudflareaccess.com/cdn-cgi/access/sso/oidc/client-123/token \
--oauth-jwks-url https://example.cloudflareaccess.com/cdn-cgi/access/sso/oidc/client-123/jwks \
--oauth-client-id cloudflare-access-client-id \
--oauth-client-secret cloudflare-access-client-secret \
--oauth-audience https://mcp.example.com/mcp \
--oauth-store-path /var/lib/ynab-mcp-bridge/oauth-store.json \
--oauth-token-signing-secret replace-with-a-long-random-secret \
--oauth-scopes openid,profileFor a stricter remote deployment, switch oauth-single-tenant to oauth-hardened.
Docker
Build the image:
docker build -t ynab-mcp-bridge .Run the default HTTP server:
docker run --rm \
-p 3000:3000 \
-e YNAB_API_TOKEN=your-token \
ynab-mcp-bridgeRate Limiting
YNAB documents a limit of 200 requests per rolling hour per access token. The bridge applies a shared per-token sliding-window limiter and retries 429 Too Many Requests responses conservatively.
Development
npm test
npm run build
npm run lint:duplicates
npm run tech-debt:reportlint:duplicates runs a JSCPD baseline across maintained production, tooling, and configuration files. It excludes generated/vendor paths, Markdown, specs, contracts, local task docs, and the auth2 harness so the duplicate baseline tracks the production-facing codebase.
tech-debt:report prints the current advisory duplicate-remediation baseline together with dead-export and suppression counts so local cleanup work has one repeatable snapshot command.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mossipcams/ynab-mcp-bridge'
If you have feedback or need assistance with the MCP directory API, please join our Discord server