@desktopaccountingapi/quickbooks-desktop-mcp 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/README.md +4 -4
- package/dist/catalog.js +1 -1
- package/dist/catalog.json +10 -11
- package/dist/cli.js +1 -1
- package/dist/http.js +1 -1
- package/dist/index.js +1 -1
- package/dist/key.js +1 -1
- package/dist/server.js +1 -1
- package/dist/stdio.js +1 -1
- package/dist/tools.js +38 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## 0.1.0
|
|
4
4
|
|
|
5
|
-
First release of `@desktopaccountingapi/quickbooks-desktop-mcp`, generated from API contract sha256 `
|
|
5
|
+
First release of `@desktopaccountingapi/quickbooks-desktop-mcp`, generated from API contract sha256 `79b06eb20083` (API version 1.0.0, 275 operations).
|
|
6
6
|
|
|
7
7
|
- Local MCP server over stdio: `npx -y @desktopaccountingapi/quickbooks-desktop-mcp`. Node.js 20 or later on Windows, macOS and Linux; no other runtime and no runtime dependencies.
|
|
8
8
|
- Tools: `list_end_users`, `list_api_endpoints`, `get_api_endpoint_schema`, `invoke_api_endpoint`, `search_docs`; optional one tool per operation with `--resources`.
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
- Writes carry an idempotency key and are never retried blindly. Read-only keys are enforced by the API itself.
|
|
7
7
|
- Runs over stdio with Node.js 20 or later on Windows, macOS and Linux, with no runtime dependencies.
|
|
8
8
|
|
|
9
|
-
The current version is **0.
|
|
9
|
+
The current version is **0.3.0**. [MCP guide](https://www.desktopaccountingapi.com/docs/guides/mcp/) · [Documentation](https://www.desktopaccountingapi.com/docs/) · [Changelog](CHANGELOG.md) · [Status](https://status.desktopaccountingapi.com)
|
|
10
10
|
|
|
11
11
|
## Hosted server or local package
|
|
12
12
|
|
|
@@ -37,14 +37,14 @@ Open **Settings > Developer > Edit Config** (`claude_desktop_config.json`) and a
|
|
|
37
37
|
"mcpServers": {
|
|
38
38
|
"quickbooks-desktop": {
|
|
39
39
|
"command": "npx",
|
|
40
|
-
"args": ["-y", "@desktopaccountingapi/quickbooks-desktop-mcp@0.
|
|
40
|
+
"args": ["-y", "@desktopaccountingapi/quickbooks-desktop-mcp@0.3.0"],
|
|
41
41
|
"env": { "DAAPI_SECRET_KEY": "sk_live_..." }
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
If the file already has an `mcpServers` section, add the `quickbooks-desktop` entry inside it, then restart Claude Desktop. Drop `@0.
|
|
47
|
+
If the file already has an `mcpServers` section, add the `quickbooks-desktop` entry inside it, then restart Claude Desktop. Drop `@0.3.0` from the package name to always run the latest version.
|
|
48
48
|
|
|
49
49
|
### Claude Code
|
|
50
50
|
|
|
@@ -173,7 +173,7 @@ Clients send their own secret key as `Authorization: Bearer sk_...`; the server
|
|
|
173
173
|
## Versioning and changelog
|
|
174
174
|
|
|
175
175
|
- The package follows [semantic versioning](https://semver.org/) and is released together with the [Node.js](https://github.com/DesktopAccountingAPI/quickbooks-desktop-node), [Python](https://github.com/DesktopAccountingAPI/quickbooks-desktop-python), [.NET](https://github.com/DesktopAccountingAPI/quickbooks-desktop-dotnet) and [Java](https://github.com/DesktopAccountingAPI/quickbooks-desktop-java) SDKs, with the same version number.
|
|
176
|
-
- It is generated from the Desktop Accounting API contract (sha256 `
|
|
176
|
+
- It is generated from the Desktop Accounting API contract (sha256 `79b06eb20083...` for this release) by the same pipeline as the SDKs.
|
|
177
177
|
- Every release is listed in [CHANGELOG.md](CHANGELOG.md) and tagged `v<version>` on GitHub.
|
|
178
178
|
|
|
179
179
|
## Support
|
package/dist/catalog.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Code generated by packages/sdk-generator from packages/mcp/src. DO NOT EDIT.
|
|
2
|
-
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:
|
|
2
|
+
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:79b06eb2008381661d24d3bd5e23643483ec1bf9b70d5f4cdff2a2a71e22fe94
|
|
3
3
|
// MCP endpoint catalog: a compact, input-only view of the public OpenAPI contract that the MCP
|
|
4
4
|
// tools search, describe and invoke. Built from packages/api-contract/generated/openapi.json by
|
|
5
5
|
// scripts/api-contract.mjs (committed as packages/mcp/generated/catalog.json, drift-checked) and
|
package/dist/catalog.json
CHANGED
|
@@ -11490,13 +11490,10 @@
|
|
|
11490
11490
|
},
|
|
11491
11491
|
{
|
|
11492
11492
|
"name": "fiscalYear",
|
|
11493
|
-
"required":
|
|
11493
|
+
"required": true,
|
|
11494
11494
|
"description": "Filter by fiscal year.",
|
|
11495
11495
|
"schema": {
|
|
11496
|
-
"type":
|
|
11497
|
-
"integer",
|
|
11498
|
-
"null"
|
|
11499
|
-
]
|
|
11496
|
+
"type": "integer"
|
|
11500
11497
|
}
|
|
11501
11498
|
},
|
|
11502
11499
|
{
|
|
@@ -33128,14 +33125,15 @@
|
|
|
33128
33125
|
"request.outcome_unknown",
|
|
33129
33126
|
"request.outcome_resolved",
|
|
33130
33127
|
"connection.setup_completed",
|
|
33131
|
-
"connection.status_changed"
|
|
33128
|
+
"connection.status_changed",
|
|
33129
|
+
"connection.company_file_remarked"
|
|
33132
33130
|
],
|
|
33133
|
-
"description": "`request.succeeded`, `request.failed`, `request.canceled`: a request reached that status. `request.outcome_unknown`: a write was sent without a confirmed result. `request.outcome_resolved`: such a write was resolved. `connection.setup_completed`: an end user finished setup and the first check passed. `connection.status_changed`: the derived connection status changed (offline is announced after it holds for 30 seconds).",
|
|
33131
|
+
"description": "`request.succeeded`, `request.failed`, `request.canceled`: a request reached that status. `request.outcome_unknown`: a write was sent without a confirmed result. `request.outcome_resolved`: such a write was resolved. `connection.setup_completed`: an end user finished setup and the first check passed. `connection.status_changed`: the derived connection status changed (offline is announced after it holds for 30 seconds). `connection.company_file_remarked`: the private marker that identifies the connected company file was created, written back after the file lost it (for example a restored backup) or adopted from the file; `data.reason` is `marker_created`, `marker_restored` or `marker_adopted`.",
|
|
33134
33132
|
"x-daapi-open-enum": true,
|
|
33135
33133
|
"example": "request.succeeded"
|
|
33136
33134
|
},
|
|
33137
33135
|
"minItems": 1,
|
|
33138
|
-
"maxItems":
|
|
33136
|
+
"maxItems": 8,
|
|
33139
33137
|
"description": "Event types to deliver. At least one."
|
|
33140
33138
|
},
|
|
33141
33139
|
"description": {
|
|
@@ -33182,14 +33180,15 @@
|
|
|
33182
33180
|
"request.outcome_unknown",
|
|
33183
33181
|
"request.outcome_resolved",
|
|
33184
33182
|
"connection.setup_completed",
|
|
33185
|
-
"connection.status_changed"
|
|
33183
|
+
"connection.status_changed",
|
|
33184
|
+
"connection.company_file_remarked"
|
|
33186
33185
|
],
|
|
33187
|
-
"description": "`request.succeeded`, `request.failed`, `request.canceled`: a request reached that status. `request.outcome_unknown`: a write was sent without a confirmed result. `request.outcome_resolved`: such a write was resolved. `connection.setup_completed`: an end user finished setup and the first check passed. `connection.status_changed`: the derived connection status changed (offline is announced after it holds for 30 seconds).",
|
|
33186
|
+
"description": "`request.succeeded`, `request.failed`, `request.canceled`: a request reached that status. `request.outcome_unknown`: a write was sent without a confirmed result. `request.outcome_resolved`: such a write was resolved. `connection.setup_completed`: an end user finished setup and the first check passed. `connection.status_changed`: the derived connection status changed (offline is announced after it holds for 30 seconds). `connection.company_file_remarked`: the private marker that identifies the connected company file was created, written back after the file lost it (for example a restored backup) or adopted from the file; `data.reason` is `marker_created`, `marker_restored` or `marker_adopted`.",
|
|
33188
33187
|
"x-daapi-open-enum": true,
|
|
33189
33188
|
"example": "request.succeeded"
|
|
33190
33189
|
},
|
|
33191
33190
|
"minItems": 1,
|
|
33192
|
-
"maxItems":
|
|
33191
|
+
"maxItems": 8,
|
|
33193
33192
|
"description": "Event types to deliver. At least one."
|
|
33194
33193
|
},
|
|
33195
33194
|
"description": {
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Code generated by packages/sdk-generator from packages/mcp/src. DO NOT EDIT.
|
|
3
|
-
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:
|
|
3
|
+
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:79b06eb2008381661d24d3bd5e23643483ec1bf9b70d5f4cdff2a2a71e22fe94
|
|
4
4
|
// Local (stdio) MCP server for the Desktop Accounting API.
|
|
5
5
|
//
|
|
6
6
|
// npx -y @desktopaccountingapi/quickbooks-desktop-mcp [--read-only] [--resources invoices,customers] [--end-user-id eu_...]
|
package/dist/http.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Code generated by packages/sdk-generator from packages/mcp/src. DO NOT EDIT.
|
|
2
|
-
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:
|
|
2
|
+
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:79b06eb2008381661d24d3bd5e23643483ec1bf9b70d5f4cdff2a2a71e22fe94
|
|
3
3
|
// Streamable HTTP transport (MCP 2025-03-26 and later), stateless: each POST carries one JSON-RPC
|
|
4
4
|
// message (or a batch, for 2025-03-26 clients) and is answered with application/json. No server
|
|
5
5
|
// sessions and no server-initiated stream, so GET and DELETE return 405. Web-standard Request and
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Code generated by packages/sdk-generator from packages/mcp/src. DO NOT EDIT.
|
|
2
|
-
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:
|
|
2
|
+
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:79b06eb2008381661d24d3bd5e23643483ec1bf9b70d5f4cdff2a2a71e22fe94
|
|
3
3
|
// Runtime-agnostic MCP server core (no Node-only imports); the tool design is in tools.ts.
|
|
4
4
|
export { buildCatalog, defsFor } from './catalog.js';
|
|
5
5
|
export { handleMcpRequest } from './http.js';
|
package/dist/key.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Code generated by packages/sdk-generator from packages/mcp/src. DO NOT EDIT.
|
|
2
|
-
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:
|
|
2
|
+
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:79b06eb2008381661d24d3bd5e23643483ec1bf9b70d5f4cdff2a2a71e22fe94
|
|
3
3
|
// Local secret-key check (same rule as the API and SDKs, docs/api-conventions.md section 2):
|
|
4
4
|
// `sk_live_`/`sk_test_` + 40 base62 chars, the last 6 being the base62 CRC32 of the first 34.
|
|
5
5
|
// Rejecting a mistyped key locally keeps it from counting against the API's per-IP
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Code generated by packages/sdk-generator from packages/mcp/src. DO NOT EDIT.
|
|
2
|
-
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:
|
|
2
|
+
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:79b06eb2008381661d24d3bd5e23643483ec1bf9b70d5f4cdff2a2a71e22fe94
|
|
3
3
|
// Model Context Protocol server core: JSON-RPC 2.0 message handling for the `tools` capability.
|
|
4
4
|
// Transport-independent; stdio.ts and http.ts carry the messages. Implements the lifecycle
|
|
5
5
|
// (initialize with version negotiation, notifications/initialized, ping) plus tools/list and
|
package/dist/stdio.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Code generated by packages/sdk-generator from packages/mcp/src. DO NOT EDIT.
|
|
2
|
-
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:
|
|
2
|
+
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:79b06eb2008381661d24d3bd5e23643483ec1bf9b70d5f4cdff2a2a71e22fe94
|
|
3
3
|
// stdio transport: newline-delimited JSON-RPC on stdin/stdout (MCP specification, "stdio").
|
|
4
4
|
// Only protocol messages go to stdout; diagnostics go to stderr. Pure Node.js (no Deno, no
|
|
5
5
|
// POSIX-only features), so it runs the same on Windows, macOS and Linux.
|
package/dist/tools.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Code generated by packages/sdk-generator from packages/mcp/src. DO NOT EDIT.
|
|
2
|
-
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:
|
|
2
|
+
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:79b06eb2008381661d24d3bd5e23643483ec1bf9b70d5f4cdff2a2a71e22fe94
|
|
3
3
|
// MCP tools over the Desktop Accounting API. Runtime-agnostic (fetch and Web Crypto only), so the
|
|
4
4
|
// same code runs in the hosted Cloudflare Worker (apps/mcp) and the stdio npm package.
|
|
5
5
|
//
|
|
@@ -449,26 +449,54 @@ export class Tools {
|
|
|
449
449
|
}
|
|
450
450
|
errorResult(r, e, idempotencyKey) {
|
|
451
451
|
const err = r.json?.error;
|
|
452
|
-
if (!err)
|
|
453
|
-
|
|
452
|
+
if (!err) {
|
|
453
|
+
// A non-JSON error (edge or proxy page) says nothing about whether a write happened.
|
|
454
|
+
const write = e?.write && idempotencyKey ? ` It is unknown whether this write happened: do not send it with a new idempotency key; repeat this exact call with idempotency_key "${idempotencyKey}", which returns the original result instead of writing twice.` : '';
|
|
455
|
+
return text(`HTTP ${r.status} from the API: ${r.raw.slice(0, 2000)}${write}`, true);
|
|
456
|
+
}
|
|
454
457
|
const guidance = [];
|
|
455
458
|
const outcome = err.outcome;
|
|
459
|
+
// The request that keeps running is details.requestId (504 QBD_REQUEST_TIMEOUT, 502 outcome unknown); the
|
|
460
|
+
// error's own requestId is this HTTP call, which for a lost submit identifies nothing (Fable F-19).
|
|
461
|
+
const details = (err.details ?? {});
|
|
462
|
+
const pendingId = typeof details.requestId === 'string' ? details.requestId : null;
|
|
463
|
+
const retrieve = (id, wait) => `invoke_api_endpoint endpoint "requests.retrieve" with args {"id": "${id}"${wait ? ', "waitSeconds": 60' : ''}}`;
|
|
464
|
+
const sameKey = idempotencyKey ? ` Only ever repeat this call with idempotency_key "${idempotencyKey}"; that returns the original request instead of a second write.` : '';
|
|
456
465
|
if (err.code === 'API_KEY_READ_ONLY')
|
|
457
466
|
guidance.push('This secret key is read-only. Do not try other ways to make the change; tell the user it needs a full-access key.');
|
|
458
|
-
else if (outcome === '
|
|
459
|
-
guidance.push(`
|
|
467
|
+
else if (outcome === 'pending') {
|
|
468
|
+
guidance.push(`QuickBooks is still processing this write${pendingId ? ` (request ${pendingId})` : ''}. Do not send it again.${pendingId ? ` Wait for its result with ${retrieve(pendingId, true)}.` : ''}${sameKey}`);
|
|
469
|
+
}
|
|
470
|
+
else if (outcome === 'unknown') {
|
|
471
|
+
guidance.push(`It is unknown whether this write took effect, and the API will not find out on its own${pendingId ? ` (request ${pendingId})` : ''}. Do not send it again with a new idempotency key.${pendingId ? ` Check ${retrieve(pendingId, false)}, and check the record in QuickBooks${typeof details.externalId === 'string' ? ` (externalId ${details.externalId})` : ''} before doing anything else.` : ''}${sameKey}`);
|
|
472
|
+
}
|
|
473
|
+
else if (err.code === 'QBD_REQUEST_TIMEOUT' && pendingId) {
|
|
474
|
+
guidance.push(`QuickBooks is still working on this read. Get its result with ${retrieve(pendingId, true)} instead of repeating the call.`);
|
|
475
|
+
}
|
|
476
|
+
else if (err.retryable === true) {
|
|
477
|
+
const after = r.headers.get('Retry-After');
|
|
478
|
+
const wait = after ? ` Wait ${after} seconds first (Retry-After).` : '';
|
|
479
|
+
guidance.push(e?.write && idempotencyKey ? `Retryable: repeat with idempotency_key "${idempotencyKey}".${wait}` : `Retryable: try again shortly.${wait}`);
|
|
460
480
|
}
|
|
461
|
-
else if (err.retryable === true)
|
|
462
|
-
guidance.push(e?.write && idempotencyKey ? `Retryable: repeat with idempotency_key "${idempotencyKey}".` : 'Retryable: try again shortly.');
|
|
463
481
|
if (err.code === 'END_USER_ID_MISSING' || err.code === 'RESOURCE_MISSING')
|
|
464
482
|
guidance.push('Check end_user_id and IDs with list_end_users or a list operation.');
|
|
465
483
|
const keep = ['type', 'code', 'message', 'userFacingMessage', 'cause', 'fixes', 'outcome', 'retryable', 'param', 'details', 'requestId', 'docsUrl', 'integrationCode'];
|
|
466
484
|
const slim = { httpStatus: r.status };
|
|
485
|
+
// `cause` and `fixes` are fixed catalog text (never caller or QuickBooks data), so they go outside
|
|
486
|
+
// the envelope whose preamble says not to follow instructions in it (Fable re-review F-19).
|
|
467
487
|
for (const k of keep)
|
|
468
|
-
if (err[k] !== undefined && err[k] !== null && !(k === 'details' && Object.keys(err[k]).length === 0))
|
|
488
|
+
if (k !== 'cause' && k !== 'fixes' && err[k] !== undefined && err[k] !== null && !(k === 'details' && Object.keys(err[k]).length === 0))
|
|
469
489
|
slim[k] = err[k];
|
|
470
|
-
|
|
471
|
-
|
|
490
|
+
const catalog = [];
|
|
491
|
+
if (typeof err.code === 'string')
|
|
492
|
+
catalog.push(`Error ${err.code}${typeof err.type === 'string' ? ` (${err.type})` : ''}.`);
|
|
493
|
+
if (typeof err.cause === 'string')
|
|
494
|
+
catalog.push(`Cause: ${err.cause}`);
|
|
495
|
+
const fixes = Array.isArray(err.fixes) ? err.fixes.filter((f) => typeof f?.action === 'string') : [];
|
|
496
|
+
if (fixes.length)
|
|
497
|
+
catalog.push(`How to fix: ${fixes.map((f) => `${f.actor === 'end_user' ? 'the end user' : f.actor === 'support' ? 'support' : 'you'}: ${String(f.action)}`).join(' ')}`);
|
|
498
|
+
// Error messages and details can quote QuickBooks data (names, memos), so they get the envelope.
|
|
499
|
+
return text(`${catalog.length ? `${catalog.join('\n')}\n` : ''}${untrusted(JSON.stringify(slim))}${guidance.length ? `\n${guidance.join(' ')}` : ''}`, true);
|
|
472
500
|
}
|
|
473
501
|
async searchDocs(args) {
|
|
474
502
|
const query = String(args.query ?? '').trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desktopaccountingapi/quickbooks-desktop-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Model Context Protocol (MCP) server for Desktop Accounting API: QuickBooks Desktop for Claude, Cursor, VS Code, Codex and other AI tools.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Desktop Accounting API",
|