Metadata-Version: 2.4
Name: rogo-ynab-mcp
Version: 0.8.0
Summary: MCP server for YNAB API
Project-URL: Homepage, https://gitlab.com/therogoman-public/ynab-mcp
Project-URL: Repository, https://gitlab.com/therogoman-public/ynab-mcp
Project-URL: Changelog, https://gitlab.com/therogoman-public/ynab-mcp/-/blob/main/CHANGELOG.md
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: fastmcp>=2.0
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.0
Description-Content-Type: text/markdown

# ynab-mcp

[![pipeline status](https://gitlab.com/therogoman-public/ynab-mcp/badges/main/pipeline.svg)](https://gitlab.com/therogoman-public/ynab-mcp/-/pipelines)
[![coverage report](https://gitlab.com/therogoman-public/ynab-mcp/badges/main/coverage.svg)](https://gitlab.com/therogoman-public/ynab-mcp/-/pipelines)
[![PyPI version](https://img.shields.io/pypi/v/rogo-ynab-mcp.svg)](https://pypi.org/project/rogo-ynab-mcp/)
[![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)

MCP server exposing YNAB budget data to Claude.

## Status

Maintained on a best-effort basis for the [YNAB API v1](https://api.ynab.com/).
It is feature-complete for its scope (the tools listed below); there is no
roadmap for broader coverage. Bug reports and merge requests are welcome and
reviewed as time allows, but timely responses are not guaranteed. Breaking
changes are released under a new major version and recorded in
[CHANGELOG.md](CHANGELOG.md). To report a security issue, see
[SECURITY.md](SECURITY.md).

## Tools

| Tool | Description |
|---|---|
| `list_budgets` | List all budgets |
| `list_accounts` | List accounts for a budget |
| `get_month_summary` | Current month budgeted vs spent by category group |
| `list_transactions` | Recent transactions (filterable by account/date) |
| `list_categories` | Categories with budgeted and activity amounts |
| `list_payees` | List payees for a budget |
| `get_payee` | Get a single payee by id |
| `get_spending_by_category` | Per-category spending for a month (biggest spend first) |
| `create_transaction` | Add a new transaction (idempotent — identical retries are deduplicated, not duplicated) |
| `set_category_budget` | Set a category's budgeted amount for a month |
| `update_transaction` | Edit an existing transaction (only the fields you pass are changed) |
| `delete_transaction` | Delete a transaction |

## Requirements

- Python >= 3.12 (managed by `uv`/`uvx`)
- A YNAB Personal Access Token (YNAB → Settings → Developer Settings)
- Any OS: the token is read from the `YNAB_TOKEN` environment variable;
  on macOS the Keychain is supported as the recommended storage

## Setup

### 1. Provide the YNAB token

Cross-platform (environment variable):

```bash
export YNAB_TOKEN="<your-token>"
```

macOS (Keychain, recommended — no token in shell profiles):

```bash
security add-generic-password -s "ynab-mcp" -a "ynab-token" -w "<your-token>"
```

The env var takes precedence when both are set.

### 2. Add to Claude Code (`~/.claude/settings.json`)

Installed from PyPI (recommended):

```json
"mcpServers": {
  "ynab": {
    "type": "stdio",
    "command": "uvx",
    "args": ["rogo-ynab-mcp"]
  }
}
```

Or from a source checkout: `uv sync`, then use
`"command": "uv", "args": ["run", "--project", "/path/to/ynab-mcp", "rogo-ynab-mcp"]`.

### 3. Or add to Claude Desktop (`claude_desktop_config.json`)

Add the server under the top-level `mcpServers` key (Settings → Developer →
Edit Config), then restart Claude Desktop:

```json
{
  "mcpServers": {
    "ynab": {
      "command": "uvx",
      "args": ["rogo-ynab-mcp"]
    }
  }
}
```

## Client compatibility

This is a standard stdio MCP server launched with `uvx rogo-ynab-mcp`, so any
MCP client that supports the stdio transport should work. Only the Claude Code
setup is documented and exercised here; the others use the same command and are
expected to work but are not separately verified.

| Client | Status | Notes |
|---|---|---|
| Claude Code | Documented / used | Config shown in [Setup](#2-add-to-claude-code-claudesettingsjson) (`~/.claude/settings.json`). |
| Claude Desktop | Expected to work | Add the same `mcpServers` block to `claude_desktop_config.json`. |
| Cursor | Expected to work | Register as an MCP server (stdio): command `uvx`, args `["rogo-ynab-mcp"]`. |
| Antigravity | Expected to work | Register as an MCP server (stdio): command `uvx`, args `["rogo-ynab-mcp"]`. |
| Generic MCP clients | Expected to work | Any client speaking the MCP stdio transport; point it at `uvx rogo-ynab-mcp`. |

## Usage

Once the server is registered, ask your MCP client in natural language — it
picks the right tool and fills in the arguments.

### What you can ask

- "Which YNAB budgets can you see?" → `list_budgets`
- "How much is in my checking account?" → `list_accounts`
- "How am I doing against my budget this month?" → `get_month_summary`
- "What did I spend on groceries this month?" → `list_categories`
- "Show my transactions since the start of the month." → `list_transactions`
- "Which payees do I have?" → `list_payees`
- "Add a 12.50 coffee expense to checking, dated today." → `create_transaction`
- "Move that coffee transaction to the Dining Out category." → `update_transaction`

### Sample tool output

Amounts are returned as plain numbers in the budget's currency (YNAB
milliunits are converted for you). Responses are trimmed to the essential
fields to keep the client's context small. Values below are illustrative fakes.

`list_transactions`:

```json
[
  {
    "id": "b1f2c3d4-5678-49ab-9cde-1234567890ab",
    "date": "2026-07-03",
    "amount": -45.9,
    "payee": "Sample Coffee",
    "category": "Dining Out",
    "account": "Everyday Checking",
    "memo": null,
    "approved": true
  }
]
```

`create_transaction` (retries are idempotent — an identical call returns a
`duplicate` marker instead of creating a second transaction):

```json
{
  "duplicate": false,
  "id": "9c3d5e7f-1a2b-4c8d-8e9f-abcdef012345",
  "date": "2026-07-06",
  "amount": -45.9,
  "payee": "Sample Coffee"
}
```

`update_transaction` (only the arguments you pass are changed; everything
else on the transaction is left as-is):

```json
{
  "id": "9c3d5e7f-1a2b-4c8d-8e9f-abcdef012345",
  "date": "2026-07-06",
  "amount": -45.9,
  "payee": "Sample Coffee",
  "category": "Dining Out",
  "memo": null,
  "approved": true
}
```

## Troubleshooting

| Symptom | Fix |
|---|---|
| "YNAB token invalid or expired" (401) | Regenerate a Personal Access Token at YNAB → Account Settings → Developer Settings and update `YNAB_TOKEN` (or the macOS Keychain entry). |
| "YNAB rate limit reached" (429) | The YNAB API allows 200 requests per hour per token. Wait for the window to reset and retry. |
| Empty results for a month or account | Usually not an error: the account may have no transactions in that range, or the `since_date`/`month` filter excludes them. Confirm you are using the intended budget and account, and that the month has activity. Closed accounts and hidden categories are omitted by design. |

## Development

```bash
uv run pytest                         # run tests with coverage
uv run pytest --cov-report=html       # HTML coverage report
```

## Disclaimer

Not affiliated with or endorsed by YNAB. YNAB is a trademark of its owner;
this project only calls the public [YNAB API](https://api.ynab.com/).

## License

Apache-2.0
