Skip to main content
Glama
cesarzea

caton mcp

by cesarzea
README.md
# Catón AI

[![CI](https://github.com/cesarzea/caton-ai/actions/workflows/ci.yml/badge.svg)](https://github.com/cesarzea/caton-ai/actions/workflows/ci.yml)
[![CodeQL](https://github.com/cesarzea/caton-ai/actions/workflows/codeql.yml/badge.svg)](https://github.com/cesarzea/caton-ai/actions/workflows/codeql.yml)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/cesarzea/caton-ai/badge)](https://scorecard.dev/viewer/?uri=github.com/cesarzea/caton-ai)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**A local-first, plugin-based financial watchdog for individuals and small businesses.**

Catón AI connects to where your money actually goes — bank accounts, cards and the usage APIs of
the services you pay for — keeps everything on your own machine, and lets plugins and AI agents
watch it for you: budgets, goals, cash-flow forecasts, and alerts such as _"tell me when my
Anthropic spend goes over $20"_.

> **Status: pre-alpha.** A first command-line version syncs European bank accounts through
> Enable Banking into a local ledger, reports monthly outflows and serves the ledger read-only to AI
> assistants over MCP. Plugins, agents, alerts and the web UI are not built yet.

## Why the name

Cato the Elder was the Roman censor famous for his austerity and for reprimanding waste. Catón AI
plays the same role for your finances: it keeps watch, and it tells you when something is off.

## Principles

- **Local-first and private.** Data and credentials stay on your machine. No telemetry.
- **Minimal core, everything else is a plugin.** Data sources, budgets, goals and watchdog agents
  are plugins, including the official ones.
- **Permissions, not trust.** Plugins declare what they can read, write and whether they may use
  the network; nothing gets network access by default.
- **Reviewed before activation.** Plugins are checked automatically — including an AI-assisted
  security and functionality review — before they touch your data.

## Documentation

- [Architecture (arc42 + C4)](docs/architecture/README.md)
- [Architecture Decision Records](docs/adr/README.md)

## Engineering standards

Enforced standards run through `npm run check` locally and in CI on every pull request; `main`
cannot change unless they all pass. Each gate was verified to fail on a deliberate violation when
it was introduced.

**Architecture and code**

| Standard                                                                                                                                                                                             | Status   |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| Architecture documented with [arc42](https://arc42.org) and [C4](https://c4model.com); decisions recorded as [MADR](https://adr.github.io/madr/) [architecture decision records](docs/adr/README.md) | Adopted  |
| Module boundaries checked by [dependency-cruiser](https://github.com/sverweij/dependency-cruiser): public entry points only, no cycles, no undeclared or development dependencies in production code | Enforced |
| TypeScript [`@tsconfig/strictest`](https://github.com/tsconfig/bases); `any` forbidden                                                                                                               | Enforced |
| [typescript-eslint](https://typescript-eslint.io) `strict-type-checked` + `stylistic-type-checked`, SonarJS cognitive complexity                                                                     | Enforced |
| Tests with [Vitest](https://vitest.dev) and ≥ 90 % coverage                                                                                                                                          | Enforced |
| Small units: files ≤ 150 lines, functions ≤ 30 lines, cyclomatic complexity ≤ 8                                                                                                                      | Enforced |
| Dead-code detection with [knip](https://knip.dev): no unused files, exports or dependencies                                                                                                          | Enforced |
| [Google TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html) conventions (named exports only); Prettier formatting                                                              | Enforced |

**Security and supply chain**

| Standard                                                                                  | Status   |
| ----------------------------------------------------------------------------------------- | -------- |
| [OpenSSF Scorecard](https://scorecard.dev) published on every change to `main`            | Enforced |
| [CodeQL](https://codeql.github.com) `security-and-quality` analysis on every pull request | Enforced |
| GitHub Actions pinned by commit SHA, least-privilege workflow tokens                      | Enforced |
| Secret scanning with push protection; private vulnerability reporting                     | Enforced |
| Automated dependency updates with Dependabot                                              | Enforced |

**Process**

| Standard                                                                                                                                                             | Status   |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| Protected `main`: pull requests only, required checks (Node.js 24 and 26, CodeQL, PR title), linear history, squash merges                                           | Enforced |
| Code review following [Google's engineering practices](https://google.github.io/eng-practices/review/), with a definition of done in [CONTRIBUTING](CONTRIBUTING.md) | Adopted  |
| [Conventional Commits](https://www.conventionalcommits.org) for every commit on `main`                                                                               | Enforced |

**Planned**

| Standard                                                                                                                                       | Status  |
| ---------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| [OWASP Top 10 for LLM Applications 2025](https://genai.owasp.org/llm-top-10/) mapping and threat model                                         | Planned |
| [OpenSSF Best Practices](https://www.bestpractices.dev) badge                                                                                  | Planned |
| Signed releases with SLSA provenance and SBOM                                                                                                  | Planned |
| [OWASP ASVS 5.0](https://owasp.org/www-project-application-security-verification-standard/) level 2, before offering the product to businesses | Planned |
| Mutation testing of the accounting domain                                                                                                      | Planned |

## Getting started

Requires Node.js 24 LTS or newer and an [Enable Banking](https://enablebanking.com) application
in restricted mode with your own accounts linked and authorised (one session per bank).

Create `~/.config/caton-ai/config.json`, readable only by you (`chmod 600`). Each entry is an
**instance** of a plugin; its secret variables are never written here:

```json
{
  "instances": [
    {
      "id": "mybank",
      "title": "My bank",
      "plugin": "enable-banking",
      "settings": {"app-id": "<application id>", "session-id": "<authorised session id>"}
    }
  ]
}
```

Secrets go in an encrypted store ([ADR 0017](docs/adr/0017-secret-store.md)) as
`plugin:instance:variable`, here `enable-banking:mybank:private-key`. One secret can serve several
instances: save it under a name of your choice and enter `${that-name}` as their value
([ADR 0019](docs/adr/0019-plugin-variables-instances-and-shared-secrets.md)). The web interface
lists every secret an instance needs, with what it is and where to get it:

```sh
npm run build           # builds the web interface once
npm start -- serve      # prints a one-time link to http://127.0.0.1:7170
```

See [ADR 0018](docs/adr/0018-local-web-interface.md) for how it is protected. A configuration in
the first format (`plugins` and `connections`) is converted with `npm start -- config migrate`.

```sh
npm ci --ignore-scripts
npm start -- sync       # fetch accounts, movements and balances into the local ledger
npm start -- accounts   # accounts and latest balances
npm start -- spend 3    # money leaving your accounts per month, last 3 months (cash basis)
npm start -- status     # last sync of every connection
```

Outflows still include transfers between your own accounts; transfer detection and
categorisation are next. Unattended PSD2 access allows only a few requests per account per day
(the exact quota is still to be verified), so sync at most once or twice a day. Current limitations are tracked in [ADR 0012](docs/adr/0012-interim-local-secrets-and-ledger-storage.md).

## Ask your AI assistant (MCP)

`caton mcp` serves the ledger **read-only** over the
[Model Context Protocol](https://modelcontextprotocol.io) (specification 2026-07-28; hosts still on
2025-era versions are served too). It never contacts a bank, amounts are exact decimals, and every
answer says whether the figures are complete. See [ADR 0013](docs/adr/0013-read-only-mcp-server.md).

| Tool                 | What it answers                                                         |
| -------------------- | ----------------------------------------------------------------------- |
| `sync_status`        | When each bank connection last synced, and why it failed                |
| `list_accounts`      | Accounts and their latest balances                                      |
| `list_transactions`  | Movements by date, account, direction or text, one page at a time       |
| `monthly_outflows`   | Money that left the accounts per month (includes own-account transfers) |
| `top_counterparties` | Who received the most money                                             |

Register it in your MCP host with absolute paths; the host must start Node.js 24 or newer, which
may not be the `node` on its `PATH`. For Claude Code:

```sh
claude mcp add caton-ai -- /path/to/node24/bin/node /path/to/caton-ai/packages/cli/src/main.ts mcp
```

For hosts configured with JSON, such as Claude Desktop:

```json
{
  "mcpServers": {
    "caton-ai": {
      "command": "/path/to/node24/bin/node",
      "args": ["/path/to/caton-ai/packages/cli/src/main.ts", "mcp"]
    }
  }
}
```

Account names, descriptions and counterparties come from banks and third parties; the server
tells the assistant to treat them as data, never as instructions. With a hosted model, what the
tools return is sent to its provider.

## Development

Requires Node.js 24 LTS or newer.

```sh
npm ci --ignore-scripts
npm run check   # types, lint, format, architecture rules, dead code, web build, tests + coverage
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for the quality gates and conventions.

## Security

Please report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).

## License

[MIT](LICENSE) © César Zea