QuickBooks MCP Server
This server lets Claude Desktop answer plain-English questions about a QuickBooks Online company using live, read-only data.
Get receivables aging: who owes money, how much, and how overdue.
Get payables aging: bills owed to vendors and upcoming payment obligations.
Get profit and loss: income, expenses, and net profit over a period.
Get balance sheet: assets, liabilities, equity, and cash position.
Find customers or vendors by name, with contact details and balances.
Get company info: name, address, and fiscal year.
Run arbitrary read-only QuickBooks SELECT queries for anything else.
Describe schema: list queryable entities, fields, and query dialect rules.
Read-only by design: only GET requests are issued, with no code path that can write to QuickBooks.
Runs locally and keeps credentials outside the repository, connecting to your own sandbox or production company.
Read-only integration with QuickBooks Online, providing tools to query receivables, payables, profit and loss, balance sheet, contacts, and run arbitrary queries against the QuickBooks API.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@QuickBooks MCP ServerWho owes us money and how overdue is it?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
QuickBooks MCP server — local, read-only
Ask Claude Desktop questions about a QuickBooks Online company in plain English and get answers from the live books.
RESPONSES WILL BE DIFFERENT THAN THOSE WRITTEN HERE: EXAMPLE FIGURES ARE DERIVED FROM YOUR OWN SANDBOX
You: Who owes us money and how overdue is it?
Claude: Sandbox Company US 4768 has $5,281.52 outstanding across 20 open invoices. Most of it isn't late yet — $3,756.02 is still current. Of the $1,525.50 that is overdue, the worst is Red Rock Diner at $156.00, 65 days past due (invoice 1024, due 3 June). Kookies by Kathy ($75.00) and Bill's Windsurf Shop ($85.00) are both around 45 days out.
Everything runs on your own machine. Nothing is hosted or deployed, and no company data leaves your computer except to Claude, in the answer.
The figures above are from one particular sandbox. You'll get your own sandbox company when you sign up, with its own company number, its own customers and its own dates — so your totals and overdue days will not match these. That's expected, not a fault. Every example in this README is illustrative for the same reason.
What you can ask
Question | Tool used |
Who owes us money? How overdue? Who should we chase? |
|
What bills do we owe? What's due soon? |
|
How profitable were we this year? What did we spend on X? |
|
What do we own and owe? How much cash? |
|
What's Amy's email? Who are our customers? |
|
Which company am I connected to? |
|
Anything else in the books |
|
The aging tools compute days overdue and bucket everything (current, 1–30, 31–60, 61–90, 90+) rather than making Claude do date arithmetic on raw invoices.
Related MCP server: qbo-mcp
What "read-only" means here
The server cannot write to QuickBooks. Not "writes are switched off" — there is no code that could write:
client.pyis the only module that can reach the QuickBooks API. It exposes a single request method, the HTTP verb is a hardcoded literal"GET", and every path is checked against an allowlist of exactly two prefixes (/queryand/reports/). QuickBooks writes arePOSTs to/v3/company/{realm}/{entity}, which is neither.tests/test_readonly.pyparses the source and fails the build if a non-GET verb appears in that module, if any other module grows its own HTTP client, or if the path allowlist changes.
The honest caveat: Intuit publishes no read-only scope for accounting data.
The OAuth token this server holds is capable of writing — the restriction lives
in this code, not in the token. If you don't trust the code, don't trust the
claim; read client.py (about 200 lines) and the test that guards it.
One POST does exist, in auth.py: the OAuth token exchange. It is pinned to
Intuit's token endpoint, which is a different host from the accounting API, and
the test asserts that too.
Want to see for yourself? Every request is logged. Open the MCP log
(%APPDATA%\Claude\logs\mcp-server-quickbooks.log) and you'll see only GET
lines.
Install
Takes about 10 minutes, most of it on Intuit's website.
If someone has handed you a Client ID, Client Secret, Realm ID and refresh token from an existing connection, skip steps 3 and 4 and run
uv run qbo-mcp-importinstead. Read When someone genuinely cannot make their own account first — it works, but you give up the ability to fix your own installation.
1. Install uv
uv runs the server and handles Python and dependencies for you.
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"Close and reopen your terminal afterwards. Check it worked:
uv --version(macOS or Linux: curl -LsSf https://astral.sh/uv/install.sh | sh)
2. Get this code
git clone https://github.com/CharlesPiccioneBTP/Local-MCP-server-for-QuickBooks-Sandbox.gitRemember where you put it — you'll need the full path in step 5.
3. Create your own Intuit app
Free, no card required. Do this yourself even if a colleague has already set theirs up — sandbox companies belong to the developer account that created them, so you can't connect to someone else's. You'll get your own, with its own sample data.
Sign up at developer.intuit.com. A sandbox company with realistic sample data is created for you automatically.
My Hub → Workspaces → + → Get Started → fill in the basic details.
+ to add an app → choose QuickBooks Online (Accounting) → name it anything (e.g. "Claude read-only").
Go to Settings → Redirect URIs → Development tab → Add URI and paste this exactly:
http://localhost:8000/callbackThis is the step people get wrong. It must be
http(nothttps), with no trailing slash. If it doesn't match character-for-character, Intuit refuses the sign-in and setup fails.Open Keys & credentials and keep the tab open. You need the Client ID and Client Secret from the Development section. (Sandbox and production have different keys — use Development.)
4. Connect your company
In a terminal, from the folder you cloned into:
uv run qbo-mcp-setupIt asks for the environment (press Enter for sandbox), then your Client ID and
Client Secret. Your browser opens; sign in and choose the sandbox company. The
secret isn't shown as you type and isn't saved to your shell history.
You should see:
Saved credentials to C:\Users\you\AppData\Local\qbo-mcp\credentials.json
Verifying the connection...
Success - connected to "Sandbox Company US 4768".5. Tell Claude Desktop about it
Open Claude Desktop → Settings → Developer → Edit Config. That opens
claude_desktop_config.json. Add the quickbooks block inside mcpServers:
{
"mcpServers": {
"quickbooks": {
"command": "uv",
"args": [
"--directory",
"C:\\Users\\you\\path\\to\\Local-MCP-server-for-QuickBooks-Sandbox",
"run",
"qbo-mcp"
]
}
}
}Two things to get right:
Replace the path with your folder from step 2. On Windows use double backslashes (
\\) as shown.If Claude Desktop reports it can't find
uv, replace"uv"with its full path. Find it by runningwhere uv(Windows) orwhich uv(Mac/Linux) — it's usuallyC:\Users\you\.local\bin\uv.exe.
No credentials go in this file — that's deliberate. This file gets shared and screenshotted when people ask for help. Your secrets live in the separate credentials file from step 4.
6. Restart Claude Desktop
Fully quit and reopen it — closing the window isn't enough. Then ask:
Who owes us money and how overdue is it?
Checking it works
uv run qbo-mcp-doctorThis runs without Claude and checks the whole chain. What matters is that every
line says ok — the company name and figures will be your sandbox's, not these:
[ ok ] Credentials loaded from C:\Users\you\AppData\Local\qbo-mcp\credentials.json
environment=sandbox realm=93414576XXXXXXXX
[ ok ] Token refresh succeeded
[ ok ] Refresh token unchanged this time (rotation is periodic); file is current
[ ok ] Refresh token expires in 101 days
[ ok ] Connected to "Sandbox Company US 4768"
[ ok ] Receivables aging: 20 open invoice(s), total outstanding 5281.52
current 3,756.02
1-30 1,128.50
31-60 241.00
61-90 156.00
90+ 0.00Run this first whenever something seems wrong — it separates "the server is broken" from "Claude Desktop isn't talking to it".
Troubleshooting
"No credentials found" — Setup hasn't been run, or it ran as a different
Windows user. Run uv run qbo-mcp-setup.
The QuickBooks tools don't appear in Claude Desktop — Almost always the
config file. Check the path uses double backslashes, then confirm the file is
valid JSON (a stray comma breaks it silently). Then fully quit and reopen the
app. If it still fails, check
%APPDATA%\Claude\logs\mcp-server-quickbooks.log.
"Port 8000 is already in use" during setup — Something else is using that port. Stop it and retry. The port must match the redirect URI registered with Intuit, so changing it means changing both.
Setup opens the browser but fails after sign-in — The redirect URI doesn't
match. It must be exactly http://localhost:8000/callback in Settings →
Redirect URIs → Development.
"Intuit rejected the client credentials (401)" — Wrong keys, or production keys used for a sandbox company. Copy them again from the Development section and re-run setup.
It worked for weeks, now it says "invalid_grant" — The connection needs
re-authorising. This happens if the token expires (see below), if it was revoked
in QuickBooks, or if two copies of the server fought over a refresh. Fix:
uv run qbo-mcp-setup.
Keeping it working
Access tokens last 1 hour and the server refreshes them automatically. The refresh token rotates roughly daily, and Intuit invalidates the old one immediately, so the new value is written to disk atomically, under a cross-process lock, before it's used. If two copies of the server start at once, the second adopts the first's tokens instead of refreshing again — which would otherwise disconnect both.
The refresh token itself is currently good for ~101 days from setup, and
that window resets every time it's used. So the server keeps working
indefinitely as long as it's used occasionally. If it goes unused for that long,
re-run uv run qbo-mcp-setup. The doctor warns you when fewer than 30 days
remain.
Where things live
Credentials |
|
Claude Desktop config |
|
Server logs |
|
Credentials are stored outside this repository so they cannot be committed by
accident. That's the actual protection — .gitignore is a backstop. Setup will
refuse outright to write them anywhere inside a git repository. The file is
locked to your user account (icacls on Windows, mode 600 elsewhere).
Rolling it out to colleagues
Send them this repository and point them at the install steps above. There is nothing to hand over besides the link — no keys, no config, no credentials file.
Each person follows the whole guide, including creating their own Intuit developer account. They get their own sandbox company, their own app, and their own Client ID and Secret, so there is nothing to share and nothing to keep secret between you. That is the intended setup, not a workaround.
Never copy a credentials.json between machines, even though it might look
like a shortcut. Two people sharing one credentials file will fight over the
refresh token — Intuit invalidates the old value each time it rotates, so
whoever refreshes second gets logged out, and eventually both do. Each person
needs their own OAuth grant.
When someone genuinely cannot make their own account
There is a second entry point for the case where a colleague must be got working without an Intuit account of their own — evaluating the tool before committing to accounts, or a non-technical user who should not be walked through a developer signup:
uv run qbo-mcp-importIt asks for the Client ID, Client Secret, Realm ID and refresh token of an
existing connection and writes the same credentials file qbo-mcp-setup writes.
No code path differs after that: the file format, the refresh logic and the
read-only guarantee are identical. The only thing skipped is the OAuth flow that
would have produced those four values.
Know what you are trading away before using it:
A refresh token works on one machine at a time. This is the same rotation problem as copying
credentials.json, because it is the same token. Whoever supplies it must stop using it. If both machines refresh, the second one is disconnected permanently.The recipient cannot repair their own installation. Every other failure in this README is fixed by re-running setup. They can't — that needs an account. Every outage becomes a request to whoever issued the token, and outages are guaranteed eventually: the refresh token expires after ~100 days of disuse.
Access tokens are not refresh tokens. The access token is the value people reach for, because it is the one that visibly works in
curl. It lasts an hour and cannot be renewed.importer.pydetects a pasted JWT or authorization code and says so, rather than failing opaquely three steps later.
For anyone who will use this for more than a trial, their own account remains the right answer. It takes about five minutes and removes every bullet above.
Tested end to end on Windows 11 only. The macOS and Linux credential paths
are covered by tests, so they resolve to the right place, but nobody has run the
real OAuth flow on those platforms yet. If a colleague is the first Mac user,
expect to iron something out — most likely the browser handoff during setup, or
file permissions on the credentials directory
(~/Library/Application Support/qbo-mcp/). Please report back if so.
Using it with a real company
This is built and tested against a sandbox. The code will work unchanged
against live books — answer production at the setup prompt and use Production
keys — but there is a real gate in front of you:
Intuit requires an app assessment before issuing production keys. Every app that touches production data must complete a legal, technical and security questionnaire and be approved by Intuit's security team. This applies whether or not the app is listed in the QuickBooks app store, and whether or not it is only ever used internally on your own company's books. Budget about an hour for the questionnaire, plus review time, and expect to supply your host domain, launch URL, disconnect URL, and the countries and IP addresses the app runs from — questions that assume a hosted web app and take some thought to answer for something that runs on a laptop.
Two other things worth deciding before you point this at real books:
Everything the tools return is sent to Claude in order to answer your question. Real customer names, balances and financial statements leave your machine. Whether that's acceptable is a policy call, not a technical one.
The read-only guarantee is unchanged — same code, same test — but the stakes of the honest caveat above go up. On real books, "the token could write, the code cannot" is worth someone else reading
client.pyto confirm.
For developers
uv run --group dev pytest # 101 tests, no network or credentials neededModule | Role |
| The only route to the QuickBooks API. GET-only, path-allowlisted. |
| OAuth setup and refresh. Holds the package's only POST. |
| Adopts a connection someone else authorised. No network of its own. |
| Credential storage: atomic writes, cross-process lock, git refusal. |
| QuickBooks operations, callable without an MCP session. |
| MCP tool definitions and descriptions. |
|
|
| Aging arithmetic, report flattening. |
| Static entity reference for |
Notes on design decisions worth knowing before changing things:
Tools raise plain exceptions, never
MCPError. In this SDK a plain exception becomes a tool error whose message the model reads and can act on;MCPErrorbecomes a protocol error the model never sees. Every failure here is one the user needs told about.AR aging is computed from
Invoicerows, not Intuit's aging report. Invoice rows already embedCustomerRef.name, so no join is needed, and it keeps the bucket boundaries under our control. Their Reports API is used for the financial statements, where the aggregation genuinely has to come from QuickBooks.describe_schemais a tool, not an MCP resource, even though resources fit static reference data better. Claude Desktop requires resources to be attached by hand, so it would never be read — andrun_querywithout it is guesswork.The QuickBooks query language is not SQL. No
JOIN,GROUP BY,OR,HAVING, or!=.qbo_sql.pycatches these and explains the workaround rather than passing them through to an opaque Intuit 400.minorversionis pinned (currently 75). Intuit changes response shapes between minor versions; an unattended server shouldn't have its output shift underneath it.
License
MIT — see LICENSE.
Available Tools
8 toolsdescribe_schemaA
Which QuickBooks entities and fields can be queried, and the dialect rules.
Call this before writing a run_query statement. With no argument it lists every queryable entity and available report; with an entity name it returns that entity's key fields. Costs no API call.
Args: entity: Entity name such as "Invoice" or "Customer". Omit for the overview.
| Name | Required | Description | Default |
|---|---|---|---|
| entity | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden. It discloses that the call costs no API call and details both invocation modes (overview vs. entity-specific fields). Although it does not explicitly state read-only, the cost disclosure and behavior description provide strong transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and well-structured: a summary line, a usage directive, behavior details, cost note, and an args section. Every sentence provides necessary information with no wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
This is a simple tool with one optional parameter and an output schema. The description covers invocation modes, cost, and the nature of the returned data. The output schema handles return format details, so nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even though the schema coverage metric is 0%, the description includes an Args section explaining the 'entity' parameter with concrete examples ('Invoice', 'Customer') and instructs to omit it for the overview. This adds significant meaning beyond the schema's simple default null.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that the tool identifies which QuickBooks entities and fields can be queried and explains dialect rules. It differs from the sibling query tools by being a preparatory metadata tool, and explicitly instructs to call it before run_query.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit context: 'Call this before writing a run_query statement.' It also describes behavior with and without an argument. However, it does not mention when not to use it or name alternatives, so it stops short of a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_contactsA
Look up customers or vendors, with contact details and balances.
Use to find someone by partial name, to list who the business deals with, or to get an email or phone number. The balance returned is that contact's total outstanding; for invoice-level detail and overdue days use get_receivables_aging.
Args: name_contains: Case-insensitive partial name. Omit to list everyone. kind: "customer" or "vendor". include_inactive: Include archived records. Defaults to active only. limit: Maximum records to return (capped at 500).
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | customer | |
| limit | No | ||
| name_contains | No | ||
| include_inactive | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It adds meaningful context: the balance is the total outstanding (not invoice-level), the name_contains is case-insensitive, include_inactive defaults to active only, and limit is capped at 500. It also clarifies that omitting name_contains lists everyone. While it doesn't explicitly state that the operation is read-only, 'Look up' implies this, and the added semantics go beyond what the schema provides. A small gap is the lack of details on potential errors or authentication requirements, but for a simple lookup tool this is reasonably transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and concise. It starts with a clear one-sentence summary, then a brief usage sentence, a sentence providing contextual detail about the balance, and finally a neatly formatted Args list. Every sentence adds value, and the parameter documentation is easy to scan. No unnecessary fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that the tool has four optional parameters and no annotations, the description covers all essential aspects: purpose, usage, parameter semantics, and a key behavioral nuance (balance is total outstanding). It also directs users to an alternative for more detailed aging information. Since an output schema exists, the description doesn't need to explain return values. This is a complete and self-sufficient description for effective tool selection and invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero description coverage, so the description must compensate, and it does so thoroughly. The Args section explains each parameter: name_contains as a case-insensitive partial name with the behavior when omitted, kind as 'customer' or 'vendor', include_inactive as toggling archived records with a default of active only, and limit with a maximum cap. This adds substantial meaning beyond the raw schema defaults and names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Look up customers or vendors, with contact details and balances.' It specifies the resource (contacts) and the action (look up), and further elaborates with concrete use cases like finding by partial name, listing all business contacts, or retrieving email/phone. It distinguishes from the sibling tool get_receivables_aging by explicitly mentioning it as an alternative for invoice-level detail, which helps with sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use this tool ('Use to find someone by partial name, to list who the business deals with, or to get an email or phone number') and provides an alternative: 'for invoice-level detail and overdue days use get_receivables_aging.' This is clear, actionable guidance on both when to use this tool and when to choose a different one.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_balance_sheetA
Assets, liabilities and equity at a point in time.
Use for questions about what the business owns and owes overall, cash position, or financial standing. For "who owes us money" specifically, get_receivables_aging gives a far more useful breakdown than the single receivables line on this report.
Args: as_of: Balance sheet date, YYYY-MM-DD. Defaults to today. accounting_method: "Accrual" or "Cash". Defaults to the company setting.
| Name | Required | Description | Default |
|---|---|---|---|
| as_of | No | ||
| accounting_method | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It conveys the point-in-time nature and parameter defaults, which is helpful, but does not explicitly state that the operation is read-only, mention data freshness, or disclose any potential limitations. For a simple financial report, this is adequate but not richly transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded with the core definition. The usage guidance is a single sentence, and the args list is minimal. Every sentence provides necessary value with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity and the presence of an output schema, the description is complete. It covers purpose, when to use, an alternative, and both parameters with defaults. No important context seems missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description fully compensates. It documents both parameters with format and defaults: 'as_of: Balance sheet date, YYYY-MM-DD. Defaults to today' and 'accounting_method: "Accrual" or "Cash". Defaults to the company setting.' This adds significant meaning beyond the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns 'Assets, liabilities and equity at a point in time,' which is a specific and accurate definition of a balance sheet. It distinguishes itself from siblings like get_profit_and_loss (period-based) and get_receivables_aging (specific breakdown) by describing its point-in-time nature.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance is provided: 'Use for questions about what the business owns and owes overall, cash position, or financial standing.' It also names an alternative, get_receivables_aging, for a more specific need, fulfilling the 'when/when-not' requirement perfectly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_company_infoA
Name, address and fiscal year of the connected QuickBooks company.
Useful for confirming which company the answers refer to, and for finding the fiscal year start before asking for period reports.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
There are no annotations, so the description must carry the behavioral disclosure burden. It does not explicitly state that the operation is read-only, nor does it mention side effects, permissions, or error behavior. The name and data fields imply a non-destructive getter, but the description itself adds no behavioral transparency beyond the output data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loads the core output, and then provides practical usage context. Every sentence earns its place, with no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and zero parameters, the description sufficiently covers what the tool does and when to use it. The output schema can define the exact return fields, while the description adds the essential 'why' context. No critical information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so parameter semantics are not applicable. The schema already shows an empty object, and the description does not need to add parameter information. The baseline of 4 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns company metadata (name, address, fiscal year) for the connected QuickBooks company, and the tool name 'get_company_info' reinforces the getter action. It is naturally distinct from sibling reporting/search tools, but it does not explicitly contrast with them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit use cases: confirming which company answers refer to and finding the fiscal year start before requesting period reports. It does not mention when not to use it, but the guidance is clear and contextually useful.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_payables_agingA
Who the business owes money to, and how overdue each bill is.
The accounts-payable mirror of get_receivables_aging: use it for unpaid bills, money owed to suppliers or vendors, and upcoming payment obligations. Same shape of answer, aged into the same buckets.
Args: as_of: Date to age against as YYYY-MM-DD. Defaults to today. min_days_overdue: Only include bills at least this many days late. vendor: Case-insensitive substring to limit results to one vendor.
| Name | Required | Description | Default |
|---|---|---|---|
| as_of | No | ||
| vendor | No | ||
| min_days_overdue | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It describes the output conceptually but does not disclose side effects, data freshness, or exceptions. The reference to 'same shape of answer' adds some context but remains vague about the exact response format.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact—three sentences cover purpose, usage, and output shape, followed by a terse Args block. There is no padding or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a simple 3-parameter tool and an available output schema, the description covers purpose, usage, parameters, and the relationship to a sibling. It is complete enough for an agent to select and invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite the schema having no per-parameter descriptions, the tool description's Args section explains as_of, min_days_overdue, and vendor with format and default behavior. This gives the agent the exact meaning and usage for each parameter, fully compensating for the schema gaps.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence 'Who the business owes money to, and how overdue each bill is' clearly states the purpose with a specific verb and resource. It also distinguishes the tool from its sibling get_receivables_aging by labeling it as the accounts-payable mirror.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells when to use it: 'use it for unpaid bills, money owed to suppliers or vendors, and upcoming payment obligations.' It also signals the alternative by mentioning get_receivables_aging as the mirror, providing clear exclusion boundaries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_profit_and_lossA
Income, expenses and net profit over a period.
Use for questions about revenue, sales totals, expenses, spending, margins or whether the business is profitable. Defaults to the current fiscal year to date when no dates are given.
Args: start_date: Period start, YYYY-MM-DD. end_date: Period end, YYYY-MM-DD. accounting_method: "Accrual" or "Cash". Defaults to the company setting.
| Name | Required | Description | Default |
|---|---|---|---|
| end_date | No | ||
| start_date | No | ||
| accounting_method | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses important defaults: current fiscal year to date when no dates are given, and accounting method defaulting to the company setting. This is meaningful behavioral context beyond what the schema shows.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and well-structured: a clear purpose statement, usage guidance, default behavior, and a parameter list. Every sentence adds value, with no fluff or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has 3 optional parameters and an output schema, so the description does not need to explain return values. It fully covers purpose, when to use, parameter semantics, and defaults, making it complete for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only types and defaults with 0% description coverage, but the description's Args section explains each parameter fully: start_date and end_date with YYYY-MM-DD format, and accounting_method with allowed values 'Accrual' or 'Cash' plus default behavior. This compensates completely for the schema gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool retrieves income, expenses, and net profit over a period, and explicitly lists use cases such as revenue, sales totals, expenses, spending, margins, and profitability. This distinguishes it from sibling tools like get_balance_sheet and aging reports.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit guidance on when to use the tool ('Use for questions about revenue, sales totals, expenses, spending, margins, or whether the business is profitable'). It does not name alternative tools or provide exclusion criteria, but the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_receivables_agingA
Who owes the business money, and how overdue each amount is.
Use this for any question about unpaid customer invoices, accounts receivable, overdue accounts, collections, or who is behind on payment. Returns every open invoice with the customer name, due date, days overdue and outstanding balance; per-customer totals ranked worst-first; and totals per aging bucket (current, 1-30, 31-60, 61-90, 90+ days).
Args: as_of: Date to age against as YYYY-MM-DD. Defaults to today. min_days_overdue: Only include invoices at least this many days late. 0 (the default) includes invoices that are not yet due. customer: Case-insensitive substring to limit results to one customer.
| Name | Required | Description | Default |
|---|---|---|---|
| as_of | No | ||
| customer | No | ||
| min_days_overdue | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses the query behavior in detail, including what it returns (every open invoice with specific fields, per-customer totals, aging buckets) and the effect of parameters. Since no annotations are provided, this information is essential; it doesn't mention side effects or auth, but for a reporting tool this is sufficient.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and well-structured, leading with purpose and usage, then listing parameters with clear explanations. Every sentence adds value, and the arg format is easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers all parameter semantics, returns detailed information about the output, and provides usage context. With an output schema also present, the tool is fully specified for an agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although the input schema only provides types and defaults, the description fully explains each parameter's semantics: as_of with date format and default, min_days_overdue with inclusion logic, and customer as a case-insensitive substring. This compensates for the 0% schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose ('Who owes the business money, and how overdue each amount is') and explicitly maps it to use cases like unpaid invoices and overdue accounts. It distinguishes from siblings by focusing on receivables ('unpaid customer invoices') rather than payables or other financial reports.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Use this for any question about unpaid customer invoices, accounts receivable, overdue accounts, collections, or who is behind on payment.' This provides clear when-to-use guidance. It doesn't mention alternatives but the scope is so well-defined that exclusion is implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_queryA
Run a read-only QuickBooks query for anything the other tools miss.
Only SELECT statements are accepted, and this server can only issue read requests, so no query can modify data. The language is a restricted subset of SQL: no JOIN, GROUP BY, OR, or aggregate functions. Call describe_schema first to check entity and field names, and prefer the purpose-built tools where one fits -- they do arithmetic this cannot.
Example: SELECT * FROM Invoice WHERE TxnDate >= '2026-01-01'
Args: query: A QuickBooks SELECT statement.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses behavior: read-only, only SELECT statements, restricted SQL subset (no JOIN, GROUP BY, OR, aggregates), and that the server cannot modify data. These are critical limitations an agent must know, and they are clearly stated upfront.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the main purpose, followed by essential constraints and an example. Every sentence adds value with no filler. The structure is logical: purpose, restrictions, guidance, example, parameter description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema exists, so return value details aren't required. The description covers purpose, usage, prerequisites, constraints, and parameter semantics — everything an agent needs to invoke the tool correctly in context. It is complete without being verbose.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, but the description fully compensates by explaining 'query' as a QuickBooks SELECT statement, detailing the allowed subset, and providing a concrete example. This adds substantial meaning beyond the bare schema type of 'string'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Run a read-only QuickBooks query' with a specific verb and resource. It explicitly positions itself as a catch-all for 'anything the other tools miss', distinguishing it from the purpose-built siblings. The scope and restrictions are immediately clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to 'call describe_schema first' and to 'prefer the purpose-built tools where one fits' — providing clear when-to-use and when-not-to-use guidance. This goes beyond minimal naming of alternatives by explaining why (they do arithmetic this cannot).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
8 tool updates
v0.1.0- First observed
describe_schema - First observed
find_contacts - First observed
get_balance_sheet - First observed
get_company_info - First observed
get_payables_aging - First observed
get_profit_and_loss - First observed
get_receivables_aging - First observed
run_query
TDQS
Scored across 8 tools
Each tool targets a distinct domain area: receivables, payables, profit/loss, balance sheet, company info, contacts, querying, and schema discovery. Even the two aging tools are clear mirrors for AR vs AP. No overlap or ambiguity.
Most tools follow a consistent get_<noun> pattern, with find_contacts as a slight deviation and run_query/describe_schema as verb_noun. All are lowercase snake_case, so the pattern is predictable and readable.
8 tools is well-scoped for a read-only QuickBooks reporting server. Each tool has a distinct purpose, and the count is neither too thin nor overwhelming.
The server covers core financial statements, aging reports, contact lookup, company info, and a flexible query tool with schema discovery. This provides comprehensive coverage for typical accounting questions, with run_query bridging any remaining gaps.
Maintenance
Related MCP Connectors
Read-only MCP server for ClassQuill, a tutoring-business-management platform.
- mcpOAuthcom.gibsonai
GibsonAI MCP server: manage your databases with natural language
An MCP server that provides read access to your cloud storage providers, bank accounts and more.
Hosted MCP server for Mini Accountant: invoices, expenses, customers, analytics, tax estimates.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA secure, local-first MCP server that enables users to query QuickBooks data such as accounts, bills, and customers using natural language within Claude Desktop. It provides a direct interface to the QuickBooks API for streamlined financial data retrieval and management.10-
- AlicenseAqualityDmaintenanceMCP server for QuickBooks Online providing read-only access to customers, vendors, invoices, bills, and chart of accounts. Enables natural language queries to your financial data through Claude or any MCP client.8MIT
- AlicenseNot gradedqualityBmaintenanceA local MCP server that exposes QuickBooks Online data and actions as callable tools for AI assistants.MIT
- AlicenseNot gradedqualityAmaintenanceA local MCP server that exposes QuickBooks Online data and actions as callable tools for AI assistants, supporting entities like customers, invoices, bills, and financial reports.4MIT