{
  "markdown": "# QuickBooks Online MCP Server\n\nModel Context Protocol (MCP) server for the [QuickBooks Online Accounting API](https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/account). Exposes 130+ tools across 22 QBO entities plus 10 financial reports for Claude and other MCP-compatible clients.\n\n## Features\n\n- **Interactive invoice card (MCP Apps, SEP-1865)**: `qbo_invoices_get` renders as a read-only interactive card in MCP Apps hosts (Claude Desktop/web) — customer, status, dates, line items, totals — neutral by default, brandable via `window.__BRAND__` injection or `MCP_BRAND_*` env vars. Non-App hosts see the same JSON payload (plus a `_card` field).\n\n## One-Click Deployment\n\n[![Deploy to DO](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/WYRE-AI/qbo-mcp/tree/main)\n\n[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/WYRE-AI/qbo-mcp)\n\n> **Note on registry auth:** This server depends only on public npm packages, so the Cloudflare and DigitalOcean cloud builders install its dependencies anonymously — no token is required for one-click deploy. (If a future release adds a private `@wyre-ai/*` dependency, you would supply a GitHub PAT with `read:packages` as a build variable — `NODE_AUTH_TOKEN` for Cloudflare Workers, a build-time `GITHUB_TOKEN` secret for DigitalOcean.)\n>\n> **Installing the published package:** The released package is published to the [GitHub Packages](https://github.com/WYRE-AI/qbo-mcp/pkgs/npm/qbo-mcp) npm registry, which requires authentication on every install (even for public packages). To install it, authenticate npm to `npm.pkg.github.com` with a GitHub PAT that has `read:packages`:\n>\n> ```bash\n> export NODE_AUTH_TOKEN=$(gh auth token)\n> npm install @wyre-ai/qbo-mcp\n> ```\n\n## Quick Start\n\n### Prerequisites\n\n- Node.js >= 20\n- QuickBooks Online OAuth2 app credentials (requires an Intuit developer account)\n\n### Install and Build\n\n```bash\nnpm install\nnpm run build\n```\n\n### Run (stdio mode)\n\n```bash\nQBO_ACCESS_TOKEN=your-access-token QBO_REALM_ID=your-realm-id npm start\n```\n\n### Run (HTTP mode)\n\n```bash\nMCP_TRANSPORT=http QBO_ACCESS_TOKEN=your-access-token QBO_REALM_ID=your-realm-id npm start\n```\n\nThe server listens on `http://0.0.0.0:8080/mcp` by default.\n\n### Docker\n\n```bash\ndocker build -t qbo-mcp .\ndocker run -p 8080:8080 \\\n  -e MCP_TRANSPORT=http \\\n  -e QBO_ACCESS_TOKEN=your-access-token \\\n  -e QBO_REALM_ID=your-realm-id \\\n  qbo-mcp\n```\n\n## Environment Variables\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `QBO_ACCESS_TOKEN` | Yes (env mode) | — | QuickBooks Online OAuth2 access token |\n| `QBO_REALM_ID` | Yes (env mode) | — | QuickBooks Online company (realm) ID |\n| `QBO_ENV` | No | `production` | API environment: `production` or `sandbox` |\n| `QBO_CREDENTIALS_FILE` | No | — | Path to a dotenv-format file re-read on every request; its `QBO_ACCESS_TOKEN` / `QBO_REALM_ID` / `QBO_ENV` override the environment variables (see [Token rotation](#token-rotation-env-mode)) |\n| `MCP_TRANSPORT` | No | `stdio` | Transport type: `stdio` or `http` |\n| `MCP_HTTP_PORT` | No | `8080` | HTTP server port |\n| `MCP_HTTP_HOST` | No | `0.0.0.0` | HTTP server bind address |\n| `AUTH_MODE` | No | `env` | Auth mode: `env` or `gateway` |\n| `MCP_BRAND_NAME` | No | — | Brand name shown on the MCP Apps invoice card (card is neutral when unset) |\n| `MCP_BRAND_LOGO_URL` | No | — | Logo URL for the invoice card |\n| `MCP_BRAND_PRIMARY_COLOR` | No | `#2563eb` | Invoice card primary color |\n| `MCP_BRAND_ACCENT_COLOR` | No | `#e5e7eb` | Invoice card accent color |\n| `MCP_BRAND_BG` | No | `#ffffff` | Invoice card background color |\n| `MCP_BRAND_TEXT` | No | `#333333` | Invoice card text color |\n\n## Authentication\n\nThe server does not handle the OAuth flow — it consumes a pre-obtained access token. Two modes:\n\n**env mode (default).** Token comes from `QBO_ACCESS_TOKEN` (or from the file named by `QBO_CREDENTIALS_FILE`, which wins when both are set). Single tenant.\n\n**gateway mode.** Token comes from per-request HTTP headers, isolated through `AsyncLocalStorage` so concurrent requests never share credentials. Set `AUTH_MODE=gateway` and send:\n\n| Header | Required | Description |\n|---|---|---|\n| `X-Qbo-Access-Token` | Yes | OAuth2 access token |\n| `X-Qbo-Realm-Id` | Yes | Company (realm) ID |\n| `X-Qbo-Environment` | No | `production` or `sandbox` (defaults to `production`) |\n\nWhen QBO rejects the access token, the server returns an MCP error whose text begins with the literal prefix `QBO_UNAUTHORIZED:`. The intended contract is that the gateway detects this prefix, refreshes the OAuth token, and retries the request.\n\n### Token rotation (env mode)\n\nQBO access tokens expire after ~60 minutes, so env-mode deployments typically rotate them with a cron job. **A rotated token in a Docker `env_file` never reaches a running container**: Docker injects `env_file` only at container *creation*, so `docker restart` keeps the old environment and the refresh loop silently becomes a no-op until calls start failing with `QBO_UNAUTHORIZED` / `Token revoked` (#63).\n\nSet `QBO_CREDENTIALS_FILE` to skip environment reinjection entirely. The server re-reads the file on every request, so a rotation takes effect immediately — no restart or recreate at all:\n\n```yaml\n# docker-compose.yml\nservices:\n  qbo-mcp:\n    image: ghcr.io/wyre-ai/qbo-mcp\n    environment:\n      MCP_TRANSPORT: http\n      QBO_CREDENTIALS_FILE: /secrets/qbo.env\n    volumes:\n      - ./secrets:/secrets:ro   # mount the DIRECTORY, not the file\n```\n\nYour refresh job then just rewrites `./secrets/qbo.env` (dotenv format: `QBO_ACCESS_TOKEN=...`, optionally `QBO_REALM_ID=...` and `QBO_ENV=...`) and is done — drop the `docker restart` from the script. Mount the containing directory rather than the file itself: tools like `sed -i` replace the file's inode, and a single-file bind mount would keep pointing at the old one. If the file is missing or unreadable, tool calls fail loudly instead of silently falling back to a stale environment token.\n\nIf you'd rather keep plain `env_file` injection, the rotation script must recreate the container — `docker compose up -d --force-recreate` — a `docker restart` is never enough.\n\n## Sandbox Testing\n\nSet `QBO_ENV=sandbox` (env mode) or `X-Qbo-Environment: sandbox` (gateway mode) to target Intuit's sandbox API at `https://sandbox-quickbooks.api.intuit.com` instead of production. Unrecognized values fail loudly (no silent fallback to production).\n\n## Available Tools\n\nTools are organized by domain. Call `qbo_navigate` with a domain name (e.g. `customers`, `vendors`, `bills`) to discover the tools in that domain. All tools are always callable — navigation is a discovery aid, not a prerequisite.\n\n### Entities (config-driven, 116 tools across 22 entities)\n\nEach entity exposes some subset of `list`, `get`, `create`, `update`, `search`. Transactional entities support `startDate`/`endDate` filtering on the list operation. Updates are sparse and require the current `SyncToken` from a prior get.\n\n**Sales workflow**\n- `qbo_customers_*` — list, get, create, search\n- `qbo_invoices_*` — list (Paid/Unpaid/Overdue status filter), get, create, send\n- `qbo_estimates_*` — list, get, create, update\n- `qbo_sales_receipts_*` — list, get, create, update\n- `qbo_credit_memos_*` — list, get, create, update\n- `qbo_refund_receipts_*` — list, get, create, update\n- `qbo_payments_*` — list, get, create\n\n**Purchase workflow**\n- `qbo_vendors_*` — list, get, create, update, search\n- `qbo_bills_*` — list, get, create, update, search\n- `qbo_bill_payments_*` — list, get, create, update\n- `qbo_vendor_credits_*` — list, get, create, update\n- `qbo_purchases_*` — list, get, create, update (point-of-sale expenses)\n- `qbo_purchase_orders_*` — list, get, create, update\n\n**Bank & money movement**\n- `qbo_deposits_*` — list, get, create, update\n- `qbo_transfers_*` — list, get, create, update\n- `qbo_journal_entries_*` — list, get, create, update (balanced debit/credit)\n\n**Products & accounts**\n- `qbo_items_*` — list, get, create, update, search (products and services)\n- `qbo_accounts_*` — list, get, create, update, search (chart of accounts)\n\n**Classification & terms**\n- `qbo_classes_*` — list, get, create, update, search\n- `qbo_departments_*` — list, get, create, update, search\n- `qbo_terms_*` — list, get, create, update, search (Net 30, etc.)\n- `qbo_payment_methods_*` — list, get, create, update, search\n\n**Tax & company**\n- `qbo_tax_codes_*` — list, get, search (read-only)\n- `qbo_tax_rates_*` — list, get, search (read-only)\n- `qbo_company_info_*` — list, get (read-only singleton)\n\n**People & time**\n- `qbo_employees_*` — list, get, create, update, search\n- `qbo_time_activities_*` — list, get, create, update (billable time)\n\n**Attachments**\n- `qbo_attachables_*` — list, get, create, update (metadata only; file upload uses a separate QBO endpoint)\n\n### Reports (10 tools)\n\n- `qbo_reports_profit_and_loss`\n- `qbo_reports_balance_sheet`\n- `qbo_reports_cash_flow`\n- `qbo_reports_trial_balance`\n- `qbo_reports_general_ledger`\n- `qbo_reports_aged_receivables`\n- `qbo_reports_aged_payables`\n- `qbo_reports_customer_sales`\n- `qbo_reports_customer_balance`\n- `qbo_reports_vendor_expenses`\n\n### Legacy expense tools (backwards compatibility)\n\n`qbo_expenses_list_purchases`, `qbo_expenses_get_purchase`, `qbo_expenses_list_bills`, `qbo_expenses_get_bill` remain available. New work should use the dedicated `qbo_purchases_*` and `qbo_bills_*` tool families, which add create/update/search.\n\n## Testing\n\n```bash\nnpm test                   # unit suite — fast, no credentials needed\nnpm run test:integration   # hits a real QBO sandbox; skipped without creds\n```\n\nThe integration suite calls one read tool per entity tier (customers, vendors, accounts, items, journal entries, company info) against Intuit's sandbox API. It only runs when both `QBO_SANDBOX_ACCESS_TOKEN` and `QBO_SANDBOX_REALM_ID` are present in the environment. CI wires these from the matching repo secrets and skips the job (with a clear notice) when they're absent — so dependabot/fork PRs don't fail.\n\n## License\n\nApache-2.0\n",
  "bytes": 10250,
  "sha": "3b480e2746dee37699ccd634ba34a649f91a6eca29fa9b0a162a75416eb81024",
  "repo_slug": "wyre-ai/qbo-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_wyre_ai_qbo_mcp_4afabe02/readme"
}