Loading...
Loading...
QuickBooks Online MCP server providing 144 tools for CRUD operations on 29 entities and 11 financial reports via OAuth 2.0
npx skill4agent add aradotso/mcp-skills quickbooks-online-mcp-serverSkill by ara.so — MCP Skills collection.
git clone https://github.com/intuit/quickbooks-online-mcp-server.git
cd quickbooks-online-mcp-server
npm install
npm run build.envQUICKBOOKS_CLIENT_ID=your_client_id_from_intuit_developer
QUICKBOOKS_CLIENT_SECRET=your_client_secret_from_intuit_developer
QUICKBOOKS_ENVIRONMENT=sandbox
QUICKBOOKS_REFRESH_TOKEN=your_refresh_token_from_oauth_flow
QUICKBOOKS_REALM_ID=your_company_idQUICKBOOKS_ENVIRONMENTsandboxproductionQUICKBOOKS_REALM_IDQUICKBOOKS_REFRESH_TOKEN~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.json{
"mcpServers": {
"quickbooks": {
"command": "node",
"args": ["/absolute/path/to/quickbooks-online-mcp-server/dist/index.js"],
"env": {
"QUICKBOOKS_CLIENT_ID": "your_client_id",
"QUICKBOOKS_CLIENT_SECRET": "your_client_secret",
"QUICKBOOKS_REFRESH_TOKEN": "your_refresh_token",
"QUICKBOOKS_REALM_ID": "your_realm_id",
"QUICKBOOKS_ENVIRONMENT": "sandbox"
}
}
}
}http://localhost:8000/callbackcom.intuit.quickbooks.accountingimport express from 'express';
import axios from 'axios';
const app = express();
const CLIENT_ID = process.env.QUICKBOOKS_CLIENT_ID;
const CLIENT_SECRET = process.env.QUICKBOOKS_CLIENT_SECRET;
const REDIRECT_URI = 'http://localhost:8000/callback';
// Step 1: Authorization URL
app.get('/auth', (req, res) => {
const authUrl = `https://appcenter.intuit.com/connect/oauth2?` +
`client_id=${CLIENT_ID}&` +
`response_type=code&` +
`scope=com.intuit.quickbooks.accounting&` +
`redirect_uri=${encodeURIComponent(REDIRECT_URI)}&` +
`state=security_token`;
res.redirect(authUrl);
});
// Step 2: Handle callback and exchange code for tokens
app.get('/callback', async (req, res) => {
const { code, realmId } = req.query;
try {
const response = await axios.post(
'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer',
new URLSearchParams({
grant_type: 'authorization_code',
code: code as string,
redirect_uri: REDIRECT_URI,
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64'),
},
}
);
console.log('Refresh Token:', response.data.refresh_token);
console.log('Realm ID:', realmId);
res.send(`Success! Save these:\nRefresh Token: ${response.data.refresh_token}\nRealm ID: ${realmId}`);
} catch (error) {
console.error('Token exchange failed:', error);
res.status(500).send('Token exchange failed');
}
});
app.listen(8000, () => console.log('Visit http://localhost:8000/auth to start OAuth flow'));// Create a customer
const customer = await use_mcp_tool("quickbooks", "create_customer", {
DisplayName: "Acme Corporation",
GivenName: "John",
FamilyName: "Smith",
PrimaryEmailAddr: { Address: "john@acme.com" },
PrimaryPhone: { FreeFormNumber: "555-1234" },
BillAddr: {
Line1: "123 Main St",
City: "San Francisco",
CountrySubDivisionCode: "CA",
PostalCode: "94105",
Country: "USA"
}
});
// Search customers
const results = await use_mcp_tool("quickbooks", "search_customers", {
query: "SELECT * FROM Customer WHERE DisplayName LIKE '%Acme%'"
});
// Get customer by ID
const customerDetail = await use_mcp_tool("quickbooks", "get_customer", {
id: "123"
});
// Update customer
const updated = await use_mcp_tool("quickbooks", "update_customer", {
Id: "123",
SyncToken: "0", // Required for updates (from get_customer response)
DisplayName: "Acme Corporation Ltd",
sparse: true // Partial update
});// Create an invoice
const invoice = await use_mcp_tool("quickbooks", "create_invoice", {
CustomerRef: { value: "123" },
Line: [
{
Amount: 1000.00,
DetailType: "SalesItemLineDetail",
SalesItemLineDetail: {
ItemRef: { value: "1" }, // Item ID from QuickBooks
Qty: 10,
UnitPrice: 100
}
}
],
DueDate: "2025-06-30",
TxnDate: "2025-05-20"
});
// Search invoices (unpaid)
const unpaid = await use_mcp_tool("quickbooks", "search_invoices", {
query: "SELECT * FROM Invoice WHERE Balance > '0' MAXRESULTS 100"
});
// Update invoice (add a line)
const updatedInvoice = await use_mcp_tool("quickbooks", "update_invoice", {
Id: "456",
SyncToken: "1",
Line: [
{
Amount: 1000.00,
DetailType: "SalesItemLineDetail",
SalesItemLineDetail: {
ItemRef: { value: "1" },
Qty: 10,
UnitPrice: 100
}
},
{
Amount: 500.00,
DetailType: "SalesItemLineDetail",
SalesItemLineDetail: {
ItemRef: { value: "2" },
Qty: 5,
UnitPrice: 100
}
}
]
});
// Void/delete invoice
await use_mcp_tool("quickbooks", "delete_invoice", {
id: "456",
syncToken: "2"
});// Record a customer payment
const payment = await use_mcp_tool("quickbooks", "create_payment", {
CustomerRef: { value: "123" },
TotalAmt: 1000.00,
TxnDate: "2025-05-20",
Line: [
{
Amount: 1000.00,
LinkedTxn: [
{
TxnId: "456", // Invoice ID
TxnType: "Invoice"
}
]
}
]
});
// Search payments in date range
const payments = await use_mcp_tool("quickbooks", "search_payments", {
query: "SELECT * FROM Payment WHERE TxnDate >= '2025-01-01' AND TxnDate <= '2025-05-31'"
});// Create a vendor
const vendor = await use_mcp_tool("quickbooks", "create_vendor", {
DisplayName: "Office Supplies Co",
PrimaryEmailAddr: { Address: "billing@officesupplies.com" },
BillAddr: {
Line1: "456 Vendor Ave",
City: "Oakland",
CountrySubDivisionCode: "CA",
PostalCode: "94612",
Country: "USA"
}
});
// Create a bill
const bill = await use_mcp_tool("quickbooks", "create_bill", {
VendorRef: { value: "789" },
Line: [
{
Amount: 500.00,
DetailType: "AccountBasedExpenseLineDetail",
AccountBasedExpenseLineDetail: {
AccountRef: { value: "45" } // Expense account ID
}
}
],
DueDate: "2025-06-15",
TxnDate: "2025-05-20"
});
// Pay a bill
const billPayment = await use_mcp_tool("quickbooks", "create_bill_payment", {
VendorRef: { value: "789" },
TotalAmt: 500.00,
PayType: "Check",
CheckPayment: {
BankAccountRef: { value: "35" } // Bank account ID
},
Line: [
{
Amount: 500.00,
LinkedTxn: [
{
TxnId: "890", // Bill ID
TxnType: "Bill"
}
]
}
]
});const balanceSheet = await use_mcp_tool("quickbooks", "get_balance_sheet", {
start_date: "2025-01-01",
end_date: "2025-05-31",
accounting_method: "Accrual", // or "Cash"
summarize_column_by: "Month" // Daily, Week, Month, Quarter, Year
});
// Access report data
console.log(balanceSheet.Header.ReportName);
console.log(balanceSheet.Rows); // Array of report rowsconst profitLoss = await use_mcp_tool("quickbooks", "get_profit_and_loss", {
start_date: "2025-01-01",
end_date: "2025-05-31",
accounting_method: "Accrual",
summarize_column_by: "Quarter"
});
// Filter by customer
const customerPL = await use_mcp_tool("quickbooks", "get_profit_and_loss", {
start_date: "2025-01-01",
end_date: "2025-05-31",
customer: "123" // Customer ID
});const cashFlow = await use_mcp_tool("quickbooks", "get_cash_flow", {
start_date: "2025-01-01",
end_date: "2025-05-31",
summarize_column_by: "Month"
});const agedReceivables = await use_mcp_tool("quickbooks", "get_aged_receivables", {
as_of_date: "2025-05-31",
aging_method: "Current", // Current, Report_Date
num_periods: 4,
aging_period: 30 // Days per aging bucket
});
// Detailed aging
const detailedAging = await use_mcp_tool("quickbooks", "get_aged_receivables_detail", {
as_of_date: "2025-05-31",
num_periods: 4,
aging_period: 30
});const generalLedger = await use_mcp_tool("quickbooks", "get_general_ledger", {
start_date: "2025-01-01",
end_date: "2025-05-31",
accounting_method: "Accrual"
});// All active customers
SELECT * FROM Customer WHERE Active = true
// Invoices in date range
SELECT * FROM Invoice WHERE TxnDate >= '2025-01-01' AND TxnDate <= '2025-05-31'
// Unpaid invoices over $1000
SELECT * FROM Invoice WHERE Balance > '1000' AND Balance > '0'
// Vendors with email
SELECT * FROM Vendor WHERE PrimaryEmailAddr IS NOT NULL
// Limit results
SELECT * FROM Customer WHERE Active = true MAXRESULTS 50// Multiple conditions
SELECT * FROM Invoice WHERE CustomerRef = '123' AND TxnDate >= '2025-01-01' ORDER BY TxnDate DESC
// Text search (LIKE)
SELECT * FROM Customer WHERE DisplayName LIKE '%Corporation%'
// Count query
SELECT COUNT(*) FROM Invoice WHERE Balance > '0'
// Specific fields
SELECT Id, DisplayName, Balance FROM Customer WHERE Balance > '100'// Search with query
const customers = await use_mcp_tool("quickbooks", "search_customers", {
query: "SELECT * FROM Customer WHERE Active = true MAXRESULTS 100"
});
// Iterate results
customers.QueryResponse.Customer.forEach(customer => {
console.log(`${customer.DisplayName}: ${customer.Balance}`);
});Line// Sales item line (for products/services)
{
Amount: 100.00,
DetailType: "SalesItemLineDetail",
SalesItemLineDetail: {
ItemRef: { value: "1" }, // Item from QuickBooks
Qty: 1,
UnitPrice: 100.00,
TaxCodeRef: { value: "TAX" } // Optional
}
}
// Account-based expense line (for bills/purchases)
{
Amount: 50.00,
DetailType: "AccountBasedExpenseLineDetail",
AccountBasedExpenseLineDetail: {
AccountRef: { value: "45" }, // Chart of accounts ID
ClassRef: { value: "200" } // Optional class tracking
}
}
// Discount line
{
Amount: 10.00,
DetailType: "DiscountLineDetail",
DiscountLineDetail: {
PercentBased: true,
DiscountPercent: 10
}
}
// Subtotal line
{
Amount: 100.00,
DetailType: "SubTotalLineDetail",
SubTotalLineDetail: {}
}SyncToken// 1. Get current entity
const entity = await use_mcp_tool("quickbooks", "get_customer", { id: "123" });
// 2. Update with SyncToken
const updated = await use_mcp_tool("quickbooks", "update_customer", {
Id: "123",
SyncToken: entity.Customer.SyncToken, // Required!
DisplayName: "New Name",
sparse: true // Only update specified fields
});// Sparse update (only changes specified fields)
{
Id: "123",
SyncToken: "1",
DisplayName: "Updated Name",
sparse: true // Leave other fields unchanged
}
// Full update (replaces entire object)
{
Id: "123",
SyncToken: "1",
DisplayName: "Updated Name",
GivenName: "John",
FamilyName: "Smith",
// Must include ALL fields you want to keep
sparse: false // or omit sparse parameter
}// Customer reference
CustomerRef: { value: "123" } // Customer ID
// Item reference
ItemRef: { value: "45", name: "Consulting Service" } // name is optional
// Account reference
AccountRef: { value: "67" }
// Class reference (for tracking)
ClassRef: { value: "200" }
// Department reference
DepartmentRef: { value: "300" }YYYY-MM-DDTxnDate: "2025-05-20"
DueDate: "2025-06-30"
start_date: "2025-01-01"// Create an account
const account = await use_mcp_tool("quickbooks", "create_account", {
Name: "Office Expenses",
AccountType: "Expense", // Asset, Liability, Equity, Revenue, Expense
AccountSubType: "OfficeExpenses"
});
// Search accounts by type
const expenses = await use_mcp_tool("quickbooks", "search_accounts", {
query: "SELECT * FROM Account WHERE AccountType = 'Expense'"
});// Create a service item
const item = await use_mcp_tool("quickbooks", "create_item", {
Name: "Consulting Service",
Type: "Service",
IncomeAccountRef: { value: "89" }, // Revenue account
UnitPrice: 150.00
});
// Create an inventory item
const product = await use_mcp_tool("quickbooks", "create_item", {
Name: "Widget",
Type: "Inventory",
QtyOnHand: 100,
InvStartDate: "2025-01-01",
IncomeAccountRef: { value: "89" },
AssetAccountRef: { value: "90" },
ExpenseAccountRef: { value: "91" },
UnitPrice: 25.00
});
// Search items
const services = await use_mcp_tool("quickbooks", "search_items", {
query: "SELECT * FROM Item WHERE Type = 'Service' AND Active = true"
});// Create a journal entry
const journalEntry = await use_mcp_tool("quickbooks", "create_journal_entry", {
TxnDate: "2025-05-20",
Line: [
{
Amount: 1000.00,
DetailType: "JournalEntryLineDetail",
JournalEntryLineDetail: {
PostingType: "Debit",
AccountRef: { value: "35" } // Bank account
}
},
{
Amount: 1000.00,
DetailType: "JournalEntryLineDetail",
JournalEntryLineDetail: {
PostingType: "Credit",
AccountRef: { value: "89" } // Revenue account
}
}
]
});// Create a class
const projectClass = await use_mcp_tool("quickbooks", "create_class", {
Name: "Project Alpha"
});
// Create a department
const dept = await use_mcp_tool("quickbooks", "create_department", {
Name: "Marketing"
});
// Use in transaction
const invoice = await use_mcp_tool("quickbooks", "create_invoice", {
CustomerRef: { value: "123" },
Line: [
{
Amount: 1000.00,
DetailType: "SalesItemLineDetail",
SalesItemLineDetail: {
ItemRef: { value: "1" },
Qty: 1,
UnitPrice: 1000.00,
ClassRef: { value: "400" }, // Project tracking
DepartmentRef: { value: "500" } // Department tracking
}
}
]
});401 UnauthorizedInvalid tokenQUICKBOOKS_REALM_IDsandboxproduction// Check token expiration in QuickBooks API response headers
// X-Rate-Limit-* headers indicate API quota status400 Bad RequestCustomerRefSyncTokenYYYY-MM-DD// Get current entity first to see valid structure
const existing = await use_mcp_tool("quickbooks", "get_invoice", { id: "123" });
console.log(JSON.stringify(existing, null, 2));
// Use response as template for updatesSELECT * FROM CustomerCustomersWHERE DisplayName = 'Acme'WHERE TxnDate >= '2025-01-01'MAXRESULTS 1000Account, Bill, BillPayment, Class, CompanyInfo, CreditMemo, Customer,
Department, Deposit, Employee, Estimate, Invoice, Item, JournalEntry,
Payment, PaymentMethod, Purchase, PurchaseOrder, RefundReceipt,
SalesReceipt, TaxAgency, TaxCode, TaxRate, Term, TimeActivity, Transfer,
Vendor, VendorCreditend_date >= start_dateYYYY-MM-DDAccrualCash// Get company info to check fiscal year and accounting method
const companyInfo = await use_mcp_tool("quickbooks", "get_company_info", {});
console.log(companyInfo.CompanyInfo.FiscalYearStartMonth);429 Too Many Requests// The MCP server doesn't automatically retry
// Implement retry logic in your calling code
async function retryOperation(operation, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
continue;
}
throw error;
}
}
}https://your-subdomain.ngrok.io/callbackStale object error// Retry with fresh fetch
try {
const updated = await use_mcp_tool("quickbooks", "update_customer", {...});
} catch (error) {
if (error.message.includes('stale')) {
// Refetch and try again
const fresh = await use_mcp_tool("quickbooks", "get_customer", { id: "123" });
const retry = await use_mcp_tool("quickbooks", "update_customer", {
...updateData,
SyncToken: fresh.Customer.SyncToken
});
}
}# Run all tests
npm test
# Run specific test suite
npm test -- customer.test.ts
# Run with coverage
npm test -- --coveragesrc/__tests__/tools/src/__tests__/reports/