QuickBooks MCP Server: Complete Setup Guide for AI Agents
The QuickBooks Online MCP Server lets AI assistants — Claude, Cursor, or any MCP-compatible client — read and write QuickBooks Online accounting data through structured tool calls, without requiring the AI to understand raw REST API mechanics. You get customer lookup, invoice creation, expense analysis, and financial reporting as discrete, discoverable tools that an AI agent can invoke like functions.
This guide covers the full journey: developer account setup, OAuth 2.0 configuration, sandbox testing, MCP client integration, and production deployment — using only the capabilities documented by Intuit's implementation.
What Is the QuickBooks Online MCP Server?
The QuickBooks Online MCP Server is an open-source project maintained in the official Intuit GitHub organization at github.com/intuit/quickbooks-mcp-server. Intuit built and maintains this server as their first-party bridge between the Model Context Protocol ecosystem and the QuickBooks Online REST API.
It implements the Model Context Protocol specification, running as a local stdio-based server. MCP clients — Claude Desktop, Cursor, Claude Code, or a custom agent — launch the process, discover its tools via the MCP tool listing handshake, and invoke those tools using JSON-RPC 2.0 messages. The server translates each tool call into one or more authenticated QuickBooks Online REST API requests and returns structured results.
Key distinctions to understand before reading further:
- This is Intuit's own implementation — not a community wrapper, not a third-party integration.
- It targets QuickBooks Online (cloud) exclusively. QuickBooks Desktop is not supported.
- The server speaks the QBO REST API (v3, JSON format) — the same API you'd call directly, but abstracted behind MCP tool definitions.
- Authentication is OAuth 2.0 with the Intuit identity platform (accounts.intuit.com).
Architecture and Data Flow
Understanding the request lifecycle helps you debug problems and design production workflows correctly.
┌─────────────────────────────────────────────────────────────┐
│ MCP Client Layer │
│ (Claude Desktop / Cursor / Claude Code / Custom Agent) │
└───────────────────────┬─────────────────────────────────────┘
│ stdio (JSON-RPC 2.0)
│ tools/list → tools/call
┌───────────────────────▼─────────────────────────────────────┐
│ QuickBooks Online MCP Server │
│ (Node.js process, local) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Tool Router │──▶│ OAuth Client │──▶│ QBO REST Client │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
│ │ │ │ │
│ │ Token refresh HTTP/S │
│ │ (auto-managed) │ │
└─────────┼─────────────────┼──────────────────┼─────────────┘
│ │ │
│ ┌────────▼──────┐ ┌────────▼──────────────┐
│ │ Intuit OAuth │ │ QuickBooks Online API │
│ │ (accounts. │ │ (quickbooks.api. │
│ │ intuit.com) │ │ intuit.com/v3/) │
│ └───────────────┘ └───────────────────────┘
│
Structured JSON response
returned to MCP client
Request lifecycle for a get_invoice tool call:
- The MCP client sends a
tools/callJSON-RPC message withname: "get_invoice"andarguments: { invoice_id: "123" }. - The MCP Server's tool router matches the tool name and extracts arguments.
- The OAuth client checks token validity; if the access token is expired, it calls
accounts.intuit.com/oauth2/v1/tokens/bearerwith the refresh token. - The QBO REST client issues
GET /v3/company/{realmId}/invoice/123with the bearer token. - The JSON response is parsed and returned as a tool result to the MCP client.
The entire exchange happens over a single local stdio pipe. There is no HTTP server, no WebSocket, no port to open. This is important for your security model.
QuickBooks MCP Server vs QuickBooks Online API
This is the first decision you need to make. The MCP Server is not always the right tool.
| Dimension | QuickBooks MCP Server | Direct QBO REST API |
|---|---|---|
| Primary audience | AI agents, LLM applications | Backend services, traditional apps |
| Authentication | OAuth managed by server | OAuth managed by your app |
| Tool discovery | Automatic via MCP protocol | Manual — you read the docs |
| Implementation complexity | Low — configure and run | High — build HTTP client, error handling, pagination |
| Customization | Limited to exposed tools | Full API surface |
| Latency overhead | JSON-RPC + stdio + API | API only |
| Batch operations | Not designed for bulk | Can be optimized for bulk |
| Webhooks / event-driven | Not supported | Full webhook support |
| Production scalability | Single process, local stdio | Horizontally scalable |
| Audit trail | Limited | Full control over logging |
Use the MCP Server when:
- You're building an AI assistant that needs to answer accounting questions or perform bookkeeping tasks on behalf of a user.
- You want tool discovery to happen automatically so an LLM can reason about what QuickBooks operations are available.
- You're prototyping and want to avoid building an OAuth flow from scratch.
- Your use case maps well to the server's exposed tool set.
Use the QBO REST API directly when:
- You're building a traditional SaaS integration (payroll sync, e-commerce reconciliation, inventory management).
- You need webhooks, bulk imports, or API endpoints not exposed by the MCP Server.
- You need horizontal scalability or multi-tenant architecture.
- You need a custom audit trail or fine-grained error handling.
Want to analyze your API security?
Import your OpenAPI spec and generate a Security Report automatically.
Prerequisites
Before installing anything, confirm you have:
- Node.js 18 or later — The MCP Server is a Node.js application. Check with
node --version. - npm or npx — Included with Node.js.
- A QuickBooks Online account — Either a paid subscription or a developer account (free).
- An Intuit Developer account — Required to create an app and get OAuth credentials. Sign up at developer.intuit.com.
- An MCP-compatible client — Claude Desktop, Cursor, Claude Code, or a custom agent.
You do not need to clone the repository manually. The server is published to npm and runs via npx.
QuickBooks Developer Account and App Setup
This step is required even for sandbox testing. You cannot call the QBO API without OAuth 2.0 credentials, and you cannot get those without an Intuit Developer app.
Step 1: Create an Intuit Developer Account
- Go to developer.intuit.com and sign in or create an account.
- From the dashboard, click Dashboard → Create an app.
- Select QuickBooks Online and Payments as the platform.
- Give your app a name (e.g.,
my-qbo-mcp-agent). The name is internal — users won't see it during sandbox testing.
Step 2: Retrieve Your App Credentials
After creating the app:
- Open the app and navigate to Keys & credentials.
- You'll see two environments: Sandbox and Production.
- Copy the Client ID and Client Secret for the Sandbox environment.
Important: Sandbox and Production credentials are different. Never use production credentials during development — a mistake could modify real company data.
Step 3: Configure Redirect URIs
The OAuth 2.0 authorization flow requires a redirect URI. For local development:
- In the app settings, go to Redirect URIs.
- Add
http://localhost:3000/callback(or whatever port you'll use for the initial OAuth flow). - Intuit requires HTTPS for production redirect URIs.
Step 4: Set Up a Sandbox Company
Intuit provides free sandbox companies with pre-populated synthetic data:
- From the Intuit Developer dashboard, click Sandbox in the top navigation.
- Click Add sandbox company if one doesn't already exist.
- Note the Company ID (also called Realm ID) — you'll need it to configure the MCP Server.
The sandbox base URL is https://sandbox-quickbooks.api.intuit.com. The production URL is https://quickbooks.api.intuit.com. The MCP Server handles this distinction based on an environment variable.
OAuth 2.0 Authorization Flow
The QuickBooks Online MCP Server requires a valid OAuth 2.0 access token and refresh token before it can serve tool calls. These tokens are obtained through the standard Authorization Code flow — you complete this once, and the server handles token refresh automatically going forward.
Understanding QBO OAuth 2.0 Token Lifecycle
| Token | Expiry | Behavior |
|---|---|---|
| Access Token | 1 hour | Used on every API request as Bearer token |
| Refresh Token | 100 days (rolling) | Used to obtain new access tokens |
When the refresh token expires (100 days of inactivity), you must re-run the full authorization flow. Plan for this in production.
Getting Your Initial Tokens
The MCP Server does not run a built-in web server for the OAuth callback — you need to complete the authorization flow separately to obtain the initial token pair. You have two options:
Option A: Use the Intuit OAuth 2.0 Playground
- Go to developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0-playground.
- Enter your Client ID and Client Secret.
- Select scopes: at minimum
com.intuit.quickbooks.accounting. - Complete the authorization and copy the returned access token and refresh token.
Option B: Run a minimal Node.js OAuth flow
Create a temporary script to complete the authorization:
// oauth-setup.js — run once to get initial tokens
import express from 'express';
import OAuthClient from 'intuit-oauth';
const oauthClient = new OAuthClient({
clientId: process.env.QB_CLIENT_ID,
clientSecret: process.env.QB_CLIENT_SECRET,
environment: 'sandbox', // or 'production'
redirectUri: 'http://localhost:3000/callback',
});
const app = express();
// Step 1: Build the authorization URL
app.get('/auth', (req, res) => {
const authUri = oauthClient.authorizeUri({
scope: [OAuthClient.scopes.Accounting, OAuthClient.scopes.OpenId],
state: 'testState',
});
res.redirect(authUri);
});
// Step 2: Handle the callback and print tokens
app.get('/callback', async (req, res) => {
const parseRedirect = req.url;
const authResponse = await oauthClient.createToken(parseRedirect);
const tokenData = authResponse.getJson();
console.log('ACCESS_TOKEN:', tokenData.access_token);
console.log('REFRESH_TOKEN:', tokenData.refresh_token);
console.log('REALM_ID:', req.query.realmId);
res.send('Tokens printed to console. You can close this window.');
});
app.listen(3000, () => {
console.log('Visit http://localhost:3000/auth to start OAuth flow');
});
Run it:
npm install intuit-oauth express
QB_CLIENT_ID=your_client_id QB_CLIENT_SECRET=your_client_secret node oauth-setup.js
Open http://localhost:3000/auth in your browser, authorize the app, and copy the tokens from the console output.
Installation
The QuickBooks Online MCP Server is available on npm. You can run it directly via npx without a global install:
# Run directly (recommended for MCP client configuration)
npx @intuit/quickbooks-mcp-server
# Or install globally
npm install -g @intuit/quickbooks-mcp-server
Verify the package name against the official Intuit repository. The canonical source is the npm package published from
github.com/intuit/quickbooks-mcp-server. If you're unsure whether a package is official, check the MCPForge Verified Directory which flags community-verified servers alongside their source repositories.
Environment Variables
The server reads its configuration from environment variables. Set these before launching:
# Required
export QB_CLIENT_ID="your_intuit_client_id"
export QB_CLIENT_SECRET="your_intuit_client_secret"
export QB_ACCESS_TOKEN="your_oauth_access_token"
export QB_REFRESH_TOKEN="your_oauth_refresh_token"
export QB_REALM_ID="your_company_realm_id"
# Optional — defaults to 'sandbox'; set to 'production' for live data
export QB_ENVIRONMENT="sandbox"
Never hard-code these values. Never commit them to version control. We'll cover secure storage in the production section.
Verifying the Installation
Test the server manually before connecting an MCP client:
QB_CLIENT_ID=... QB_CLIENT_SECRET=... QB_ACCESS_TOKEN=... \
QB_REFRESH_TOKEN=... QB_REALM_ID=... QB_ENVIRONMENT=sandbox \
npx @intuit/quickbooks-mcp-server
The process should start and wait for JSON-RPC input on stdin. You can send a raw tools/list request to verify tool discovery:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | \
QB_CLIENT_ID=... npx @intuit/quickbooks-mcp-server
A successful response returns a JSON array of available tools.
Available MCP Tools
The following tools are exposed by the QuickBooks Online MCP Server. Each tool name corresponds to a specific QBO API operation. Arguments and response shapes follow the QuickBooks Online v3 API JSON format.
Customer Tools
get_customer — Retrieve a single customer by ID. Returns the full customer object including billing address, email, phone, and balance.
query_customers — Query customers using QBO's SQL-like query language. Supports filtering by name, email, active status, and other fields.
create_customer — Create a new customer record with display name, address, contact information, and payment settings.
update_customer — Update an existing customer. Requires the customer ID and SyncToken (optimistic concurrency control — QBO requires this to prevent conflicting writes).
Vendor Tools
get_vendor — Retrieve a vendor by ID.
query_vendors — Query vendors with filter expressions.
create_vendor — Create a new vendor record.
update_vendor — Update vendor details, requires SyncToken.
Invoice Tools
get_invoice — Retrieve a specific invoice by ID with full line item detail.
query_invoices — Query invoices by customer, date range, status (open, paid, overdue), or amount.
create_invoice — Create a new invoice with customer reference, line items, due date, and terms.
update_invoice — Modify an existing invoice. Use with caution on sent invoices.
send_invoice — Send an invoice to the customer's email address (requires email to be configured on the customer or invoice).
void_invoice — Void an invoice without deleting it — preserves the audit trail.
Payment Tools
get_payment — Retrieve a payment record by ID.
query_payments — Query payments by customer, date range, or linked invoice.
create_payment — Record a customer payment and optionally link it to one or more open invoices.
Expense and Purchase Tools
get_purchase — Retrieve a purchase (expense) record.
query_purchases — Query expenses by vendor, date, account, or payment method.
create_purchase — Create a new purchase record (expense entry).
Account Tools
get_account — Retrieve a chart-of-accounts entry by ID.
query_accounts — Query accounts by type, classification, or name. Useful for mapping expense categories before creating transactions.
Financial Reports
Important: The QuickBooks Online API exposes several report types (Profit and Loss, Balance Sheet, Trial Balance, Accounts Receivable Aging, etc.) at the
/v3/company/{realmId}/reports/endpoint. Whether specific report tools are exposed by the current version of the MCP Server should be verified against the repository's documented tool list at runtime usingtools/list. Report endpoints are notably more complex than entity endpoints and may not all be exposed.
If reports are available, they typically surface as:
get_profit_loss_report — Retrieves a Profit and Loss statement for a given date range.
get_balance_sheet_report — Retrieves a Balance Sheet snapshot.
Always confirm available tools at runtime — the tool list reflects the current server version.
MCP Client Configuration
Claude Desktop
Add the QuickBooks MCP Server to Claude Desktop's configuration file. The config file location depends on your OS:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"quickbooks": {
"command": "npx",
"args": ["-y", "@intuit/quickbooks-mcp-server"],
"env": {
"QB_CLIENT_ID": "your_client_id",
"QB_CLIENT_SECRET": "your_client_secret",
"QB_ACCESS_TOKEN": "your_access_token",
"QB_REFRESH_TOKEN": "your_refresh_token",
"QB_REALM_ID": "your_realm_id",
"QB_ENVIRONMENT": "sandbox"
}
}
}
}
After saving the config, restart Claude Desktop. Open a new conversation and you should see the QuickBooks tools available in the tool panel. Test with a simple prompt: "List my QuickBooks customers" — Claude should invoke query_customers and return results.
Security note for Claude Desktop: Credentials stored in
claude_desktop_config.jsonare readable by any process running as your user. For personal development this is acceptable; for shared machines or production scenarios, use a secrets manager and inject credentials at launch time.
Cursor
Cursor's MCP configuration follows the same pattern as Claude Desktop. In Cursor's settings, navigate to the MCP section and add a server entry:
{
"mcpServers": {
"quickbooks": {
"command": "npx",
"args": ["-y", "@intuit/quickbooks-mcp-server"],
"env": {
"QB_CLIENT_ID": "your_client_id",
"QB_CLIENT_SECRET": "your_client_secret",
"QB_ACCESS_TOKEN": "your_access_token",
"QB_REFRESH_TOKEN": "your_refresh_token",
"QB_REALM_ID": "your_realm_id",
"QB_ENVIRONMENT": "sandbox"
}
}
}
}
Cursor is particularly useful for AI-assisted development workflows where you want to query QuickBooks data while building an integration — for example, asking Cursor to inspect your sandbox invoice data structure while you write transformation code.
Claude Code
Claude Code (Anthropic's CLI-based coding assistant) supports MCP servers through its configuration mechanism. Add the QuickBooks server to your Claude Code MCP config:
# Using the Claude Code CLI config command
claude mcp add quickbooks \
--command "npx" \
--args "-y,@intuit/quickbooks-mcp-server" \
--env "QB_CLIENT_ID=your_client_id" \
--env "QB_CLIENT_SECRET=your_client_secret" \
--env "QB_ACCESS_TOKEN=your_access_token" \
--env "QB_REFRESH_TOKEN=your_refresh_token" \
--env "QB_REALM_ID=your_realm_id" \
--env "QB_ENVIRONMENT=sandbox"
Or manually in the Claude Code config file (~/.config/claude/mcp_servers.json):
{
"quickbooks": {
"command": "npx",
"args": ["-y", "@intuit/quickbooks-mcp-server"],
"env": {
"QB_CLIENT_ID": "your_client_id",
"QB_CLIENT_SECRET": "your_client_secret",
"QB_ACCESS_TOKEN": "your_access_token",
"QB_REFRESH_TOKEN": "your_refresh_token",
"QB_REALM_ID": "your_realm_id",
"QB_ENVIRONMENT": "sandbox"
}
}
}
Custom MCP Agent
If you're building a custom agent using the MCP TypeScript or Python SDK, spawn the server as a subprocess:
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
const transport = new StdioClientTransport({
command: 'npx',
args: ['-y', '@intuit/quickbooks-mcp-server'],
env: {
...process.env, // inherit environment
QB_CLIENT_ID: process.env.QB_CLIENT_ID!,
QB_CLIENT_SECRET: process.env.QB_CLIENT_SECRET!,
QB_ACCESS_TOKEN: process.env.QB_ACCESS_TOKEN!,
QB_REFRESH_TOKEN: process.env.QB_REFRESH_TOKEN!,
QB_REALM_ID: process.env.QB_REALM_ID!,
QB_ENVIRONMENT: 'production',
},
});
const client = new Client(
{ name: 'my-qbo-agent', version: '1.0.0' },
{ capabilities: {} }
);
await client.connect(transport);
// Discover available tools
const tools = await client.listTools();
console.log('Available tools:', tools.tools.map(t => t.name));
// Query invoices
const result = await client.callTool({
name: 'query_invoices',
arguments: {
query: "SELECT * FROM Invoice WHERE Balance > '0.00' ORDER BY DueDate ASC"
}
});
console.log('Open invoices:', result.content);
await client.close();
Real-World QuickBooks MCP Workflows
These are the use cases where the QuickBooks MCP Server genuinely accelerates development over building raw API integrations.
Invoice Management Automation
An AI agent handling accounts receivable could:
- Query all open invoices past due date:
query_invoiceswith a filter forBalance > 0 AND DueDate < today. - For each overdue invoice, look up the customer:
get_customerto retrieve email address. - Send invoice reminders:
send_invoiceto re-send the invoice. - Record incoming payments:
create_paymentlinked to the invoice.
In Claude Desktop, this entire workflow can be triggered with a natural language prompt: "Find all overdue invoices and send reminders to customers who haven't paid in 30+ days."
Example tool chain in a structured agent:
// 1. Get overdue invoices
const overdueInvoices = await client.callTool({
name: 'query_invoices',
arguments: {
query: `SELECT * FROM Invoice WHERE Balance > '0.00'
AND DueDate < '${new Date().toISOString().split('T')[0]}'
MAXRESULTS 100`
}
});
// 2. For each overdue invoice, send reminder
for (const invoice of overdueInvoices.content) {
await client.callTool({
name: 'send_invoice',
arguments: { invoice_id: invoice.Id }
});
console.log(`Reminder sent for invoice ${invoice.DocNumber}`);
}
Customer Lookup and Enrichment
A sales assistant integrated with Claude can look up a customer's payment history before a call:
"What's the outstanding balance for Acme Corp and when was their last payment?"
Claude would invoke:
query_customerswith name filter"Acme Corp"query_invoicesfiltered by the customer ID, ordered by datequery_paymentsfiltered by the customer ID
And synthesize a natural language summary without the salesperson touching QuickBooks.
Expense Analysis
For bookkeeping automation, an agent can categorize and analyze spending:
// Get all purchases for the current quarter
const purchases = await client.callTool({
name: 'query_purchases',
arguments: {
query: `SELECT * FROM Purchase
WHERE TxnDate >= '2025-01-01' AND TxnDate <= '2025-03-31'
MAXRESULTS 500`
}
});
// Agent can then group by account, flag uncategorized expenses,
// and surface anomalies for human review
A natural language prompt like "Summarize my Q1 expenses by category and flag anything over $5,000 that's categorized as miscellaneous" becomes feasible without any custom reporting code.
AI Accounting Assistant
The highest-leverage use case: an AI assistant embedded in an accounting workflow that can answer questions like:
- "What's our current accounts receivable balance?"
- "Which vendors have we paid the most this year?"
- "Create an invoice for TechCorp for 10 hours of consulting at $150/hour, due in 30 days."
- "Did we receive payment from Riverside LLC for last month's invoice?"
Each of these maps to one or two MCP tool calls. The LLM handles intent interpretation and response synthesis; the MCP Server handles authentication and API interaction.
Financial Reporting
If the MCP Server exposes report endpoints, an agent can pull a Profit and Loss statement:
const plReport = await client.callTool({
name: 'get_profit_loss_report',
arguments: {
start_date: '2025-01-01',
end_date: '2025-03-31',
accounting_method: 'Accrual'
}
});
Combined with an LLM, this enables natural language financial analysis: "How does our gross margin compare to the same quarter last year?" — if you store previous reports for comparison, the agent can answer this without a BI tool.
Security and Privacy Considerations
QuickBooks data is sensitive — customer PII, revenue figures, vendor relationships. Treat your MCP deployment accordingly.
OAuth Token Storage
Development: Environment variables are acceptable but don't commit them.
Production: Use a secrets manager:
# AWS Secrets Manager — retrieve at launch time
QB_ACCESS_TOKEN=$(aws secretsmanager get-secret-value \
--secret-id prod/quickbooks/tokens \
--query 'SecretString' \
--output text | jq -r '.access_token')
For HashiCorp Vault:
QB_ACCESS_TOKEN=$(vault kv get -field=access_token secret/quickbooks/tokens)
QB_REFRESH_TOKEN=$(vault kv get -field=refresh_token secret/quickbooks/tokens)
Token rotation: When the MCP Server automatically refreshes an access token, it updates the token in memory but not in your secrets manager. You need a mechanism to persist the new token. Consider wrapping the server in a process that listens for token refresh events and updates your secrets store. Alternatively, implement a refresh cron that re-runs the OAuth flow before tokens expire.
Scope Minimization
Request only the OAuth scopes your use case requires:
| Scope | Access Granted |
|---|---|
com.intuit.quickbooks.accounting | Full read/write to accounting data |
com.intuit.quickbooks.payment | Payment processing operations |
openid | User identity |
profile | User profile information |
email | User email |
For a read-only analytics assistant, you don't need write access. QuickBooks Online doesn't expose separate read-only scopes in the same way some APIs do — but you can enforce read-only at the agent layer by only registering read tools.
Network Exposure
The stdio transport is inherently local — only processes on the same machine can communicate with the MCP Server. This is a security feature. If you need to expose the server remotely:
- Wrap it in an SSE transport (see the MCP specification).
- Place it behind an authenticated reverse proxy (nginx + mTLS or Caddy with auth middleware).
- Never expose the raw MCP endpoint or OAuth credentials over an unencrypted channel.
Data Handling
Be aware that MCP tool results containing QuickBooks data pass through the LLM's context window. In a cloud-hosted LLM (like Claude API), this means financial data leaves your infrastructure. Review Anthropic's data handling policies, and consider whether sensitive fields (SSN, bank account numbers) should be filtered from tool responses before they reach the LLM context.
API Limits and Rate Limiting
The QuickBooks Online API enforces rate limits per company (realm ID):
- Standard limit: ~500 requests per minute per realm
- Report endpoints: Lower limits — reports are expensive computationally
- Throttle response: HTTP 429 with a
Retry-Afterheader
The MCP Server does not implement request throttling or retry logic at the transport layer. An AI agent that calls tools in a tight loop (e.g., fetching 1000 customer records individually instead of using a query) will hit rate limits quickly.
Best practices for rate limit management:
- Use query tools instead of get tools for bulk data.
query_customerswithMAXRESULTS 100is one API call; 100 individualget_customercalls is 100 API calls. - Implement exponential backoff in your agent orchestration layer when you receive tool errors indicating rate limiting.
- Cache frequently-read, rarely-changed data (chart of accounts, customer list) at the agent layer.
- Schedule report generation during off-peak hours if you're hitting report endpoint limits.
Common Errors and Troubleshooting
AuthenticationFailed / 401 Unauthorized
Why it happens: Access token has expired and refresh failed, or credentials are incorrect.
How to identify: Tool calls return an authentication error; server logs show 401 responses from quickbooks.api.intuit.com.
How to fix:
- Verify all four credential environment variables are set and non-empty.
- Check that
QB_REALM_IDmatches the company you authorized — tokens are realm-specific. - If the refresh token is older than 100 days, re-run the full OAuth flow.
- Verify you're using the right environment — sandbox tokens don't work against production endpoints and vice versa.
InvalidToken / Token Mismatch
Why it happens: You're using a sandbox token with QB_ENVIRONMENT=production or vice versa.
How to fix: Ensure QB_ENVIRONMENT matches the environment in which you obtained your tokens.
SyncToken Required / Business Validation Error
Why it happens: Update operations in QBO require the current SyncToken for optimistic concurrency control. If you attempt to update an entity without fetching its current SyncToken first, the API rejects the request.
How to fix: Always call get_customer, get_invoice, etc. before calling the corresponding update tool. Use the SyncToken from the fetched object in your update call.
// Correct update pattern
const customer = await client.callTool({
name: 'get_customer',
arguments: { customer_id: '123' }
});
await client.callTool({
name: 'update_customer',
arguments: {
customer_id: '123',
sync_token: customer.content.SyncToken, // required
display_name: 'Acme Corp (Updated)',
// ... other fields
}
});
Tool Not Found
Why it happens: The tool name in your agent's call doesn't match the server's registered tool names.
How to identify: MCP returns an error like "Tool 'get_invoices' not found".
How to fix: Call tools/list first and use the exact names returned. Common mistake: using plural forms when the server uses singular (get_invoice vs get_invoices).
QueryParseError
Why it happens: The QBO query language has specific syntax requirements. Common mistakes include:
- String values not quoted with single quotes:
WHERE Name = 'Acme'notWHERE Name = Acme - Dates not in YYYY-MM-DD format
- Unsupported fields in WHERE clauses (not all fields are queryable)
How to fix: Refer to the QBO Query Language documentation for supported filter fields per entity type.
Rate Limit Exceeded (429)
Why it happens: Too many API calls in a short time window.
How to fix: Implement exponential backoff in your agent loop. Wait and retry. Consider batching queries.
npx Server Fails to Start
Why it happens: Missing environment variables, incompatible Node.js version, or npm registry issues.
How to identify: The MCP client reports it couldn't connect to the server; running the npx command manually shows an error.
How to fix:
# Check Node.js version
node --version # Must be 18+
# Test with all env vars explicitly set
QB_CLIENT_ID=xxx QB_CLIENT_SECRET=xxx QB_ACCESS_TOKEN=xxx \
QB_REFRESH_TOKEN=xxx QB_REALM_ID=xxx QB_ENVIRONMENT=sandbox \
npx -y @intuit/quickbooks-mcp-server
# Clear npx cache if package resolution fails
npm cache clean --force
Production Deployment
Environment Configuration
Switch from sandbox to production:
# Production environment variables
QB_ENVIRONMENT=production
QB_CLIENT_ID=your_production_client_id # Different from sandbox
QB_CLIENT_SECRET=your_production_client_secret
QB_ACCESS_TOKEN=your_production_access_token
QB_REFRESH_TOKEN=your_production_refresh_token
QB_REALM_ID=your_production_company_realm_id
Remember: production apps require Intuit's approval. Submit your app for review from the Intuit Developer dashboard once development is complete.
Process Management
For agent infrastructure running the MCP Server as a background process:
// ecosystem.config.js (PM2)
module.exports = {
apps: [{
name: 'qbo-mcp-server',
script: 'npx',
args: ['-y', '@intuit/quickbooks-mcp-server'],
env: {
QB_ENVIRONMENT: 'production',
QB_CLIENT_ID: process.env.QB_CLIENT_ID,
QB_CLIENT_SECRET: process.env.QB_CLIENT_SECRET,
QB_ACCESS_TOKEN: process.env.QB_ACCESS_TOKEN,
QB_REFRESH_TOKEN: process.env.QB_REFRESH_TOKEN,
QB_REALM_ID: process.env.QB_REALM_ID,
},
autorestart: true,
watch: false,
max_memory_restart: '256M',
error_file: './logs/qbo-mcp-error.log',
out_file: './logs/qbo-mcp-out.log',
}]
};
Token Persistence Strategy
The most fragile part of production operation is token refresh persistence. Here's a recommended pattern using a file-based token store (replace with your secrets manager of choice):
// token-store.ts — wrap around MCP server process
import { readFileSync, writeFileSync } from 'fs';
import { SecretsManagerClient, UpdateSecretCommand } from '@aws-sdk/client-secrets-manager';
const sm = new SecretsManagerClient({ region: 'us-east-1' });
async function persistRefreshedTokens(accessToken: string, refreshToken: string) {
await sm.send(new UpdateSecretCommand({
SecretId: 'prod/quickbooks/tokens',
SecretString: JSON.stringify({
access_token: accessToken,
refresh_token: refreshToken,
updated_at: new Date().toISOString()
})
}));
console.log('Tokens persisted to Secrets Manager');
}
Monitoring
For production deployments, monitor:
- Process health: Is the MCP Server process running? Use PM2's health checks or a systemd service with restart policies.
- Token age: Alert when the refresh token is approaching 100 days old so you can proactively re-authorize.
- API error rates: Log MCP tool call failures, particularly 401 and 429 responses.
- Latency: Each tool call involves an HTTP round-trip to QBO's API. P95 latency above 3-5 seconds may indicate API issues or network problems.
For general guidance on running MCP servers in production — including health checks, logging patterns, and deployment architectures — the MCPForge production deployment guide covers these topics in depth.
Intuit App Approval for Production
Before using production QBO credentials:
- Complete your app on the Intuit Developer dashboard.
- Submit for app assessment (Intuit's review process).
- App assessment verifies your OAuth implementation, scopes requested, and data handling practices.
- Approval can take 1-3 weeks.
- Some scopes (like Payments) require additional review.
Limitations
Be explicit with your team about what the MCP Server cannot do:
Not supported:
- QuickBooks Desktop (on-premises)
- QuickBooks Self-Employed
- Bulk import/export operations (IIF files, CSV uploads)
- Webhooks and event subscriptions — you cannot subscribe to real-time changes from QBO through the MCP Server
- Attachments and document uploads
- Payroll APIs (separate product, separate authentication)
- QuickBooks Payments processing (may require additional scope/approval)
- Multi-company fan-out from a single server instance
- Offline operation — requires active internet connection to QBO API
Architectural limitations:
- stdio transport means one client at a time per process
- No built-in request caching
- No rate limit handling — your agent must manage this
- Token persistence is your responsibility
QuickBooks MCP Server Alternatives
If the Intuit MCP Server doesn't fit your use case — perhaps you need a different transport, a hosted option, or deeper integrations — several third-party alternatives exist:
Pipedream offers a hosted MCP server environment with QuickBooks Online as a pre-built integration, suitable if you want a managed, no-infrastructure approach.
CData provides MCP Server drivers for QuickBooks that expose QBO data as queryable tables, closer to a SQL-over-MCP model than the entity-based approach Intuit uses.
n8n workflows can be exposed as MCP tools using n8n's MCP node, letting you build complex QuickBooks automation pipelines triggered by AI agents.
Zapier and Coupler.io offer similar workflow-based approaches where QuickBooks actions are encapsulated in pre-built integrations that can be surfaced to AI agents.
For evaluating third-party MCP integrations, the MCPForge Verified Directory catalogues community and vendor-submitted servers with verification status, making it easier to assess maturity and trustworthiness before committing to an integration.
For most teams building an AI accounting assistant against QuickBooks Online, the Intuit-maintained server is the right starting point — it's the most directly supported path and avoids dependency on third-party infrastructure.
Best Practices Checklist
Before going to production with the QuickBooks MCP Server:
Security
- All credentials stored in a secrets manager, not in config files or environment files in version control
- OAuth scopes are limited to what's actually needed
-
.envfiles are in.gitignore - Token refresh persistence strategy is implemented
- Refresh token age is monitored
Testing
- All development and testing done against sandbox environment
- Write operations (create_invoice, create_payment) tested thoroughly in sandbox
- SyncToken flow tested for all update operations
- Rate limit behavior tested with realistic load
Operations
- Process manager configured with auto-restart
- Logging enabled for tool calls and errors
- Alert on authentication failures
- Intuit app review completed before production launch
Agent Design
- Agent implements retry with exponential backoff for 429 responses
- Agent uses query tools for bulk data, not individual get calls
- Agent does not pass raw financial data to LLM without considering data privacy requirements
- Tool result filtering implemented if sensitive fields should not reach LLM context
Verifying Your MCP Server
When you've deployed your QuickBooks MCP Server, it's worth validating that it correctly exposes its tools and responds to standard MCP protocol messages. The MCPForge Verify tool can inspect a running MCP server's tool manifest and confirm protocol compliance — useful for catching misconfigured tool definitions before an AI agent does.
Primary Sources and References
- Intuit QuickBooks Online MCP Server: github.com/intuit/quickbooks-mcp-server
- QuickBooks Online REST API Reference: developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities
- Intuit OAuth 2.0 Documentation: developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization
- QBO Query Language: developer.intuit.com/app/developer/qbo/docs/learn/explore-the-quickbooks-online-api/data-queries
- Model Context Protocol Specification: modelcontextprotocol.io/docs
- Intuit Developer Dashboard: developer.intuit.com