Skip to main content
Glama
rjpaganini

quickbooks-mcp-server

by rjpaganini
README.md
# quickbooks-mcp-server

**A single-file MCP server that gives Claude full read/write access to QuickBooks Online,
plus reconciliation tools that find the things a human scrolling a register won't.**

```console
$ qbo_health_check

  transport ...... stdio (Claude Desktop, Claude Code, claude.ai)
  api ............ QuickBooks Online v3
  tools .......... 22
  version ........ 2.0.0
  deploy ......... local, or HTTP on a Raspberry Pi behind Tailscale
```

---

## Why this exists

Bookkeeping questions are almost never one query. *"Did we double-pay that vendor?"*
means listing purchases, grouping by amount and date proximity, checking against the
bank feed, and then looking at whether the duplicate was later voided. That's a loop, and
loops are what an agent is for.

The design bet: give the model a **small set of composable tools** and let it do the
reasoning, rather than shipping one giant `reconcile()` that encodes my judgment about
what a discrepancy means.

That bet is why v2.0.0 has **22 tools, down from 45 in v1.0.0.** The first version had a
tool for every question I'd ever asked. It turned out most of those were the model
composing two primitives, so the primitives stayed and the compounds were deleted. See
[`CHANGELOG.md`](./CHANGELOG.md) for the migration map.

---

## Tools

```console
$ qbo --list-tools

  read
    qbo_company_info            qbo_get_preferences         qbo_health_check
    qbo_list_accounts           qbo_list_customers          qbo_get_customer
    qbo_list_bills              qbo_list_deposits           qbo_get_entity
    qbo_get_invoice             qbo_get_transaction         qbo_get_transfer
    qbo_get_journal_entry       qbo_get_report              qbo_account_register

  write
    qbo_create_invoice          qbo_create_payment          qbo_create_purchase
    qbo_delete_journal_entry    qbo_delete_purchase         qbo_delete_transfer
    qbo_batch_delete

  reconciliation
    qbo_detect_bank_feed_gaps       find dates where the feed silently stopped
    qbo_find_duplicate_transactions near-match scan across amount + date window
    qbo_duplicate_scan_by_account   the same, scoped to one account
    qbo_analyze_reconciliation      month-end variance with the arithmetic shown
```

The reconciliation tools are the ones worth stealing. **Bank feed gaps are the failure
mode nobody catches.** The feed stops for four days, transactions never arrive, and the
books balance perfectly against an incomplete picture. Nothing errors. You find it in
March when the year-end doesn't tie.

---

## Install

```console
$ git clone https://github.com/rjpaganini/quickbooks-mcp-server
$ cd quickbooks-mcp-server
$ pip install -r requirements.txt
$ cp .env.example .env        # then fill in your QBO app credentials
```

Register an app at [developer.intuit.com](https://developer.intuit.com) to get a client
ID and secret. You will need:

```console
  QB_REALM_ID          your company id
  QB_CLIENT_ID         from the Intuit developer app
  QB_CLIENT_SECRET     from the Intuit developer app
  QB_REFRESH_TOKEN     from the OAuth playground
  QB_ACCESS_TOKEN      refreshed automatically once seeded
  QB_SANDBOX           true while testing, set false deliberately
```

Then point your MCP client at it:

```json
{
  "mcpServers": {
    "quickbooks": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}
```

---

## Safety

**This server can delete things in live accounting data.** That is not a hypothetical.
`qbo_batch_delete` exists because cleaning up a bad import by hand is miserable.

Read [`SECURITY.md`](./SECURITY.md) before pointing it at a real company file. The short
version: start in sandbox, keep `QB_SANDBOX=true` until you've watched it work, and treat
the write and delete tools as you'd treat `rm` with a wildcard.

```console
$ pytest                       # unit tests, no network
$ pytest -m live               # smoke tests against a live sandbox company
```

---

## Notes

- Single file on purpose. `server.py` is large, but the whole thing is greppable and
  there's no import graph to hold in your head.
- Also runs as an HTTP service on a Raspberry Pi via `deploy-to-pi.sh`, which is how I
  reach it from claude.ai on a phone over Tailscale.
- No credentials, company data, or financial records are in this repository.

---

## License

MIT.