Skip to main content
Glama
SimpleFIN Bridge

SimpleFIN Bridge

by jointheroost
README.md
# SimpleFIN Bridge for Roost

An MCP server that reads your own banks through your [SimpleFIN Bridge](https://bridge.simplefin.org) account: balances and transactions, read only. It is an official Roost server: Roost downloads the release file from this repository, checks its SHA-256, and runs it on your hub. Your access URL stays on your hub. It never goes to Roost.

SimpleFIN is a read-only protocol. This server has no tool that moves money.

## Set up in Roost

1. Get a SimpleFIN Bridge account at [bridge.simplefin.org](https://bridge.simplefin.org) ($1.50 a month or $15 a year, paid by you) and connect your banks there.
2. Open [bridge.simplefin.org/simplefin/create](https://bridge.simplefin.org/simplefin/create), sign in, and make a setup token for a new app. Copy the **setup token**.
3. In Roost, open **Marketplace**, find **SimpleFIN Bridge** (it has the **By Roost** chip), and click **Connect**.
4. Paste the setup token and click **Check and save**. Roost claims the token once, keeps only the access URL that the Bridge gives back, and checks it with `list_accounts`.

A setup token works once. If the claim fails (the token was used, is wrong, or the Bridge refused it), Roost saves nothing: make a new setup token on the Bridge and paste it. If you already have an access URL, you can paste it in the same field.

## Tools

| Tool | What it does |
| --- | --- |
| `list_accounts` | Accounts with balance, available balance, currency, bank, and balance date; totals by currency; bank connection problems |
| `get_transactions` | Transactions of one, several, or all accounts between two days (at most 90), newest first; pending ones when asked; a search word; pages |
| `get_spending_summary` | Money out and money in between two days: by currency, account, category (when the bank gives one), and the top payees |

Dates are `YYYY-MM-DD` in the hub's time zone. A negative amount is money out. Every answer has `fetchedAt`, `fromCache`, `requestsToday`, and `problems`: each bank connection that needs attention, in plain words.

## Limits

The Bridge updates about once a day and expects 24 requests a day or fewer; it turns off an access URL that asks much more. So:

- Each answer is kept in memory for an hour. `list_accounts` with `refresh: true` asks again only when the kept answer is over 5 minutes old.
- A transactions call for any range inside the last 90 days asks once for the whole 90 days of every account, and later calls use that copy.
- At most 20 requests in any 24 hours (`SIMPLEFIN_MAX_REQUESTS_PER_DAY`). Past that, a call answers from the kept copy with `old: true`, or says when it can ask again.
- A 429 is not retried.
- At most 90 days for each call, as the Bridge allows.

The kept copy is only in memory. When Roost stops the server (after it is idle, or at a hub restart), the copy and the request count start again.

## Errors

A failed call returns `isError: true` and `structuredContent.error` with `class` and `message`:

| Class | Meaning |
| --- | --- |
| `auth` | The access URL is missing, refused (403), turned off, or the Bridge needs payment (402). Make a new setup token and paste it on the app page |
| `invalid` | The input is wrong, such as a range over 90 days |
| `transient` | The Bridge did not answer or had a problem. Try again later |
| `limit` | Too many requests. Wait, then try again |
| `failed` | Anything else |

No message, result, or log carries the access URL or any part of it.

## Environment

Roost sets these when it starts the server. You need them only to run it by hand.

| Variable | Default | Purpose |
| --- | --- | --- |
| `SIMPLEFIN_ACCESS_URL` | none | The access URL (with its credentials) |
| `SIMPLEFIN_MAX_REQUESTS_PER_DAY` | `20` | Requests to the Bridge in any 24 hours |
| `SIMPLEFIN_ALLOW_LOCAL_HTTP` | off | `1` lets an http access URL on this computer work. Tests only |

## Develop

```bash
bun install
bun test            # against a fake SimpleFIN Bridge; no network, no real token
bun run typecheck
bun run build       # dist/roost-mcp-simplefin.js, one file, no install step
bun run drift       # compare the public protocol page with protocol/simplefin-protocol.json
```

The server has no runtime dependencies. `@modelcontextprotocol/sdk` is only for the stdio test.

## Release

1. Set `version` in `package.json` (for example `0.1.1`), then push the tag `v0.1.1`.
2. The Release workflow tests, builds, and publishes `roost-mcp-simplefin.js` and `roost-mcp-simplefin.js.sha256` as release assets.
3. In the Roost repository, set the SimpleFIN Bridge row of `app-catalog.json` to the new tag and the SHA-256 from the `.sha256` file. Each hub downloads the new file the next time it starts the server.

## Upkeep

The Protocol drift workflow runs each Monday and fails when the protocol page differs from `protocol/simplefin-protocol.json`. Read the page's Changes section, fix the tools, then update the copy. `SOURCE.md` lists each page and the date it was read.