@countfinancial/cli 0.1.0 → 0.1.2

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.
Files changed (54) hide show
  1. package/README.md +5 -28
  2. package/dist/__tests__/localCallbackServer.test.js +20 -0
  3. package/dist/__tests__/localCallbackServer.test.js.map +1 -1
  4. package/dist/__tests__/mcpLauncher.test.d.ts +1 -0
  5. package/dist/__tests__/mcpLauncher.test.js +40 -0
  6. package/dist/__tests__/mcpLauncher.test.js.map +1 -0
  7. package/dist/commands/mcp.command.js +1 -3
  8. package/dist/commands/mcp.command.js.map +1 -1
  9. package/dist/constants.d.ts +1 -0
  10. package/dist/constants.js +6 -0
  11. package/dist/constants.js.map +1 -1
  12. package/dist/index.js +2 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/partner-mcp/config.d.ts +6 -0
  15. package/dist/partner-mcp/config.js +25 -0
  16. package/dist/partner-mcp/config.js.map +1 -0
  17. package/dist/partner-mcp/index.d.ts +2 -0
  18. package/dist/partner-mcp/index.js +17 -0
  19. package/dist/partner-mcp/index.js.map +1 -0
  20. package/dist/partner-mcp/partnerApiClient.d.ts +34 -0
  21. package/dist/partner-mcp/partnerApiClient.js +233 -0
  22. package/dist/partner-mcp/partnerApiClient.js.map +1 -0
  23. package/dist/partner-mcp/resources/registerResources.d.ts +8 -0
  24. package/dist/partner-mcp/resources/registerResources.js +70 -0
  25. package/dist/partner-mcp/resources/registerResources.js.map +1 -0
  26. package/dist/partner-mcp/server.d.ts +7 -0
  27. package/dist/partner-mcp/server.js +17 -0
  28. package/dist/partner-mcp/server.js.map +1 -0
  29. package/dist/partner-mcp/signing.d.ts +22 -0
  30. package/dist/partner-mcp/signing.js +20 -0
  31. package/dist/partner-mcp/signing.js.map +1 -0
  32. package/dist/partner-mcp/tokenPersistence.d.ts +7 -0
  33. package/dist/partner-mcp/tokenPersistence.js +19 -0
  34. package/dist/partner-mcp/tokenPersistence.js.map +1 -0
  35. package/dist/partner-mcp/tools/definitions.d.ts +7 -0
  36. package/dist/partner-mcp/tools/definitions.js +1158 -0
  37. package/dist/partner-mcp/tools/definitions.js.map +1 -0
  38. package/dist/partner-mcp/tools/registerTools.d.ts +8 -0
  39. package/dist/partner-mcp/tools/registerTools.js +173 -0
  40. package/dist/partner-mcp/tools/registerTools.js.map +1 -0
  41. package/dist/partner-mcp/tools/schemas.d.ts +32 -0
  42. package/dist/partner-mcp/tools/schemas.js +47 -0
  43. package/dist/partner-mcp/tools/schemas.js.map +1 -0
  44. package/dist/partner-mcp/types.d.ts +48 -0
  45. package/dist/partner-mcp/types.js +2 -0
  46. package/dist/partner-mcp/types.js.map +1 -0
  47. package/dist/services/localCallbackServer.service.js +41 -15
  48. package/dist/services/localCallbackServer.service.js.map +1 -1
  49. package/dist/services/mcpLauncher.service.d.ts +2 -4
  50. package/dist/services/mcpLauncher.service.js +12 -16
  51. package/dist/services/mcpLauncher.service.js.map +1 -1
  52. package/dist/services/oauthLogin.service.js +3 -1
  53. package/dist/services/oauthLogin.service.js.map +1 -1
  54. package/package.json +4 -3
@@ -0,0 +1,1158 @@
1
+ import { bodyInputSchema, describeEndpointInputSchema, emptyInputSchema, idBodyInputSchema, idInputSchema, idOptionalBodyInputSchema, queryInputSchema, } from './schemas.js';
2
+ const tools = [
3
+ {
4
+ name: 'COUNT_auth_status',
5
+ title: 'COUNT Auth Status',
6
+ description: 'Return the MCP server authentication configuration state without exposing secrets.',
7
+ method: 'GET',
8
+ pathTemplate: '/__local/auth-status',
9
+ inputSchema: emptyInputSchema,
10
+ requiresUserAuth: false,
11
+ readOnly: true,
12
+ destructive: false,
13
+ },
14
+ {
15
+ name: 'COUNT_describe_endpoint',
16
+ title: 'Describe COUNT Tool',
17
+ description: 'Return human-readable guidance for a specific COUNT tool, including the partner API path it wraps, expected query/body fields from the public COUNT developer documentation, and tips for which IDs to pass. Useful for checking request shape before unfamiliar create/update operations.',
18
+ method: 'GET',
19
+ pathTemplate: '/__local/describe-endpoint',
20
+ inputSchema: describeEndpointInputSchema,
21
+ requiresUserAuth: false,
22
+ readOnly: true,
23
+ destructive: false,
24
+ },
25
+ {
26
+ name: 'COUNT_refresh_access_token',
27
+ title: 'Refresh COUNT Access Token',
28
+ description: 'Refresh the COUNT partner access token using the configured refresh token. The refreshed token is kept in this MCP process memory.',
29
+ method: 'POST',
30
+ pathTemplate: '/partners/refresh-user-access-token',
31
+ inputSchema: emptyInputSchema,
32
+ requiresUserAuth: false,
33
+ readOnly: false,
34
+ destructive: false,
35
+ idempotent: false,
36
+ },
37
+ {
38
+ name: 'COUNT_list_transactions',
39
+ title: 'List Transactions',
40
+ description: 'List register/bank transactions for the authenticated workspace. Common `query` filters: `accUuid` (single bank/cash account UUID), `categoryAccountUuid`, `vendorUuid`, `customerUuid`, `projectUuid`, `tagUuids` (comma-separated), `startDate` / `endDate` (YYYY-MM-DD), `transactionTypes` ("Expense" | "Income" | "Transfer" | "Journal Entry"; `type` is also accepted as an alias), `reviewed: "true" | "false"`, `reconciled: "true" | "false"`, `search` (substring against description), `page`, and `limit` (default 50, server cap applies). Returns transaction rows with `id` exposed as the external UUID — pass that same UUID back to `update_transaction` / `change_transaction_category` / `delete_transaction`.',
41
+ method: 'GET',
42
+ pathTemplate: '/partners/transactions',
43
+ inputSchema: queryInputSchema,
44
+ requiresUserAuth: true,
45
+ readOnly: true,
46
+ destructive: false,
47
+ },
48
+ {
49
+ name: 'COUNT_get_transaction',
50
+ title: 'Get Transaction',
51
+ description: 'Get a single transaction by external UUID. Returns transactions hidden from the workspace UI as well. Optional `query` fields are forwarded as Sequelize `include` directives to expand related entities (e.g. category, vendor, customer, tags, taxes). Use the returned `id` (the external UUID) as the next call argument for `update_transaction`, `change_transaction_category`, or `delete_transaction`.',
52
+ method: 'GET',
53
+ pathTemplate: '/partners/transactions/{id}',
54
+ inputSchema: idInputSchema,
55
+ requiresUserAuth: true,
56
+ readOnly: true,
57
+ destructive: false,
58
+ },
59
+ {
60
+ name: 'COUNT_create_transaction',
61
+ title: 'Create Transaction',
62
+ description: 'Create a register/bank transaction in the authenticated COUNT workspace. **Set the category, vendor/customer, project, tags, and taxes in this single call — do NOT follow up with `change_transaction_category` when you already know the category at creation time.** The create endpoint resolves the category UUID, posts the journal entries, and writes tax associations atomically. Required body: `accUuid` (account UUID from `list_accounts` — the bank/cash account the money moves through), `amount` (positive decimal — see sign convention below), and either `postedDate` or `date` (YYYY-MM-DD). Optional body: `description`, `currency`, `type` (`Expense` | `Income` | `Transfer` | `Journal Entry` — case-sensitive, but lowercase `expense`/`income`/`transfer`/`journal`/`journal_entry` are also accepted and normalized server-side), `categoryAccountUuid` (income/expense category — same UUID source as `accUuid`; **set this here at creation, no separate `change_transaction_category` call needed**), `vendorUuid`, `customerUuid`, `projectUuid`, `tagUuids: string[]`, `taxes: number[]` (numeric internal tax ids — taxes are not yet UUID-resolved on the partner API), `notes`, `authorizedDate`. **Sign convention**: pass `amount` as a positive number; the server flips the sign for income internally. **Type omission**: when `type` is not sent, the server infers it from the category-account type. **FK resolution**: `vendorUuid`/`customerUuid`/`projectUuid`/`tagUuids` are resolved into the internal numeric ids the service expects; do not send `vendorId`/`customerId`/`projectId`/`tags` numeric arrays unless you really do have internal ids. **Use `change_transaction_category` only to modify the category on an EXISTING transaction.**',
63
+ method: 'POST',
64
+ pathTemplate: '/partners/transactions',
65
+ inputSchema: bodyInputSchema,
66
+ requiresUserAuth: true,
67
+ readOnly: false,
68
+ destructive: false,
69
+ },
70
+ {
71
+ name: 'COUNT_bulk_create_transactions',
72
+ title: 'Bulk Create Transactions',
73
+ description: 'Create up to 100 register/bank transactions in a single call. Body: `{ "transactions": [<row>, ...] }` where each `<row>` uses the EXACT same shape as `COUNT_create_transaction` (required `accUuid`, `amount`, `postedDate`/`date`; optional `description`, `currency`, `type`, `categoryAccountUuid`, `vendorUuid`, `customerUuid`, `projectUuid`, `tagUuids: string[]`, `taxes: number[]`, `notes`, `authorizedDate`). **Set the category, vendor/customer, project, tags, and taxes ON EACH ROW in this single call — no follow-up `change_transaction_category` needed.** **Partial-success contract**: each row runs in its own DB transaction; one row failing never rolls back another row\'s writes. Response envelope: `{ "successCount": N, "errorCount": M, "results": [{ "index": 0, "success": true, "transaction": {...} } | { "index": 1, "success": false, "error": "..." }, ...] }` — read `errorCount` first; if non-zero, iterate `results` and only retry the rows where `success: false`. The HTTP status is always `201` when the batch was accepted, even if all rows failed. **Cap**: 100 rows per call (split larger imports into multiple calls). **Use cases**: ingest a CSV of bank transactions, replay a missed sync window, backfill historical activity. For a single transaction, prefer `COUNT_create_transaction` — the response shape is simpler.',
74
+ method: 'POST',
75
+ pathTemplate: '/partners/transactions/bulk',
76
+ inputSchema: bodyInputSchema,
77
+ requiresUserAuth: true,
78
+ readOnly: false,
79
+ destructive: false,
80
+ },
81
+ {
82
+ name: 'COUNT_update_transaction',
83
+ title: 'Update Transaction',
84
+ description: 'Update non-category fields on an EXISTING transaction by external UUID. Body fields: `vendorUuid`, `customerUuid`, `projectUuid`, `tagUuids: string[]` (replaces all tags — `[]` clears them), plus regular safe Transaction columns (`description`, `notes`, `amount`, `postedDate`, `authorizedDate`, etc.). Do not send `categoryAccountUuid` or `categoryAccountId` here; use `COUNT_change_transaction_category` to change/clear the category on an existing transaction so taxes, transfers, fixed assets, and journal side effects run through the accounting workflow. (When CREATING a new transaction, pass `categoryAccountUuid` directly to `COUNT_create_transaction` — no follow-up call is needed.) The response includes `_partnerWarnings: [{ field, reason, message }]` when fields you sent were ignored (`reason: "internal_only"` for fields managed internally — e.g. `linkedTransferTransactionId`, `providerTransactionId`, `billId`; `reason: "unknown_field"` for typos / fields not on the transaction model).',
85
+ method: 'PATCH',
86
+ pathTemplate: '/partners/transactions/{id}',
87
+ inputSchema: idBodyInputSchema,
88
+ requiresUserAuth: true,
89
+ readOnly: false,
90
+ destructive: false,
91
+ },
92
+ {
93
+ name: 'COUNT_change_transaction_category',
94
+ title: 'Change Transaction Category',
95
+ description: 'Change, clear, or transfer-link the category (GL account) on an EXISTING transaction by external UUID. **Use this tool only when modifying the category of a transaction that already exists.** When creating a NEW transaction, pass `categoryAccountUuid` directly to `COUNT_create_transaction` instead — the create endpoint resolves the category, posts the journal entries, and writes tax associations in a single call (no follow-up `change_transaction_category` is needed). Body: `categoryAccountUuid` (the `id`, `accUuid`, or `accountUuid` of the target account from `list_accounts`); pass `null` to uncategorize. Optional body fields: `notes`, `taxes`, `autoCreateMatching`, `autoReview`, `linkedTransferTransactionUuid` (or UUID-shaped `linkedTransferTransactionId` from `list_transactions`), `fixedAssetAssignment`. The category-change workflow ensures taxes, transfers, fixed assets, and journal side effects all stay in sync on existing rows.',
96
+ method: 'PATCH',
97
+ pathTemplate: '/partners/transactions/{id}/change-category',
98
+ inputSchema: idBodyInputSchema,
99
+ requiresUserAuth: true,
100
+ readOnly: false,
101
+ destructive: false,
102
+ },
103
+ {
104
+ name: 'COUNT_assign_transaction_to_bills_invoices',
105
+ title: 'Assign Transaction To Bills Or Invoices',
106
+ description: 'Apply an existing bank/expense/income transaction as payment against one or more invoices or bills. URL `{id}` is the transaction UUID from `list_transactions`. Body: `{ matchingType: "invoice" | "bill", records: [{ id: <invoice or bill UUID>, paymentAmount: <decimal>, notes?: string }], withCaution?: boolean, parentCategoryAccountUuid?: string }`. For invoices use `matchingType: "invoice"` with an **Income** transaction (bank deposit); invoice refunds use **Expense**. For bills use `matchingType: "bill"` with an **Expense** transaction (vendor payment); vendor memos use **Income**. `paymentAmount` must not exceed the document open balance; partial payments may split the transaction. Optional `parentCategoryAccountUuid` sets the category for any remainder when the payment is less than the transaction amount. Do not set `invoiceId` / `billId` on transaction update — use this endpoint instead.',
107
+ method: 'POST',
108
+ pathTemplate: '/partners/transactions/{id}/assign-to-bills-invoices',
109
+ inputSchema: idBodyInputSchema,
110
+ requiresUserAuth: true,
111
+ readOnly: false,
112
+ destructive: false,
113
+ },
114
+ {
115
+ name: 'COUNT_delete_transaction',
116
+ title: 'Delete Transaction',
117
+ description: 'Delete a transaction by external UUID.',
118
+ method: 'DELETE',
119
+ pathTemplate: '/partners/transactions/{id}',
120
+ inputSchema: idInputSchema,
121
+ requiresUserAuth: true,
122
+ readOnly: false,
123
+ destructive: true,
124
+ },
125
+ {
126
+ name: 'COUNT_list_accounts',
127
+ title: 'List Chart Of Accounts',
128
+ description: 'List chart of accounts for the authenticated COUNT workspace. Optional filters (pass under `query`): `type` must be one of `Assets`, `Liabilities`, `Equity`, `Income`, `Expenses` (case-sensitive, plural — singular forms like "Asset" are rejected); `subTypeId` is a positive integer; `search` is a substring matched against account name / number; boolean-string filters `inactive`, `includeBalances`, `includeHidden`, `includeHiddenAccounts`, `is1099Box`, `notAssignedToReporter`, `onlyCategoryAccounts`, `includeDeleteMeta` accept `"true"` / `"false"`.',
129
+ method: 'GET',
130
+ pathTemplate: '/partners/chart-of-accounts',
131
+ inputSchema: queryInputSchema,
132
+ requiresUserAuth: true,
133
+ readOnly: true,
134
+ destructive: false,
135
+ },
136
+ {
137
+ name: 'COUNT_create_account',
138
+ title: 'Create Account',
139
+ description: 'Create a chart-of-accounts entry. Required body: `name` (string) and `subTypeId` (numeric AccountSubType id — read it from the `subType.id` field on any `list_accounts` row that already lives under the bucket you want, e.g. an existing "Assets > Bank" row gives you the right `subTypeId` to create another bank account). Optional body: `accountNumber`, `currency` (defaults to the workspace currency), `description`, `color` (hex), `parentAccountId` (numeric id of an existing parent account — sub-accounts inherit the parent\'s `type` / `currency` / `status` and a sub-account cannot itself have sub-accounts), `institutionId`, `taxes: number[]` (internal numeric tax ids — required for NZ workspaces), `status` ("active" | "inactive"). The high-level `type` (Assets/Liabilities/Equity/Income/Expenses) is derived from `subTypeId` server-side; do not send it.',
140
+ method: 'POST',
141
+ pathTemplate: '/partners/chart-of-accounts',
142
+ inputSchema: bodyInputSchema,
143
+ requiresUserAuth: true,
144
+ readOnly: false,
145
+ destructive: false,
146
+ },
147
+ {
148
+ name: 'COUNT_update_account',
149
+ title: 'Update Account',
150
+ description: 'Update a chart-of-accounts entry by external UUID. Pass only the fields you want to change. Editable: `name`, `accountNumber`, `description`, `color`, `status` ("active" | "inactive"), `subTypeId` (must keep the same high-level `type` — switching a Bank account into an Income sub-type is rejected), `parentAccountId` (set to a valid parent id to nest, or pass `isChildAccount: false` to detach), `connectionSyncEnabled` (only meaningful on connected feed accounts). Do not send `type` (derived from `subTypeId`), `teamId`, `connectionId`, or `systemCreated` — they are stripped. System / non-editable accounts (e.g. payroll defaults) accept only a small allowlist of cosmetic fields plus name on physical accounts; expect a 403 if you try to edit a protected field. An empty body returns 400.',
151
+ method: 'PATCH',
152
+ pathTemplate: '/partners/chart-of-accounts/{id}',
153
+ inputSchema: idBodyInputSchema,
154
+ requiresUserAuth: true,
155
+ readOnly: false,
156
+ destructive: false,
157
+ },
158
+ {
159
+ name: 'COUNT_delete_account',
160
+ title: 'Delete Account',
161
+ description: 'Delete a chart of accounts entry by external UUID.',
162
+ method: 'DELETE',
163
+ pathTemplate: '/partners/chart-of-accounts/{id}',
164
+ inputSchema: idInputSchema,
165
+ requiresUserAuth: true,
166
+ readOnly: false,
167
+ destructive: true,
168
+ },
169
+ {
170
+ name: 'COUNT_list_vendors',
171
+ title: 'List Vendors',
172
+ description: 'List vendors for the authenticated COUNT workspace.',
173
+ method: 'GET',
174
+ pathTemplate: '/partners/vendors',
175
+ inputSchema: queryInputSchema,
176
+ requiresUserAuth: true,
177
+ readOnly: true,
178
+ destructive: false,
179
+ },
180
+ {
181
+ name: 'COUNT_create_vendor',
182
+ title: 'Create Vendor',
183
+ description: 'Create a vendor.',
184
+ method: 'POST',
185
+ pathTemplate: '/partners/vendors',
186
+ inputSchema: bodyInputSchema,
187
+ requiresUserAuth: true,
188
+ readOnly: false,
189
+ destructive: false,
190
+ },
191
+ {
192
+ name: 'COUNT_update_vendor',
193
+ title: 'Update Vendor',
194
+ description: 'Update a vendor by external UUID.',
195
+ method: 'PATCH',
196
+ pathTemplate: '/partners/vendors/{id}',
197
+ inputSchema: idBodyInputSchema,
198
+ requiresUserAuth: true,
199
+ readOnly: false,
200
+ destructive: false,
201
+ },
202
+ {
203
+ name: 'COUNT_delete_vendor',
204
+ title: 'Delete Vendor',
205
+ description: 'Delete a vendor by external UUID.',
206
+ method: 'DELETE',
207
+ pathTemplate: '/partners/vendors/{id}',
208
+ inputSchema: idInputSchema,
209
+ requiresUserAuth: true,
210
+ readOnly: false,
211
+ destructive: true,
212
+ },
213
+ {
214
+ name: 'COUNT_list_customers',
215
+ title: 'List Customers',
216
+ description: 'List customers for the authenticated COUNT workspace.',
217
+ method: 'GET',
218
+ pathTemplate: '/partners/customers',
219
+ inputSchema: queryInputSchema,
220
+ requiresUserAuth: true,
221
+ readOnly: true,
222
+ destructive: false,
223
+ },
224
+ {
225
+ name: 'COUNT_get_customer',
226
+ title: 'Get Customer',
227
+ description: 'Get a customer by external UUID.',
228
+ method: 'GET',
229
+ pathTemplate: '/partners/customers/{id}',
230
+ inputSchema: idInputSchema,
231
+ requiresUserAuth: true,
232
+ readOnly: true,
233
+ destructive: false,
234
+ },
235
+ {
236
+ name: 'COUNT_create_customer',
237
+ title: 'Create Customer',
238
+ description: 'Create a customer.',
239
+ method: 'POST',
240
+ pathTemplate: '/partners/customers',
241
+ inputSchema: bodyInputSchema,
242
+ requiresUserAuth: true,
243
+ readOnly: false,
244
+ destructive: false,
245
+ },
246
+ {
247
+ name: 'COUNT_update_customer',
248
+ title: 'Update Customer',
249
+ description: 'Update a customer by external UUID.',
250
+ method: 'PUT',
251
+ pathTemplate: '/partners/customers/{id}',
252
+ inputSchema: idBodyInputSchema,
253
+ requiresUserAuth: true,
254
+ readOnly: false,
255
+ destructive: false,
256
+ },
257
+ {
258
+ name: 'COUNT_delete_customer',
259
+ title: 'Delete Customer',
260
+ description: 'Delete a customer by external UUID.',
261
+ method: 'DELETE',
262
+ pathTemplate: '/partners/customers/{id}',
263
+ inputSchema: idInputSchema,
264
+ requiresUserAuth: true,
265
+ readOnly: false,
266
+ destructive: true,
267
+ },
268
+ {
269
+ name: 'COUNT_list_people',
270
+ title: 'List People',
271
+ description: 'List people records for the authenticated COUNT workspace.',
272
+ method: 'GET',
273
+ pathTemplate: '/partners/people',
274
+ inputSchema: queryInputSchema,
275
+ requiresUserAuth: true,
276
+ readOnly: true,
277
+ destructive: false,
278
+ },
279
+ {
280
+ name: 'COUNT_get_person',
281
+ title: 'Get Person',
282
+ description: 'Get a people record by external UUID.',
283
+ method: 'GET',
284
+ pathTemplate: '/partners/people/{id}',
285
+ inputSchema: idInputSchema,
286
+ requiresUserAuth: true,
287
+ readOnly: true,
288
+ destructive: false,
289
+ },
290
+ {
291
+ name: 'COUNT_list_products',
292
+ title: 'List Products',
293
+ description: 'List products and services for the authenticated COUNT workspace.',
294
+ method: 'GET',
295
+ pathTemplate: '/partners/products',
296
+ inputSchema: queryInputSchema,
297
+ requiresUserAuth: true,
298
+ readOnly: true,
299
+ destructive: false,
300
+ },
301
+ {
302
+ name: 'COUNT_get_product',
303
+ title: 'Get Product',
304
+ description: 'Get a product or service by external UUID.',
305
+ method: 'GET',
306
+ pathTemplate: '/partners/products/{id}',
307
+ inputSchema: idInputSchema,
308
+ requiresUserAuth: true,
309
+ readOnly: true,
310
+ destructive: false,
311
+ },
312
+ {
313
+ name: 'COUNT_create_product',
314
+ title: 'Create Product',
315
+ description: 'Create a product or service.',
316
+ method: 'POST',
317
+ pathTemplate: '/partners/products',
318
+ inputSchema: bodyInputSchema,
319
+ requiresUserAuth: true,
320
+ readOnly: false,
321
+ destructive: false,
322
+ },
323
+ {
324
+ name: 'COUNT_update_product',
325
+ title: 'Update Product',
326
+ description: 'Update a product or service by external UUID.',
327
+ method: 'PATCH',
328
+ pathTemplate: '/partners/products/{id}',
329
+ inputSchema: idBodyInputSchema,
330
+ requiresUserAuth: true,
331
+ readOnly: false,
332
+ destructive: false,
333
+ },
334
+ {
335
+ name: 'COUNT_delete_product',
336
+ title: 'Delete Product',
337
+ description: 'Delete a product or service by external UUID.',
338
+ method: 'DELETE',
339
+ pathTemplate: '/partners/products/{id}',
340
+ inputSchema: idInputSchema,
341
+ requiresUserAuth: true,
342
+ readOnly: false,
343
+ destructive: true,
344
+ },
345
+ {
346
+ name: 'COUNT_list_tags',
347
+ title: 'List Tags',
348
+ description: 'List tags for the authenticated COUNT workspace.',
349
+ method: 'GET',
350
+ pathTemplate: '/partners/tags',
351
+ inputSchema: queryInputSchema,
352
+ requiresUserAuth: true,
353
+ readOnly: true,
354
+ destructive: false,
355
+ },
356
+ {
357
+ name: 'COUNT_get_tag',
358
+ title: 'Get Tag',
359
+ description: 'Get a tag by external UUID.',
360
+ method: 'GET',
361
+ pathTemplate: '/partners/tags/{id}',
362
+ inputSchema: idInputSchema,
363
+ requiresUserAuth: true,
364
+ readOnly: true,
365
+ destructive: false,
366
+ },
367
+ {
368
+ name: 'COUNT_create_tag',
369
+ title: 'Create Tag',
370
+ description: 'Create a tag.',
371
+ method: 'POST',
372
+ pathTemplate: '/partners/tags',
373
+ inputSchema: bodyInputSchema,
374
+ requiresUserAuth: true,
375
+ readOnly: false,
376
+ destructive: false,
377
+ },
378
+ {
379
+ name: 'COUNT_update_tag',
380
+ title: 'Update Tag',
381
+ description: 'Update a tag by external UUID.',
382
+ method: 'PATCH',
383
+ pathTemplate: '/partners/tags/{id}',
384
+ inputSchema: idBodyInputSchema,
385
+ requiresUserAuth: true,
386
+ readOnly: false,
387
+ destructive: false,
388
+ },
389
+ {
390
+ name: 'COUNT_delete_tag',
391
+ title: 'Delete Tag',
392
+ description: 'Delete a tag by external UUID.',
393
+ method: 'DELETE',
394
+ pathTemplate: '/partners/tags/{id}',
395
+ inputSchema: idInputSchema,
396
+ requiresUserAuth: true,
397
+ readOnly: false,
398
+ destructive: true,
399
+ },
400
+ {
401
+ name: 'COUNT_list_tag_groups',
402
+ title: 'List Tag Groups',
403
+ description: 'List tag groups for the authenticated COUNT workspace.',
404
+ method: 'GET',
405
+ pathTemplate: '/partners/tags/groups',
406
+ inputSchema: queryInputSchema,
407
+ requiresUserAuth: true,
408
+ readOnly: true,
409
+ destructive: false,
410
+ },
411
+ {
412
+ name: 'COUNT_get_tag_group',
413
+ title: 'Get Tag Group',
414
+ description: 'Get a tag group by external UUID.',
415
+ method: 'GET',
416
+ pathTemplate: '/partners/tags/groups/{id}',
417
+ inputSchema: idInputSchema,
418
+ requiresUserAuth: true,
419
+ readOnly: true,
420
+ destructive: false,
421
+ },
422
+ {
423
+ name: 'COUNT_create_tag_group',
424
+ title: 'Create Tag Group',
425
+ description: 'Create a tag group. Body: `name` (required, string, unique per workspace), `color` (optional string), and `tagUuids` (optional array of tag UUIDs from `list_tags`) — each tag may belong to at most one group, so passing a tag already in another group rejects the request with a 400. Send tag UUIDs (not numeric ids); the server resolves them to internal ids automatically.',
426
+ method: 'POST',
427
+ pathTemplate: '/partners/tags/groups',
428
+ inputSchema: bodyInputSchema,
429
+ requiresUserAuth: true,
430
+ readOnly: false,
431
+ destructive: false,
432
+ },
433
+ {
434
+ name: 'COUNT_update_tag_group',
435
+ title: 'Update Tag Group',
436
+ description: 'Update a tag group by external UUID. Body: optional `name`, `color`, and `tagUuids` (array of tag UUIDs from `list_tags`). When `tagUuids` is sent the group\'s membership is REPLACED with that exact set (existing tags not in the array are removed, new ones added); pass `[]` to clear all tags. A tag can only belong to one group at a time. Send tag UUIDs (not numeric ids); the server resolves them to internal ids automatically.',
437
+ method: 'PATCH',
438
+ pathTemplate: '/partners/tags/groups/{id}',
439
+ inputSchema: idBodyInputSchema,
440
+ requiresUserAuth: true,
441
+ readOnly: false,
442
+ destructive: false,
443
+ },
444
+ {
445
+ name: 'COUNT_delete_tag_group',
446
+ title: 'Delete Tag Group',
447
+ description: 'Delete a tag group by external UUID.',
448
+ method: 'DELETE',
449
+ pathTemplate: '/partners/tags/groups/{id}',
450
+ inputSchema: idInputSchema,
451
+ requiresUserAuth: true,
452
+ readOnly: false,
453
+ destructive: true,
454
+ },
455
+ {
456
+ name: 'COUNT_list_invoices',
457
+ title: 'List Invoices',
458
+ description: 'List invoices for the authenticated COUNT workspace.',
459
+ method: 'GET',
460
+ pathTemplate: '/partners/invoices',
461
+ inputSchema: queryInputSchema,
462
+ requiresUserAuth: true,
463
+ readOnly: true,
464
+ destructive: false,
465
+ },
466
+ {
467
+ name: 'COUNT_get_invoice',
468
+ title: 'Get Invoice',
469
+ description: 'Get an invoice by external UUID.',
470
+ method: 'GET',
471
+ pathTemplate: '/partners/invoices/{id}',
472
+ inputSchema: idInputSchema,
473
+ requiresUserAuth: true,
474
+ readOnly: true,
475
+ destructive: false,
476
+ },
477
+ {
478
+ name: 'COUNT_create_invoice',
479
+ title: 'Create Invoice',
480
+ description: 'Create an invoice, credit memo, or estimate. Required body: `customerUuid` (from `list_customers`), `date` (YYYY-MM-DD), and `products: [{ productUuid | uuid, quantity, unitPrice, ... }]` (each line resolves a product UUID into the internal product id; `unitPrice` is per-unit in workspace currency). Optional body: `invoiceType` ("invoice" — default | "estimate" | "memo"), `invoiceNumber`, `dueDate` (YYYY-MM-DD), `currency` (defaults to workspace currency), `discount` + `discountDescription`, `notes`, `tagUuids: string[]` (replaces all tags), `appliedToInvoiceUuid` (only for `invoiceType: "memo"` — links the credit memo to the original invoice), `isDraft` (boolean — drafts skip approval / journals), `applyCredit` + `creditMemo: { id, amount }` (apply an existing memo), and recurring-template fields (`recurrencePattern`, `inAdvanceCreationDays`) when creating a recurring schedule via this endpoint. **Do not send numeric ids for `customer`/`products[].id`/`tags`/`appliedToInvoiceId`** — the partner middleware resolves UUIDs into them. Memos cannot be recurring; recurring memos return 400.',
481
+ method: 'POST',
482
+ pathTemplate: '/partners/invoices',
483
+ inputSchema: bodyInputSchema,
484
+ requiresUserAuth: true,
485
+ readOnly: false,
486
+ destructive: false,
487
+ },
488
+ {
489
+ name: 'COUNT_update_invoice',
490
+ title: 'Update Invoice',
491
+ description: 'Update an invoice by external UUID. Pass only the fields you want to change. ' +
492
+ 'Lifecycle/state fields (`paymentStatus`, `isSent`, `approved`, `total`, `isDeleted`, `teamId`) are managed internally and cannot be set here — use the dedicated endpoints (e.g. /approve, /send, /apply-multiple-credit-to-invoice) instead. ' +
493
+ 'When the body is empty or contains internal-only or unknown fields, the response is still 200 with the unchanged invoice but includes a `_partnerWarnings: [{ field, reason, message }]` array describing each ignored field — read it before assuming the update succeeded.',
494
+ method: 'PATCH',
495
+ pathTemplate: '/partners/invoices/{id}',
496
+ inputSchema: idBodyInputSchema,
497
+ requiresUserAuth: true,
498
+ readOnly: false,
499
+ destructive: false,
500
+ },
501
+ {
502
+ name: 'COUNT_delete_invoice',
503
+ title: 'Delete Invoice',
504
+ description: 'Delete an invoice by external UUID.',
505
+ method: 'DELETE',
506
+ pathTemplate: '/partners/invoices/{id}',
507
+ inputSchema: idInputSchema,
508
+ requiresUserAuth: true,
509
+ readOnly: false,
510
+ destructive: true,
511
+ },
512
+ {
513
+ name: 'COUNT_approve_invoice',
514
+ title: 'Approve Invoice',
515
+ description: 'Approve a draft invoice by external UUID. Moves the invoice from `draft` → `approved`, posts the underlying journal entries, and makes the invoice eligible for `send`. Body is optional — pass nothing for the standard approval. Errors with 400 if the invoice is already approved, sent, or paid; not valid for `invoiceType: "memo"` drafts (use the regular update + send flow on memos).',
516
+ method: 'PATCH',
517
+ pathTemplate: '/partners/invoices/{id}/approve',
518
+ inputSchema: idOptionalBodyInputSchema,
519
+ requiresUserAuth: true,
520
+ readOnly: false,
521
+ destructive: false,
522
+ },
523
+ {
524
+ name: 'COUNT_send_invoice',
525
+ title: 'Send Invoice',
526
+ description: 'Send (or re-send) an approved invoice to its customer by external UUID. Optional body: `recipients: string[]` (override the customer\'s default email), `subject`, `message`, `attachPdf: boolean` (defaults to true), `cc: string[]`, `bcc: string[]`. The invoice must be approved first via `approve_invoice`; calling `send` on a draft returns 400. After a successful send the invoice transitions to `sent`/`unpaid` and becomes the canonical version the customer sees in the public link.',
527
+ method: 'POST',
528
+ pathTemplate: '/partners/invoices/{id}/send',
529
+ inputSchema: idOptionalBodyInputSchema,
530
+ requiresUserAuth: true,
531
+ readOnly: false,
532
+ destructive: false,
533
+ },
534
+ {
535
+ name: 'COUNT_get_invoice_public_link',
536
+ title: 'Get Invoice Public Link',
537
+ description: 'Get a public payment/view link for an invoice by external UUID.',
538
+ method: 'GET',
539
+ pathTemplate: '/partners/invoices/{id}/public-link',
540
+ inputSchema: idInputSchema,
541
+ requiresUserAuth: true,
542
+ readOnly: true,
543
+ destructive: false,
544
+ },
545
+ {
546
+ name: 'COUNT_get_invoice_audit_log',
547
+ title: 'Get Invoice Audit Log',
548
+ description: 'Get the audit log for an invoice by external UUID.',
549
+ method: 'GET',
550
+ pathTemplate: '/partners/invoices/{id}/audit-log',
551
+ inputSchema: idInputSchema,
552
+ requiresUserAuth: true,
553
+ readOnly: true,
554
+ destructive: false,
555
+ },
556
+ {
557
+ name: 'COUNT_add_invoice_attachments_from_urls',
558
+ title: 'Add Invoice Attachments From URLs',
559
+ description: 'Attach documents to an invoice (or estimate / memo) by external UUID using already-hosted URLs — the COUNT server fetches each URL once and stores it as a regular invoice attachment. Body: `{ attachments: [{ url: string, title?: string }] }`. The URL must be publicly fetchable from the COUNT server (no signed-cookie / VPN-only links). For uploading raw bytes use the dedicated `/attachments/upload` multipart endpoint instead — that path is not exposed as an MCP tool because MCP tools cannot send multipart bodies.',
560
+ method: 'POST',
561
+ pathTemplate: '/partners/invoices/{id}/attachments',
562
+ inputSchema: idBodyInputSchema,
563
+ requiresUserAuth: true,
564
+ readOnly: false,
565
+ destructive: false,
566
+ },
567
+ {
568
+ name: 'COUNT_apply_multiple_credits_to_invoice',
569
+ title: 'Apply Multiple Credits To Invoice',
570
+ description: 'Apply one or more credit memos to a single invoice by external UUID. Body: `{ creditMemos: [{ id: <invoice or memo UUID>, amount: <decimal> }, ...] }`. Each `id` accepts a UUID, a numeric id, or a numeric string — the partner middleware resolves them and asserts each memo belongs to the same workspace. The sum of `amount` values cannot exceed the open balance of the target invoice or the available balance on the memos; the request rolls back atomically if any memo is over-applied.',
571
+ method: 'PATCH',
572
+ pathTemplate: '/partners/invoices/{id}/apply-multiple-credit-to-invoice',
573
+ inputSchema: idBodyInputSchema,
574
+ requiresUserAuth: true,
575
+ readOnly: false,
576
+ destructive: false,
577
+ },
578
+ {
579
+ name: 'COUNT_apply_credit_to_multiple_invoices',
580
+ title: 'Apply Credit To Multiple Invoices',
581
+ description: 'Apply a single credit memo (the URL `id`) across multiple target invoices in one call. Body: `{ invoices: [{ id: <invoice UUID or numeric id>, amount: <decimal> }, ...] }`. The URL `id` is the credit memo to draw from; each entry in `invoices` is a target invoice plus the dollar amount of the memo to apply to it. Total applied amount cannot exceed the memo\'s remaining balance. The partner middleware resolves UUIDs to numeric ids and rejects cross-workspace references with 404.',
582
+ method: 'PATCH',
583
+ pathTemplate: '/partners/invoices/{id}/apply-credit-to-multiple-invoices',
584
+ inputSchema: idBodyInputSchema,
585
+ requiresUserAuth: true,
586
+ readOnly: false,
587
+ destructive: false,
588
+ },
589
+ {
590
+ name: 'COUNT_remove_invoice_credit',
591
+ title: 'Remove Invoice Credit',
592
+ description: 'Remove a previously-applied credit memo from an invoice by external UUID. Body: `{ memoId: <credit memo UUID or numeric id> }`. The memo\'s balance is restored and the target invoice\'s open balance increases by the unapplied amount. 404 if the memo isn\'t currently applied to this invoice.',
593
+ method: 'PATCH',
594
+ pathTemplate: '/partners/invoices/{id}/remove-credit',
595
+ inputSchema: idBodyInputSchema,
596
+ requiresUserAuth: true,
597
+ readOnly: false,
598
+ destructive: false,
599
+ },
600
+ {
601
+ name: 'COUNT_remove_invoice_transaction',
602
+ title: 'Remove Invoice Transaction Payment',
603
+ description: 'Remove a previously-applied transaction payment from an invoice by external UUID. Body: `{ transactionId: <transaction UUID from list_transactions>, withCaution?: boolean }`. Restores the invoice open balance and unlinks the transaction from Accounts Receivable. Manual payment links only — automated Stripe links may not be removable via this endpoint.',
604
+ method: 'PATCH',
605
+ pathTemplate: '/partners/invoices/{id}/remove-transaction',
606
+ inputSchema: idBodyInputSchema,
607
+ requiresUserAuth: true,
608
+ readOnly: false,
609
+ destructive: false,
610
+ },
611
+ {
612
+ name: 'COUNT_list_recurring_invoice_templates',
613
+ title: 'List Recurring Invoice Templates',
614
+ description: 'List recurring invoice templates for the authenticated COUNT workspace.',
615
+ method: 'GET',
616
+ pathTemplate: '/partners/recurring-invoice-templates',
617
+ inputSchema: queryInputSchema,
618
+ requiresUserAuth: true,
619
+ readOnly: true,
620
+ destructive: false,
621
+ },
622
+ {
623
+ name: 'COUNT_get_recurring_invoice_template',
624
+ title: 'Get Recurring Invoice Template',
625
+ description: 'Get a recurring invoice template by external UUID.',
626
+ method: 'GET',
627
+ pathTemplate: '/partners/recurring-invoice-templates/{id}',
628
+ inputSchema: idInputSchema,
629
+ requiresUserAuth: true,
630
+ readOnly: true,
631
+ destructive: false,
632
+ },
633
+ {
634
+ name: 'COUNT_create_recurring_invoice_template',
635
+ title: 'Create Recurring Invoice Template',
636
+ description: 'Create a recurring invoice template. Same body shape as `create_invoice` (`customerUuid`, `products`, etc.) PLUS a required `recurrencePattern` object describing the cadence (e.g. monthly, weekly). Optional `inAdvanceCreationDays` controls how many days before each scheduled date the next invoice instance is generated. `invoiceType` defaults to "invoice"; "memo" is rejected (credit memos cannot recur). `isDraft` defaults to true so the template is paused until you explicitly resume it via `resume_recurring_invoice_template`.',
637
+ method: 'POST',
638
+ pathTemplate: '/partners/recurring-invoice-templates',
639
+ inputSchema: bodyInputSchema,
640
+ requiresUserAuth: true,
641
+ readOnly: false,
642
+ destructive: false,
643
+ },
644
+ {
645
+ name: 'COUNT_update_recurring_invoice_template',
646
+ title: 'Update Recurring Invoice Template',
647
+ description: 'Update a recurring invoice template by external UUID.',
648
+ method: 'PATCH',
649
+ pathTemplate: '/partners/recurring-invoice-templates/{id}',
650
+ inputSchema: idBodyInputSchema,
651
+ requiresUserAuth: true,
652
+ readOnly: false,
653
+ destructive: false,
654
+ },
655
+ {
656
+ name: 'COUNT_delete_recurring_invoice_template',
657
+ title: 'Delete Recurring Invoice Template',
658
+ description: 'Delete a recurring invoice template by external UUID.',
659
+ method: 'DELETE',
660
+ pathTemplate: '/partners/recurring-invoice-templates/{id}',
661
+ inputSchema: idInputSchema,
662
+ requiresUserAuth: true,
663
+ readOnly: false,
664
+ destructive: true,
665
+ },
666
+ {
667
+ name: 'COUNT_pause_recurring_invoice_template',
668
+ title: 'Pause Recurring Invoice Template',
669
+ description: 'Pause a recurring invoice template by external UUID.',
670
+ method: 'POST',
671
+ pathTemplate: '/partners/recurring-invoice-templates/{id}/pause',
672
+ inputSchema: idOptionalBodyInputSchema,
673
+ requiresUserAuth: true,
674
+ readOnly: false,
675
+ destructive: false,
676
+ },
677
+ {
678
+ name: 'COUNT_resume_recurring_invoice_template',
679
+ title: 'Resume Recurring Invoice Template',
680
+ description: 'Resume a recurring invoice template by external UUID.',
681
+ method: 'POST',
682
+ pathTemplate: '/partners/recurring-invoice-templates/{id}/resume',
683
+ inputSchema: idOptionalBodyInputSchema,
684
+ requiresUserAuth: true,
685
+ readOnly: false,
686
+ destructive: false,
687
+ },
688
+ {
689
+ name: 'COUNT_list_bills',
690
+ title: 'List Bills',
691
+ description: 'List vendor bills for the authenticated workspace. Common `query` filters: `page`, `limit` (default 50), `orderBy`, `orderDirection`, `projectUuids` (comma-separated project UUIDs — the partner middleware resolves these into the internal `projects` filter; do NOT pass `projects` directly, it is stripped server-side), `vendors` (comma-separated numeric vendor ids — UUID resolution is not yet wired for this filter), `approvalStatus`, `status` (comma-separated combination of approval + payment status tokens, e.g. `"draft,approved,paid"`), `billType` (`"bill"` | `"vendor_memo"`), `currency`, `search` (substring against bill number / vendor name), `startDate` / `endDate` (bill-date window, YYYY-MM-DD), `startDueDate` / `endDueDate` (due-date window), `amount` + `amountOperator` (e.g. `amount=100&amountOperator=gte`). Returns bill rows with `id` exposed as the external UUID — pass that UUID back to `get_bill` / `update_bill` / `approve_bill` / `delete_bill`.',
692
+ method: 'GET',
693
+ pathTemplate: '/partners/bills',
694
+ inputSchema: queryInputSchema,
695
+ requiresUserAuth: true,
696
+ readOnly: true,
697
+ destructive: false,
698
+ },
699
+ {
700
+ name: 'COUNT_get_bill',
701
+ title: 'Get Bill',
702
+ description: 'Get a single bill by external UUID. Returns the bill record with line items, attachments, payments, current approval/payment status, and the linked vendor. Note: this endpoint also returns soft-deleted bills, so check the `isDeleted` field on the response if you need to filter them out client-side.',
703
+ method: 'GET',
704
+ pathTemplate: '/partners/bills/{id}',
705
+ inputSchema: idInputSchema,
706
+ requiresUserAuth: true,
707
+ readOnly: true,
708
+ destructive: false,
709
+ },
710
+ {
711
+ name: 'COUNT_create_bill',
712
+ title: 'Create Bill',
713
+ description: 'Create a vendor bill (accounts payable). Required body: `vendorId` (numeric vendor id from `list_vendors`), `date` (YYYY-MM-DD), `dueDate` (YYYY-MM-DD), and `lineItems: [{ categoryAccountId, description?, quantity, price, total?, taxes?, projectUuid?, customerUuid? }]` where `categoryAccountId` is the bill expense/category account id. Optional body: `billNumber`, `purchaseOrderNumber`, `notes`, `currency` (defaults to workspace currency), `attachments: [{ url, title? }]`, `tags: number[]` (numeric tag ids — partner UUID resolution is NOT yet wired for bills). The created bill enters the `draft` state; call `approve_bill` to post journals and make it eligible for payment. Returns 201 with the bill, or a `{ statusCode, message }` error envelope on validation failure.',
714
+ method: 'POST',
715
+ pathTemplate: '/partners/bills',
716
+ inputSchema: bodyInputSchema,
717
+ requiresUserAuth: true,
718
+ readOnly: false,
719
+ destructive: false,
720
+ },
721
+ {
722
+ name: 'COUNT_update_bill',
723
+ title: 'Update Bill',
724
+ description: 'Update a bill by external UUID. Pass only the fields you want to change. Editable: `vendorId`, `date`, `dueDate`, `billNumber`, `purchaseOrderNumber`, `notes`, `currency`, `lineItems` (replaces all lines when provided; use `categoryAccountId`, `quantity`, and `price` on each line), `tags`, `attachments`. Approved/paid bills have a more restrictive editable set — expect 400 on date/line changes after approval; use `delete_bill` + recreate if you need a structural change post-approval.',
725
+ method: 'PATCH',
726
+ pathTemplate: '/partners/bills/{id}',
727
+ inputSchema: idBodyInputSchema,
728
+ requiresUserAuth: true,
729
+ readOnly: false,
730
+ destructive: false,
731
+ },
732
+ {
733
+ name: 'COUNT_delete_bill',
734
+ title: 'Delete Bill',
735
+ description: 'Delete a bill by external UUID. Soft-delete on draft bills; bills that have any payments applied (`paidAmount > 0`) cannot be deleted and return 400 — unapply payments first via the standard payment endpoints.',
736
+ method: 'DELETE',
737
+ pathTemplate: '/partners/bills/{id}',
738
+ inputSchema: idInputSchema,
739
+ requiresUserAuth: true,
740
+ readOnly: false,
741
+ destructive: true,
742
+ },
743
+ {
744
+ name: 'COUNT_approve_bill',
745
+ title: 'Approve Bill',
746
+ description: 'Approve a draft bill by external UUID. Posts the underlying journal entries (debit each line\'s expense account, credit Accounts Payable) and makes the bill eligible for payment. Body is optional. Errors with 400 if the bill is already approved or has no line items.',
747
+ method: 'POST',
748
+ pathTemplate: '/partners/bills/{id}/approve',
749
+ inputSchema: idOptionalBodyInputSchema,
750
+ requiresUserAuth: true,
751
+ readOnly: false,
752
+ destructive: false,
753
+ },
754
+ {
755
+ name: 'COUNT_apply_vendor_memos_to_bill',
756
+ title: 'Apply Vendor Memos To Bill',
757
+ description: 'Apply one or more vendor credit memos toward an approved bill by external UUID. Body: `{ memosBills: [{ id: <vendor-memo bill UUID from list_bills with billType vendor_memo>, amount: <decimal> }, ...] }`. Each memo must belong to the same vendor as the target bill; total applied cannot exceed the bill open balance or memo available balance. Vendor memos are bill records with `billType: "vendor_memo"` — use their UUID as `id`.',
758
+ method: 'POST',
759
+ pathTemplate: '/partners/bills/{id}/apply-vendor-memos',
760
+ inputSchema: idBodyInputSchema,
761
+ requiresUserAuth: true,
762
+ readOnly: false,
763
+ destructive: false,
764
+ },
765
+ {
766
+ name: 'COUNT_unassign_bill_transaction',
767
+ title: 'Unassign Bill Transaction Payment',
768
+ description: 'Remove a previously-applied transaction payment from a bill by external UUID. Body: `{ transactionId: <transaction UUID from list_transactions>, withCaution?: boolean }`. Restores the bill open balance and clears the transaction\'s bill link.',
769
+ method: 'POST',
770
+ pathTemplate: '/partners/bills/{id}/unassign-transaction',
771
+ inputSchema: idBodyInputSchema,
772
+ requiresUserAuth: true,
773
+ readOnly: false,
774
+ destructive: false,
775
+ },
776
+ {
777
+ name: 'COUNT_list_journal_entries',
778
+ title: 'List Journal Entries',
779
+ description: 'List journal entries for the authenticated COUNT workspace.',
780
+ method: 'GET',
781
+ pathTemplate: '/partners/journal-entries',
782
+ inputSchema: queryInputSchema,
783
+ requiresUserAuth: true,
784
+ readOnly: true,
785
+ destructive: false,
786
+ },
787
+ {
788
+ name: 'COUNT_create_journal_entry',
789
+ title: 'Create Journal Entry',
790
+ description: 'Create a manual journal entry. Body: required `descriptionEntry` (memo), `date` (YYYY-MM-DD), and `lines` (array). Each line needs `accountUuid` (the `id`, `accUuid`, or `accountUuid` from `list_accounts`) plus exactly one of `amountDebit` or `amountCredit` (decimal numbers as strings or numbers — never both on the same line, never neither). Optional per-line: `descriptionLine`. Optional top-level: `refNumber`, `withCaution: true` to skip book-closeness checks (use sparingly). The server validates each account exists in this workspace before posting; manual entries can fail if any line\'s posting date falls within a reconciled period for that account.',
791
+ method: 'POST',
792
+ pathTemplate: '/partners/journal-entries',
793
+ inputSchema: bodyInputSchema,
794
+ requiresUserAuth: true,
795
+ readOnly: false,
796
+ destructive: false,
797
+ },
798
+ {
799
+ name: 'COUNT_bulk_create_journal_entries',
800
+ title: 'Bulk Create Journal Entries',
801
+ description: 'Create up to 100 manual journal entry postings in a single call. Body: `{ "journalEntries": [<posting>, ...] }` where each `<posting>` uses the EXACT same shape as `COUNT_create_journal_entry` (required `descriptionEntry`, `date`, `lines`; each line needs `accountUuid` + exactly one of `amountDebit` / `amountCredit`; optional per-line `descriptionLine`; optional top-level `refNumber`, `withCaution`). **Partial-success contract**: each posting runs in its own DB transaction; one bad posting never rolls back another posting\'s writes. Response envelope: `{ "successCount": N, "errorCount": M, "results": [{ "index": 0, "success": true, "journalEntry": [...lines] } | { "index": 1, "success": false, "error": "..." }, ...] }` — read `errorCount` first; if non-zero, iterate `results` and only retry the postings where `success: false`. The HTTP status is always `201` when the batch was accepted, even if all postings failed. **Cap**: 100 postings per call (split larger imports into multiple calls). **Performance note**: account UUIDs are resolved in a SINGLE batched lookup across the whole batch, so a 100-posting × 10-line request issues one account query — bulk is strictly cheaper than calling `COUNT_create_journal_entry` 100 times. **Use cases**: replay manual postings from another GL, backfill accruals, ingest a CSV of journal entries. For a single posting, prefer `COUNT_create_journal_entry` — the response shape is simpler.',
802
+ method: 'POST',
803
+ pathTemplate: '/partners/journal-entries/bulk',
804
+ inputSchema: bodyInputSchema,
805
+ requiresUserAuth: true,
806
+ readOnly: false,
807
+ destructive: false,
808
+ },
809
+ {
810
+ name: 'COUNT_update_journal_entry',
811
+ title: 'Update Journal Entry',
812
+ description: 'Update a manually-created journal entry by external UUID. Same body shape as `create_journal_entry` (top-level `descriptionEntry`, `date`, `refNumber`, plus the full replacement `lines` array — each line takes `accountUuid` and exactly one of `amountDebit`/`amountCredit`). Only manually-created entries (and Square integration entries) can be updated; system-generated journal entries from invoices, bills, payroll, etc. are read-only and return 400.',
813
+ method: 'PATCH',
814
+ pathTemplate: '/partners/journal-entries/{id}',
815
+ inputSchema: idBodyInputSchema,
816
+ requiresUserAuth: true,
817
+ readOnly: false,
818
+ destructive: false,
819
+ },
820
+ {
821
+ name: 'COUNT_delete_journal_entry',
822
+ title: 'Delete Journal Entry',
823
+ description: 'Delete a journal entry by external UUID.',
824
+ method: 'DELETE',
825
+ pathTemplate: '/partners/journal-entries/{id}',
826
+ inputSchema: idInputSchema,
827
+ requiresUserAuth: true,
828
+ readOnly: false,
829
+ destructive: true,
830
+ },
831
+ {
832
+ name: 'COUNT_list_tasks',
833
+ title: 'List Tasks',
834
+ description: 'List tasks for the authenticated workspace. `query` filters that accept comma-separated tokens where each token is EITHER an external UUID OR a workspace-scoped numeric id (partner middleware resolves both): `statusId` (task statuses), `assigneeId` / `assignerId` / `createdById` (workspace user references), `projects` (project filter), `commentId` (single token only — passing multiple returns 400). Pass-through filters: `page`, `limit`, `orderBy`, `orderDirection`, `search` (ILIKE on task name), `customId`, `visibility` (`"firm-team"` | `"team-only"` | `"firm-only"` — defaults to `["firm-team","team-only"]` when omitted), `priority`, `overduesOnly`, `deadlineDate` / `createdAt` / `updatedAt` (date windows), `status` (string `task.status` column). The legacy filters `clients`, `openTaskOnly`, and `isForClosing` are always stripped server-side. Numeric internal foreign keys are removed from response rows.',
835
+ method: 'GET',
836
+ pathTemplate: '/partners/tasks',
837
+ inputSchema: queryInputSchema,
838
+ requiresUserAuth: true,
839
+ readOnly: true,
840
+ destructive: false,
841
+ },
842
+ {
843
+ name: 'COUNT_get_task',
844
+ title: 'Get Task',
845
+ description: 'Get a single task by external UUID. Default visibility is `["firm-team","team-only"]`; tasks created with `firm-only` visibility (notably system-generated INTERNAL_TASK rows) return 404 unless you explicitly pass `visibility=firm-only` under `query`.',
846
+ method: 'GET',
847
+ pathTemplate: '/partners/tasks/{id}',
848
+ inputSchema: idInputSchema,
849
+ requiresUserAuth: true,
850
+ readOnly: true,
851
+ destructive: false,
852
+ },
853
+ {
854
+ name: 'COUNT_create_task',
855
+ title: 'Create Task',
856
+ description: 'Create a task. Required body: `type` (string — the task type identifier). The following optional FK fields each accept EITHER an external UUID OR a workspace-scoped numeric id (partner middleware resolves both and verifies workspace scope): `assigneeId`, `statusId`, `onCompleteStatusId`, `onCompleteAssigneeId`, `projectId`, `billId`, `transactionId`, `taskListId`, `taskListTemplateId`. Other safe body fields: `name`, `description`, `priority`, `deadline` (YYYY-MM-DD), `customId`, `visibility`. `tags` accepts an array OR a JSON-encoded string — every element must be the same kind (all UUIDs or all positive integer ids); mixed inputs return 400. If `assigneeId` is omitted it defaults to the partner-acting user. INTERNAL_TASK rows are always created with `firm-only` visibility regardless of the `visibility` value you pass. The partner endpoint is JSON-only — multipart attachments are not supported.',
857
+ method: 'POST',
858
+ pathTemplate: '/partners/tasks',
859
+ inputSchema: bodyInputSchema,
860
+ requiresUserAuth: true,
861
+ readOnly: false,
862
+ destructive: false,
863
+ },
864
+ {
865
+ name: 'COUNT_update_task',
866
+ title: 'Update Task',
867
+ description: 'Update a task by external UUID. Pass only the fields you want to change; the same body shape as `create_task` applies. `tags` accepts either an array or a JSON-encoded string and replaces all tags atomically. UUID-resolvable optional FKs: `assigneeId`, `statusId`, `onCompleteStatusId`, `onCompleteAssigneeId`, `projectId`, `billId`, `transactionId`, `taskListId`, `taskListTemplateId` — each accepts a UUID or a workspace-scoped numeric id.',
868
+ method: 'PATCH',
869
+ pathTemplate: '/partners/tasks/{id}',
870
+ inputSchema: idBodyInputSchema,
871
+ requiresUserAuth: true,
872
+ readOnly: false,
873
+ destructive: false,
874
+ },
875
+ {
876
+ name: 'COUNT_delete_task',
877
+ title: 'Delete Task',
878
+ description: 'Delete a task by external UUID.',
879
+ method: 'DELETE',
880
+ pathTemplate: '/partners/tasks/{id}',
881
+ inputSchema: idInputSchema,
882
+ requiresUserAuth: true,
883
+ readOnly: false,
884
+ destructive: true,
885
+ },
886
+ {
887
+ name: 'COUNT_list_projects',
888
+ title: 'List Projects',
889
+ description: 'List projects for the authenticated workspace. Soft-deleted projects are always excluded. `query` filters: `page` (default 1), `limit` (default 20), `search` (ILIKE on project name), `customerUuids` (comma-separated customer UUIDs — partner middleware resolves these into the internal `customers` filter), and `statusUuids` (comma-separated project-status UUIDs — discover them via `list_project_statuses`). The internal `customers`, `statusIds`, and `status` filters are stripped server-side; only the `*Uuids` variants take effect.',
890
+ method: 'GET',
891
+ pathTemplate: '/partners/projects',
892
+ inputSchema: queryInputSchema,
893
+ requiresUserAuth: true,
894
+ readOnly: true,
895
+ destructive: false,
896
+ },
897
+ {
898
+ name: 'COUNT_list_project_statuses',
899
+ title: 'List Project Statuses',
900
+ description: 'List the project statuses available in the authenticated workspace. Returns each status with its name, color, ordering, and external UUID — use the returned UUID as `statusUuid` on `create_project` / `update_project`, or in `statusUuids` (CSV) on `list_projects`.',
901
+ method: 'GET',
902
+ pathTemplate: '/partners/projects/statuses',
903
+ inputSchema: queryInputSchema,
904
+ requiresUserAuth: true,
905
+ readOnly: true,
906
+ destructive: false,
907
+ },
908
+ {
909
+ name: 'COUNT_get_project',
910
+ title: 'Get Project',
911
+ description: 'Get a single project by external UUID. Soft-deleted projects return 404.',
912
+ method: 'GET',
913
+ pathTemplate: '/partners/projects/{id}',
914
+ inputSchema: idInputSchema,
915
+ requiresUserAuth: true,
916
+ readOnly: true,
917
+ destructive: false,
918
+ },
919
+ {
920
+ name: 'COUNT_list_project_tasks',
921
+ title: 'List Project Tasks',
922
+ description: 'List the tasks attached to a project by external UUID. `query` filters: `page` (default 1), `limit` (default 20), `search` (ILIKE on task name). Returns the same partner-sanitized task envelope as `list_tasks`.',
923
+ method: 'GET',
924
+ pathTemplate: '/partners/projects/{id}/tasks',
925
+ inputSchema: idInputSchema,
926
+ requiresUserAuth: true,
927
+ readOnly: true,
928
+ destructive: false,
929
+ },
930
+ {
931
+ name: 'COUNT_create_project',
932
+ title: 'Create Project',
933
+ description: 'Create a project. Required body: non-empty `name`. Optional body: `customerUuid` (from `list_customers`; pass `null` or omit to leave the project uncustomered), `statusUuid` (from `list_project_statuses` — defaults to the workspace\'s default status if omitted), plus any safe project columns (e.g. `description`, `customId`, `startDate`, `endDate`). The numeric internal FKs `customerId`, `statusId`, and `teamId` are always stripped before the request is processed — use the `*Uuid` variants instead.',
934
+ method: 'POST',
935
+ pathTemplate: '/partners/projects',
936
+ inputSchema: bodyInputSchema,
937
+ requiresUserAuth: true,
938
+ readOnly: false,
939
+ destructive: false,
940
+ },
941
+ {
942
+ name: 'COUNT_update_project',
943
+ title: 'Update Project',
944
+ description: 'Update a project by external UUID. Pass only the fields you want to change. `customerUuid` accepts a UUID (set the customer), `null`/`""` (clear the customer), or omission (leave unchanged). `statusUuid` accepts a UUID (set the status) or omission (leave unchanged). The numeric `customerId`, `statusId`, and `teamId` fields are stripped server-side. If you pass `customId` matching the existing value it may be discarded silently.',
945
+ method: 'PATCH',
946
+ pathTemplate: '/partners/projects/{id}',
947
+ inputSchema: idBodyInputSchema,
948
+ requiresUserAuth: true,
949
+ readOnly: false,
950
+ destructive: false,
951
+ },
952
+ {
953
+ name: 'COUNT_delete_project',
954
+ title: 'Delete Project',
955
+ description: 'Delete a project by external UUID (soft delete).',
956
+ method: 'DELETE',
957
+ pathTemplate: '/partners/projects/{id}',
958
+ inputSchema: idInputSchema,
959
+ requiresUserAuth: true,
960
+ readOnly: false,
961
+ destructive: true,
962
+ },
963
+ {
964
+ name: 'COUNT_list_time_entries',
965
+ title: 'List Time Entries',
966
+ description: 'List time entries for the authenticated workspace. Soft-deleted entries are always excluded. `query` filters: `page` (default 1), `limit` (default 50), and the comma-separated UUID filters `peopleUuids`, `projectUuids`, `customerUuids`, `productServiceUuids` (each resolved into its internal numeric filter by the partner middleware). Direct numeric filters (`people`, `projects`, `customers`, `productServices`) are stripped server-side; only the `*Uuids` variants take effect. Date window filter: both `startDate` AND `endDate` (YYYY-MM-DD) must be provided together for the `createdAt`-range filter to apply — passing only one is ignored.',
967
+ method: 'GET',
968
+ pathTemplate: '/partners/time-entries',
969
+ inputSchema: queryInputSchema,
970
+ requiresUserAuth: true,
971
+ readOnly: true,
972
+ destructive: false,
973
+ },
974
+ {
975
+ name: 'COUNT_get_time_entry',
976
+ title: 'Get Time Entry',
977
+ description: 'Get a single time entry by external UUID. Soft-deleted entries return 404.',
978
+ method: 'GET',
979
+ pathTemplate: '/partners/time-entries/{id}',
980
+ inputSchema: idInputSchema,
981
+ requiresUserAuth: true,
982
+ readOnly: true,
983
+ destructive: false,
984
+ },
985
+ {
986
+ name: 'COUNT_create_time_entry',
987
+ title: 'Create Time Entry',
988
+ description: 'Create one or more time entry rows. Required body: `peopleUuid` (from `list_people` — must be present and non-empty on create; partner middleware resolves it into the internal `peopleId`) and `timeEntries: [{ date: "YYYY-MM-DD", minutes: <integer>, ... }]` (array of row objects). Per-row optional fields: `description`, `projectUuid` (resolves to the project; setting it auto-fills the matching customer), `customerUuid`, `productServiceUuid`, `billable`, `wage`. Daily cap: the sum of `minutes` across entries on the same calendar day for the same person cannot exceed 24h — the server returns 400 with the remaining-minutes if exceeded. The numeric `peopleId`, `projectId`, `customerId`, `productServiceId`, and `teamId` fields are stripped server-side; use the `*Uuid` variants.',
989
+ method: 'POST',
990
+ pathTemplate: '/partners/time-entries',
991
+ inputSchema: bodyInputSchema,
992
+ requiresUserAuth: true,
993
+ readOnly: false,
994
+ destructive: false,
995
+ },
996
+ {
997
+ name: 'COUNT_update_time_entry',
998
+ title: 'Update Time Entry',
999
+ description: 'Update a time entry by external UUID. Pass only the fields you want to change. `peopleUuid` cannot be cleared on update (passing `null`/`""` returns 400). `projectUuid` / `customerUuid` / `productServiceUuid` each accept a UUID (set) or `null` (clear). Time entries that belong to a processed pay period are read-only and return 400.',
1000
+ method: 'PATCH',
1001
+ pathTemplate: '/partners/time-entries/{id}',
1002
+ inputSchema: idBodyInputSchema,
1003
+ requiresUserAuth: true,
1004
+ readOnly: false,
1005
+ destructive: false,
1006
+ },
1007
+ {
1008
+ name: 'COUNT_delete_time_entry',
1009
+ title: 'Delete Time Entry',
1010
+ description: 'Delete a time entry by external UUID (soft delete). Time entries that belong to a processed pay period cannot be deleted and return 400 — adjust the pay period first.',
1011
+ method: 'DELETE',
1012
+ pathTemplate: '/partners/time-entries/{id}',
1013
+ inputSchema: idInputSchema,
1014
+ requiresUserAuth: true,
1015
+ readOnly: false,
1016
+ destructive: true,
1017
+ },
1018
+ // All three report endpoints are POST + read-only for MCP: report filters
1019
+ // live on the **query** object, and MCP exposes only generated report output.
1020
+ // This keeps the tool annotations truthful for Claude connector review.
1021
+ {
1022
+ name: 'COUNT_list_expense_receipts',
1023
+ title: 'List Expense Receipts',
1024
+ description: 'List expense receipts (pending receipts) for the authenticated COUNT workspace.',
1025
+ method: 'GET',
1026
+ pathTemplate: '/partners/expense-receipts',
1027
+ inputSchema: queryInputSchema,
1028
+ requiresUserAuth: true,
1029
+ readOnly: true,
1030
+ destructive: false,
1031
+ },
1032
+ {
1033
+ name: 'COUNT_list_unmatched_expense_receipts',
1034
+ title: 'List Unmatched Expense Receipts',
1035
+ description: 'List unmatched expense receipts (pending receipts) for the authenticated COUNT workspace.',
1036
+ method: 'GET',
1037
+ pathTemplate: '/partners/expense-receipts/unmatched',
1038
+ inputSchema: queryInputSchema,
1039
+ requiresUserAuth: true,
1040
+ readOnly: true,
1041
+ destructive: false,
1042
+ },
1043
+ {
1044
+ name: 'COUNT_create_expense_receipt',
1045
+ title: 'Create Expense Receipt',
1046
+ description: 'Create an expense receipt (pending receipt). MCP forwards JSON only; to attach a receipt image call the partner HTTP API directly as multipart/form-data with field `receipt`. Partner bodies use UUID fields such as accountUuid, categoryAccountUuid, optional expenseReportTypeUuid, vendorUuid, projectUuid, tagUuids, taxUuids — see COUNT Partner API docs. API responses omit receiptUrl by design.',
1047
+ method: 'POST',
1048
+ pathTemplate: '/partners/expense-receipts',
1049
+ inputSchema: bodyInputSchema,
1050
+ requiresUserAuth: true,
1051
+ readOnly: false,
1052
+ destructive: false,
1053
+ },
1054
+ {
1055
+ name: 'COUNT_update_expense_receipt',
1056
+ title: 'Update Expense Receipt',
1057
+ description: 'Update an expense receipt by external UUID. MCP forwards JSON only; to replace the receipt image use multipart field `receipt` on the partner HTTP API.',
1058
+ method: 'PATCH',
1059
+ pathTemplate: '/partners/expense-receipts/{id}',
1060
+ inputSchema: idBodyInputSchema,
1061
+ requiresUserAuth: true,
1062
+ readOnly: false,
1063
+ destructive: false,
1064
+ },
1065
+ {
1066
+ name: 'COUNT_delete_expense_receipt',
1067
+ title: 'Delete Expense Receipt',
1068
+ description: 'Delete an unmatched expense receipt by external UUID.',
1069
+ method: 'DELETE',
1070
+ pathTemplate: '/partners/expense-receipts/{id}',
1071
+ inputSchema: idInputSchema,
1072
+ requiresUserAuth: true,
1073
+ readOnly: false,
1074
+ destructive: true,
1075
+ },
1076
+ {
1077
+ name: 'COUNT_match_expense_receipt_manually',
1078
+ title: 'Match Expense Receipt Manually',
1079
+ description: 'Match an expense receipt to an expense transaction. Body uses transactionUuid (COUNT transaction external UUID).',
1080
+ method: 'POST',
1081
+ pathTemplate: '/partners/expense-receipts/{id}/match-manually',
1082
+ inputSchema: idBodyInputSchema,
1083
+ requiresUserAuth: true,
1084
+ readOnly: false,
1085
+ destructive: false,
1086
+ },
1087
+ {
1088
+ name: 'COUNT_unmatch_expense_receipt',
1089
+ title: 'Unmatch Expense Receipt',
1090
+ description: 'Unmatch an expense receipt from its linked transaction.',
1091
+ method: 'DELETE',
1092
+ pathTemplate: '/partners/expense-receipts/{id}/unmatch',
1093
+ inputSchema: idInputSchema,
1094
+ requiresUserAuth: true,
1095
+ readOnly: false,
1096
+ destructive: true,
1097
+ },
1098
+ {
1099
+ name: 'COUNT_generate_trial_balance',
1100
+ title: 'Generate Trial Balance',
1101
+ description: 'Generate a trial balance for the authenticated workspace. Put filters under `query`: `endDate` (required, YYYY-MM-DD — the as-of date), `startDate` (optional, defaults to fiscal-year start), `currency` (defaults to workspace currency), `transactionStatus` ("all" | "reviewed" | "unreviewed" | "reconciled" | "unreconciled"), `accounts` (comma-separated numeric account ids to restrict to), `tags` (comma-separated tag ids), `noTags: "true"` (only entries with no tags), `accountType` (one of "Assets", "Liabilities", "Equity", "Income", "Expenses"). This MCP tool is read-only; no `body` is accepted.',
1102
+ method: 'POST',
1103
+ pathTemplate: '/partners/reports/trial-balance',
1104
+ inputSchema: queryInputSchema,
1105
+ requiresUserAuth: true,
1106
+ readOnly: true,
1107
+ destructive: false,
1108
+ },
1109
+ {
1110
+ name: 'COUNT_generate_profit_and_loss',
1111
+ title: 'Generate Profit And Loss',
1112
+ description: 'Generate a P&L (income statement) for the authenticated workspace. Put filters under `query`: required `startDate` and `endDate` (YYYY-MM-DD); optional `currency`, `transactionStatus` ("all" | "reviewed" | "unreviewed" | "reconciled" | "unreconciled"), `tags`, `customers`, `vendors`, `projects`, `projectId`, `categoryAccount`, `isPnLByTag: true` (column-per-tag breakdown), `includeNoTag: true`, `reportYear` ("financial" | "calendar"). This MCP tool is read-only; no `body` is accepted. **Sign convention**: revenue rows are returned as positive amounts and expense rows as negative; net profit is the algebraic sum.',
1113
+ method: 'POST',
1114
+ pathTemplate: '/partners/reports/pnl',
1115
+ inputSchema: queryInputSchema,
1116
+ requiresUserAuth: true,
1117
+ readOnly: true,
1118
+ destructive: false,
1119
+ },
1120
+ {
1121
+ name: 'COUNT_generate_balance_sheet',
1122
+ title: 'Generate Balance Sheet',
1123
+ description: 'Generate a balance sheet for the authenticated workspace. Put filters under `query`: required `endDate` (YYYY-MM-DD — the as-of date); optional `startDate` (used as the prior-period comparison anchor), `currency`, `transactionStatus`, `accounts`, `tags`. This MCP tool is read-only; no `body` is accepted. Returns Assets / Liabilities / Equity sections with subtotals; the report fails to balance only when there are unposted manual entries — those are surfaced in the response payload.',
1124
+ method: 'POST',
1125
+ pathTemplate: '/partners/reports/balance-sheet',
1126
+ inputSchema: queryInputSchema,
1127
+ requiresUserAuth: true,
1128
+ readOnly: true,
1129
+ destructive: false,
1130
+ },
1131
+ // ---------------- Workspace Stats (aggregated business snapshot) ----------------
1132
+ {
1133
+ name: 'COUNT_get_workspace_stats',
1134
+ title: 'Get Workspace Stats',
1135
+ description: 'Aggregated business snapshot for the authenticated workspace — the one-stop call for a CFO-style dashboard. Returns:\n' +
1136
+ '- `workspace`: identity + accounting basis (name, currency, country, industry, fiscalYearMonth, dateFormat, cutoverDate, reportType, `bookClosedThroughDate`).\n' +
1137
+ '- `cash`: `currentCash` (Cash & Bank + Credit Cards across provider, reconciliation, and COUNT-book sources — credit-card debt subtracts from cash), `bankAndCashAssetsTotal`, `creditCardLiabilitiesTotal`, `accountSourceCounts` (how many accounts contributed via each source), `staleProviderBalanceAccountCount` (provider balances older than 24h), `currentCashAsOf`.\n' +
1138
+ '- `profitability`: `currentMonth`, `currentQuarter`, `yearToDate`, `trailingTwelveMonths`, `priorYearSamePeriod` each as `{ revenue, expenses, netProfit, startDate, endDate }`. Sign convention: revenue and expenses are both positive numbers; `netProfit = revenue - expenses`. Plus `rollingThreeMonths` with monthly averages, `netBurn`, and `runwayMonths` (null when not burning).\n' +
1139
+ '- `receivables`: `totalOutstanding`, `currentDueTotal`, `overdueTotal`, `overdueCount`, `agingBuckets` (notYetOverdue/lessThanOrEqualTo30/between31And60/between61And90/moreThan90), `oldestOutstandingDate`, `topCustomersOutstanding[]` (with COUNT customer UUIDs), `daysSalesOutstanding`.\n' +
1140
+ '- `payables`: same shape as receivables for vendors, plus `outstandingBills` (gross document-side unpaid total), `dueInNext7Days`, `dueInNext30Days`, and `daysPayableOutstanding`.\n' +
1141
+ '- `taxObligations`: `isTaxRegistered`, `taxRegime` (`"NZ_GST"` or null), `totalObligation` (positive = owed to IRD, negative = refund), `hasOverdueOrUpcomingFiling`, `obligations[]` (per period with status + dueDate), `lastFiledPeriodEnd`. `irdAuthorizationFailed` set when IRD auth is stale.\n' +
1142
+ '- `connections`: `totalCount`, `activeCount`, `disconnectedCount`, `revokedCount`, `accountsAwaitingReauthCount`, `staleConnectionCount24h`, `oldestLastSyncAt`, `byProvider`.\n' +
1143
+ '- `asOf`: `computedAt`, `cacheTtlSeconds`, `cashAsOf`, `currency`, `fromCache` (true when this response came from the 5-minute Redis cache).\n\n' +
1144
+ 'Optional `query.include` is a comma-separated list of blocks to compute (omit any block you do not need): `workspace,cash,profitability,receivables,payables,taxObligations,connections`. Omitting `include` returns every block. This is the cheapest single call to populate an end-of-period dashboard or a "how is the business doing right now" summary.',
1145
+ method: 'GET',
1146
+ pathTemplate: '/partners/workspace-stats',
1147
+ inputSchema: queryInputSchema,
1148
+ requiresUserAuth: true,
1149
+ readOnly: true,
1150
+ destructive: false,
1151
+ },
1152
+ ];
1153
+ export const toolDefinitions = tools;
1154
+ export function getToolDefinition(params) {
1155
+ const { toolName } = params;
1156
+ return toolDefinitions.find((_tool) => _tool.name === toolName);
1157
+ }
1158
+ //# sourceMappingURL=definitions.js.map