jaz-clio 5.4.3 → 5.4.5
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/README.md +4 -4
- package/assets/skills/api/SKILL.md +2 -2
- package/assets/skills/cli/SKILL.md +4 -3
- package/assets/skills/cli/references/command-catalog.md +1 -1
- package/assets/skills/conversion/SKILL.md +1 -1
- package/assets/skills/jobs/SKILL.md +1 -1
- package/assets/skills/practice/SKILL.md +1 -1
- package/assets/skills/transaction-recipes/SKILL.md +1 -1
- package/cli.mjs +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,8 +18,8 @@ Requires **Node.js 18+** ([nodejs.org](https://nodejs.org)). Also fully compatib
|
|
|
18
18
|
## Contents
|
|
19
19
|
|
|
20
20
|
- [Three Ways In](#three-ways-in) — CLI, MCP, Skills
|
|
21
|
-
- [CLI](#cli) —
|
|
22
|
-
- [MCP Server](#mcp-server) —
|
|
21
|
+
- [CLI](#cli) — 55 command groups
|
|
22
|
+
- [MCP Server](#mcp-server) — 274 tools for AI agents
|
|
23
23
|
- [Skills](#skills) — Teach any AI the Jaz API
|
|
24
24
|
- [Setup](#setup) — Auth, multi-org, automation
|
|
25
25
|
|
|
@@ -27,7 +27,7 @@ Requires **Node.js 18+** ([nodejs.org](https://nodejs.org)). Also fully compatib
|
|
|
27
27
|
|
|
28
28
|
| | What happens | Try it |
|
|
29
29
|
|---|---|---|
|
|
30
|
-
| **CLI** |
|
|
30
|
+
| **CLI** | 55 command groups, every accounting operation | `clio invoices list` |
|
|
31
31
|
| **MCP** | Plug into Claude Code, Cursor, Codex, Copilot | `clio mcp` |
|
|
32
32
|
| **Skills** | Teach any AI tool the Jaz API | `clio init` |
|
|
33
33
|
|
|
@@ -48,7 +48,7 @@ clio practice onboard --name "Acme Pte Ltd" --fy-end 12-31 --gst quarterly
|
|
|
48
48
|
clio practice create-engagement acme-pte-ltd --type monthly-close --period 2026-03
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
55 command groups. 16 report types. 13 calculators. 12 job playbooks. New in v5.2.0: practitioner workspace (`clio practice`). Every command supports `--json`. Run `clio --help` for the full list.
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-api
|
|
3
|
-
version: 5.4.
|
|
3
|
+
version: 5.4.5
|
|
4
4
|
description: >-
|
|
5
5
|
Use this skill whenever you call, debug, or review code that touches the Jaz
|
|
6
|
-
REST API. Covers field names, response shapes,
|
|
6
|
+
REST API. Covers field names, response shapes, 141 production gotchas, error
|
|
7
7
|
recovery (422/400/404/500), search filters, pagination, and edge cases for
|
|
8
8
|
every endpoint — invoices, bills, credit notes, journals, cash entries,
|
|
9
9
|
payments, contacts, CoA, items, tax profiles, bank records, fixed assets,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-cli
|
|
3
|
-
version: 5.4.
|
|
3
|
+
version: 5.4.5
|
|
4
4
|
description: >-
|
|
5
5
|
Use this skill when running Clio CLI commands, building shell scripts with
|
|
6
6
|
Clio, debugging auth issues, understanding --json output, paginating results,
|
|
@@ -8,6 +8,7 @@ description: >-
|
|
|
8
8
|
command groups, auth precedence, output formats, entity resolution, and common
|
|
9
9
|
workflow patterns. Also use when the user asks how to use clio, what commands
|
|
10
10
|
are available, or how to automate accounting tasks from the command line.
|
|
11
|
+
Covers all 55 command groups and 274 agent tools.
|
|
11
12
|
license: MIT
|
|
12
13
|
compatibility: Requires Node.js >= 18.0.0. Install via npm install -g jaz-clio.
|
|
13
14
|
---
|
|
@@ -16,7 +17,7 @@ compatibility: Requires Node.js >= 18.0.0. Install via npm install -g jaz-clio.
|
|
|
16
17
|
|
|
17
18
|
> **Audience note:** for power users and CI/automation. Most practitioners using Claude Desktop or `jaz-practice` workflows can ignore this skill — the practice MCP tools (`practice_*`) cover the day-to-day flows. Load this skill only when you're scripting from a terminal, building shell pipelines, or debugging from `clio --json` output.
|
|
18
19
|
|
|
19
|
-
You are working with **Clio** (`jaz-clio`) — the CLI for the Jaz accounting platform.
|
|
20
|
+
You are working with **Clio** (`jaz-clio`) — the CLI for the Jaz accounting platform. 55 command groups, 13 calculators, 12 job blueprints, 274 agent tools. Also fully compatible with Juan Accounting (same API, same endpoints).
|
|
20
21
|
|
|
21
22
|
## When to Use This Skill
|
|
22
23
|
|
|
@@ -33,7 +34,7 @@ You are working with **Clio** (`jaz-clio`) — the CLI for the Jaz accounting pl
|
|
|
33
34
|
| Need | Skill |
|
|
34
35
|
|------|-------|
|
|
35
36
|
| CLI command syntax, flags, output | **jaz-cli** (this skill) |
|
|
36
|
-
| API field names, error codes,
|
|
37
|
+
| API field names, error codes, 141 gotchas | **jaz-api** |
|
|
37
38
|
| IFRS transaction recipes (depreciation, leases, loans) | **jaz-recipes** |
|
|
38
39
|
| Month-end close, bank recon, GST filing workflows | **jaz-jobs** |
|
|
39
40
|
| Migration from Xero/QuickBooks/Sage | **jaz-conversion** |
|
package/cli.mjs
CHANGED
|
@@ -816,7 +816,7 @@ Returns {data: {errors: []}} on success. accountResourceId must be a bank-type C
|
|
|
816
816
|
- Optional: contactResourceId (pre-fills contact on created DCE), internalNotes, currencySettings, taxCurrencySettings
|
|
817
817
|
Minimum payload: { reconcileWithDirectCashEntry: { amountAllocationType: "PERCENTAGE", reference: "{{bankReference}}", percentageAllocation: [{ organizationAccountResourceId: "<acct>", amount: 100 }] } }
|
|
818
818
|
Full payload: { reconcileWithDirectCashEntry: { amountAllocationType: "PERCENTAGE", reference: "{{bankPayee}} - {{bankDescription}}", percentageAllocation: [...], fixedAllocation: [...], contactResourceId: "<uuid>", internalNotes: "Auto-reconciled", tags: ["auto"], taxInclusion: false, taxVatApplicable: false } }
|
|
819
|
-
Dynamic strings for name/reference: {{bankReference}} (e.g., INV-03/01/2025-01), {{bankPayee}} (e.g., Fruit Planet), {{bankDescription}} (e.g., QR Payment) \u2014 replaced with actual bank record values during reconciliation.`,params:{name:{type:"string",description:"Rule name \u2014 supports dynamic strings: {{bankReference}}, {{bankPayee}}, {{bankDescription}}"},appliesToReconciliationAccount:{type:"string",description:"Bank account UUID this rule applies to"},configuration:{type:"object",description:"MUST nest under reconcileWithDirectCashEntry key. reference is REQUIRED. See tool description for full structure."}},required:["name","appliesToReconciliationAccount","configuration"],group:"bank_rules",readOnly:!1,searchHint:"create automatic bank reconciliation rule",execute:async(e,t)=>YS(e.client,t)},{name:"update_bank_rule",description:"Update an existing bank reconciliation rule. FULL REPLACEMENT \u2014 the API replaces the entire rule on update. You MUST provide the complete configuration, appliesToReconciliationAccount, and name every time. Use get_bank_rule first to read the current state, merge your changes, then call this tool with the full payload. Dynamic strings: {{bankReference}}, {{bankPayee}}, {{bankDescription}}.",params:{resourceId:{type:"string",description:"Bank rule resourceId"},name:{type:"string",description:"Rule name \u2014 supports dynamic strings: {{bankReference}}, {{bankPayee}}, {{bankDescription}}"},appliesToReconciliationAccount:{type:"string",description:"Bank account UUID this rule applies to (required for update \u2014 full replacement)"},configuration:{type:"object",description:"Full configuration object \u2014 must nest under reconcileWithDirectCashEntry key (same structure as create). reference is REQUIRED."}},required:["resourceId","name","appliesToReconciliationAccount","configuration"],group:"bank_rules",readOnly:!1,searchHint:"update bank rule conditions actions matching",execute:async(e,t)=>JS(e.client,t.resourceId,{resourceId:t.resourceId,name:t.name,appliesToReconciliationAccount:t.appliesToReconciliationAccount,configuration:t.configuration})},mn("delete_bank_rule","Delete a bank reconciliation rule.","bank_rules",(e,t)=>GS(e,t),"permanently delete a bank reconciliation rule"),{name:"view_auto_reconciliation",description:"Generate auto-reconciliation
|
|
819
|
+
Dynamic strings for name/reference: {{bankReference}} (e.g., INV-03/01/2025-01), {{bankPayee}} (e.g., Fruit Planet), {{bankDescription}} (e.g., QR Payment) \u2014 replaced with actual bank record values during reconciliation.`,params:{name:{type:"string",description:"Rule name \u2014 supports dynamic strings: {{bankReference}}, {{bankPayee}}, {{bankDescription}}"},appliesToReconciliationAccount:{type:"string",description:"Bank account UUID this rule applies to"},configuration:{type:"object",description:"MUST nest under reconcileWithDirectCashEntry key. reference is REQUIRED. See tool description for full structure."}},required:["name","appliesToReconciliationAccount","configuration"],group:"bank_rules",readOnly:!1,searchHint:"create automatic bank reconciliation rule",execute:async(e,t)=>YS(e.client,t)},{name:"update_bank_rule",description:"Update an existing bank reconciliation rule. FULL REPLACEMENT \u2014 the API replaces the entire rule on update. You MUST provide the complete configuration, appliesToReconciliationAccount, and name every time. Use get_bank_rule first to read the current state, merge your changes, then call this tool with the full payload. Dynamic strings: {{bankReference}}, {{bankPayee}}, {{bankDescription}}.",params:{resourceId:{type:"string",description:"Bank rule resourceId"},name:{type:"string",description:"Rule name \u2014 supports dynamic strings: {{bankReference}}, {{bankPayee}}, {{bankDescription}}"},appliesToReconciliationAccount:{type:"string",description:"Bank account UUID this rule applies to (required for update \u2014 full replacement)"},configuration:{type:"object",description:"Full configuration object \u2014 must nest under reconcileWithDirectCashEntry key (same structure as create). reference is REQUIRED."}},required:["resourceId","name","appliesToReconciliationAccount","configuration"],group:"bank_rules",readOnly:!1,searchHint:"update bank rule conditions actions matching",execute:async(e,t)=>JS(e.client,t.resourceId,{resourceId:t.resourceId,name:t.name,appliesToReconciliationAccount:t.appliesToReconciliationAccount,configuration:t.configuration})},mn("delete_bank_rule","Delete a bank reconciliation rule.","bank_rules",(e,t)=>GS(e,t),"permanently delete a bank reconciliation rule"),{name:"view_auto_reconciliation",description:"Generate auto-reconciliation suggestions for a bank account. READ-ONLY \u2014 does NOT write reconciliations. To execute a reconciliation, use quick_reconcile (bulk async) or apply_bank_rule (rule-driven async) or one of the per-entry reconcile_* tools (sync). Returns magic match suggestions.",params:{bankAccountResourceId:{type:"string",description:"Bank account resourceId"},recommendationType:{type:"string",enum:["MAGIC_MATCH","MAGIC_RECONCILE_WITH_CASH_TRANSFER","MAGIC_RECONCILE_WITH_BANK_RULE","MAGIC_QUICK_RECONCILE"],description:"Type of recommendation to generate"}},required:["recommendationType"],group:"bank",readOnly:!0,searchHint:"view automatic bank reconciliation matches",isConcurrencySafe:!0,execute:async(e,t)=>{try{return await e.client.post("/api/v1/search-magic-reconciliation",{bankAccountResourceId:t.bankAccountResourceId,recommendationType:t.recommendationType})}catch(r){if((r.status??r.response?.status)===404)return{notSupported:!0,reason:"Endpoint /search-magic-reconciliation not available"};throw r}}},yt("list_fixed_assets","List fixed assets. Returns name, reference, type, category, status, purchase amount, net book value.","fixed_assets",(e,t,r)=>VS(e,{limit:r,offset:t}),"list fixed assets with depreciation status"),Hn("get_fixed_asset","Get full fixed asset details including depreciation schedule, accounts, disposal info.","fixed_assets",(e,t)=>HS(e,t),"get fixed asset details depreciation schedule"),Rt({name:"search_fixed_assets",description:"Search fixed assets by name, status, category, tag, type, date, amount range.",group:"fixed_assets",fields:ef,defaults:tf,fetcher:WS,searchHint:"find fixed assets by name status category amount"}),{name:"create_fixed_asset",description:`Register a fixed asset linked to an existing purchase transaction (bill or journal).
|
|
820
820
|
- saveAsDraft defaults to true. Set to false to activate \u2014 requires ALL fields below.
|
|
821
821
|
- ACTIVE assets require: purchaseBusinessTransactionType + purchaseBusinessTransactionResourceId
|
|
822
822
|
(links to the bill/journal that recorded the purchase).
|
|
@@ -862,7 +862,7 @@ Response: { updated: string[], failed: [{ resourceId, error, errorCode }] }. On
|
|
|
862
862
|
|
|
863
863
|
DO NOT use this tool for analytical or audit reports (anomalous invoices, anomalous bills, cashflow anomalies, GL journal audit, exchange rate audit, receivables customer risk, cash expense health), financial statements (trial balance, P&L, balance sheet, cashflow, general ledger), aging (AR/AP report), summary reports (sales-summary, purchase-summary, etc.), or statement of account. Those go through download_export.`,params:{entityType:$$,query:dG,filter:pG,columns:fG,sort:mG},required:["entityType"],group:"export_records",readOnly:!0,isConcurrencySafe:!0,searchHint:"export records download xlsx file url",execute:async(e,t)=>w_(e.client,{entityType:t.entityType,outputFormat:"XLSX",query:t.query,filter:t.filter,columns:t.columns,sort:t.sort})},Rt({name:"search_background_jobs",description:"Search and poll background jobs. ANY operation that returns a jobId (bulk_upsert_contacts, bulk_upsert_items, bank statement import, magic file processing, etc.) can be tracked here. Filter by resourceId (the jobId value) to poll a specific job. Poll until status is SUCCESS, FAILED, or PARTIAL_SUCCESS. Use processedCount/failedCount/totalRecords for progress. IMPORTANT: filter by resourceId field, NOT jobId \u2014 they differ.",group:"background_jobs",fields:of,defaults:sf,fetcher:eh,searchHint:"poll background job status by jobId type status"}),{name:"bulk_upsert_contacts",description:"Create or update contacts in bulk (max 500 per call). Provide resourceId per contact to update (partial \u2014 omitted fields preserve existing values, including customer/supplier flags). Omit resourceId to create. Returns a jobId \u2014 async; poll search_background_jobs with filter {resourceId:{eq:jobId}} until status is SUCCESS / FAILED / PARTIAL_SUCCESS. Per-row failures live in the background job's errorDetails (NOT failedRows \u2014 that's the sync bulk-upsert pattern). Common 422 cases (whole batch rejected at request level): at least one of `customer` or `supplier` must be true per contact (after defaults+backfill from existing); duplicate emails within a contact's emailList (case-insensitive after trim); payment-terms `value` must be a positive integer when `name` != CUSTOM; duplicate names within the batch (after whitespace+case normalize); when a billingAddress / shippingAddress object is provided, its `addressLine1` field is required.",params:{contacts:{type:"array",description:"Array of contacts to create or update (max 500)",items:{type:"object",properties:{resourceId:{type:"string",description:"Contact resourceId (omit for create, provide UUID for update)"},billingName:{type:"string",description:"Billing name (required for create)"},name:{type:"string",description:"Display name (defaults to billingName)"},emails:{type:"array",items:{type:"string"},description:"Email addresses"},customer:{type:"boolean",description:"Mark as customer"},supplier:{type:"boolean",description:"Mark as supplier"},taxId:{type:"string",description:"Tax ID / GST registration number"},taxIdType:{type:"string",description:"Tax ID type (e.g., GST, VAT)"},registrationNumber:{type:"string",description:"Business registration number"},currencyCode:{type:"string",description:"Default currency code (e.g., SGD)"},status:{type:"string",enum:["ACTIVE","INACTIVE"],description:"Contact status"},paymentTerms:{type:"number",description:"Payment terms in days (e.g., 30 for Net 30)"},notes:{type:"string",description:"Internal notes"},billingAddress:{type:"object",description:"Billing address",properties:{address:{type:"string"},city:{type:"string"},state:{type:"string"},postalCode:{type:"string"},country:{type:"string"}}},shippingAddress:{type:"object",description:"Shipping address",properties:{address:{type:"string"},city:{type:"string"},state:{type:"string"},postalCode:{type:"string"},country:{type:"string"}}}}}}},required:["contacts"],group:"contacts",readOnly:!1,searchHint:"bulk create update contacts upsert",execute:async(e,t)=>(cr(t.contacts,"contacts"),jy(e.client,t.contacts))},{name:"bulk_upsert_invoices",description:"Create or update invoices in bulk (max 500 per call). FLAT shape: ONE line per invoice via `itemDescription` + `totalAmount` + `invoiceAccountResourceId` at row level. For multi-line invoices use `bulk_upsert_invoice_line_items` instead \u2014 that variant takes nested `lineItems[]`. Natural key: `invoiceReference` (rows sharing one within a batch are MERGED \u2014 last wins). Provide resourceId per invoice to update via UUID; or omit and rely on invoiceReference for upsert. Async \u2014 returns a jobId. Poll search_background_jobs with filter {resourceId:{eq:jobId}} until SUCCESS / FAILED / PARTIAL_SUCCESS; on PARTIAL_SUCCESS read data[0].errorDetails on the same response for per-row failures. Dates are ISO 8601 (YYYY-MM-DD) only \u2014 the dateFormat field was removed.",params:{invoices:{type:"array",description:"Array of invoices to create or update (max 500). Each row needs at least: invoiceReference, contactResourceId, valueDate, totalAmount, invoiceAccountResourceId.",items:{type:"object",properties:{rowIndex:{type:"string",description:"Optional caller-supplied row index for error reporting"},resourceId:{type:"string",description:"Invoice resourceId (UUID) \u2014 provide to update by ID"},invoiceReference:{type:"string",description:"Natural key (e.g., INV-2025-001) \u2014 required"},contactResourceId:{type:"string",description:"Customer contact UUID \u2014 required"},valueDate:{type:"string",description:"Invoice date YYYY-MM-DD \u2014 required"},dueDate:{type:"string",description:"Due date YYYY-MM-DD"},totalAmount:{type:"number",description:"Total amount (BigDecimal)"},currencyCode:{type:"string",description:"Currency (e.g., SGD)"},invoiceAccountResourceId:{type:"string",description:"Revenue account UUID"},itemDescription:{type:"string",description:"Single-line item description"},internalNotes:{type:"string",description:"Internal notes"},tags:{type:"array",items:{type:"string"},description:"Tag resourceIds"}}}}},required:["invoices"],group:"invoices",readOnly:!1,searchHint:"bulk create update invoices upsert import migrate",execute:async(e,t)=>{cr(t.invoices,"invoices");let r=t.invoices;return no(r,["valueDate","dueDate"]),Jb(e.client,r)}},{name:"bulk_upsert_invoice_line_items",description:"Create or update invoices with nested line items in bulk (max 500 invoices per call). Each row carries lineItems[] under its parent invoice (scoped by invoiceReference). Async \u2014 returns a jobId. Poll search_background_jobs filtered by resourceId; on PARTIAL_SUCCESS read data[0].errorDetails for per-row failures. Dates are ISO 8601 only.",params:{invoices:{type:"array",description:"Array of invoices with line items (max 500). Each invoice row has lineItems[].",items:{type:"object",properties:{rowIndex:{type:"string"},resourceId:{type:"string",description:"Invoice resourceId \u2014 provide to update by ID"},invoiceReference:{type:"string",description:"Natural key \u2014 required"},contactResourceId:{type:"string",description:"Customer UUID \u2014 required"},valueDate:{type:"string",description:"YYYY-MM-DD \u2014 required"},dueDate:{type:"string",description:"YYYY-MM-DD"},currencyCode:{type:"string"},internalNotes:{type:"string"},tags:{type:"array",items:{type:"string"}},lineItems:{type:"array",description:"Line items belonging to this invoice",items:{type:"object",properties:{itemDescription:{type:"string",description:"Line description \u2014 required"},quantity:{type:"number"},unit:{type:"string",description:"Unit of measure (pcs, kg, etc.)"},unitPrice:{type:"number"},accountResourceId:{type:"string",description:"Revenue account UUID"},taxProfileResourceId:{type:"string"}}}}}}}},required:["invoices"],group:"invoices",readOnly:!1,searchHint:"bulk invoices line items multi-line upsert import",execute:async(e,t)=>{cr(t.invoices,"invoices");let r=t.invoices;return no(r,["valueDate","dueDate"]),Gb(e.client,r)}},{name:"bulk_upsert_bills",description:"Create or update bills in bulk (max 500 per call). FLAT shape: ONE line per bill via `itemDescription` + `totalAmount` + `billAccountResourceId` at row level. For multi-line bills use `bulk_upsert_bill_line_items` instead \u2014 that variant takes nested `lineItems[]`. Natural key: `billReference` (rows sharing one within a batch are MERGED \u2014 last wins). Async \u2014 returns a jobId. Poll search_background_jobs filtered by resourceId; on PARTIAL_SUCCESS read data[0].errorDetails for per-row failures. Dates are ISO 8601 only \u2014 dateFormat field was removed.",params:{bills:{type:"array",description:"Array of bills to create or update (max 500). Each row needs at least: billReference, contactResourceId, valueDate, totalAmount, billAccountResourceId.",items:{type:"object",properties:{rowIndex:{type:"string"},resourceId:{type:"string"},billReference:{type:"string",description:"Natural key \u2014 required"},contactResourceId:{type:"string",description:"Supplier UUID \u2014 required"},valueDate:{type:"string",description:"YYYY-MM-DD \u2014 required"},dueDate:{type:"string"},totalAmount:{type:"number"},currencyCode:{type:"string"},billAccountResourceId:{type:"string",description:"Expense account UUID"},itemDescription:{type:"string"},internalNotes:{type:"string"},tags:{type:"array",items:{type:"string"}}}}}},required:["bills"],group:"bills",readOnly:!1,searchHint:"bulk create update bills upsert import migrate",execute:async(e,t)=>{cr(t.bills,"bills");let r=t.bills;return no(r,["valueDate","dueDate"]),ky(e.client,r)}},{name:"bulk_upsert_bill_line_items",description:"Create or update bills with nested line items in bulk (max 500 bills per call). Each row carries lineItems[] under its parent bill (scoped by billReference). Async \u2014 returns a jobId. Poll search_background_jobs filtered by resourceId; on PARTIAL_SUCCESS read data[0].errorDetails for per-row failures.",params:{bills:{type:"array",description:"Array of bills with line items (max 500). Each bill row has lineItems[].",items:{type:"object",properties:{rowIndex:{type:"string"},resourceId:{type:"string"},billReference:{type:"string",description:"Natural key \u2014 required"},contactResourceId:{type:"string",description:"Supplier UUID \u2014 required"},valueDate:{type:"string",description:"YYYY-MM-DD \u2014 required"},dueDate:{type:"string"},currencyCode:{type:"string"},internalNotes:{type:"string"},tags:{type:"array",items:{type:"string"}},lineItems:{type:"array",items:{type:"object",properties:{itemDescription:{type:"string",description:"Required"},quantity:{type:"number"},unit:{type:"string"},unitPrice:{type:"number"},accountResourceId:{type:"string",description:"Expense account UUID"},taxProfileResourceId:{type:"string"}}}}}}}},required:["bills"],group:"bills",readOnly:!1,searchHint:"bulk bills line items multi-line upsert import",execute:async(e,t)=>{cr(t.bills,"bills");let r=t.bills;return no(r,["valueDate","dueDate"]),Fy(e.client,r)}},{name:"bulk_upsert_customer_credit_notes",description:"Create or update customer credit notes in bulk (max 500 per call). Natural key: creditNoteReference. Async \u2014 returns a jobId. Poll search_background_jobs filtered by resourceId; on PARTIAL_SUCCESS read data[0].errorDetails for per-row failures. Dates are ISO 8601 only.",params:{customerCreditNotes:{type:"array",description:"Array of customer credit notes (max 500).",items:{type:"object",properties:{rowIndex:{type:"string"},resourceId:{type:"string"},creditNoteReference:{type:"string",description:"Natural key \u2014 required"},contactResourceId:{type:"string",description:"Customer UUID \u2014 required"},valueDate:{type:"string",description:"YYYY-MM-DD \u2014 required"},currencyCode:{type:"string"},internalNotes:{type:"string"},tags:{type:"array",items:{type:"string"}},lineItems:{type:"array",items:{type:"object",properties:{itemDescription:{type:"string",description:"Required"},quantity:{type:"number"},unitPrice:{type:"number"},accountResourceId:{type:"string"},taxProfileResourceId:{type:"string"}}}}}}}},required:["customerCreditNotes"],group:"customer_credit_notes",readOnly:!1,searchHint:"bulk customer credit notes refund upsert import",execute:async(e,t)=>{cr(t.customerCreditNotes,"customerCreditNotes",500,"customer credit notes");let r=t.customerCreditNotes;return no(r,["valueDate"]),M0(e.client,r)}},{name:"bulk_upsert_supplier_credit_notes",description:"Create or update supplier credit notes in bulk (max 500 per call). Natural key: creditNoteReference. Async \u2014 returns a jobId. Poll search_background_jobs filtered by resourceId; on PARTIAL_SUCCESS read data[0].errorDetails for per-row failures. Dates are ISO 8601 only.",params:{supplierCreditNotes:{type:"array",description:"Array of supplier credit notes (max 500).",items:{type:"object",properties:{rowIndex:{type:"string"},resourceId:{type:"string"},creditNoteReference:{type:"string",description:"Natural key \u2014 required"},contactResourceId:{type:"string",description:"Supplier UUID \u2014 required"},valueDate:{type:"string",description:"YYYY-MM-DD \u2014 required"},currencyCode:{type:"string"},internalNotes:{type:"string"},tags:{type:"array",items:{type:"string"}},lineItems:{type:"array",items:{type:"object",properties:{itemDescription:{type:"string",description:"Required"},quantity:{type:"number"},unitPrice:{type:"number"},accountResourceId:{type:"string"},taxProfileResourceId:{type:"string"}}}}}}}},required:["supplierCreditNotes"],group:"supplier_credit_notes",readOnly:!1,searchHint:"bulk supplier credit notes refund upsert import",execute:async(e,t)=>{cr(t.supplierCreditNotes,"supplierCreditNotes",500,"supplier credit notes");let r=t.supplierCreditNotes;return no(r,["valueDate"]),Y0(e.client,r)}},{name:"bulk_upsert_journals",description:"Create or update manual journals in bulk (max 500 per call). NATURAL KEY: `journalReference` (NOT `reference` \u2014 every other bulk-upsert uses entityReference, this one is asymmetric). LEGS field: `journalEntries[]` (NOT `entries[]` \u2014 different from `clio journals create` which uses entries). Each leg has `accountResourceId` + `debitAmount` + `creditAmount` (debit + credit must balance per row). Async \u2014 returns a jobId. Poll search_background_jobs filtered by resourceId; on PARTIAL_SUCCESS read data[0].errorDetails for per-row failures. Dates are ISO 8601 (YYYY-MM-DD) only \u2014 dateFormat field was removed.",params:{journals:{type:"array",description:"Array of journals to create or update (max 500). Each row has entries[] (debit/credit legs).",items:{type:"object",properties:{rowIndex:{type:"string"},resourceId:{type:"string"},journalReference:{type:"string",description:"Natural key \u2014 required (NOTE: field is `journalReference`, not `reference`)"},valueDate:{type:"string",description:"YYYY-MM-DD \u2014 required"},currencyCode:{type:"string"},internalNotes:{type:"string"},tags:{type:"array",items:{type:"string"}},journalEntries:{type:"array",description:"Journal legs (debit + credit amounts must balance) \u2014 field is `journalEntries`, NOT `entries`",items:{type:"object",properties:{accountResourceId:{type:"string",description:"Required"},description:{type:"string"},debitAmount:{type:"number"},creditAmount:{type:"number"},contactResourceId:{type:"string"},taxProfileResourceId:{type:"string"}}}}}}}},required:["journals"],group:"journals",readOnly:!1,searchHint:"bulk manual journals upsert import migration",execute:async(e,t)=>{cr(t.journals,"journals");let r=t.journals;return no(r,["valueDate"]),Qb(e.client,r)}},{name:"bulk_upsert_fixed_assets",description:'\u26A0\uFE0F DATE FIELD MISMATCH: bulk-upsert REQUEST uses `valueDate` (not `purchaseDate`); the GET response uses `purchaseDate`. Sending `purchaseDate` returns a cryptic 400 "Invalid request body" with no field detail. Create or update fixed assets in bulk (max 500 per call). Natural key: `reference`. Each row REQUIRED: `reference`, `registrationType` (NEW | TRANSFER). Recommended: `typeCode`, `typeName`, `category` (TANGIBLE | INTANGIBLE), `cost` (or synonym `purchaseAmount`), `valueDate`, `depreciationStartDate`, `effectiveLife` (or synonym `usefulLifeMonths`), `depreciationMethod`, `purchaseAssetAccountResourceId`, `depreciationExpenseAccountResourceId`, `accumulatedDepreciationAccountResourceId`. Account fields must be valid UUIDv4. Async \u2014 returns a jobId. Poll search_background_jobs filtered by resourceId; on PARTIAL_SUCCESS read data[0].errorDetails for per-row failures. Dates are ISO 8601 (YYYY-MM-DD) only.',params:{fixedAssets:{type:"array",description:"Array of fixed assets to create or update (max 500).",items:{type:"object",properties:{rowIndex:{type:"string"},resourceId:{type:"string"},reference:{type:"string",description:"Natural key \u2014 required"},registrationType:{type:"string",enum:["NEW","TRANSFER"],description:"NEW = newly acquired, TRANSFER = inherited from prior system \u2014 required"},typeCode:{type:"string",description:"Asset class code (e.g., FURNITURE_AND_FIXTURE)"},typeName:{type:"string",description:'Human label for the typeCode (e.g., "Furniture and Fixtures")'},category:{type:"string",description:"TANGIBLE | INTANGIBLE"},name:{type:"string",description:"Asset name"},valueDate:{type:"string",description:"Acquisition date YYYY-MM-DD (NOTE: bulk uses `valueDate`, the GET response uses `purchaseDate` \u2014 different fields)"},cost:{type:"number",description:"Cost basis (alias for purchaseAmount on the bulk endpoint)"},purchaseAmount:{type:"number",description:"Cost basis (synonym of `cost`)"},currencyCode:{type:"string"},depreciationStartDate:{type:"number",description:'Epoch milliseconds (NOT YYYY-MM-DD \u2014 inconsistent with valueDate which IS YYYY-MM-DD on this same endpoint). Sending YYYY-MM-DD returns generic 400 "Invalid request body" with no detail. Omit to default to valueDate.'},effectiveLife:{type:"number",description:"Useful life in months (alias for usefulLifeMonths)"},usefulLifeMonths:{type:"number",description:"Useful life in months (synonym of effectiveLife)"},depreciationMethod:{type:"string",description:"e.g., STRAIGHT_LINE, DOUBLE_DECLINING_BALANCE"},purchaseAssetAccountResourceId:{type:"string",description:"Asset (PPE) account UUID \u2014 must be valid UUIDv4"},depreciationExpenseAccountResourceId:{type:"string",description:"Depreciation expense account UUID \u2014 must be valid UUIDv4"},accumulatedDepreciationAccountResourceId:{type:"string",description:"Accumulated depreciation account UUID"},internalNotes:{type:"string"},tags:{type:"array",items:{type:"string"}}}}}},required:["fixedAssets"],group:"fixed_assets",readOnly:!1,searchHint:"bulk fixed assets ppe register upsert import migrate",execute:async(e,t)=>{cr(t.fixedAssets,"fixedAssets",500,"fixed assets");let r=t.fixedAssets;return no(r,["valueDate"]),r_(e.client,r)}},{name:"quick_reconcile",description:"Bulk-reconcile bank statement entries against a list of journals (max 500). Reconciliation mode is hardcoded to QUICK_RECON. Async \u2014 returns a jobId. Poll search_background_jobs filtered by resourceId; on PARTIAL_SUCCESS read data[0].errorDetails. Caller provides bankAccountResourceId + journalsForReconciliation[] (each row: bankStatementEntryResourceId, journalReference, journalAccountResourceId).",params:{bankAccountResourceId:{type:"string",description:"Bank account UUID (the entries belong to this account)"},journalsForReconciliation:{type:"array",description:"Per-row reconciliation instructions (max 500).",items:{type:"object",properties:{bankStatementEntryResourceId:{type:"string",description:"Bank entry UUID \u2014 required"},journalReference:{type:"string",description:"Journal reference text \u2014 required"},journalAccountResourceId:{type:"string",description:"Offsetting account UUID (revenue for cash-in, expense for cash-out) \u2014 required"},contactResourceId:{type:"string",description:"Contact UUID"},capsuleResourceId:{type:"string",description:"Capsule UUID"},taxProfileResourceId:{type:"string",description:"Tax profile UUID"},internalNotes:{type:"string"},journalEntryDescription:{type:"string"},tags:{type:"array",items:{type:"string"}},rowIndex:{type:"string",description:"Optional caller-supplied index for error correlation"}}}}},required:["bankAccountResourceId","journalsForReconciliation"],group:"reconciliations",readOnly:!1,searchHint:"bulk reconcile bank statement journals match",execute:async(e,t)=>(cr(t.journalsForReconciliation,"journalsForReconciliation",500,"journals"),k_(e.client,t))},{name:"apply_bank_rule",description:"Apply a pre-configured bank rule (action shortcut) to a batch of bank statement entries (max 500). Async \u2014 returns a jobId. Poll search_background_jobs filtered by resourceId. The rule executes its configured action (e.g. RECONCILE_WITH_DIRECT_CASH_ENTRY) on each entry.",params:{actionShortcutResourceId:{type:"string",description:"Bank rule UUID \u2014 required"},businessTransactionResourceIds:{type:"array",description:"Bank statement entry UUIDs to apply the rule to (max 500). Note: the field is named businessTransactionResourceIds for legacy reasons but accepts bank entry IDs.",items:{type:"string"}}},required:["actionShortcutResourceId","businessTransactionResourceIds"],group:"reconciliations",readOnly:!1,searchHint:"bank rule apply action shortcut bulk reconcile",execute:async(e,t)=>(cr(t.businessTransactionResourceIds,"businessTransactionResourceIds",500,"entries"),F_(e.client,t))},{name:"reconcile_direct_cash_entry",description:"Reconcile a bank statement entry with a single cash-in / cash-out line. Direction (cash-in vs cash-out) is INFERRED from the bank entry sign. Sync \u2014 returns the reconciled entry status. NOT idempotent: calling twice on the same bankStatementEntryResourceId creates duplicate journals. Confirm reconciled-state via view_auto_reconciliation before retrying.",params:{bankStatementEntryResourceId:{type:"string",description:"Bank entry UUID \u2014 required"},bankAccountResourceId:{type:"string",description:"Bank account UUID (used to look up the entry) \u2014 required"},reference:{type:"string",description:"Reference for the cash entry \u2014 required"},accountResourceId:{type:"string",description:"Offsetting account UUID (revenue for cash-in, expense for cash-out) \u2014 required"},amount:{type:"number",description:"Amount in source currency. Defaults to bank entry amount if omitted."},description:{type:"string"},contactResourceId:{type:"string"},taxProfileResourceId:{type:"string"},internalNotes:{type:"string"},bankAccountJournalEntryDescription:{type:"string",description:"Description for the bank-side journal entry"},tags:{type:"array",items:{type:"string"}},taxVatApplicable:{type:"boolean"},taxInclusion:{type:"boolean"},capsuleResourceId:{type:"string"}},required:["bankStatementEntryResourceId","bankAccountResourceId","reference","accountResourceId"],group:"reconciliations",readOnly:!1,searchHint:"reconcile bank entry direct cash in out single line",execute:async(e,t)=>P_(e.client,t)},{name:"reconcile_cash_journal",description:"Reconcile a bank statement entry with a multi-line cashflow journal (multiple cash splits, max 200 lines). Sync \u2014 returns the reconciled entry status. NOT idempotent \u2014 confirm reconciled-state before retrying.",params:{bankStatementEntryResourceId:{type:"string",description:"Bank entry UUID \u2014 required"},bankAccountResourceId:{type:"string",description:"Bank account UUID \u2014 required"},reference:{type:"string",description:"Journal reference \u2014 required"},journalEntries:{type:"array",description:"Cash journal lines (max 200). Each row is one debit OR credit against an account.",items:{type:"object",properties:{accountResourceId:{type:"string",description:"Account UUID \u2014 required"},amount:{type:"number",description:"Amount in source currency \u2014 required (>0)"},description:{type:"string"},taxProfileResourceId:{type:"string"}}}},contactResourceId:{type:"string"},bankAccountJournalEntryDescription:{type:"string"},internalNotes:{type:"string"},tags:{type:"array",items:{type:"string"}},taxVatApplicable:{type:"boolean"},taxInclusion:{type:"boolean"},capsuleResourceId:{type:"string"}},required:["bankStatementEntryResourceId","bankAccountResourceId","reference","journalEntries"],group:"reconciliations",readOnly:!1,searchHint:"reconcile bank entry multi line cash journal splits",execute:async(e,t)=>(cr(t.journalEntries,"journalEntries",200,"entries"),L_(e.client,t))},{name:"reconcile_manual_journal",description:'\u26A0\uFE0F CALLER PROVIDES ONLY THE OFFSET LEG(S) \u2014 backend AUTO-ADDS the bank-side leg from the statement entry. Sending both debit AND credit legs \u2192 422 "sum of debit and credit amounts are not equal" (your sides duplicate after the auto-add). Reconcile a bank statement entry with a double-entry manual journal. Sync \u2014 returns the reconciled entry status. valueDate prefills from bank entry if omitted. NOT idempotent.',params:{bankStatementEntryResourceId:{type:"string",description:"Bank entry UUID \u2014 required"},bankAccountResourceId:{type:"string",description:"Bank account UUID \u2014 required"},status:{type:"string",enum:["DRAFT","ACTIVE"],description:"Journal status \u2014 required"},journalEntries:{type:"array",description:"Offset-side journal entries only (max 200). The bank-side leg is auto-added by the API.",items:{type:"object",properties:{accountResourceId:{type:"string",description:"Account UUID \u2014 required"},type:{type:"string",enum:["DEBIT","CREDIT"]},amount:{type:"number",description:"Amount \u2014 required"},description:{type:"string"},contactResourceId:{type:"string"},taxProfileResourceId:{type:"string"}}}},reference:{type:"string"},valueDate:{type:"string",description:"YYYY-MM-DD. Defaults to bank entry value date."},contactResourceId:{type:"string"},internalNotes:{type:"string"},bankAccountJournalEntryDescription:{type:"string"},taxVatApplicable:{type:"boolean"},taxInclusion:{type:"boolean"},tags:{type:"array",items:{type:"string"}},capsuleResourceId:{type:"string"}},required:["bankStatementEntryResourceId","bankAccountResourceId","status","journalEntries"],group:"reconciliations",readOnly:!1,searchHint:"reconcile bank entry manual journal double entry",execute:async(e,t)=>(cr(t.journalEntries,"journalEntries",200,"entries"),j_(e.client,t))},{name:"reconcile_cash_transfer",description:"Reconcile a bank statement entry with an inter-account transfer. Sync \u2014 returns the reconciled entry status. amount is required only when the counterparty account is in a non-functional currency. NOT idempotent.",params:{bankStatementEntryResourceId:{type:"string",description:"Bank entry UUID \u2014 required"},bankAccountResourceId:{type:"string",description:"Bank account UUID \u2014 required"},accountResourceId:{type:"string",description:"Counterparty cash account UUID \u2014 required"},reference:{type:"string",description:"Transfer reference \u2014 required"},amount:{type:"number",description:"Amount in source currency. Required only for cross-currency transfers."},contactResourceId:{type:"string"},internalNotes:{type:"string"},tags:{type:"array",items:{type:"string"}},capsuleResourceId:{type:"string"}},required:["bankStatementEntryResourceId","bankAccountResourceId","accountResourceId","reference"],group:"reconciliations",readOnly:!1,searchHint:"reconcile bank entry transfer between accounts",execute:async(e,t)=>M_(e.client,t)},{name:"reconcile_invoice_receipt",description:'\u26A0\uFE0F BSE must have `paymentDirection: PAYIN` (money in \u2014 AR). Produce via `bank add-records` with a POSITIVE amount (creates `credit_amount > 0` \u2192 PAYIN). Statement-imported BSEs from `bank import` also work. The error code "Invalid business transaction type" (422) is misleadingly named \u2014 the actual gate is direction, not entry type. Create an invoice and auto-reconcile it to a bank statement entry (AR). Sync \u2014 returns the reconciled entry status. valueDate / dueDate / payment amount prefill from the bank entry if omitted. NOT idempotent.',params:{bankStatementEntryResourceId:{type:"string",description:"Bank entry UUID \u2014 required"},bankAccountResourceId:{type:"string",description:"Bank account UUID \u2014 required"},invoiceDetails:{type:"object",description:"The invoice to create. Required.",properties:{reference:{type:"string",description:"Invoice reference \u2014 required"},contactResourceId:{type:"string",description:"Customer UUID"},valueDate:{type:"string",description:"YYYY-MM-DD. Defaults to bank entry value date."},dueDate:{type:"string",description:"YYYY-MM-DD. Defaults to value date."},taxInclusion:{type:"boolean"},isTaxVATApplicable:{type:"boolean"},lineItems:{type:"array",description:"Invoice line items (max 500) \u2014 required",items:{type:"object",properties:{name:{type:"string",description:"Line description \u2014 required"},quantity:{type:"number"},unit:{type:"string"},unitPrice:{type:"number"},organizationAccountResourceId:{type:"string",description:"Revenue account UUID"},itemResourceId:{type:"string"},taxProfileResourceId:{type:"string"}}}},recordedPayment:{type:"object",description:"Optional. The matching payment record \u2014 auto-built from the bank entry if omitted.",properties:{reference:{type:"string"},valueDate:{type:"string",description:"YYYY-MM-DD. Defaults to bank entry value date."},organizationAccountResourceId:{type:"string"},paymentAmount:{type:"number"},transactionAmount:{type:"number"},paymentMethod:{type:"string",description:"Defaults to BANK_TRANSFER"}}},capsuleResourceId:{type:"string"}}}},required:["bankStatementEntryResourceId","bankAccountResourceId","invoiceDetails"],group:"reconciliations",readOnly:!1,searchHint:"reconcile bank entry invoice receipt AR sale",execute:async(e,t)=>(w$(t.invoiceDetails,"invoiceDetails"),B_(e.client,t))},{name:"reconcile_bill_receipt",description:'\u26A0\uFE0F BSE must have `paymentDirection: PAYOUT` (money out \u2014 AP). Produce via `bank add-records` with a NEGATIVE amount (creates `debit_amount > 0` \u2192 PAYOUT). Statement-imported BSEs from `bank import` also work. The error code "Invalid business transaction type" (422) is misleadingly named \u2014 the actual gate is direction, not entry type. Create a bill and auto-reconcile it to a bank statement entry (AP). Sync \u2014 returns the reconciled entry status. valueDate / dueDate / payment amount prefill from the bank entry if omitted. NOT idempotent.',params:{bankStatementEntryResourceId:{type:"string",description:"Bank entry UUID \u2014 required"},bankAccountResourceId:{type:"string",description:"Bank account UUID \u2014 required"},billDetails:{type:"object",description:"The bill to create. Required.",properties:{reference:{type:"string",description:"Bill reference \u2014 required"},contactResourceId:{type:"string",description:"Supplier UUID"},valueDate:{type:"string",description:"YYYY-MM-DD. Defaults to bank entry value date."},dueDate:{type:"string",description:"YYYY-MM-DD. Defaults to value date."},taxInclusion:{type:"boolean"},isTaxVATApplicable:{type:"boolean"},lineItems:{type:"array",description:"Bill line items (max 500) \u2014 required",items:{type:"object",properties:{name:{type:"string",description:"Line description \u2014 required"},quantity:{type:"number"},unit:{type:"string"},unitPrice:{type:"number"},organizationAccountResourceId:{type:"string",description:"Expense account UUID"},itemResourceId:{type:"string"},taxProfileResourceId:{type:"string"}}}},recordedPayment:{type:"object",description:"Optional. The matching payment record \u2014 auto-built from the bank entry if omitted.",properties:{reference:{type:"string"},valueDate:{type:"string",description:"YYYY-MM-DD"},organizationAccountResourceId:{type:"string"},paymentAmount:{type:"number"},transactionAmount:{type:"number"},paymentMethod:{type:"string",description:"Defaults to BANK_TRANSFER"}}},capsuleResourceId:{type:"string"}}}},required:["bankStatementEntryResourceId","bankAccountResourceId","billDetails"],group:"reconciliations",readOnly:!1,searchHint:"reconcile bank entry bill receipt AP purchase",execute:async(e,t)=>(w$(t.billDetails,"billDetails"),U_(e.client,t))},{name:"validate_drafts",description:'BULK validate \u2014 process up to 500 draft business transactions in ONE call, mixing any combination of invoices, bills, customer credit notes, and supplier credit notes (no per-type tools needed). SYNC: returns per-item errors and display data inline; no state change. Each result also carries `contactSignals` (Mid-7 contact-history insight: cadence, outliers, severity, divergences from the contact\'s modal pattern \u2014 null when the draft has no contact or the contact has no history) and `breakdown` (full Balance-panel payload: line items + trx-level metadata like subtotal, tax, total, balance, exchange rate). Top-level `contactSignalsMeta.unavailable=true` signals the freshness layer was unavailable for the whole batch. USE THIS to score a specific draft against contact history; for stand-alone "what does this contact normally look like?" lookups WITHOUT a draft, use `get_contact_signals` instead. Run before convert_drafts_to_active to surface eligibility issues client-side. btType enum: SALE = invoice, PURCHASE = bill, SALE_CREDIT_NOTE = customer CN, PURCHASE_CREDIT_NOTE = supplier CN. Journals are NOT in this enum.',params:{items:{type:"array",description:"Draft business transaction references (max 500). Mix any btType freely.",items:{type:"object",properties:{btResourceId:{type:"string",description:"Draft BT resource ID (UUID) \u2014 required"},btType:{type:"string",enum:["SALE","PURCHASE","SALE_CREDIT_NOTE","PURCHASE_CREDIT_NOTE"],description:"Required"}}}}},required:["items"],group:"drafts",readOnly:!0,isConcurrencySafe:!0,searchHint:"validate drafts eligibility check before convert promote",execute:async(e,t)=>(hl(t.items),$_(e.client,t.items))},{name:"convert_drafts_to_active",description:"BULK promote \u2014 finalize up to 500 draft business transactions to ACTIVE in ONE call, mixing any combination of invoices, bills, and credit notes (no per-type tools needed). ASYNC: returns a jobId \u2014 poll search_background_jobs filtered by resourceId; on PARTIAL_SUCCESS read data[0].errorDetails for per-row failures. NOT idempotent: a second call on already-promoted drafts returns 422. Filter the draft list by status: DRAFT before submitting. Pair with validate_drafts for a pre-flight check on large batches.",params:{items:{type:"array",description:"Draft BT references (max 500). Mix any btType.",items:{type:"object",properties:{btResourceId:{type:"string",description:"Draft BT resource ID (UUID) \u2014 required"},btType:{type:"string",enum:["SALE","PURCHASE","SALE_CREDIT_NOTE","PURCHASE_CREDIT_NOTE"],description:"Required"}}}}},required:["items"],group:"drafts",readOnly:!1,searchHint:"convert drafts to active promote finalize bulk",execute:async(e,t)=>(hl(t.items),R_(e.client,t.items))},{name:"submit_drafts_for_approval",description:"BULK submit \u2014 route up to 500 draft business transactions into the approval workflow in ONE call, mixing any combination of invoices, bills, and credit notes. ASYNC: returns a jobId \u2014 poll search_background_jobs filtered by resourceId; on PARTIAL_SUCCESS read data[0].errorDetails for per-row failures. NOT idempotent on drafts that already have an in-flight approval request.",params:{items:{type:"array",description:"Draft BT references (max 500). Mix any btType.",items:{type:"object",properties:{btResourceId:{type:"string",description:"Draft BT resource ID (UUID) \u2014 required"},btType:{type:"string",enum:["SALE","PURCHASE","SALE_CREDIT_NOTE","PURCHASE_CREDIT_NOTE"],description:"Required"}}}}},required:["items"],group:"drafts",readOnly:!1,searchHint:"submit drafts for approval workflow review bulk",execute:async(e,t)=>(hl(t.items),N_(e.client,t.items))},{name:"search_help_center",description:`Search the Jaz help center for how-to articles, feature guides, and accounting concepts. Use this for "how do I..." or "what is..." questions about Jaz: bank reconciliation, GST/VAT filing, multi-currency, fixed assets, custom fields, integrations, and so on. Returns top matching articles with title, section, snippet, and source URL.
|
|
864
864
|
|
|
865
|
-
Works without an API key \u2014 bundle ships the help center corpus locally.`,params:{query:{type:"string",description:'Natural-language question or keywords (e.g. "how do I reconcile a bank statement", "set up GST", "multi-currency invoice").'},limit:{type:"number",description:"Maximum number of articles to return (default 3, max 10)."},section:{type:"string",description:'Optional: restrict to a single help-center section slug (e.g. "bank-reconciliations", "invoices").'}},required:["query"],group:"help_center",readOnly:!0,searchHint:"help center docs how to guide article search question",isConcurrencySafe:!0,maxResultSizeChars:2e4,execute:async(e,t)=>{let r=String(t.query??"").trim();if(!r)return{error:"Query is required.",hint:"Pass a natural-language question or keywords."};let n=typeof t.limit=="number"&&!Number.isNaN(t.limit)?t.limit:3,o=Math.max(1,Math.min(10,Math.floor(n))),i=typeof t.section=="string"?t.section:void 0;try{let s=Gf();if(i&&!s.sections.some(c=>c.slug===i))return{error:`Unknown section: "${i}".`,available:s.sections.map(c=>c.slug),hint:"Omit the section parameter to search every section, or pick a slug from the available list."};let a=Li(s,r,{limit:o,section:i});return a.length===0?{results:[],hint:"No matching articles. Try broader keywords or rephrase the question."}:{results:a.map(c=>({title:c.article.title,section:c.section.name,sectionSlug:c.section.slug,snippet:c.article.snippet,url:c.article.sourceUrl,score:Math.round(c.score*1e3)/1e3,matchedTerms:c.matchedTerms})),totalArticles:s.articleCount,searchedSections:i?[i]:void 0}}catch(s){return{error:"Help center search failed.",detail:s instanceof Error?s.message:String(s),hint:"The bundled help-center index may be missing or corrupt."}}}},{name:"practice_init",description:"Scaffold the practitioner workspace at ~/Documents/Jaz Practice (or PRACTICE_HOME env override). Creates clients/, templates/, and a starter PRACTICE.md from the firm name. Idempotent on re-run. Filesystem-only. Run once before practice_onboard_client.",params:{firmName:{type:"string",description:"Display name shown on outputs."},defaultBaseCurrency:{type:"string",description:'Default currency for new clients (e.g., "SGD"). Defaults to SGD.'},defaultJurisdiction:{type:"string",description:'ISO-3166 alpha-2 (e.g., "SG"). Defaults to SG.'},defaultJazApiKey:{type:"string",description:"Optional default JAZ_API_KEY for all clients (last in resolution chain after CLIENT.md override and env)."},practiceHome:{type:"string",description:"Optional override for the practice home dir. Defaults to ~/Documents/Jaz Practice or PRACTICE_HOME env."}},required:["firmName"],group:"practice",readOnly:!1,searchHint:"practice init firm setup workspace scaffold",isConcurrencySafe:!1,maxResultSizeChars:5e3,execute:async(e,t)=>{let r=cn(t.practiceHome);ja(r);let n=Ma(r),o=n??{v:1,firm_name:String(t.firmName),default_jaz_api_key:t.defaultJazApiKey,default_base_currency:t.defaultBaseCurrency??"SGD",default_jurisdiction:t.defaultJurisdiction??"SG",materiality_default:1e3,body:"Firm-level defaults that flow into every new client. Changes here apply to NEW clients only."};return n&&(t.firmName&&(o.firm_name=String(t.firmName)),t.defaultBaseCurrency&&(o.default_base_currency=String(t.defaultBaseCurrency)),t.defaultJurisdiction&&(o.default_jurisdiction=String(t.defaultJurisdiction)),t.defaultJazApiKey&&(o.default_jaz_api_key=String(t.defaultJazApiKey))),Tp(r,o),{practiceHome:r,firmName:o.firm_name,defaultBaseCurrency:o.default_base_currency,defaultJurisdiction:o.default_jurisdiction,clientsDir:`${r}/clients`,wasExisting:n!==null}}},{name:"practice_list_clients",description:"List all clients in the practitioner workspace. Returns slugs and last-active engagement per client. Filesystem-only. Use as a dashboard entry point before practice_load_client.",params:{practiceHome:{type:"string",description:"Optional override; defaults to ~/Documents/Jaz Practice."}},required:[],group:"practice",readOnly:!0,searchHint:"practice list clients dashboard",isConcurrencySafe:!0,maxResultSizeChars:2e4,execute:async(e,t)=>{let r=cn(t.practiceHome),o=Np(r).map(i=>{let s=Ni(r,i),a=qa(r,i);return{slug:i,legalEntityName:s?.legal_entity_name??i,fyEnd:s?.fy_end,baseCurrency:s?.base_currency,gstScheme:s?.gst_scheme,hasJazApiKeyOverride:!!s?.jaz_api_key_override,activeEngagements:a}});return{practiceHome:r,total:o.length,clients:o}}},{name:"practice_load_client",description:"Read a client's CLIENT.md master file. Returns the full record (legal entity, FY, COA mapping notes, banks, recurring accruals, recurring engagements, etc.) plus the list of active engagements. Filesystem-only. Use this to give the agent client context before invoking jaz-jobs blueprints or jaz-recipes recipes.",params:{slug:{type:"string",description:'Client slug (lowercase, hyphenated; e.g., "acme-pte-ltd").'},practiceHome:{type:"string",description:"Optional override."}},required:["slug"],group:"practice",readOnly:!0,searchHint:"practice load client read context CLIENT.md",isConcurrencySafe:!0,maxResultSizeChars:3e4,execute:async(e,t)=>{let r=cn(t.practiceHome),n=String(t.slug);try{In(n,"client slug")}catch(s){return{error:s instanceof Error?s.message:String(s)}}let o=Ni(r,n);if(!o)return{error:`Client not found: ${n}`,hint:"Run practice_list_clients to see available slugs, or practice_onboard_client to create a new one."};let i=qa(r,n).map(s=>{let a=za(r,n,s);return a?{slug:s,type:a.type,period:a.period,status:a.status,scope:a.scope_summary}:null}).filter(Boolean);return{client:$p(o),engagements:i}}},{name:"practice_onboard_client",description:"Scaffold a new client folder and write the initial CLIENT.md. Captures legal entity, FY end, GST scheme, banks, recurring accruals, recurring engagements. Optionally sets a per-client JAZ_API_KEY override for multi-org agencies. Filesystem-only. After this, call practice_create_engagement for the first period.",params:{legalEntityName:{type:"string",description:"Full legal name as registered with ACRA / equivalent."},uen:{type:"string",description:"Singapore UEN if applicable."},registeredAddress:{type:"string",description:"ACRA-registered address."},country:{type:"string",description:"ISO-3166 alpha-2; defaults to PRACTICE.md.default_jurisdiction."},baseCurrency:{type:"string",description:"ISO-4217; defaults to PRACTICE.md.default_base_currency."},fyEnd:{type:"string",description:'MM-DD format (e.g., "12-31"). Drives annual-statutory engagement period boundaries.'},gstScheme:{type:"string",enum:["quarterly","monthly","not-registered"],description:"GST registration cadence; drives quarterly-gst engagement scheduling."},gstRegistrationNumber:{type:"string",description:"Required when gstScheme != not-registered."},corporateTaxBracket:{type:"string",enum:["standard","startup_exemption","partial_exemption"],description:"Drives Form C-S computation path during annual-statutory."},jazApiKeyOverride:{type:"string",description:"Optional per-client API key. Resolution: this override -> PRACTICE.md.default_jaz_api_key -> JAZ_API_KEY env."},jazOrgId:{type:"string",description:"Optional Jaz org UUID. Pinned for tool invocations against this client."},materialityThreshold:{type:"number",description:"In base currency. Defaults to PRACTICE.md.materiality_default."},bankAccounts:{type:"array",description:"Array of {name, currency, account_number_ref?, jaz_resource_id?}. Read by monthly-close before search_bank_records.",items:{type:"object"}},recurringAccruals:{type:"array",description:'Array of {name, gl_account, vendor?, estimation_method?, fixed_amount?}. Drives plan_recipe(name: "accrued-expense") in monthly-close.',items:{type:"object"}},recurringEngagements:{type:"array",description:'Array of {type, cadence}. e.g. [{type: "monthly-close", cadence: "monthly"}]. Drives engagement scheduling.',items:{type:"object"}},keyContacts:{type:"array",description:"Array of {name, role, email?}.",items:{type:"object"}},practiceHome:{type:"string",description:"Optional override."}},required:["legalEntityName","fyEnd","gstScheme"],group:"practice",readOnly:!1,searchHint:"practice onboard new client setup CLIENT.md",isConcurrencySafe:!1,maxResultSizeChars:5e3,execute:async(e,t)=>{let r=cn(t.practiceHome);ja(r);let n=Ma(r);try{let o=kp(r,{legal_entity_name:String(t.legalEntityName),uen:t.uen,registered_address:t.registeredAddress,country:t.country,base_currency:t.baseCurrency,fy_end:String(t.fyEnd),gst_scheme:t.gstScheme,gst_registration_number:t.gstRegistrationNumber,corporate_tax_bracket:t.corporateTaxBracket,jaz_api_key_override:t.jazApiKeyOverride,jaz_org_id:t.jazOrgId,materiality_threshold:t.materialityThreshold,bank_accounts:t.bankAccounts,recurring_accruals:t.recurringAccruals,recurring_engagements:t.recurringEngagements,key_contacts:t.keyContacts},n);return{slug:o.slug,path:o.path,nextStep:"Call practice_create_engagement to scaffold the first engagement (e.g., type: monthly-close for the current period)."}}catch(o){return{error:o instanceof Error?o.message:String(o),hint:"If the slug already exists, choose a different legal_entity_name or remove the existing folder."}}}},{name:"practice_create_engagement",description:"Scaffold a new engagement folder under a client. Creates ENGAGEMENT.md from the type-specific template plus inputs/, workpapers/, deliverables/ subdirs. Filesystem-only. Engagement type drives which jaz-jobs blueprint and jaz-recipes recipes the agent should invoke. period is REQUIRED for monthly-close (YYYY-MM), quarterly-gst (YYYY-QN), and annual-statutory (YYYY) \u2014 only onboarding and adhoc accept free-text or omitted period.",params:{clientSlug:{type:"string",description:"Existing client slug."},type:{type:"string",enum:["monthly-close","quarterly-gst","annual-statutory","onboarding","adhoc"],description:"Engagement type. monthly-close uses generate_month_end_blueprint; quarterly-gst uses generate_gst_vat_blueprint; annual-statutory uses generate_year_end_blueprint + generate_audit_prep_blueprint + generate_statutory_filing_blueprint."},period:{type:"string",description:"Period in the format the type expects. REQUIRED for monthly-close (YYYY-MM, e.g. 2026-03), quarterly-gst (YYYY-QN, e.g. 2026-Q1), annual-statutory (YYYY, e.g. 2026). Free-text or omitted accepted only for onboarding and adhoc. Validated server-side; malformed periods rejected before scaffolding to prevent audit-trail gaps."},scopeSummary:{type:"string",description:"One sentence describing what this engagement produces."},targetCompletionDate:{type:"string",description:"YYYY-MM-DD. Used by practice_list_clients to flag overdue work."},practiceHome:{type:"string",description:"Optional override."}},required:["clientSlug","type"],group:"practice",readOnly:!1,searchHint:"practice create engagement scaffold ENGAGEMENT.md monthly close gst statutory",isConcurrencySafe:!1,maxResultSizeChars:5e3,execute:async(e,t)=>{let r=cn(t.practiceHome),n=String(t.clientSlug);try{In(n,"client slug")}catch(s){return{error:s instanceof Error?s.message:String(s)}}let o=Ni(r,n);if(!o)return{error:`Client not found: ${n}`,hint:"Run practice_list_clients or practice_onboard_client first."};let i=t.type;try{Ru(i,t.period)}catch(s){return{error:s instanceof Error?s.message:String(s),hint:"Recurring/statutory engagements require a period: monthly-close=YYYY-MM, quarterly-gst=YYYY-QN, annual-statutory=YYYY."}}try{let s=Pp(r,n,{type:i,period:t.period,scope_summary:t.scopeSummary,target_completion_date:t.targetCompletionDate},o.jaz_org_id);return{slug:s.slug,path:s.path,nextStep:"Open ENGAGEMENT.md and follow the type-specific playbook in jaz-practice/references/<type>.md."}}catch(s){return{error:s instanceof Error?s.message:String(s),hint:"If the engagement folder already exists, pick a different period or load the existing one with practice_load_engagement."}}}},{name:"practice_load_engagement",description:"Read an engagement's ENGAGEMENT.md file. Returns scope, status, period, queries, decisions log. Pair with practice_load_client to get full context before driving jaz-jobs / jaz-recipes work.",params:{clientSlug:{type:"string",description:"Client slug."},engagementSlug:{type:"string",description:'Engagement slug (e.g., "monthly-close-2026-03").'},practiceHome:{type:"string",description:"Optional override."}},required:["clientSlug","engagementSlug"],group:"practice",readOnly:!0,searchHint:"practice load engagement read ENGAGEMENT.md context",isConcurrencySafe:!0,maxResultSizeChars:2e4,execute:async(e,t)=>{let r=cn(t.practiceHome),n=String(t.clientSlug),o=String(t.engagementSlug);try{In(n,"client slug"),In(o,"engagement slug")}catch(s){return{error:s instanceof Error?s.message:String(s)}}let i=za(r,n,o);return i?{engagement:i}:{error:`Engagement not found: ${n}/${o}`,hint:"Run practice_load_client to see active engagement slugs."}}}];var q_=null;function l_e(){if(!q_){q_=new Map;for(let e of yl)q_.set(e.name,e)}return q_}function z_(e){return l_e().get(e)}function N$(){return yl}function Dl(e){return N$().filter(t=>t.group===e)}ts();function d_e(e){switch(e){case 401:return"Invalid or missing API key. Run `clio auth add` or set JAZ_API_KEY.";case 403:return"Insufficient permissions for this operation.";case 404:return"Resource not found \u2014 check the resourceId. Use a search/list tool to look it up.";case 409:return"Conflict \u2014 resource may have been modified. Re-fetch and retry.";case 422:return"Validation error \u2014 check field values against the tool description.";case 429:return"Rate limited \u2014 wait a moment and retry.";default:return""}}function DG(e){if(e instanceof es){let r=d_e(e.status);return{error:e.message,status:e.status,endpoint:e.endpoint,...r?{hint:r}:{}}}return{error:e instanceof Error?e.message:String(e)}}function bG(e,t){if(e.readOnly)return{valid:!0,errors:[]};let r=[];for(let n of e.required){let o=t[n];if(o==null){r.push(`Missing required field: ${n}`);continue}typeof o=="string"&&o.trim()===""&&r.push(`Required field "${n}" is empty`)}for(let[n,o]of Object.entries(t)){if(o==null)continue;let i=e.params[n];if(!i)continue;let s=p_e(n,o,i);s&&r.push(s),i.enum&&typeof o=="string"&&(i.enum.includes(o)||r.push(`Field "${n}" value "${o}" not in allowed values: ${i.enum.join(", ")}`))}return{valid:r.length===0,errors:r}}function p_e(e,t,r){switch(r.type){case"string":if(typeof t!="string")return`Field "${e}" expected string, got ${typeof t}`;break;case"number":if(typeof t!="number"||!Number.isFinite(t))return`Field "${e}" expected finite number, got ${typeof t=="number"?"non-finite":typeof t}`;break;case"boolean":if(typeof t!="boolean")return`Field "${e}" expected boolean, got ${typeof t}`;break;case"array":if(!Array.isArray(t))return`Field "${e}" expected array, got ${typeof t}`;break;case"object":if(typeof t!="object"||Array.isArray(t))return`Field "${e}" expected object, got ${Array.isArray(t)?"array":typeof t}`;break}return null}var bl=[{name:"invoices",description:"Sales invoices (INV/SI). Create, search, get, update, delete, pay, finalize, apply credits, download PDF. Also: receivables, AR, billing, overdue invoices.",groups:["invoices"]},{name:"customer_credit_notes",description:"Customer credit notes (CN). Create, search, update, delete, finalize, refund, download PDF. Also: sales returns, customer CN.",groups:["customer_credit_notes"]},{name:"bills",description:"Purchase bills (PO/PI). Create, search, get, update, delete, pay, finalize, apply credits. Also: payables, AP, vendor invoices, supplier bills.",groups:["bills"]},{name:"supplier_credit_notes",description:"Supplier credit notes. Create, search, update, delete, finalize, refund. Also: purchase returns, debit notes, supplier CN.",groups:["supplier_credit_notes"]},{name:"journals",description:"Journal entries (JE). Create, search, update, delete manual journals. Also: adjusting entries, accruals, reclassifications, corrections.",groups:["journals"]},{name:"cash_entries",description:"Cash-in receipts and cash-out disbursements for external cash movements. WHEN TO USE: money received from customers/external \u2192 cash-in. Money paid to suppliers/external \u2192 cash-out. For internal account-to-account transfers, use cash_transfers namespace.",groups:["cash_entries"]},{name:"cash_transfers",description:"Cash transfers between your own bank/cash accounts and cashflow transaction search. WHEN TO USE: moving funds between own accounts (main bank \u2192 petty cash, USD \u2192 SGD). For external receipts/payments, use cash_entries namespace.",groups:["cash_transfers"]},{name:"bank_accounts",description:"Bank accounts, bank statement imports (CSV/OFX), bank records search, auto-reconciliation. For unreconciled queries: ALWAYS search bank records with status UNRECONCILED after listing accounts. Also: bank feeds, bank balance.",groups:["bank"]},{name:"bank_rules",description:"Bank reconciliation rules (action shortcuts). Create, search, update, delete bank rules. Configure auto-matching rules for bank records.",groups:["bank_rules"]},{name:"reconciliations",description:"Apply a reconciliation decision to a bank statement entry \u2014 write side. Distinct from bank_accounts/bank_rules (which configure auto-reconciliation) and view_auto_reconciliation (which queries suggestions). Eight endpoints: quick_reconcile + apply_bank_rule (async, bulk), and direct_cash_entry / cash_journal / manual_journal / cash_transfer / invoice_receipt / bill_receipt (sync, per-entry). Most fields prefill from the bank entry when omitted.",groups:["reconciliations"]},{name:"financial_reports",description:"Core financial statements: trial balance (TB), balance sheet (BS/B/S), profit & loss (PnL/P&L/income statement), cash flow, general ledger (GL), cash balance/position, equity movement, VAT/GST ledger. Also: how profitable, what is the balance.",groups:["financial_reports"]},{name:"operational_reports",description:"Aging and operational reports: aged receivables (AR aging), aged payables (AP aging), AR report, bank balance summary, bank reconciliation reports, fixed asset (FA) summary, FA reconciliation. Data exports (CSV/Excel/XLSX). Anomaly detection and audit analysis: anomalous invoices, anomalous bills, cashflow anomalies, GL journal audit, exchange rate audit, receivables customer risk, cash expense health. Also: overdue analysis, how much owed, suspicious transactions, audit trail.",groups:["operational_reports","exports"]},{name:"contacts",description:"Contacts (customers/suppliers/vendors), contact groups. Create, search, get, update, delete contacts. Bulk upsert contacts (async \u2014 returns jobId, poll background_jobs). List/create contact groups.",groups:["contacts","contact_groups"]},{name:"items_and_inventory",description:"Products, services, inventory items. Create, search, get, update, delete items. Check inventory balance. Also: SKU, catalog, stock.",groups:["items","inventory"]},{name:"tags_and_custom_fields",description:"Tags for categorizing transactions. Custom fields for adding metadata (text, date, dropdown). Create, search, delete tags and custom fields.",groups:["tags","custom_fields"]},{name:"nano_classifiers",description:"Nano classifiers (tracking categories/dimensions). List, search, create, update, delete classifiers and their classes. Used for line-item tagging and dimensional reporting. Also: tracking categories, cost centers, departments, projects.",groups:["nano_classifiers"]},{name:"chart_of_accounts",description:"Chart of accounts (COA/GL accounts). Create, search, update accounts. Bookmarks (favorites/shortcuts). Also: ledger codes, account types.",groups:["accounts","bookmarks"]},{name:"currencies",description:"Currencies, exchange rates (FX/forex). List/add org currencies. Set, update, import currency rates. Also: multi-currency, FX rates.",groups:["currencies"]},{name:"tax_profiles",description:"Tax profiles (GST/VAT/sales tax), withholding tax codes (WHT/ATC). Search, create, update tax profiles. List WHT codes.",groups:["tax_profiles"]},{name:"capsules_and_recipes",description:"Capsules (transaction groupings/capsule types). Financial recipes: amortization, depreciation, deferred revenue, IFRS 16 leases, hire purchase, fixed deposits, FX revaluation, loan schedules, ECL/expected credit loss, IAS 37 provisions, asset disposal. Plan and execute recipes. Keywords: calculate, provision, schedule, expected credit loss, revaluation.",groups:["capsules","recipes"]},{name:"scheduled_transactions",description:"Scheduled/recurring invoices, bills, journals. Create scheduled invoices/bills/journals, search scheduled transactions. Also: recurring, auto-generate.",groups:["schedulers"]},{name:"subscriptions",description:"Subscriptions (recurring billing/payment plans). Create, update, cancel, search subscriptions. Also: recurring charges, subscription schedules.",groups:["subscriptions"]},{name:"organization",description:"Organization info (name, currency, country, fiscal year). User management: invite, update, remove, search org users. Bulk invite.",groups:["organization","org_users"]},{name:"document_ai",description:"File attachments, AI document extraction (magic/OCR). Upload/list attachments. Create transactions from PDFs/images (invoice scanning, bill extraction). Track extraction workflows.",groups:["attachments","magic"]},{name:"fixed_assets",description:"Fixed assets (PP&E/property, plant, equipment). Search, create, update, discard, sell, transfer, undo disposal. Also: depreciation, asset register.",groups:["fixed_assets"]},{name:"payments_and_search",description:"Payment records: get, update, delete individual payments. List payments/credits on invoices and bills. Reverse credit applications. Cashflow transaction search. Universal cross-entity search. Also: void payment, payment history, credit note applications.",groups:["payments","cashflow","search"]},{name:"quick_fix",description:"Quick Fix: bulk-update multiple transactions or line items in one call. Change dates, contacts, tags, accounts, tax profiles, custom fields across many invoices/bills/journals/credit-notes/cash-entries/schedulers at once. Also: batch update, mass edit.",groups:["quick_fix"]},{name:"export_records",description:"Export records to XLSX. List available columns, preview export scope (row count + sample), generate export file with pre-signed download URL. Supports any entity type: invoices, bills, contacts, items, journals, bank records, cashflow, fixed assets, etc. Pass query (structured search syntax) or filter (JSON), never both.",groups:["export_records"]},{name:"background_jobs",description:"Background job tracking. Poll any async operation by jobId (contacts bulk-upsert, items bulk-upsert, bank import, magic file processing, etc.). Filter by resourceId field to look up a specific job. Poll until status is SUCCESS, FAILED, or PARTIAL_SUCCESS.",groups:["background_jobs"]},{name:"drafts",description:"Draft business transactions \u2014 both local payload validation (invoices, bills, journals, credit notes) AND BULK-FRIENDLY server-side lifecycle: validate_drafts (sync eligibility check), convert_drafts_to_active (async promote to ACTIVE), submit_drafts_for_approval (async route to approval). The lifecycle tools are GENERIC and BULK \u2014 one call accepts up to 500 items mixing any combination of {btResourceId, btType: SALE|PURCHASE|SALE_CREDIT_NOTE|PURCHASE_CREDIT_NOTE}. No need for per-entity tools when promoting/submitting drafts at scale. NOT idempotent on already-promoted drafts.",groups:["drafts"]},{name:"close_procedures",description:"Period-end close checklists: month-end, quarter-end, year-end close. Bank reconciliation job. GST/VAT filing job. Audit preparation. Returns structured blueprints.",groups:["close_jobs"]},{name:"operational_jobs",description:"Operational job checklists: payment runs, credit control/collections, supplier reconciliation, fixed asset review, document collection, statutory filing. Returns structured blueprints.",groups:["operational_jobs"]},{name:"help_center",description:"Search the Jaz help center for how-to articles, feature guides, accounting concepts, and troubleshooting. Returns top matches with title, section, snippet, and source URL. Works without an API key.",groups:["help_center"]},{name:"practice",description:"Practitioner workspace scaffolding: per-client folders, per-engagement files, schemas. Use to onboard new clients (capture legal entity / FY / GST scheme / banks / recurring accruals into CLIENT.md), list active engagements, scaffold a monthly-close / quarterly-gst / annual-statutory engagement, and load client/engagement context before driving accounting work via jaz-jobs blueprints + jaz-recipes. All filesystem-only, offline. Pair with jaz-practice skill for the canonical playbooks.",groups:["practice"]}];function vG(e,t=5){let r=e.trim().toLowerCase();if(!r)return[];let n=r.split(/[\s_-]+/).filter(i=>i.length>=2);return n.length===0?[]:bl.map(i=>{let s=0,a=i.name.toLowerCase(),c=i.description.toLowerCase();(a===r||a===r.replace(/\s+/g,"_"))&&(s+=5),a.includes(r)&&(s+=3),c.includes(r)&&(s+=1);for(let u of n)a.includes(u)&&(s+=2),c.includes(u)&&(s+=1);return{ns:i,score:s}}).filter(i=>i.score>0).sort((i,s)=>s.score-i.score).slice(0,t).map(i=>i.ns)}function Y_(e){let t={type:e.type};if(e.description&&(t.description=e.description),e.enum&&(t.enum=e.enum),e.type==="array"&&e.items&&(t.items=Y_(e.items)),e.type==="object"&&e.properties){let r={};for(let[n,o]of Object.entries(e.properties))r[n]=Y_(o);t.properties=r,e.required?.length&&(t.required=e.required)}return t}function cc(e,t){let r={};for(let[n,o]of Object.entries(e))r[n]=Y_(o);return{type:"object",properties:r,...t.length>0?{required:t}:{}}}function SG(e){return{name:e.name,description:e.description,input_schema:cc(e.params,e.required),...e.searchHint?{searchHint:e.searchHint}:{}}}function f_e(e){if(typeof e!="string")return e.isDestructive??!1;let t=e;return t.startsWith("delete_")||t.startsWith("pay_")||t.startsWith("finalize_")||t.includes("refund")||t==="remove_org_user"}function _G(e){if(!e.trim())return{namespaces:bl.map(o=>{let i=o.groups.flatMap(s=>Dl(s));return{name:o.name,description:o.description,toolCount:i.length,tools:i.map(s=>s.name)}}),hint:"Call describe_tools with specific tool names to get full schemas before executing."};let t=vG(e,5);if(t.length===0)return{matches:[],hint:`No tools match "${e}". Try broader terms or call search_tools with empty query to see all namespaces.`};let n=e.toLowerCase().split(/\s+/).filter(Boolean);return{matches:t.map(o=>{let s=o.groups.flatMap(a=>Dl(a)).map(a=>{let c=`${a.name} ${a.searchHint??""} ${a.description}`.toLowerCase(),u=n.filter(l=>c.includes(l)).length;return{tool:a,score:u}}).sort((a,c)=>c.score-a.score);return{namespace:o.name,description:o.description,tools:s.map(a=>({name:a.tool.name,description:a.tool.description.split(`
|
|
865
|
+
Works without an API key \u2014 bundle ships the help center corpus locally.`,params:{query:{type:"string",description:'Natural-language question or keywords (e.g. "how do I reconcile a bank statement", "set up GST", "multi-currency invoice").'},limit:{type:"number",description:"Maximum number of articles to return (default 3, max 10)."},section:{type:"string",description:'Optional: restrict to a single help-center section slug (e.g. "bank-reconciliations", "invoices").'}},required:["query"],group:"help_center",readOnly:!0,searchHint:"help center docs how to guide article search question",isConcurrencySafe:!0,maxResultSizeChars:2e4,execute:async(e,t)=>{let r=String(t.query??"").trim();if(!r)return{error:"Query is required.",hint:"Pass a natural-language question or keywords."};let n=typeof t.limit=="number"&&!Number.isNaN(t.limit)?t.limit:3,o=Math.max(1,Math.min(10,Math.floor(n))),i=typeof t.section=="string"?t.section:void 0;try{let s=Gf();if(i&&!s.sections.some(c=>c.slug===i))return{error:`Unknown section: "${i}".`,available:s.sections.map(c=>c.slug),hint:"Omit the section parameter to search every section, or pick a slug from the available list."};let a=Li(s,r,{limit:o,section:i});return a.length===0?{results:[],hint:"No matching articles. Try broader keywords or rephrase the question."}:{results:a.map(c=>({title:c.article.title,section:c.section.name,sectionSlug:c.section.slug,snippet:c.article.snippet,url:c.article.sourceUrl,score:Math.round(c.score*1e3)/1e3,matchedTerms:c.matchedTerms})),totalArticles:s.articleCount,searchedSections:i?[i]:void 0}}catch(s){return{error:"Help center search failed.",detail:s instanceof Error?s.message:String(s),hint:"The bundled help-center index may be missing or corrupt."}}}},{name:"practice_init",description:"Scaffold the practitioner workspace at ~/Documents/Jaz Practice (or PRACTICE_HOME env override). Creates clients/, templates/, and a starter PRACTICE.md from the firm name. Idempotent on re-run. Filesystem-only. Run once before practice_onboard_client.",params:{firmName:{type:"string",description:"Display name shown on outputs."},defaultBaseCurrency:{type:"string",description:'Default currency for new clients (e.g., "SGD"). Defaults to SGD.'},defaultJurisdiction:{type:"string",description:'ISO-3166 alpha-2 (e.g., "SG"). Defaults to SG.'},defaultJazApiKey:{type:"string",description:"Optional default JAZ_API_KEY for all clients (last in resolution chain after CLIENT.md override and env)."},practiceHome:{type:"string",description:"Optional override for the practice home dir. Defaults to ~/Documents/Jaz Practice or PRACTICE_HOME env."}},required:["firmName"],group:"practice",readOnly:!1,searchHint:"practice init firm setup workspace scaffold",isConcurrencySafe:!1,maxResultSizeChars:5e3,execute:async(e,t)=>{let r=cn(t.practiceHome);ja(r);let n=Ma(r),o=n??{v:1,firm_name:String(t.firmName),default_jaz_api_key:t.defaultJazApiKey,default_base_currency:t.defaultBaseCurrency??"SGD",default_jurisdiction:t.defaultJurisdiction??"SG",materiality_default:1e3,body:"Firm-level defaults that flow into every new client. Changes here apply to NEW clients only."};return n&&(t.firmName&&(o.firm_name=String(t.firmName)),t.defaultBaseCurrency&&(o.default_base_currency=String(t.defaultBaseCurrency)),t.defaultJurisdiction&&(o.default_jurisdiction=String(t.defaultJurisdiction)),t.defaultJazApiKey&&(o.default_jaz_api_key=String(t.defaultJazApiKey))),Tp(r,o),{practiceHome:r,firmName:o.firm_name,defaultBaseCurrency:o.default_base_currency,defaultJurisdiction:o.default_jurisdiction,clientsDir:`${r}/clients`,wasExisting:n!==null}}},{name:"practice_list_clients",description:"List all clients in the practitioner workspace. Returns slugs and last-active engagement per client. Filesystem-only. Use as a dashboard entry point before practice_load_client.",params:{practiceHome:{type:"string",description:"Optional override; defaults to ~/Documents/Jaz Practice."}},required:[],group:"practice",readOnly:!0,searchHint:"practice list clients dashboard",isConcurrencySafe:!0,maxResultSizeChars:2e4,execute:async(e,t)=>{let r=cn(t.practiceHome),o=Np(r).map(i=>{let s=Ni(r,i),a=qa(r,i);return{slug:i,legalEntityName:s?.legal_entity_name??i,fyEnd:s?.fy_end,baseCurrency:s?.base_currency,gstScheme:s?.gst_scheme,hasJazApiKeyOverride:!!s?.jaz_api_key_override,activeEngagements:a}});return{practiceHome:r,total:o.length,clients:o}}},{name:"practice_load_client",description:"Read a client's CLIENT.md master file. Returns the full record (legal entity, FY, COA mapping notes, banks, recurring accruals, recurring engagements, etc.) plus the list of active engagements. Filesystem-only. Use this to give the agent client context before invoking jaz-jobs blueprints or jaz-recipes recipes.",params:{slug:{type:"string",description:'Client slug (lowercase, hyphenated; e.g., "acme-pte-ltd").'},practiceHome:{type:"string",description:"Optional override."}},required:["slug"],group:"practice",readOnly:!0,searchHint:"practice load client read context CLIENT.md",isConcurrencySafe:!0,maxResultSizeChars:3e4,execute:async(e,t)=>{let r=cn(t.practiceHome),n=String(t.slug);try{In(n,"client slug")}catch(s){return{error:s instanceof Error?s.message:String(s)}}let o=Ni(r,n);if(!o)return{error:`Client not found: ${n}`,hint:"Run practice_list_clients to see available slugs, or practice_onboard_client to create a new one."};let i=qa(r,n).map(s=>{let a=za(r,n,s);return a?{slug:s,type:a.type,period:a.period,status:a.status,scope:a.scope_summary}:null}).filter(Boolean);return{client:$p(o),engagements:i}}},{name:"practice_onboard_client",description:"Scaffold a new client folder and write the initial CLIENT.md. Captures legal entity, FY end, GST scheme, banks, recurring accruals, recurring engagements. Optionally sets a per-client JAZ_API_KEY override for multi-org agencies. Filesystem-only. After this, call practice_create_engagement for the first period.",params:{legalEntityName:{type:"string",description:"Full legal name as registered with ACRA / equivalent."},uen:{type:"string",description:"Singapore UEN if applicable."},registeredAddress:{type:"string",description:"ACRA-registered address."},country:{type:"string",description:"ISO-3166 alpha-2; defaults to PRACTICE.md.default_jurisdiction."},baseCurrency:{type:"string",description:"ISO-4217; defaults to PRACTICE.md.default_base_currency."},fyEnd:{type:"string",description:'MM-DD format (e.g., "12-31"). Drives annual-statutory engagement period boundaries.'},gstScheme:{type:"string",enum:["quarterly","monthly","not-registered"],description:"GST registration cadence; drives quarterly-gst engagement scheduling."},gstRegistrationNumber:{type:"string",description:"Required when gstScheme != not-registered."},corporateTaxBracket:{type:"string",enum:["standard","startup_exemption","partial_exemption"],description:"Drives Form C-S computation path during annual-statutory."},jazApiKeyOverride:{type:"string",description:"Optional per-client API key. Resolution: this override -> PRACTICE.md.default_jaz_api_key -> JAZ_API_KEY env."},jazOrgId:{type:"string",description:"Optional Jaz org UUID. Pinned for tool invocations against this client."},materialityThreshold:{type:"number",description:"In base currency. Defaults to PRACTICE.md.materiality_default."},bankAccounts:{type:"array",description:"Array of {name, currency, account_number_ref?, jaz_resource_id?}. Read by monthly-close before search_bank_records.",items:{type:"object"}},recurringAccruals:{type:"array",description:'Array of {name, gl_account, vendor?, estimation_method?, fixed_amount?}. Drives plan_recipe(name: "accrued-expense") in monthly-close.',items:{type:"object"}},recurringEngagements:{type:"array",description:'Array of {type, cadence}. e.g. [{type: "monthly-close", cadence: "monthly"}]. Drives engagement scheduling.',items:{type:"object"}},keyContacts:{type:"array",description:"Array of {name, role, email?}.",items:{type:"object"}},practiceHome:{type:"string",description:"Optional override."}},required:["legalEntityName","fyEnd","gstScheme"],group:"practice",readOnly:!1,searchHint:"practice onboard new client setup CLIENT.md",isConcurrencySafe:!1,maxResultSizeChars:5e3,execute:async(e,t)=>{let r=cn(t.practiceHome);ja(r);let n=Ma(r);try{let o=kp(r,{legal_entity_name:String(t.legalEntityName),uen:t.uen,registered_address:t.registeredAddress,country:t.country,base_currency:t.baseCurrency,fy_end:String(t.fyEnd),gst_scheme:t.gstScheme,gst_registration_number:t.gstRegistrationNumber,corporate_tax_bracket:t.corporateTaxBracket,jaz_api_key_override:t.jazApiKeyOverride,jaz_org_id:t.jazOrgId,materiality_threshold:t.materialityThreshold,bank_accounts:t.bankAccounts,recurring_accruals:t.recurringAccruals,recurring_engagements:t.recurringEngagements,key_contacts:t.keyContacts},n);return{slug:o.slug,path:o.path,nextStep:"Call practice_create_engagement to scaffold the first engagement (e.g., type: monthly-close for the current period)."}}catch(o){return{error:o instanceof Error?o.message:String(o),hint:"If the slug already exists, choose a different legal_entity_name or remove the existing folder."}}}},{name:"practice_create_engagement",description:"Scaffold a new engagement folder under a client. Creates ENGAGEMENT.md from the type-specific template plus inputs/, workpapers/, deliverables/ subdirs. Filesystem-only. Engagement type drives which jaz-jobs blueprint and jaz-recipes recipes the agent should invoke. period is REQUIRED for monthly-close (YYYY-MM), quarterly-gst (YYYY-QN), and annual-statutory (YYYY) \u2014 only onboarding and adhoc accept free-text or omitted period.",params:{clientSlug:{type:"string",description:"Existing client slug."},type:{type:"string",enum:["monthly-close","quarterly-gst","annual-statutory","onboarding","adhoc"],description:"Engagement type. monthly-close uses generate_month_end_blueprint; quarterly-gst uses generate_gst_vat_blueprint; annual-statutory uses generate_year_end_blueprint + generate_audit_prep_blueprint + generate_statutory_filing_blueprint."},period:{type:"string",description:"Period in the format the type expects. REQUIRED for monthly-close (YYYY-MM, e.g. 2026-03), quarterly-gst (YYYY-QN, e.g. 2026-Q1), annual-statutory (YYYY, e.g. 2026). Free-text or omitted accepted only for onboarding and adhoc. Validated server-side; malformed periods rejected before scaffolding to prevent audit-trail gaps."},scopeSummary:{type:"string",description:"One sentence describing what this engagement produces."},targetCompletionDate:{type:"string",description:"YYYY-MM-DD. Used by practice_list_clients to flag overdue work."},practiceHome:{type:"string",description:"Optional override."}},required:["clientSlug","type"],group:"practice",readOnly:!1,searchHint:"practice create engagement scaffold ENGAGEMENT.md monthly close gst statutory",isConcurrencySafe:!1,maxResultSizeChars:5e3,execute:async(e,t)=>{let r=cn(t.practiceHome),n=String(t.clientSlug);try{In(n,"client slug")}catch(s){return{error:s instanceof Error?s.message:String(s)}}let o=Ni(r,n);if(!o)return{error:`Client not found: ${n}`,hint:"Run practice_list_clients or practice_onboard_client first."};let i=t.type;try{Ru(i,t.period)}catch(s){return{error:s instanceof Error?s.message:String(s),hint:"Recurring/statutory engagements require a period: monthly-close=YYYY-MM, quarterly-gst=YYYY-QN, annual-statutory=YYYY."}}try{let s=Pp(r,n,{type:i,period:t.period,scope_summary:t.scopeSummary,target_completion_date:t.targetCompletionDate},o.jaz_org_id);return{slug:s.slug,path:s.path,nextStep:"Open ENGAGEMENT.md and follow the type-specific playbook in jaz-practice/references/<type>.md."}}catch(s){return{error:s instanceof Error?s.message:String(s),hint:"If the engagement folder already exists, pick a different period or load the existing one with practice_load_engagement."}}}},{name:"practice_load_engagement",description:"Read an engagement's ENGAGEMENT.md file. Returns scope, status, period, queries, decisions log. Pair with practice_load_client to get full context before driving jaz-jobs / jaz-recipes work.",params:{clientSlug:{type:"string",description:"Client slug."},engagementSlug:{type:"string",description:'Engagement slug (e.g., "monthly-close-2026-03").'},practiceHome:{type:"string",description:"Optional override."}},required:["clientSlug","engagementSlug"],group:"practice",readOnly:!0,searchHint:"practice load engagement read ENGAGEMENT.md context",isConcurrencySafe:!0,maxResultSizeChars:2e4,execute:async(e,t)=>{let r=cn(t.practiceHome),n=String(t.clientSlug),o=String(t.engagementSlug);try{In(n,"client slug"),In(o,"engagement slug")}catch(s){return{error:s instanceof Error?s.message:String(s)}}let i=za(r,n,o);return i?{engagement:i}:{error:`Engagement not found: ${n}/${o}`,hint:"Run practice_load_client to see active engagement slugs."}}}];var q_=null;function l_e(){if(!q_){q_=new Map;for(let e of yl)q_.set(e.name,e)}return q_}function z_(e){return l_e().get(e)}function N$(){return yl}function Dl(e){return N$().filter(t=>t.group===e)}ts();function d_e(e){switch(e){case 401:return"Invalid or missing API key. Run `clio auth add` or set JAZ_API_KEY.";case 403:return"Insufficient permissions for this operation.";case 404:return"Resource not found \u2014 check the resourceId. Use a search/list tool to look it up.";case 409:return"Conflict \u2014 resource may have been modified. Re-fetch and retry.";case 422:return"Validation error \u2014 check field values against the tool description.";case 429:return"Rate limited \u2014 wait a moment and retry.";default:return""}}function DG(e){if(e instanceof es){let r=d_e(e.status);return{error:e.message,status:e.status,endpoint:e.endpoint,...r?{hint:r}:{}}}return{error:e instanceof Error?e.message:String(e)}}function bG(e,t){if(e.readOnly)return{valid:!0,errors:[]};let r=[];for(let n of e.required){let o=t[n];if(o==null){r.push(`Missing required field: ${n}`);continue}typeof o=="string"&&o.trim()===""&&r.push(`Required field "${n}" is empty`)}for(let[n,o]of Object.entries(t)){if(o==null)continue;let i=e.params[n];if(!i)continue;let s=p_e(n,o,i);s&&r.push(s),i.enum&&typeof o=="string"&&(i.enum.includes(o)||r.push(`Field "${n}" value "${o}" not in allowed values: ${i.enum.join(", ")}`))}return{valid:r.length===0,errors:r}}function p_e(e,t,r){switch(r.type){case"string":if(typeof t!="string")return`Field "${e}" expected string, got ${typeof t}`;break;case"number":if(typeof t!="number"||!Number.isFinite(t))return`Field "${e}" expected finite number, got ${typeof t=="number"?"non-finite":typeof t}`;break;case"boolean":if(typeof t!="boolean")return`Field "${e}" expected boolean, got ${typeof t}`;break;case"array":if(!Array.isArray(t))return`Field "${e}" expected array, got ${typeof t}`;break;case"object":if(typeof t!="object"||Array.isArray(t))return`Field "${e}" expected object, got ${Array.isArray(t)?"array":typeof t}`;break}return null}var bl=[{name:"invoices",description:"Sales invoices (INV/SI). Create, search, get, update, delete, pay, finalize, apply credits, download PDF. Also: receivables, AR, AR aging, billing, overdue invoices, dunning, recurring invoices.",groups:["invoices"]},{name:"customer_credit_notes",description:"Customer credit notes (CN). Create, search, update, delete, finalize, refund, download PDF. Also: sales returns, customer CN.",groups:["customer_credit_notes"]},{name:"bills",description:"Purchase bills (PO/PI). Create, search, get, update, delete, pay, finalize, apply credits. Also: payables, AP, vendor invoices, supplier bills.",groups:["bills"]},{name:"supplier_credit_notes",description:"Supplier credit notes. Create, search, update, delete, finalize, refund. Also: purchase returns, debit notes, supplier CN.",groups:["supplier_credit_notes"]},{name:"journals",description:"Journal entries (JE). Create, search, update, delete manual journals. Also: adjusting entries, accruals, reclassifications, corrections.",groups:["journals"]},{name:"cash_entries",description:"Cash-in receipts and cash-out disbursements for external cash movements. WHEN TO USE: money received from customers/external \u2192 cash-in. Money paid to suppliers/external \u2192 cash-out. For internal account-to-account transfers, use cash_transfers namespace.",groups:["cash_entries"]},{name:"cash_transfers",description:"Cash transfers between your own bank/cash accounts and cashflow transaction search. WHEN TO USE: moving funds between own accounts (main bank \u2192 petty cash, USD \u2192 SGD). For external receipts/payments, use cash_entries namespace.",groups:["cash_transfers"]},{name:"bank_accounts",description:"Bank accounts, bank statement imports (CSV/OFX), bank records search, auto-reconciliation. For unreconciled queries: ALWAYS search bank records with status UNRECONCILED after listing accounts. Also: bank feeds, bank balance.",groups:["bank"]},{name:"bank_rules",description:"Bank reconciliation rules (action shortcuts). Create, search, update, delete bank rules. Configure auto-matching rules for bank records.",groups:["bank_rules"]},{name:"reconciliations",description:"Apply a reconciliation decision to a bank statement entry \u2014 write side. Distinct from bank_accounts/bank_rules (which configure auto-reconciliation) and view_auto_reconciliation (which queries suggestions). Eight endpoints: quick_reconcile + apply_bank_rule (async, bulk), and direct_cash_entry / cash_journal / manual_journal / cash_transfer / invoice_receipt / bill_receipt (sync, per-entry). Most fields prefill from the bank entry when omitted.",groups:["reconciliations"]},{name:"financial_reports",description:"Core financial statements: trial balance (TB), balance sheet (BS/B/S), profit & loss (PnL/P&L/income statement), cash flow, general ledger (GL), cash balance/position, equity movement, VAT/GST ledger. Also: how profitable, what is the balance.",groups:["financial_reports"]},{name:"operational_reports",description:"Aging and operational reports: aged receivables (AR aging), aged payables (AP aging), AR report, bank balance summary, bank reconciliation reports, fixed asset (FA) summary, FA reconciliation. Data exports (CSV/Excel/XLSX). Anomaly detection and audit analysis: anomalous invoices, anomalous bills, cashflow anomalies, GL journal audit, exchange rate audit, receivables customer risk, cash expense health. Also: overdue analysis, how much owed, suspicious transactions, audit trail.",groups:["operational_reports","exports"]},{name:"contacts",description:"Contacts (customers/suppliers/vendors), contact groups, customer segmentation. Create, search, get, update, delete contacts. Bulk upsert contacts from CSV / spreadsheet imports \u2014 async, returns jobId, poll background_jobs. List/create contact groups.",groups:["contacts","contact_groups"]},{name:"items_and_inventory",description:"Products, services, inventory items. Create, search, get, update, delete items. Check inventory balance. Also: SKU, catalog, stock.",groups:["items","inventory"]},{name:"tags_and_custom_fields",description:"Tags for categorizing transactions. Custom fields for adding metadata (text, date, dropdown). Create, search, delete tags and custom fields.",groups:["tags","custom_fields"]},{name:"nano_classifiers",description:"Nano classifiers (tracking categories/dimensions). List, search, create, update, delete classifiers and their classes. Used for line-item tagging and dimensional reporting. Also: tracking categories, cost centers, departments, projects.",groups:["nano_classifiers"]},{name:"chart_of_accounts",description:"Chart of accounts (COA/GL accounts). Create, search, update accounts. Bookmarks (favorites/shortcuts). Also: ledger codes, account types.",groups:["accounts","bookmarks"]},{name:"currencies",description:"Currencies, exchange rates (FX/forex). List/add org currencies. Set, update, import currency rates. Also: multi-currency, FX rates.",groups:["currencies"]},{name:"tax_profiles",description:"Tax profiles (GST/VAT/sales tax), withholding tax codes (WHT/ATC). Search, create, update tax profiles. List WHT codes.",groups:["tax_profiles"]},{name:"capsules_and_recipes",description:"Capsules (transaction groupings/capsule types). Financial recipes: amortization, depreciation, deferred revenue, IFRS 16 leases, hire purchase, fixed deposits, FX revaluation, loan schedules, ECL/expected credit loss, IAS 37 provisions, asset disposal. Plan and execute recipes. Keywords: calculate, provision, schedule, expected credit loss, revaluation.",groups:["capsules","recipes"]},{name:"scheduled_transactions",description:"Scheduled/recurring invoices, bills, journals. Create scheduled invoices/bills/journals, search scheduled transactions. Also: recurring, auto-generate.",groups:["schedulers"]},{name:"subscriptions",description:"Subscriptions (recurring billing/payment plans). Create, update, cancel, search subscriptions. Also: recurring charges, subscription schedules.",groups:["subscriptions"]},{name:"organization",description:"Organization info (name, currency, country, fiscal year). User management: invite, update, remove, search org users. Bulk invite.",groups:["organization","org_users"]},{name:"document_ai",description:"File attachments, AI document extraction (magic/OCR). Upload/list attachments. Create transactions from PDFs/images (invoice scanning, bill extraction). Track extraction workflows.",groups:["attachments","magic"]},{name:"fixed_assets",description:"Fixed assets (PP&E/property, plant, equipment). Search, create, update, discard, sell, transfer, undo disposal. Also: depreciation, asset register.",groups:["fixed_assets"]},{name:"payments_and_search",description:"Payment records: get, update, delete individual payments. List payments/credits on invoices and bills. Reverse credit applications. Cashflow transaction search. Universal cross-entity search. Also: payment run, batch payment, payment matching, void payment, payment history, credit note applications.",groups:["payments","cashflow","search"]},{name:"quick_fix",description:"Quick Fix: bulk-update multiple transactions or line items in one call. Change dates, contacts, tags, accounts, tax profiles, custom fields across many invoices/bills/journals/credit-notes/cash-entries/schedulers at once. Also: batch update, mass edit.",groups:["quick_fix"]},{name:"export_records",description:"Export records to XLSX. List available columns, preview export scope (row count + sample), generate export file with pre-signed download URL. Supports any entity type: invoices, bills, contacts, items, journals, bank records, cashflow, fixed assets, etc. Pass query (structured search syntax) or filter (JSON), never both.",groups:["export_records"]},{name:"background_jobs",description:"Background job tracking. Poll any async operation by jobId (contacts bulk-upsert, items bulk-upsert, bank import, magic file processing, etc.). Filter by resourceId field to look up a specific job. Poll until status is SUCCESS, FAILED, or PARTIAL_SUCCESS.",groups:["background_jobs"]},{name:"drafts",description:"Draft business transactions \u2014 both local payload validation (invoices, bills, journals, credit notes) AND BULK-FRIENDLY server-side lifecycle: validate_drafts (sync eligibility check), convert_drafts_to_active (async promote to ACTIVE), submit_drafts_for_approval (async route to approval). The lifecycle tools are GENERIC and BULK \u2014 one call accepts up to 500 items mixing any combination of {btResourceId, btType: SALE|PURCHASE|SALE_CREDIT_NOTE|PURCHASE_CREDIT_NOTE}. No need for per-entity tools when promoting/submitting drafts at scale. NOT idempotent on already-promoted drafts.",groups:["drafts"]},{name:"close_procedures",description:"Period-end close checklists: month-end, quarter-end, year-end close. Bank reconciliation job. GST/VAT filing job. Audit preparation. Returns structured blueprints.",groups:["close_jobs"]},{name:"operational_jobs",description:"Operational job checklists: payment runs, credit control/collections, supplier reconciliation, fixed asset review, document collection, statutory filing. Returns structured blueprints.",groups:["operational_jobs"]},{name:"help_center",description:"Search the Jaz help center for how-to articles, feature guides, accounting concepts, and troubleshooting. Returns top matches with title, section, snippet, and source URL. Works without an API key.",groups:["help_center"]},{name:"practice",description:"Practitioner workspace scaffolding: per-client folders, per-engagement files, schemas. Use to onboard new clients (capture legal entity / FY / GST scheme / banks / recurring accruals into CLIENT.md), list active engagements, scaffold a monthly-close / quarterly-gst / annual-statutory engagement, and load client/engagement context before driving accounting work via jaz-jobs blueprints + jaz-recipes. All filesystem-only, offline. Pair with jaz-practice skill for the canonical playbooks.",groups:["practice"]}];function vG(e,t=5){let r=e.trim().toLowerCase();if(!r)return[];let n=r.split(/[\s_-]+/).filter(i=>i.length>=2);return n.length===0?[]:bl.map(i=>{let s=0,a=i.name.toLowerCase(),c=i.description.toLowerCase();(a===r||a===r.replace(/\s+/g,"_"))&&(s+=5),a.includes(r)&&(s+=3),c.includes(r)&&(s+=1);for(let u of n)a.includes(u)&&(s+=2),c.includes(u)&&(s+=1);return{ns:i,score:s}}).filter(i=>i.score>0).sort((i,s)=>s.score-i.score).slice(0,t).map(i=>i.ns)}function Y_(e){let t={type:e.type};if(e.description&&(t.description=e.description),e.enum&&(t.enum=e.enum),e.type==="array"&&e.items&&(t.items=Y_(e.items)),e.type==="object"&&e.properties){let r={};for(let[n,o]of Object.entries(e.properties))r[n]=Y_(o);t.properties=r,e.required?.length&&(t.required=e.required)}return t}function cc(e,t){let r={};for(let[n,o]of Object.entries(e))r[n]=Y_(o);return{type:"object",properties:r,...t.length>0?{required:t}:{}}}function SG(e){return{name:e.name,description:e.description,input_schema:cc(e.params,e.required),...e.searchHint?{searchHint:e.searchHint}:{}}}function f_e(e){if(typeof e!="string")return e.isDestructive??!1;let t=e;return t.startsWith("delete_")||t.startsWith("pay_")||t.startsWith("finalize_")||t.includes("refund")||t==="remove_org_user"}function _G(e){if(!e.trim())return{namespaces:bl.map(o=>{let i=o.groups.flatMap(s=>Dl(s));return{name:o.name,description:o.description,toolCount:i.length,tools:i.map(s=>s.name)}}),hint:"Call describe_tools with specific tool names to get full schemas before executing."};let t=vG(e,5);if(t.length===0)return{matches:[],hint:`No tools match "${e}". Try broader terms or call search_tools with empty query to see all namespaces.`};let n=e.toLowerCase().split(/\s+/).filter(Boolean);return{matches:t.map(o=>{let s=o.groups.flatMap(a=>Dl(a)).map(a=>{let c=`${a.name} ${a.searchHint??""} ${a.description}`.toLowerCase(),u=n.filter(l=>c.includes(l)).length;return{tool:a,score:u}}).sort((a,c)=>c.score-a.score);return{namespace:o.name,description:o.description,tools:s.map(a=>({name:a.tool.name,description:a.tool.description.split(`
|
|
866
866
|
`)[0],...a.tool.searchHint?{searchHint:a.tool.searchHint}:{}}))}}),hint:"Call describe_tools with the tool names you need, then execute_tool to run them."}}var m_e={name:"search_tools",description:"Search for available Jaz accounting tools by keyword. Returns matching tool namespaces with tool names and descriptions. Call with empty query to list all namespaces. ALWAYS call this first to discover what tools are available.",inputSchema:cc({query:{type:"string",description:'Search keyword (e.g. "invoice", "bank recon", "depreciation"). Empty string lists all namespaces.'}},[])};function EG(e){if(!Array.isArray(e)||!e.every(n=>typeof n=="string"))return{error:"Invalid `tools` parameter. Expected string[]."};if(e.length===0)return{error:"Provide at least one tool name. Use search_tools first to discover tool names."};let t=[],r=[];for(let n of e){let o=z_(n);if(!o){r.push(n);continue}t.push({...SG(o),readOnly:o.readOnly,isDestructive:o.isDestructive??!1,isConcurrencySafe:o.isConcurrencySafe??!1,destructiveHint:!o.readOnly&&f_e(o),group:o.group})}return{tools:t,...r.length>0?{notFound:r,hint:`Tools not found: ${r.join(", ")}. Use search_tools to find correct names.`}:{}}}var h_e={name:"describe_tools",description:"Get full JSON Schema (parameters, types, required fields) for specific tools. Call this after search_tools to get the exact input format before calling execute_tool.",inputSchema:cc({tools:{type:"array",items:{type:"string"},description:'Tool names to describe (e.g. ["create_invoice", "search_contacts"])'}},["tools"])},g_e={name:"execute_tool",description:"Execute a Jaz accounting tool. Call describe_tools first to get the required parameters. Pass the tool name and its arguments.",inputSchema:cc({tool:{type:"string",description:'Tool name (e.g. "create_invoice")'},arguments:{type:"object",description:"Tool arguments (see describe_tools for schema)"},org_id:{type:"string",description:"Organization ID (UUID). Required in multi-org mode (PAT or multiple API keys). Call list_organizations to get available IDs."}},["tool"])},IG={name:"list_organizations",description:"List organizations available to the current authentication. Returns org names and resource IDs for use as org_id in execute_tool calls.",inputSchema:cc({},[])},k$=[m_e,h_e,g_e];Kc();ts();import{readFileSync as y_e,readdirSync as D_e,statSync as b_e}from"node:fs";import{dirname as AG,join as Us,relative as v_e}from"node:path";import{fileURLToPath as xG}from"node:url";function S_e(e,t){let r;try{r=b_e(e)}catch{return[]}if(!r.isDirectory())return[];let n=[],o=20,i=(s,a)=>{if(a>o)return;let c;try{c=D_e(s,{withFileTypes:!0})}catch{return}for(let u of c){let l=Us(s,u.name);if(u.isDirectory())i(l,a+1);else if(u.isFile()&&u.name.endsWith(".md")){let d=v_e(e,l).split(/[\\/]/).join("/");(!t||!t(d))&&n.push(d)}}};return i(e,0),n.sort()}function CG(e){let t=e.fileFilter??(()=>!0);return S_e(e.rootDir,n=>!t(n)).map(n=>{let o=e.uriPathBuilder?e.uriPathBuilder(n):n.replace(/\.md$/,"");return{uri:`${e.uriPrefix}/${o}`,name:e.nameBuilder(n),description:e.descriptionBuilder?.(n),mimeType:"text/markdown",filePath:Us(e.rootDir,n)}})}function __e(){let e=xG(import.meta.url),t=AG(e);return DT([Us(t,"..","..","..","assets","skills"),Us(t,"..","..","..","..","assets","skills"),Us(t,"skills")])}function E_e(){let e=xG(import.meta.url),t=AG(e);return DT([Us(t,"..","..","..","help-center-mirror"),Us(t,"..","..","..","assets","skills","api","help-center-mirror"),Us(t,"help-center-mirror")])}var I_e=["api","cli","conversion","jobs","transaction-recipes"],C_e={api:"Jaz REST API rules: every endpoint, field name, error code, production gotchas.",cli:"Jaz CLI commands, auth, calculators, jobs.",conversion:"Migration from Xero, QuickBooks, Sage, MYOB, or Excel exports into Jaz.",jobs:"12 recurring accounting workflow playbooks (close, reconciliation, GST/VAT, etc.).","transaction-recipes":"16 IFRS-compliant transaction recipes plus 13 financial calculators."};function A_e(e){if(e.includes("/help-center-mirror/"))return!1;let t=e.split("/")[0];return I_e.includes(t)}function x_e(e){let t=e.split("/"),r=t[0];if(t.length===2&&t[1]==="SKILL.md")return`Jaz ${r} skill`;if(t[1]==="references"){let n=t.slice(2).join("/").replace(/\.md$/,"");return`Jaz ${r} reference: ${n}`}return`Jaz ${r}: ${e}`}function w_e(e){let t=e.split("/")[0];return C_e[t]}function T_e(e){return!e.includes("/")}function O_e(e){return`Jaz help center: ${e.replace(/\.md$/,"").split("-").map(n=>n[0]?.toUpperCase()+n.slice(1)).join(" ")}`}var F$=null,P$=null;function L$(){if(F$)return F$;let e=__e(),t=E_e(),r=[];return e&&r.push(...CG({rootDir:e,uriPrefix:"jaz://skill",fileFilter:A_e,nameBuilder:x_e,descriptionBuilder:w_e,uriPathBuilder:n=>{let o=n.split("/");return o.length===2&&o[1]==="SKILL.md"?o[0]:n.replace(/\.md$/,"")}})),t&&r.push(...CG({rootDir:t,uriPrefix:"jaz://help-center",fileFilter:T_e,nameBuilder:O_e,descriptionBuilder:()=>"Jaz help center article. Same corpus search_help_center indexes."})),F$=r,P$=new Map(r.map(n=>[n.uri,n])),r}function wG(e){P$||L$();let t=P$.get(e);if(!t)throw new Error(`Resource not found: ${e}`);let r=y_e(t.filePath,"utf-8");return{mimeType:t.mimeType,text:r}}var $_e=new Set(["close_jobs","operational_jobs","help_center"]),TG="Calculators, blueprints, and help center search work without an account.",OG="Get an API key or token from jaz.ai";function j$(e,t){return!!($_e.has(e)||e==="recipes"&&t==="plan_recipe")}function R_e(e,t){return new zr({mode:"pat",token:e,orgId:t})}async function $G(e,t=5e3){let r=new Map,n=[],o=a=>Promise.race([a,new Promise(c=>setTimeout(()=>c(null),t))]),i=e.map(a=>({auth:a,client:new zr(a)})),s=i[0]?.client??null;return await Promise.all(i.map(async({auth:a,client:c})=>{try{let u=await o(Mn(c));if(u)r.set(u.resourceId,{name:u.name,resourceId:u.resourceId,currency:u.currency,client:c});else{let l=a.mode==="direct"?"api-key":a.mode;n.push(`Org lookup returned empty (${l}).`)}}catch(u){let l=a.mode==="direct"?"api-key":a.mode;n.push(`Org lookup failed (${l}): ${u instanceof Error?u.message:"unknown"}`)}})),{orgMap:r,primaryClient:s,errors:n}}function RG(e){e.command("mcp").description("Start MCP stdio server for Claude Code / Cowork").option("--api-key <key>","API key, PAT, or comma-separated keys (overrides stored/env)").action(async t=>{let r=[];try{r=QC(t.apiKey)}catch(b){let S=b instanceof Error?b.message:String(b);process.stderr.write(`jaz-ai: ${S} (continuing in offline mode)
|
|
867
867
|
`)}let n=e.version()??"0.0.0",o=new Map,i=null,s=r.length>1||r[0]?.mode==="pat";if(r.length>0){let b=await $G(r);o=b.orgMap,i=b.primaryClient;for(let S of b.errors)process.stderr.write(`warning: ${S}
|
|
868
868
|
`)}let a="",c="";if(o.size>1){let b=[...o.values()].map(S=>`${S.name} (${S.currency})`);a=`Connected to ${o.size} organizations: ${b.join(", ")}.`,c=`${o.size} orgs`}else if(o.size===1){let b=[...o.values()][0];a=`Connected to: ${b.name} (${b.currency}).`,c=b.name}else if(i){let b=yd();if(b){let S=to(b);S?.orgName&&(a=`Connected to: ${S.orgName} (${S.currency}).`,c=`${S.orgName} (${b})`)}}let u=bl.map(b=>b.name).join(", "),l=i?"All API tools hit api.getjaz.com using the configured credentials.":`${OG} to bring Jaz into Claude for real accounting and finance work. ${TG}`,d=s&&o.size>0?["","MULTI-ORG MODE:",`You have access to ${o.size} organization(s):`,...[...o.values()].map(b=>` - ${b.name} (org_id: ${b.resourceId}, ${b.currency})`),"","RULES:","1. Every execute_tool call for API tools MUST include org_id.","2. If the user mentions an org by name, match it to the org_id above.",`3. If the user hasn't specified an org, ask: "Which organization?"`,"4. Once the user picks an org, use it for all subsequent calls until they say otherwise.",'5. If the user says "switch to [org]", update the active org for subsequent calls.','6. Always confirm the org on the first tool call: "Using [Org Name]."','7. For cross-org queries (e.g. "compare Acme and Beta"), make separate execute_tool calls with different org_ids.',"8. Call list_organizations to refresh the org list if needed."]:[],f=[`Jaz accounting platform \u2014 ${yl.length} tools across ${bl.length} namespaces.`,a,"","WORKFLOW: search_tools \u2192 describe_tools \u2192 execute_tool.","1. Call search_tools with a keyword to find relevant tools.","2. Call describe_tools with tool names to get full parameter schemas.","3. Call execute_tool with the tool name and arguments to run it.","",`Namespaces: ${u}.`,"","Key API rules (apply to all tools):","- All IDs are `resourceId` (never `id`).","- All transaction dates are `valueDate` (not issueDate/invoiceDate/date). Format: YYYY-MM-DD.","- Line item text field is `name` (not `description`).","- `saveAsDraft` defaults to false \u2014 omitting it finalizes the transaction.","- Response dates are epoch milliseconds (convert with new Date(ms)).","- Pagination: use `limit`/`offset` (not page/size). Search sort is required when offset present.","- Create responses return only `{ resourceId }` \u2014 GET the full entity after creation.","","Offline tools (no auth needed): close-procedure blueprints (close_procedures, operational_jobs), plan_recipe calculator, and search_help_center for the Jaz help center.",l,...d].filter(b=>b!==void 0).join(`
|