Skip to main content
Glama
bpmj-martin

LedgerLink MCP

by bpmj-martin
README.md
# LedgerLink MCP

An MCP server that connects AI assistants to QuickBooks Online. Manage invoices, customers, expenses, and reports through natural conversation.

> "Show me all unpaid invoices" · "Create an invoice for Acme Corp for $5,000" · "What's my P&L for last quarter?"

## What It Does

LedgerLink MCP gives AI assistants (Claude Desktop, Cursor, etc.) direct access to your QuickBooks Online data through the [Model Context Protocol](https://modelcontextprotocol.io). Instead of clicking through the QuickBooks UI, you talk to your AI assistant in plain English.

**32 tools** across 10 QuickBooks entities:

| Category | Tools |
|----------|-------|
| **Invoices** | List, get, create, send, void |
| **Customers** | List, get, create, update |
| **Bills** | List, get, create |
| **Payments** | List, record |
| **Expenses** | List, get, create |
| **Reports** | Profit & Loss, Balance Sheet, Cash Flow, AR Aging, AP Aging |
| **Accounts** | List, get, create (Chart of Accounts) |
| **Vendors** | List, get, create |
| **Items** | List, get, create (Products & Services) |
| **Company** | Get company info |

## Quick Start

### 1. Get QuickBooks Credentials

1. Create a free account at [developer.intuit.com](https://developer.intuit.com)
2. Create an app → choose "QuickBooks Online and Payments"
3. Note your **Client ID** and **Client Secret**
4. Add `http://localhost:3847/callback` as a Redirect URI (under Settings)

### 2. Authenticate

```bash
# Clone and install
git clone https://github.com/YOUR_USERNAME/ledgerlink-mcp.git
cd ledgerlink-mcp
npm install

# Authenticate with QuickBooks (opens browser)
QB_CLIENT_ID=your_id QB_CLIENT_SECRET=your_secret npx tsx bin/cli.ts auth
```

A browser window opens → log in to QuickBooks → authorize → tokens are saved to `~/.ledgerlink/tokens.json`.

### 3. Add to Claude Desktop

Edit your Claude Desktop config:

**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "ledgerlink": {
      "command": "npx",
      "args": ["tsx", "/path/to/ledgerlink-mcp/src/index.ts"],
      "env": {
        "QB_CLIENT_ID": "your_client_id",
        "QB_CLIENT_SECRET": "your_client_secret",
        "QB_ENVIRONMENT": "sandbox"
      }
    }
  }
}
```

Restart Claude Desktop. You'll see the tools icon — start talking to your books.

### 4. Try It

- "Show me all unpaid invoices"
- "Who owes me the most money?"
- "What's my profit and loss for 2024?"
- "Create an invoice for customer ID 1 for $500 consulting"
- "List all bills due this week"

## How It Works

```
You: "Show me unpaid invoices"
  ↓
Claude Desktop → LedgerLink MCP (stdio) → QuickBooks API
  ↓
Claude: "You have 4 unpaid invoices totaling $3,200..."
```

LedgerLink runs as a local process. Claude Desktop spawns it, communicates over stdio (stdin/stdout), and your QuickBooks data never leaves your machine.

## Configuration

### Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `QB_CLIENT_ID` | Yes | From Intuit developer portal |
| `QB_CLIENT_SECRET` | Yes | From Intuit developer portal |
| `QB_REDIRECT_URI` | No | Default: `http://localhost:3847/callback` |
| `QB_ENVIRONMENT` | No | `sandbox` (default) or `production` |

### Token Storage

Tokens are stored in `~/.ledgerlink/tokens.json` with owner-only permissions (chmod 600). Access tokens auto-refresh when expired (1-hour lifetime, 100-day refresh token).

## Tool Reference

### Invoices

- **`list_invoices`** — Filter by status (Unpaid/Paid/Overdue), customer, date range
- **`get_invoice`** — Full invoice details by ID
- **`create_invoice`** — Create with customer, line items, due date, memo
- **`send_invoice`** — Email an invoice to the customer
- **`void_invoice`** — Void an invoice (cannot be undone)

### Customers

- **`list_customers`** — Search by name, filter active/inactive
- **`get_customer`** — Full customer details by ID
- **`create_customer`** — Create with name, email, phone, address
- **`update_customer`** — Update existing customer fields

### Bills (Accounts Payable)

- **`list_bills`** — Filter by status, vendor, due date
- **`get_bill`** — Full bill details by ID
- **`create_bill`** — Create with vendor, line items, due date

### Payments

- **`list_payments`** — Filter by date range, customer
- **`record_payment`** — Record payment, optionally applied to an invoice

### Expenses

- **`list_expenses`** — Filter by date range, vendor
- **`get_expense`** — Full expense details by ID
- **`create_expense`** — Create with account, payment type, line items

### Reports

- **`get_profit_and_loss`** — P&L for a date range, optionally by month/week
- **`get_balance_sheet`** — Balance sheet as of a date
- **`get_cash_flow`** — Cash flow statement for a date range
- **`get_aging_receivables`** — Who owes you, grouped by aging bucket
- **`get_aging_payables`** — What you owe, grouped by aging bucket

### Chart of Accounts

- **`list_accounts`** — Filter by type (Bank, Expense, Income, etc.)
- **`get_account`** — Full account details by ID
- **`create_account`** — Create new account with type and sub-type

### Vendors

- **`list_vendors`** — Search by name, filter active/inactive
- **`get_vendor`** — Full vendor details by ID
- **`create_vendor`** — Create with name, email, phone

### Items (Products & Services)

- **`list_items`** — Search by name, filter by type (Service/Inventory)
- **`get_item`** — Full item details by ID
- **`create_item`** — Create with name, type, price, income account

### Company

- **`get_company_info`** — Company name, address, fiscal year, currency

## Development

```bash
# Install dependencies
npm install

# Run in development mode
QB_CLIENT_ID=xxx QB_CLIENT_SECRET=xxx npx tsx src/index.ts

# Test with MCP Inspector
QB_CLIENT_ID=xxx QB_CLIENT_SECRET=xxx npx @modelcontextprotocol/inspector npx tsx src/index.ts

# Type check
npx tsc --noEmit

# Build for distribution
npm run build

# Run tests
npm test
```

## Architecture

```
src/
├── index.ts                 # Entry point — wires everything together
├── auth/
│   ├── oauth.ts             # OAuth 2.0 flow (local callback server)
│   └── token-store.ts       # Token persistence (~/.ledgerlink/)
├── quickbooks/
│   ├── client.ts            # HTTP client (auth, retry, rate limiting)
│   ├── types.ts             # TypeScript types for QB entities
│   ├── invoices.ts          # Invoice API operations
│   ├── customers.ts         # Customer API operations
│   └── ...                  # (10 entity modules total)
├── tools/
│   ├── invoice-tools.ts     # MCP tool registrations for invoices
│   ├── customer-tools.ts    # MCP tool registrations for customers
│   └── ...                  # (10 tool modules total)
├── resources/
│   └── index.ts             # MCP read-only resources
└── utils/
    ├── rate-limiter.ts      # Sliding window (450 req/min)
    ├── query-builder.ts     # QB query builder with sanitization
    └── formatters.ts        # Report response formatting
```

## Security

- Tokens stored with chmod 600 (owner-only read/write)
- Client credentials passed via environment variables, never committed
- Query builder sanitizes all user input to prevent QB query injection
- All QuickBooks data stays local — no external servers, no telemetry

## License

MIT