# J2 QBO MCP Server — Build Complete

**Built by:** Riv  
**Date:** 2026-05-20  
**Decision:** Option C — all writes queue-only (Tier 1), graduation rule built in  

---

## What Was Built

A custom Python MCP server (`j2-qbo`) that gives Ledger full read/write access to QuickBooks Online across J2's client book — with a hard approval gate on all writes.

**Location:** `C:\PKA\Team\Riv\j2_qbo_mcp\`

---

## File Inventory

| File | Purpose |
|---|---|
| `server.py` | Main MCP server — 22 tools, FastMCP transport |
| `queue_backend.py` | Write queue writer (local JSON, optional Sheets in Phase 2) |
| `tier_config.json` | Per-tool tier settings — edit here to graduate a tool (no code change) |
| `requirements.txt` | `mcp>=1.0.0`, `requests` — already installed |
| `mcp_config_snippet.json` | Reference for the Claude Code MCP config block |
| `write_queue.json` | Auto-created on first write — this is where Ledger's pending writes live |

**MCP registration:** `C:\PKA\.mcp.json` — `j2-qbo` entry added. Restart Claude Code to activate.

---

## Tools Available to Ledger (22 total)

### System (2)
- `list_clients` — non-chairman client slugs + display names
- `list_queue` — view pending write entries

### Read Tools (18, all direct to QBO — no queue)
- `get_report` — P&L, Balance Sheet, Cash Flow, Trial Balance, AR/AP Aging, TransactionList, GeneralLedger, and more
- `list_accounts` / `get_account` — full chart of accounts
- `list_customers` / `get_customer`
- `list_vendors` / `get_vendor`
- `list_invoices` / `get_invoice`
- `list_bills` / `get_bill`
- `list_transactions` — general ledger drill-down (date + account filters)
- `list_employees` / `get_employee`
- `list_items` / `get_item`

### Write Tools (8, all Tier 1 queue in Phase 1)
- `create_journal_entry` — stays Tier 1 indefinitely
- `create_invoice`
- `create_bill`
- `create_payment`
- `create_vendor`
- `create_customer`
- `update_transaction` — recode, memo edits (requires SyncToken)
- `void_transaction`

---

## How Writes Work (Phase 1)

1. Ledger calls a write tool (e.g. `create_journal_entry`)
2. Server validates the client (chairman fence, slug lookup)
3. Row written to `write_queue.json` with status=Pending
4. Ledger receives: `[QUEUED] Entry #N — Sheet queue.` with full payload
5. Jimmie reviews `write_queue.json` and approves
6. *(Phase 2: Apps Script worker posts approved rows to QBO automatically)*

---

## Chairman Group — Hard Fence

The following clients return an error on ALL tool calls (reads AND writes):
- `vipplay` — VIP Play TN (`chairman_group: true` in qbo_clients.json)
- Any future client added with `chairman_group: true`

Loop TV and FuzeBox AI are not yet in `qbo_clients.json` — add them with the `--chairman` flag when running `qbo_oauth_add_client.py`.

---

## Graduation Rule (Tier Promotion)

To promote a write tool from Tier 1 (queue) to Tier 2 (in-chat confirm + direct):

1. Open `C:\PKA\Team\Riv\j2_qbo_mcp\tier_config.json`
2. Change `"tier": 1` to `"tier": 2` for the target tool
3. Save — no server restart needed (tier is read per call)

Per the architecture: JEs stay Tier 1 forever. Promotion decisions belong to Jimmie.

---

## Phase 2 — Google Sheets Queue (When Ready)

To write queue rows directly to the `QBO Write Queue` Google Sheet:

1. Create a Google service account + share the Sheet with its email
2. `pip install gspread`
3. Set `QBO_QUEUE_SHEET_ID=<sheet_id>` as an env var in `.mcp.json`

The queue backend will automatically push rows to Sheets when the env var is set. Local JSON file remains as backup.

---

## Live Test Results (2026-05-20)

| Test | Result |
|---|---|
| Server loads clean (no import errors) | PASS |
| `list_clients()` returns Afton only (vipplay excluded) | PASS |
| Chairman fence blocks vipplay | PASS |
| `list_accounts(afton, Bank)` → 3 bank accounts | PASS |
| `list_accounts(afton, Accounts Receivable)` → $133,305.67 AR balance | PASS |
| `get_report(afton, ProfitAndLoss, 2026 YTD)` → 8 P&L rows | PASS |
| `create_vendor(afton, ...)` → writes to queue, NOT QBO | PASS |
| Tier config → all write tools tier=1 | PASS |

---

## Next Steps (for Larry / Jimmie)

1. **Restart Claude Code** to pick up the new `j2-qbo` MCP server from `.mcp.json`
2. **Add Loop TV + FuzeBox AI** to `qbo_clients.json` with `chairman_group=true` when those OAuth handshakes are ready
3. **Add remaining ~38 clients** via `python C:\PKA\Atlas\app\qbo_oauth_add_client.py` (one OAuth handshake per client, ~3 min each)
4. **Build Phase 2 queue executor** — Apps Script worker that polls `write_queue.json` (or Sheets) and posts approved rows to QBO

---

*Ready for Larry's review.*
