Taokeh MCP server
Server Details
Taokeh is accounting software for Malaysian SMEs — double-entry books, LHDN e-Invoice (MyInvois), SST, and full statutory payroll — and this connector opens a company's live books to the AI its owner already uses.
59 tools. The reads answer real questions from the ledger: P&L and balance sheet with server-computed comparisons, cash position, A/R and A/P aging, per-channel marketplace sales, an 8-week cash-flow forecast, tax position, document search with e-Invoice standing, and a one-call daily brief. The writes are drafts only — expenses, invoices, bills, quotes, purchase orders, receipts, credit and debit notes, adjusting journals, bank-statement imports and bank-row suggestions — every figure re-checked by the server, every draft waiting for a human tap in Taokeh. The AI can also work the Shoebox: staff snap paper on free phone logins, and the connector lists the pile, reads each photo, and files the draft with the original attached — the server maps its own stored copy, so the document trail stays byte-perfect.
One connection is bound to one company at consent; no tool takes a company argument. Migrating from another system? The same connector stages the chart of accounts, opening balances, contacts, products, historical documents and workspace settings onto the owner's own review screens. Bring your own AI subscription — no per-call fees.
The last two health checks of this connector failed. If this server requires authentication, missing or expired test credentials may be the cause. A test profile lets Glama authenticate for health checks and discover tools; it is separate from your personal connections.
If you are the author, claim ownership, then add or update a test profile under Admin → Test Profile.
- Status
- Unhealthy
- Uptime
- 0.0% over the last 24 hours
- OAuth
- Works in Glama
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
- ChatGPT
- –
- Claude
- –
TDQS
Scored across 77 tools
Many tools are clearly separated by document type, and the descriptions explicitly route between similar doors. However, the set contains overlapping report/summary clusters (business_snapshot vs daily_brief vs cash_position; ar_aging vs open_invoices; financial_timeseries vs working_capital/profit_drivers) and several create/update/revise lanes that an agent can still confuse at scale.
Names are overwhelmingly snake_case and use recognizable create_/update_/search_/get_/resolve_/stage_ prefixes, with _draft consistently marking draft lanes. Minor deviations such as draft_bank_classification, find_in_taokeh, my_work, and noun-only report names keep it from being fully uniform.
77 tools is far beyond the appropriate 3-15 range and sits above the 50+ extreme threshold. Even for a full accounting suite, this surface is too large for reliable agent selection and context economy.
Coverage is extensive across documents, reports, reconciliation, payroll, attachments, and migration, with create/update/revise/search/report loops for most document types. Gaps exist for creating delivery orders, products, employees, and buy-side debit notes through dedicated AI lanes, though several are intentional human-only operations or have staging workarounds.
Available Tools
77 toolsap_agingA/P agingARead-onlyInspect
Whom you owe money and how overdue it is — your accounts-payable aging by supplier, aged from each bill's DUE date (its own date when it states no term). WHAT A ROW IS: the PAYABLE each document created on the accounts-payable control account, less what has been paid against it by asOf — never the bill's gross total. UNMATCHED PAYMENTS: a bank payment posted to a supplier but not yet matched to a bill is already inside that supplier's bucket figures and total as a NEGATIVE amount, aged from the date it left, and listed under the row's unmatchedPayments with a kind: payment (negative), refund (a supplier refund not yet matched — positive), or booked_outside (the bank line was matched to bills for more than it booked to accounts payable — positive; the fix is on the bank line). A payment that IS matched, but to a bill dated after asOf — an advance paid to the supplier before the bill — is listed here too, kind payment, with paysLater ([{reference, date, amount}]) naming the bill(s) and the date it pays them: it sits on the supplier's account until that date and applies then, so do not suggest matching it. unmatchedPaymentsTotal is their sum, less the paysLater part (that part is paysLaterTotal), and documentTotals the buckets of the bills alone — quote documentTotals for what is OVERDUE. So a row's total is what you owe NET and can be negative. With them, this report equals the control-account balance to the cent, except for a manual journal posted straight to the control account, the period-end unrealized FX revaluation on its own date, and a bank line whose allocations were saved but which is not posted yet. Look for manual entries on the control account before doubting either figure. A cash bill, or a bill settled from somewhere other than the supplier account (an owner-paid bill booked to a shareholder loan), created no payable and is absent however its payment method reads; a debit note nets its supplier down. A figure here can therefore be lower than the same bill's total in search_documents — say which you are quoting.
| Name | Required | Description | Default |
|---|---|---|---|
| asOf | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes far beyond the readOnlyHint annotation by disclosing what a row actually represents (net payable, not gross bill total), that totals can be negative, that unmatched payments/refunds/booked_outside appear as signed amounts, and the precise reconciliation exceptions (manual journals to control account, FX revaluation, unposted saved allocations). It also discloses which documents are absent entirely (cash bills, owner-paid bills) and why figures can differ from search_documents. Consistent with readOnlyHint=true.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a plain-language summary, then a WHAT A ROW IS anchor, then progressively finer caveats. Every block carries real information (no filler), but the mid-section is dense and would benefit from more sentence breaks given the volume of sign conventions and exception clauses packed together.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the full burden and does so: it effectively documents the response shape (total, documentTotals, unmatchedPayments with kind, unmatchedPaymentsTotal, paysLater/paysLaterTotal) inline. An agent can interpret results correctly without any return-value schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter, asOf, with 0% schema description coverage — the schema gives the regex pattern but no meaning, optionality or default. The description uses asOf in context ('paid against it by asOf') but never defines it as the cutoff date or states what happens when omitted. Partial compensation only.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a concrete verb+resource framing: 'Whom you owe money and how overdue it is — your accounts-payable aging by supplier', aged from the bill's due date. It is immediately distinguishable from the sibling ar_aging (receivables) and from search_documents, which it names explicitly later.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete decision guidance: 'quote documentTotals for what is OVERDUE', 'do not suggest matching' a paysLater payment, and 'Look for manual entries on the control account before doubting either figure'. It also tells the agent to state which figure it is quoting versus search_documents. It stops short of an explicit 'use this tool when X instead of Y' statement, so it is strong context rather than full routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ar_agingA/R agingARead-onlyInspect
Who owes you money and how overdue it is — accounts-receivable aging across ALL customers, bucketed by age. For the unpaid invoices of ONE named customer, use open_invoices. WHAT A ROW IS: the RECEIVABLE each document created on the accounts-receivable control account, less what has been received against it by asOf — never the document's gross total. UNMATCHED PAYMENTS: a customer payment not yet (fully) applied to invoices — a bank receipt posted to a customer, or money recorded on Sales → Official receipts → Receive payment (into a bank or Cash) beyond what the invoices took, or a payment applied only in part (its row is what is left) — is already inside that customer's bucket figures and total as a NEGATIVE amount (money you hold for them), aged from the date it arrived, and is listed under the row's unmatchedPayments (bankTransactionId, date, amount, bucket, kind). kind says what it is: payment (money in, not yet applied — negative; suggest applying it to invoices rather than treating the invoice balance as unpaid: the owner does it in Banking → Match, which can apply part of it, or on Sales → Official receipts → Receive payment, which lists the customer's credit — a payment received in Cash only there), refund (money paid out to the customer, not yet matched — positive), or booked_outside (the bank line was matched to invoices for MORE than it booked to accounts receivable — e.g. posted to an income account first — so the invoices read settled while the control account still holds that amount; positive; the fix is on the bank line). A payment that IS matched, but to an invoice dated after asOf — a deposit the customer paid before the invoice was raised — is listed here too, kind payment, with paysLater ([{reference, date, amount}]) naming the invoice(s) and the date it pays them: it sits on the customer's account until that date and applies then, so do not suggest matching it. unmatchedPaymentsTotal is their sum, less the paysLater part (that part is paysLaterTotal), and documentTotals the buckets of the documents alone — quote documentTotals for what is OVERDUE, totals for the balance. So a row's total is what the customer owes NET, a row can be negative (they have paid ahead), and a customer can appear with no invoice at all. With them, this report equals the control-account balance to the cent, except for entries that belong to no document or bank line: a manual journal posted straight to the control account, the period-end unrealized FX revaluation (on its own date only — it reverses the next day), and a bank line whose allocations were saved but which is not posted yet. If an owner asks why the two disagree, look for those before doubting either figure. The document universe itself differs on purpose wherever the gross was never collectable: a cash or gateway-paid sale posts no receivable at all and is absent; a marketplace order reads the NET PAYOUT the channel owes (gross less the platform fee it withheld, whether that fee was known at import or only booked later from the settlement); a credit note nets its customer down; and an invoice reversed by a channel return reads nil. So a figure here can legitimately be LOWER than the same invoice's total in search_documents or on its PDF — say which you are quoting rather than treating one as an error. Movement booked after asOf is excluded, so a back-dated aging shows what was owed on that date.
| Name | Required | Description | Default |
|---|---|---|---|
| asOf | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover read-only/no-open-world safety, yet the description discloses row semantics (receivable less receipts, not gross totals), sign conventions (unmatched payments as NEGATIVE, refunds/paysLater as positive), reconciliation guarantees against the control account, and the three named exception classes (manual journals, period-end FX revaluation, unposted bank allocations). It also pre-empts a likely false-alarm: figures can be legitimately lower than search_documents or the PDF, with the reasons enumerated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded, but the body is a single dense wall of run-on sentences stuffed with parentheticals, arrow-chains and nested clauses about `kind` values and banking UI paths. Most content is substantive, yet the volume and lack of structural breaks (headers, bullets) make it hard to scan for a one-parameter read tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description takes full responsibility for the return shape and delivers: what a row is, `total` vs `totals` vs `documentTotals`, the `unmatchedPayments` fields (`bankTransactionId`, `date`, `amount`, `bucket`, `kind`), `paysLater`, `unmatchedPaymentsTotal`, and `paysLaterTotal`. An agent can interpret the response without guessing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% (only a pattern, no description), so the description carries the burden and does: `asOf` defines the cut-off for receipts applied, for matched-to-later-invoice payments, and excludes movements booked after it, enabling back-dated aging. The one gap is that it never says what happens when `asOf` is omitted, despite the parameter being optional.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific resource (accounts-receivable aging) plus scope ('across ALL customers, bucketed by age'), and the second names the sibling it is not: open_invoices, for one named customer. An agent can separate this from ap_aging and open_invoices without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit alternative routing ('For the unpaid invoices of ONE named customer, use open_invoices') plus operational guidance on which figure to quote ('quote `documentTotals` for what is OVERDUE, `totals` for the balance') and how to handle owner disputes. It never states an explicit trigger phrase for calling this tool, but the business question it answers is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
balance_sheetBalance sheetARead-onlyInspect
Your balance sheet as of a date: assets, liabilities and equity, with the balanced check. THE YEAR CLOSES BY ARITHMETIC, NOT BY A JOURNAL: financialYear is the financial year asOf falls in, and equity lines whose code starts with an em-dash (synthetic: true) are NOT accounts — nothing posts to them and they have no ledger. — 'Profit for the year' is revenue minus expenses from financialYear.start to asOf (it equals income_statement net income over those dates). When equityLayout is 'retained_earnings' (Sdn Bhd or type not set), —E 'Profit from earlier years' is the profit of every entry before financialYear.start. When it is 'capital_account' (sole proprietor or partnership), —C 'Capital at start of year' is the owner's capital and drawings before the year plus every earlier year's profit, and the equity account lines carry from = the year start because they show THIS YEAR's movement only. Every other line is the account's balance at asOf. The lines add up to totalEquity; in a COMPARISON across two financial years current and previous are each date's own sheet, while the equity VARIANCE splits both at the earlier date's year start (a note says so), so equity deltas are real activity. In the capital_account layout every equity account folds into Capital at start of year, and each one's line is this year's movement (from). Never describe a synthetic line as an account, never offer to open its ledger, and never treat it as a missing or mis-coded row. COMPARISON (optional): a balance sheet is a POSITION AT A DATE, not a period — so unlike income_statement its comparison is ONE DATE against ANOTHER DATE, never two ranges. Set compare to 'previous_month_end', 'previous_year_end' or 'same_date_last_year', OR give an explicit earlier date as compareAsOf. You get both positions in full plus, per account line and per total, deltaCents and deltaPct computed server-side in integer cents — the movement in that balance BETWEEN the two dates. deltaPct is NULL whenever the earlier balance is zero (a percentage change from zero is undefined — say "no comparable base", never ∞% or 100%), and percentages are measured against the ABSOLUTE earlier balance. points gives both dates, how many days apart they are, and whether either is today or later (still open, so the balances can move); notes states the basis in plain words, including a warning if either sheet does not balance. Never describe the gap between the two dates as a 'period' figure: these are two positions, and the delta is the movement between them.
| Name | Required | Description | Default |
|---|---|---|---|
| asOf | No | ||
| compare | No | Named comparison POINT (a single earlier date, not a range). Omit for no comparison. | |
| compareAsOf | No | An explicit earlier date to compare the position against (overrides `compare`). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover readOnly/openWorld, yet the description discloses the arithmetic year-close model, which lines are synthetic and post to no ledger, layout-dependent equity semantics, server-side delta computation in integer cents, the NULL deltaPct rule at zero base, and the contents of `points` and `notes`. That is substantial behavior beyond the annotations with no contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and each paragraph covers a distinct concern (equity arithmetic, synthetic lines, comparison, delta semantics). It is nonetheless very long for a 3-parameter tool, and the 'never describe...' instruction is restated in both the synthetic-line and period-figure passages, which is redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description must carry the return contract, and it does: line fields (code, from, synthetic), totals, deltaCents/deltaPct, `points` with date gap and open-position flags, and `notes` with an imbalance warning. An agent can call it and interpret the response correctly from this text alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67% and the description adds real meaning for `compare` (a named point that is a single earlier date) and `compareAsOf` (explicit earlier date, overrides `compare`). It does not state the format or default behavior of `asOf` even though it is not required, which is the one remaining gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opening clause gives a specific resource (balance sheet) with a specific temporal scope (as of a date) and enumerates the sections returned (assets, liabilities, equity, balanced check). It also explicitly contrasts itself with income_statement, so an agent can separate the two without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says a balance sheet is a position at a date and that comparison is one date vs another date, never two ranges, and names the three named options plus the explicit-date override. It also gives concrete 'when' guidance for the equity layouts and states what not to do (never offer to open a synthetic line's ledger).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bank_reconciliation_reportBank reconciliation reportARead-onlyInspect
The AUDIT bank reconciliation for one bank account, exactly as Banking → Reconciliation report and Clear transactions show it — so you can work the reconciliation WITH the owner instead of sending them to the app. Returns (1) report: balance per bank statement (or "Not stated" when the statement carries no closing balance — pass balance from the paper statement to work it through; used for this answer only, never stored), less unpresented payments, add deposits not yet credited, adjust for statement lines not yet in the books, = balance per books worked from the statement, beside the ledger balance and the UNEXPLAINED difference (never forced to zero), plus the Clear Transaction listing for the statement period; (2) clearStep: the book entries not yet cleared up to the statement date (each with its entryId, date, reference, description, signed amount, and any suggested statement line of the same amount), and the statement lines a tick may pair with (statementLineId, date, amount, remaining); (3) hints: the three patterns the page explains, in the page's own words (explanation) — a pair of entries that cancel out (clearDate is never before the statement starts), a cash sale with no deposit on the statement (never counter-day or POS takings), and an opening balance the books do not hold: none on the account (kind: missing) or one dated after the first statement starts (kind: late), never given when the report ties; when actionable is false, no fix is safe to name, so name none. Pick the account with bankAccountId (default: the first bank account), and the statement with statementId (default: its latest) or a date with asOf. Clear transactions lists 300 rows at a time, oldest first; offset reads the next batch. Report lists over 200 rows are truncated, their totals never. Ringgit bank accounts only — a credit card or a foreign-currency account is refused with the page's own sentence. READ-ONLY: to tick entries, file create_bank_clearing_draft; to settle a posted payment against invoices or bills, file create_payment_match_draft. The owner approves both in Taokeh.
| Name | Required | Description | Default |
|---|---|---|---|
| asOf | No | Reconcile to this date by hand instead of a statement (YYYY-MM-DD). | |
| offset | No | Clear transactions paging: skip this many uncleared rows (the page shows 300 at a time). | |
| balance | No | The closing balance printed on the paper statement — used only when the statement carries none ("Not stated"). Never stored. | |
| statementId | No | An imported statement — from `statements`. Omit for the latest (or give asOf instead). | |
| bankAccountId | No | The bank account — from `bankAccounts` in an earlier answer. Omit for the first bank account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes far beyond the annotations, disclosing read-only behavior, the 'never forced to zero' difference, handling of 'Not stated' balances, pagination limits, truncation behavior, and refusal of non-ringgit accounts. It aligns with the readOnlyHint annotation and adds substantial behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence is purposeful: it front-loads the core purpose, then systematically details return objects, edge cases, defaults, restrictions, and related tools. No fluff or redundancy; the structure aids comprehension.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description must explain return values, which it does comprehensively (report, clearStep, hints) including their subfields and special conditions. It also covers pagination, defaults, and restrictions, making the tool fully usable without external reference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even though the schema describes all 5 parameters (100% coverage), the description enriches them: explains defaults (first bank account, latest statement), the purpose of balance (only when statement lacks closing balance, never stored), offset's paging (300 rows), and asOf's hand-reconciliation role. This adds significant meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: performing an audit bank reconciliation for one bank account, matching the app's behavior. It specifies the resource (bank account) and distinguishes from siblings like bank_reconciliation_status and create_bank_clearing_draft, leaving no ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly explains when to use this tool (to work the reconciliation with the owner) and names alternative tools for actions (create_bank_clearing_draft for ticking entries, create_payment_match_draft for settling payments). It also details parameter defaults and selection logic, leaving no guesswork.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bank_reconciliation_statusBank reconciliation statusBRead-onlyInspect
Where each bank account stands on reconciliation: GL balance vs the latest statement's closing (with the difference, or an honest "unknown" when no balance is stated); rowCounts by stored status (SUGGESTED = proposed, awaiting the owner; UNCATEGORIZED = never categorised, incl. hand-typed rows; CONFIRMED = categorised, not in the ledger; POSTED; IGNORED = set aside); the unreviewed backlog (SUGGESTED + UNCATEGORIZED not paired on Clear transactions) with pairedInBooks; reconcilingItems — each item's signed effect on the difference, summing to it exactly (NOT_EXPLAINED is the honest remainder, STATEMENTS_DONT_JOIN a missing or duplicated statement). A statement line PAIRED on Clear transactions is in the books: it and the entry it is paired with sit in no other item, and PAIRED_ON_CLEAR appears only when a pair is for a different amount (its effect is what the pair leaves; pairedOnClear gives the paired line count). Cash moved with no bank line is DRIFT_: DRIFT_EXPENSE, DRIFT_GATEWAY_PAYOUT, DRIFT_OPENING, DRIFT_MANUAL, DRIFT_CASH_SALE (cash sales booked to the account), DRIFT_CASH_BILL (bills paid in cash), DRIFT_LOAN (loan instalments), DRIFT_DOCUMENT (other records: payroll, assets, credit notes, adjustments…), and DRIFT_OTHER only for what cannot be attributed; notOnStatementNotInBooks — unposted lines on no statement (hand-typed, or a recorded receipt/payment not yet posted), which do not affect the difference; and per recent statement its balances, tie-out and counts (paired = lines paired on Clear transactions, never counted as suggested, confirmed or uncategorized; pairsLeaving = that statement's pairs for a different amount and what they leave). A CREDIT CARD's figures are a liability: negative = money OWED, never cash. Same figures as the in-app Bank Reconciliation page. READ-ONLY.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, and the description reinforces it with 'READ-ONLY.' Beyond that it adds real behavioral context: it defines what each stored status means, explains pairing semantics, and states the figures match the in-app page. It does not cover auth requirements or performance, so not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single enormous run-on paragraph packed with parentheticals and nested definitions. It is front-loaded with the main purpose, but the volume and lack of structure make it hard to scan and violates the conciseness bar.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description must and does explain return contents in detail (rowCounts, reconcilingItems, drift categories, per-statement tie-outs). It is arguably over-complete rather than under-complete, which is the right side of the tradeoff for a schema-less tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, so the baseline is 4; there is nothing for the description to clarify or compensate for.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening clause ('Where each bank account stands on reconciliation: GL balance vs the latest statement's closing') states a specific verb/resource/scope clearly. It does not, however, explicitly differentiate itself from siblings like bank_reconciliation_report or bank_review_queue, so an agent must infer the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit when-to-use statement and no named alternatives, even though several siblings (bank_reconciliation_report, bank_review_queue, bank_reconciliation_status) clearly overlap. The agent gets a rich payload description but no routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bank_review_queueBank review queueARead-onlyInspect
The IMPORTED BANK REGISTER — the rows that came in on a bank or card statement, in ANY status, defaulting to the ones still awaiting review. By default (no status) it is exactly the unreviewed queue it has always been: the unposted rows of one statement, or of the latest imported statement if you don't name one. Pass status to read the settled history instead — 'confirmed' (categorised, not yet posted), 'posted' (in the ledger), 'ignored' (the owner set them aside) or 'all'. Pass from/to WITHOUT a statementId to read a DATE WINDOW across every statement this company has imported, rather than one statement; the payload then says scope:'window' and statement is null. A window holding more than 1000 rows still awaiting review is refused with the count — narrow the dates or name a statementId; settled history has no such ceiling. ⛔ HAND-RECORDED settlements are NEVER here whatever you pass: a receipt or supplier payment typed into Taokeh by hand has no statement behind it, and this tool is the imported register only. To find those, use search_documents with docType:'payment'. Each row still awaiting review carries Taokeh's LIVE suggested category + contact, a confidence score (0–1) and a plain-language reason, computed the SAME way the in-app Banking → Review screen computes them. A suggestion does NOT come only from the tenant's transaction history: the owner can write a standing RULE for a payer (What Taokeh has learned → Bank rules), and such a rule beats what Taokeh worked out on its own — those rows' reasons read "You set this rule on " rather than "Learned: … (×N)". If a suggestion looks unlike the payer's history, the rule is usually why; say so instead of calling the history wrong. A known supplier's money-out line is suggested as SUPPLIER_PAYMENT only when that supplier has open bills the amount could pay; otherwise it is suggested as EXPENSE with the supplier kept (a payment with no bill would leave payables negative). A money-out line whose learned or ruled account belongs to a loan on the register (Financing) is never suggested to that account: with exactly one ACTIVE loan on it, it is suggested as LOAN_PAYMENT (the owner records it with the interest on the line's Loan payment panel); otherwise the reason says the account is left for the owner. The same holds for an OWNER_WITHDRAWAL when the account Taokeh would repay first (2400, when no Shareholder Loan account is mapped) carries a loan on the register: LOAN_PAYMENT with one ACTIVE loan, else OTHER with no account and the reason (never for a sole proprietor or partnership, whose owner withdrawals repay no loan). EVERY row on a foreign-currency account carries notPostable, whatever its status, in Taokeh's own sentence naming the account: Taokeh posts only ringgit accounts for now, so such a line cannot be posted, matched to invoices or bills, or used for a refund — a POSTED one too (posted before the refusal shipped); it can still be unmatched or unposted in Banking. Never ask the owner to post, match or refund it. currency is the statement account's currency (MYR for a ringgit account), and every amount is in it; a date window across accounts in different currencies says currency: null and lists them in currencies. Sorted lowest-confidence first (the ones that need a human eye), capped at 200 with an honest truncation note, plus per-confidence bucket counts so you can say e.g. "46 are mechanical, 13 need eyes". A row can also carry possibleDuplicateOf: a receipt or supplier payment the owner already recorded BY HAND that looks like the same money as this imported line (same account, same amount, within three days) — it is only a resemblance, Taokeh decides nothing from it, and you should say so rather than propose a category for money that may already be in the books. What you CAN do with a row: review it, explain it, and PROPOSE a category with draft_bank_classification — and, for the three categories whose account the owner picks by hand (EXPENSE, OTHER and INTERNAL_TRANSFER), the LEDGER ACCOUNT too. EXPENSE is a plain expense paid by card or bank (no party — never A/P); an unrecognised card purchase is suggested as EXPENSE. On money IN, EXPENSE is an EXPENSE REFUND (money back from a purchase, booked against the expense account); Taokeh suggests it only for a credit on a card whose description reads as a refund (REFUND, RFND, REVERSAL, BAYARAN BALIK). Your proposal is written to the row as an "AI suggestion" the owner sees on Banking → Review, with your proposed account pre-selected in their dropdown and marked as yours; rows already carrying a proposal from you come back here as yourProposal, so you can see what you told them. An OWNER_WITHDRAWAL you proposed on money out while the account Taokeh would repay first (2400, when no Shareholder Loan account is mapped) carries a loan on the register is NOT pre-selected: the row shows what the loan fence makes of it — LOAN_PAYMENT with the one ACTIVE loan on it, else OTHER with no account — and yourProposal.shownOnReviewAs ({category, reason}) says so; tell the owner that, not that their row reads Owner withdrawal. What you can NEVER do: CONFIRM or POST a row — and proposing the account changes nothing about that: it fills a dropdown, the owner still clicks. Accepting a category and posting each line is always a human click in Taokeh; hand the owner the review link. (Since 2026-08-19 the owner can also accept Taokeh's confident suggestions, and post confirmed lines, from the /go command bar's approval deck — still one human tap either way. YOUR OWN proposals are accepted only on Banking → Review, where your reasons are one tap away on each row.) ⚠ SETTLED ROWS CARRY NO SUGGESTION. A row that has already been confirmed, posted or ignored comes back with suggestedCategory, suggestedContact, confidence, reason and possibleDuplicateOf all NULL — the classifier is not re-run over decided history, because a fresh guess printed beside a decision the owner already made reads as a second opinion on settled books. Those rows carry what was actually DECIDED instead: category, glAccount ({code,name}, null if the row has none), contact (the customer or supplier on the row), journalEntryId (null unless posted), splits (where the amount was divided across accounts) and allocations (the invoices or bills the money settled). A confirmed but not yet posted OWNER_WITHDRAWAL on money out is the exception: Taokeh decides it loan-first (the owner's loan, up to what was owed on the line's own date and still owed on every later date in the books, then drawings) at the moment it is posted, whatever glAccount or splits it shows, so read those as not yet decided; once posted they are what was booked. ⚠ A SOLE PROPRIETOR OR PARTNERSHIP has no Shareholder's Loan: when the company's business type (Settings → Company, else the type it declared in storefront seller verification) is one of those, an OWNER_DEPOSIT books to Capital Contribution (3050) and an OWNER_WITHDRAWAL books whole to Owner's Drawings, with no loan-first split; a Sdn Bhd, or a company that has not set its type, keeps the loan rule above. statusCounts always reports how the rows in scope break down by status. A statement line the owner PAIRED with a book entry on Clear transactions is already in the books through that entry: it is never in the unreviewed queue or the 'confirmed' list; 'all' lists it with pairedWith (the entry's reference, its name and date), and its statusCounts count it as PAIRED — never propose a category for it or ask the owner to post it. A row still awaiting review may carry possibleTwin {entryId, ref, name, date, kind}. When you mention either entry to the owner, call it by name (its real reference, or for a reference Taokeh minted itself — BT-<8 hex> on a bank line posted with no bank reference — the bank line's own words; null when nothing is readable: say "a bank entry" when its ref/reference starts BT- or REV-BT-, else "a book entry"), never by a BT- code. kind 'bookEntry': a book entry on no statement that is the same money (same account, same amount, within three days) — the owner's review row warns that it looks already in the books. For such a row, propose a clearing with create_bank_clearing_draft (that entryId + this row as statementLineId) instead of a category. kind 'cardBill': a money-in line on a CREDIT CARD statement with a payment into the card of the same amount already in the books (usually the bank line that paid the card bill) — it is the card bill itself, so posting it would count the same money twice; a card has no clearing, so tell the owner to exclude the line on Banking → Review instead of proposing a category. kind 'cardLinePosted': a money-out bank line paying into a credit card (a credit card payment, or any line booked to the card's own ledger account) whose card-statement settlement line (ref = that line's description) is already POSTED — posting both counts the bill twice, so the owner should unpost that card line, or post this one anyway if it is a different payment. Each is only a resemblance (a same-amount refund can look alike), so say so. A line that moves the Shareholder's Loan can be refused at Post when it would leave a posted owner withdrawal over-repaid (the loan in debit on some date) — or, for a sole proprietor or partnership, a date a manual journal reclassified no longer nil; the refusal names them and the steps.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Latest transaction date (YYYY-MM-DD). | |
| from | No | Earliest transaction date (YYYY-MM-DD). WITHOUT a statementId, from/to widen the read to a DATE WINDOW across every imported statement — 'what did the bank register show in July' rather than 'what is on this one statement'. | |
| status | No | Which rows: 'unreviewed' (DEFAULT — everything not yet posted or ignored, i.e. the review queue), 'confirmed', 'posted', 'ignored', or 'all'. Only unreviewed rows carry suggestions; the rest carry what was decided. | |
| statementId | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond readOnlyHint and openWorldHint, it discloses the 200-row cap, the 1000-row window refusal, foreign-currency notPostable behavior, settled-row null suggestions, possibleDuplicateOf/possibleTwin semantics, pairedWith rows, and the fact that suggestions can come from owner rules. These are substantial behavioral details that materially affect interpretation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The opening is front-loaded and clear, but the body is an extremely long, dense wall of ALL-CAPS warnings and repeated rules. The loan/OWNER_WITHDRAWAL exception, foreign-currency notPostable behavior, and 'only a resemblance' disclaimers are each restated in multiple places, making the description hard to scan despite its useful content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, yet the description thoroughly explains the returned payload: statusCounts, suggestions, confidence buckets, yourProposal, possibleDuplicateOf, possibleTwin, pairedWith, settled-row fields, currency handling, and truncation notes. An agent has enough information to interpret and act on the results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents from, to, and status at 75% coverage, but the description adds important semantics: the default unreviewed status, the full status meanings, the statement default to latest imported, and the special window mode when from/to are passed without statementId. It also explains currency behavior across multi-currency windows.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
It states a specific resource and scope upfront: the imported bank/card register, rows in ANY status, defaulting to unreviewed. It explicitly distinguishes this from hand-recorded settlements and names search_documents as the alternative, so an agent can tell it apart from siblings without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: default is the unreviewed queue, `status` is for settled history, and `from`/`to` without `statementId` changes the scope to a company-wide date window. It also states when NOT to use it for hand-recorded payments and directs those to search_documents.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
business_snapshotBusiness snapshotARead-onlyInspect
A one-glance health check of your business: today's and this-month's sales and expenses, so far. Sales and expenses ONLY — if you want the fuller morning read (cash position, who owes you, pending approvals, tax position, low stock) call daily_brief instead; it includes everything here.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already carry the safety profile (readOnlyHint=true, openWorldHint=false), and the description adds meaningful behavioral context beyond them: the temporal framing ('so far', today's and this-month's), the hard exclusion scope ('Sales and expenses ONLY'), and the point-in-time nature of a snapshot. It stops short of a 5 only because it doesn't describe the return format or whether figures are aggregates.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with zero wasted words: the core purpose is front-loaded ('one-glance health check'), the scope is stated precisely with 'ONLY', and the alternative routing follows immediately. The em-dash separation and caps signal emphasis without bloat.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless, read-only snapshot tool with no output schema, the description fully covers what an agent needs: what data is returned (sales and expenses, today and this month), what is excluded, and what to call instead for broader coverage. Nothing required for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema is empty and the parameter count is 0, so the baseline of 4 applies — there are no parameters whose semantics need clarification. Schema description coverage is trivially 100% for an empty schema, and the description correctly communicates that this tool requires no arguments.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource: a 'one-glance health check' of 'today's and this-month's sales and expenses'. It sharply delineates scope with 'Sales and expenses ONLY' and explicitly differentiates itself from daily_brief, so an agent can distinguish it from the 58 siblings without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives an explicit when-to-use-this vs. when-to-use-that rule: call daily_brief instead when wanting the fuller morning read (cash position, receivables, approvals, tax, low stock), noting daily_brief 'includes everything here'. This is a direct alternative with a named sibling and a specific selection condition.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cash_forecastCash-flow forecastAInspect
A forward 8-week cash-flow forecast — honest arithmetic from your books, not a model. Opening position is your LIQUID balances right now (cash + bank accounts + gateway clearing + the Shopee Wallet, Held by Shopee); money the owner has advanced to the business is a liability, not cash, so it is excluded — the assumptions list says so. Inflows are your OPEN customer invoices scheduled at their due dates; already-overdue receivables are NOT assumed collected — they are reported separately as overdueArCents (upside, stated honestly). If a sales channel is connected and reports money earned but not yet paid out (TikTok Shop's unsettled orders), that pool comes back SEPARATELY as channelUnsettled (amount, count, currency, and the date it should land, 14 days out — the late end of the channel's own settlement window). It is the channel's own ESTIMATE, so it is deliberately NOT counted into any week, any closing balance or the dip: a warning about cash running low must never be weakened by money that is not in the books. Treat it as stated upside, exactly like overdueArCents. If no channel is connected, the channel add-on has lapsed, or its API cannot be reached, the field is absent and the forecast is unchanged. Outflows are your open supplier bills (at their due date, or bill date when no term is stated; each ringgit bill net of the debit notes raised against it, as the Bills page shows it; foreign-currency bills are not netted), payroll (estimated from your last finalized run: net pay + employer EPF/SOCSO/EIS on payday), PCB/EPF/SOCSO/EIS remittances (15th of the following month), loan installments (estimated from the last recorded installment), and SST payable at its return due date if you're SST-registered. Returns week-by-week inflows/outflows/closing balance, the dip (the lowest projected closing — when and how deep cash runs lowest), and an assumptions list spelling out every estimate. All amounts are integer CENTS (RM = cents ÷ 100).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Extremely rich disclosure beyond annotations: what is excluded from opening cash (owner advances as liability), that overdue AR is not assumed collected and appears as separate upside, that channel unsettled funds are deliberately excluded from every week and the dip, and that the channel field is silently absent if unconnected or unreachable. This is exactly the behavioral context an agent needs before presenting numbers.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded, but the body runs long with heavy all-caps emphasis and repeats the 'stated upside' framing twice and the exclusion-from-the-dip point at length. Much of it earns its place given no output schema exists, but it could be tightened considerably without losing meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description fully compensates by enumerating every returned field (weekly inflows/outflows/closing balance, dip, assumptions, overdueArCents, channelUnsettled with its sub-fields) and the cent-based unit convention. An agent could present and interpret the result without opening any schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing to disambiguate and the baseline of 4 applies. The description correctly spends no effort on parameter docs and instead documents the implicit scope (whole business, 8 weeks, all currencies in cents).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource with scope: 'A forward 8-week cash-flow forecast' with explicit opening, inflow and outflow definitions. This is clearly distinguishable from sibling reporting tools like cash_position (current balances) and working_capital, since it is the only forward-projection tool and it names the horizon.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description makes the use case unmistakable — you call this when you need a projected week-by-week cash position and a dip warning, not a point-in-time balance. It doesn't explicitly name a sibling as the alternative for the backward-looking case, so it stops short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cash_positionCash positionARead-onlyInspect
How much cash you have right now: every bank account and cash pocket with its current GL balance, plus a grand total. total is LIQUID money only — physical cash + bank accounts + the payment-gateway clearing float + the Shopee Wallet (money Shopee holds for orders it has released and the seller has not yet withdrawn; that account carries heldBy: 'Shopee' — say it is Held by Shopee, not in the bank) — and accounts lists exactly those accounts. Money the OWNER has advanced to the business (the Shareholder Loan) is returned SEPARATELY as ownerFunding: it is a LIABILITY the business owes back, not cash it holds, so it is never inside total and must never be added to it or described as cash. ⚠ A SOLE PROPRIETOR OR PARTNERSHIP owes its owner nothing (its owner money is Capital Contribution): there ownerFunding appears only for a balance still on the Shareholder Loan from before the business type was set, flagged fromBeforeBusinessType: true — relay its note, and never say the business owes the owner. CREDIT CARDS are returned separately for exactly the same reason, as cards (one entry per card, with cardsOwing for the total): what sits on a card is money OWED to the issuer, not cash the business holds, so it is never inside total — report it as card debt beside the cash figure, never as cash and never as a negative bank balance. Each card carries its own direction ('owed_to_issuer', or 'in_credit' when the card is overpaid) — follow it rather than assuming a debt. Balances are MYR-booked (the books are kept in ringgit); a foreign bank account shows its currency label for context, but the balance figure is still the MYR-booked GL balance.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint=true, openWorldHint=false), and the description goes well beyond them: it discloses that `total` is liquid-only, that the Shopee Wallet carries heldBy='Shopee', that ownerFunding is a liability excluded from total, that `cards` reports debt with a per-card `direction`, and that figures are MYR-booked with foreign currencies shown only as labels. This is exactly the kind of domain-specific disclosure annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first clause, but the body is a single dense paragraph with no visual structure (bullets or headings) and repeated emphasis ('never inside total' is stated twice, 'never as cash' appears repeatedly). The redundancy reflects real failure modes, so it is not pure filler, but the block is harder to scan than it needs to be.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description must carry the entire return contract, and it does: it enumerates every top-level field, explains why ownerFunding and cards sit outside total, resolves the sole-proprietor/partnership edge case via fromBeforeBusinessType, and specifies currency handling. Nothing an agent needs to report the figure correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing to document and the baseline of 4 applies. The description instead spends its budget documenting return-field semantics (total, accounts, ownerFunding, cards, cardsOwing, direction, fromBeforeBusinessType), which is the right tradeoff.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific, unambiguous statement of what is returned: every bank account and cash pocket with its GL balance plus a grand total. The inclusion/exclusion boundary (owner funding and credit cards are NOT cash) is stated explicitly, which effectively separates it from siblings like balance_sheet or cash_forecast, and 'right now' signals the point-in-time nature versus a forecast.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The use case is implied by 'how much cash you have right now', and there is substantial guidance on how to report the values, but the description never names an alternative (cash_forecast, balance_sheet, bank_reconciliation_report) or states when NOT to call this tool. The agent must infer tool selection from scope alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
channel_summarySales-channel summaryARead-onlyInspect
Your sales broken down by CHANNEL (Shopee, TikTok, Shopify, your storefront, …) for a date range. Per channel: the order count, gross goods revenue, seller discounts (e.g. Shopee vouchers), shipping income (buyer-paid shipping), freight-out (what shipping cost you), marketplace fees, and netProceeds — the settlement booked on each sale's own entry: for credit marketplaces (Shopee/TikTok) that is the net A/R payout the channel owes you; for the storefront it is what settled into cash / the gateway clearing account at sale time. RETURNS: returns is the revenue reversed by that channel's returns/refunds in the range, from two sources: the Sales Returns & Allowances booked on its credit notes (matched on the credit note's date), PLUS the goods revenue taken back by a REVERSAL posted from a marketplace Returns page (Shopee/TikTok/Shopify — a reversing journal entry referenced -CN-, with no credit-note document). ⚠ A reversal is matched on ITS OWN date — the day it was posted, the same basis as the TikTok settlement entries below — not the date of the sale it reverses, so a June order reversed in July shows its revenue in June and its reversal in returns in July; once both dates are in range a fully reversed order nets to 0. Only the reversed GOODS revenue is folded into returns: the reversal also takes back the order's shipping income, discounts and fees, but shippingIncome, salesDiscounts, freightOut, marketplaceFees and netProceeds stay GROSS (invoice side only), so do not read them as net of reversals. netGoodsRevenue = grossGoodsRevenue − returns. The other six figures (grossGoodsRevenue, discounts, shipping, freight, marketplaceFees, netProceeds) are GROSS of returns — they come from the INVOICE side only — so a channel with heavy returns no longer looks inflated. A row appears for every channel with sales OR returns in the range — returns lag sales, so a channel whose only in-range activity is returns still shows (saleCount 0, gross figures 0, its returns, and a negative netGoodsRevenue). (A Shopee-wallet FEE credit note books marketplace fees, not a goods return, so it adds 0 to returns. Since 31 Oct 2026 a NEW Shopee wallet deduction is booked out of the Shopee Wallet account (Dr fees or advertising · Cr Shopee Wallet), not as a credit note, so it is not in this tool's output at all — it is in income_statement. The same holds for Shopee's fees on an invoice brought over from the old books, booked to Marketplace Fees when Settle on Shopee → Wallet pays that invoice (since 1 Nov 2026): they sit on the wallet settlement, not on the sale's own entry, so this tool's marketplaceFees and netProceeds do not include them; income_statement does.) A marketplace SETTLEMENT CORRECTION — the payout came in short because the buyer returned or cancelled after the invoice was already booked — is also a credit note approved by the owner against the original invoice, and the invoice side stays as it was booked. Only the GOODS portion of such a correction shows in returns: its fee and shipping components are reclassed to marketplace fees / freight on the credit note's OWN entry, while the per-channel marketplaceFees and freight figures here are read from the INVOICE side only — so a fee-only correction (the common, batched kind) moves neither returns nor marketplaceFees and is not visible in this tool's output at all. Marketplace invoices are therefore not final at import: a channel's figures for a past range can still move as corrections are approved. NOTE: marketplaceFees covers marketplace commission/service fees booked on the sale's own entry; storefront GATEWAY processing fees are booked on separate payout entries and are NOT included per-channel here (so storefront netProceeds is gross of gateway fees). TIKTOK is the one exception, and it is INCLUDED: TikTok reports no per-order fee at import, so its fees (and the freight split out of its settlement) are booked LATER on a separate settlement entry by "Book settlement fees" (journal reference TIKTOK-FEE-). Those are folded into TikTok's marketplaceFees and freightOut here, and the labelled revenue correction on them adjusts its grossGoodsRevenue. ⚠ THE DATE BASIS DIFFERS on that side: settlement entries are matched on WHEN THE SETTLEMENT WAS BOOKED (the journal entry's own date), while sales are matched on sale date — because that is when the cost hits the P&L. So a June TikTok sale whose payout is settled in July puts its goods revenue in June and its fees in July, and a July-only summary can show a tiktok row with saleCount 0 that is purely settled fees. Say which month you are reading, and never describe a channel's fees as final until its payouts are settled. Manual / non-channel sales are excluded. Give from and to as YYYY-MM-DD.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | ||
| from | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes far beyond the readOnlyHint/openWorldHint annotations by disclosing return-matching date bases, reversal behavior, gross-vs-net treatment, return-only rows, marketplace correction timing, and TikTok settlement fee timing. These behavioral caveats are exactly what an agent needs to avoid misreading the output.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and key fields are front-loaded, but the description is an extremely dense, run-on paragraph with repeated explanations of gross-vs-returns behavior and multiple nested caveats. The content is important, but its length and repetition make it harder to parse than necessary.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the full burden of explaining returned figures and does so thoroughly: it defines per-channel fields, returns sources, reversal matching, gross-vs-net behavior, edge cases like return-only channels, and timing differences for TikTok settlements. An agent has enough context to interpret the output correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has zero description coverage for the two required date parameters, and the description only adds 'Give from and to as YYYY-MM-DD.' It does not clarify inclusivity of the range, timezone, or whether 'from' and 'to' are both inclusive, so it only partially compensates for the missing schema documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the resource and scope: sales broken down by channel for a date range, with a specific list of per-channel figures. It distinguishes itself from income_statement for certain Shopee wallet fees, but does not explicitly differentiate from the sibling sales_summary tool, leaving some sibling-selection ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the purpose and by exclusions such as 'Manual / non-channel sales are excluded,' and it warns to state which month is being read and not to treat fees as final before settlement. However, it never explicitly says when to choose this tool over sales_summary or income_statement beyond noting that certain items are not in this output.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
counter_dayCounter dayARead-onlyInspect
The Counter (Kaunter) till's day, for one date (default today, Malaysia time). Returns: the receipt count and voided count, the cash and DuitNow QR totals, the 5-sen cash-rounding total, whether the day is still OPEN or has been CLOSED into the books, the receipt-number range with any voided numbers listed, and separately the sales BILLED ON CREDIT at the till. IMPORTANT — a counter day is NOT in the ledger until it is closed: while status is 'open' these figures are the till's own receipts and NOTHING has posted, so they will not appear in sales_summary, cash_position or income_statement yet, and stock/COGS have not moved either (they move once, at close). Once status is 'closed', saleId names the ONE consolidated cash sale the whole day posted as, and the figures do reconcile to the books. THE ONE EXCEPTION, and it runs the other way: creditSales are the till's "Invoice it" sales, and they POST IMMEDIATELY as ordinary credit invoices (Dr accounts receivable to a named customer, stock moved at the moment of sale). So unlike the drawer takings they ARE already in sales_summary, income_statement, open_invoices and ar_aging even while the day is open — and they are NOT in cashTotal, qrTotal or the close, because no money changed hands. Never add creditSales.total to the cash and QR totals: the drawer holds cash + QR only, and it ties to the sen. cashTotal is the sum of ROUNDED cash receipt totals (what the drawer took in); cashRefunds is what credit notes refunded from Cash paid back out of the drawer that day and cashReceipts is what invoices paid in Cash put into it (each its own entry, not the close), so the drawer holds cashReceipts.drawerExpected (= cashTotal − cashRefunds.total + cashReceipts.total; cashRefunds.drawerAfterRefunds leaves the receipts out); qrTotal is never rounded — Malaysia's 5-sen rounding applies to cash only. Receipts of RM10,000 or more cannot join a consolidated e-invoice, so each posts as its own sale: consolidatedCount/consolidatedTotal cover only the receipts that joined the day sale, individualCount/individualTotal the rest, and awaitingBuyerCount is how many of those still have no buyer particulars captured. Counter is a paid add-on (RM49/mo); a company without it gets status 'not_available'.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | The business date, YYYY-MM-DD in Malaysia time. Omit for today. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint and openWorldHint annotations, the description discloses critical behaviors: open vs closed status affects whether anything has posted, credit sales post immediately as ordinary credit invoices, cash and QR totals exclude credit sales, QR is never rounded, and the tool returns not_available without the paid add-on. These are exactly the behavioral details an agent needs and there is no contradiction with the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and return fields, and the extensive accounting details are justified because there is no output schema. However, it is a dense single paragraph with formula-heavy asides, which makes it harder to scan than a more structured presentation would be.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the lack of an output schema, the description is thorough: it explains all major return fields, the open/closed accounting implications, credit-sales treatment, drawer reconciliation math, e-invoice thresholds, and the paid add-on case. An agent has enough context to interpret the result correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% for the single optional `date` parameter, already documenting the YYYY-MM-DD format, Malaysia time, and default of today. The description repeats the default and Malaysia-time framing but adds no meaningful syntax or semantics beyond the schema, so the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific resource, the Counter (Kaunter) till's day for one date, and enumerates what it returns: receipt counts, cash/QR totals, rounding, status, receipt range, and credit sales. It also distinguishes this from sales_summary, cash_position, and income_statement by explaining ledger timing, so an agent can identify it without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context for when the figures are meaningful, especially the open-vs-closed distinction and the warning that open-day till figures are not in sales_summary, cash_position, or income_statement. It names relevant alternatives indirectly, but does not give a crisp "use this instead of X when Y" routing statement, so it falls short of full explicit alternatives guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_bank_clearing_draftPending draft: tick bank entries as cleared (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose the ticks for Banking → Clear transactions: which book entries have appeared on the bank statement, each paired with its statement line or given the date it cleared. An admin or bookkeeper reviews the list and approves; only that tap ticks them, exactly as the page's Save ticks does (a tick posts nothing to the books and the owner can un-tick it). Read bank_reconciliation_report first and take every id from it: bankAccountId, statementId (the statement you are reconciling to — or asOf, a date, when there is none), and per item entryId (from clearStep.uncleared) plus EITHER statementLineId (from clearStep.candidateLines — the entry then clears on the later of the line's date and its own) OR clearedDate (YYYY-MM-DD; omitted, it defaults to the reconciliation date, like the page). At most 300 items per draft. A cancelling pair from hints.cancellingPairs is filed as two items with no line and the pair's clearDate. REFUSED BY NAME when you file (Taokeh runs the real tick and rolls it back) and again at approval: an entry that is not uncleared on this account up to the date (posted from a statement line, the opening balance, already ticked, dated later), a line that is posted, fully paired or dated after the reconciliation date, a line without room left for the entry, a line moving money the other way, a cleared date before the entry was booked or after the reconciliation date, a date inside a locked period, a card, a foreign-currency account (Taokeh posts and reconciles only ringgit accounts for now; the refusal names the account), an entry already in another pending draft. If anything it was filed against changes before approval (a tick made, a line posted or paired, an entry edited), approval refuses and nothing is ticked — read the report again and file afresh. note: one short line for the owner saying why.
| Name | Required | Description | Default |
|---|---|---|---|
| asOf | No | Reconcile to a date by hand (no statement). Give this OR statementId. | |
| note | No | A SHORT note for the owner: why these are cleared. | |
| items | Yes | The ticks. An unknown key in an item is refused by name. | |
| notes | No | NOT a filter on this tool. Pass the owner note as `note`. | |
| ticks | No | NOT a filter on this tool. Pass the ticks as `items`: [{entryId, statementLineId?, clearedDate?}]. | |
| bankId | No | NOT a filter on this tool. Pass the bank account as `bankAccountId`. | |
| entries | No | NOT a filter on this tool. Pass the ticks as `items`: [{entryId, statementLineId?, clearedDate?}]. | |
| needsReview | No | Set true when something gave you pause. | |
| statementId | No | The statement you reconcile to, from bank_reconciliation_report `statement.id`. Give this OR asOf. | |
| bankAccountId | Yes | REQUIRED — the bank account, from bank_reconciliation_report. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Far exceeds the annotations: it explains that a tick posts nothing to the books, that the owner can un-tick it, that a 300-item cap applies, and enumerates the exact refusal cases (already ticked, locked period, foreign-currency account, entry already in another draft). It also discloses the invalidation rule — if anything filed against changes before approval, approval refuses and the agent must re-read the report and re-file.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical constraint is front-loaded in caps, and each subsequent sentence carries operational information (id sources, per-item pairing rules, refusals, re-file flow). It is dense — long clause-heavy sentences in a single block — but very little is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter mutating-draft tool with no output schema, the description covers what is filed, what blocks it, and what happens on approval. The main gap is that it never says what the tool returns (draft id? handle for revise_draft?) or how the agent later references the filed draft.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real meaning: it explains that statementLineId causes the entry to clear on the later of the line's date and its own, that clearedDate defaults to the reconciliation date, and that a cancelling pair is filed as two items with no line. The 'NOT a filter' decoy params (notes, ticks, bankId, entries) are handled by the schema descriptions rather than the prose.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — file a pending draft proposing 'ticks' for Banking → Clear transactions — and immediately scopes what it is not ('NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES'). An agent can distinguish it from create_payment_match_draft / draft_bank_classification because the description names the exact workflow step (pairing book entries to statement lines) and the prerequisite report.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit prerequisite: 'Read bank_reconciliation_report first and take every id from it', and clarifies the human-approval gate before anything applies. It does not name a sibling tool to use instead for adjacent tasks (e.g. classification vs. clearing), so the when-not guidance is implicit rather than complete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_bill_draftFile bill draftAInspect
File a supplier-bill DRAFT into Taokeh from a bill you've read. INVENTORY-ONLY — every line must be a product you stock; for services / non-stock / mixed bills use the manual bill form, or create_expense_draft if already paid. This does NOT post to the books — it creates a pending draft the user reviews and approves in Taokeh; only then does it post (receive stock, blend moving-average cost, book input SST as a non-recoverable cost). Shape the fields with intake_contract(doc_type:'bill') + resolve_vendor + resolve_product first. The server re-computes every line quantity from the tally and the grand total — so present your working, but the server's figures are authoritative. READ THE PAYMENT TERM OFF THE BILL and send it as term — a supplier's credit period is what decides when this money actually leaves, so it drives the payables aging buckets, the OVERDUE badge and the 8-week cash forecast; for one of the five preset terms Taokeh fills the due date on the draft itself and shows it on the approval screen. Set needsReview and add a SHORT reviewer note in notes (one or two sentences naming what to double-check — not lengthy reasoning) for any doubt. If you have the ORIGINAL bill image/PDF, attach it — it rides the draft and lands on the posted bill automatically on approval, so the user never has to re-upload it. Small files: attachmentBase64 + attachmentMediaType inline. Send attachmentBytes (the original file’s decoded size) with it so a truncated base64 is rejected instead of filed. Anything bigger: request_attachment_upload → PUT the bytes → pass the returned attachmentToken — unless your shell cannot reach taokeh.my (a sandboxed client behind a network allowlist), in which case inline it anyway, with attachmentBytes; never both. ⛔ LINE KEYS ARE STRICT (2026-09-09): a key this schema does not list is REFUSED BY NAME — with the key it probably meant — and NOTHING is filed. Unknown keys used to be dropped in silence, which let a line through with its price or its tax code missing.
| Name | Required | Description | Default |
|---|---|---|---|
| term | No | The SUPPLIER's payment term, exactly as the bill prints it ('Due on Receipt', 'Net 7', 'Net 14', 'Net 30', 'Net 60', or whatever this supplier actually stated — 'COD 7 days', 'Net 45', '30 days EOM'). It is stored as written, so do NOT round a real term to the nearest familiar one. Leave it out when the bill states no term: an unstated term is not 'Due on Receipt', and the bill then simply falls due on its own date. | |
| lines | Yes | ||
| notes | No | A SHORT reviewer note: one or two plain sentences, written in the reviewer's language, flagging what the human should double-check before approving — a smudged or ambiguous total, a vendor/customer you weren't sure of, a judgment call you made. The reviewer reads this on a small approval card, so keep it brief and human. This is NOT a place to dump lengthy reasoning, your working, or boilerplate — just the one thing to check. Leave it empty when there is nothing to flag. | |
| vendor | No | ||
| dueDate | No | The due date, YYYY-MM-DD, when the bill states one outright. You usually do not need it: for one of the five preset terms above, Taokeh fills the due date on the draft itself (bill date + 0/7/14/30/60 days) and shows it on the approval screen for the owner to check. State dueDate only to override that, or when the paper gives a due date that does not follow the term. A term Taokeh does not recognise fills NOTHING — if that bill has a due date, say it here, because otherwise the bill falls due on its own date and shows up in the payables aging and the cash forecast a whole credit period early. | |
| vendorId | No | ||
| reference | No | ||
| needsReview | No | ||
| printedTotal | No | ||
| purchaseDate | Yes | ||
| paymentMethod | Yes | ||
| shoeboxItemId | No | The id of a SHOEBOX photo (from shoebox_items) that this draft was read from — the paper someone in the business snapped and sent in. Taokeh attaches ITS OWN stored copy of that photo to the draft, so DO NOT also send the image: no re-upload, no base64, nothing to truncate. Mutually exclusive with attachmentBase64 and attachmentToken. The item must still be waiting: one already booked, set aside, or already carrying a pending draft is refused with the reason. Approving the draft closes that shoebox item against the document it became. | |
| attachmentBytes | No | The decoded byte size of the ORIGINAL file on disk — send it alongside attachmentBase64 and the server rejects a truncated paste instead of filing a corrupt file. | |
| attachmentToken | No | The token from request_attachment_upload, AFTER you have PUT the file bytes to its uploadUrl. Use this instead of attachmentBase64 for any real photo/PDF — it carries the file out-of-band (no base64 in this call). Mutually exclusive with attachmentBase64. The uploaded file rides the draft and lands on the posted bill on approval, exactly as an inline one does. | |
| attachmentBase64 | No | The ORIGINAL bill as base64 — SMALL files only (a few KB). For a real photo or multi-page PDF use request_attachment_upload instead (attachmentToken). Rides the draft and lands on the posted bill on approval — no re-upload. A bad type/oversize file is rejected and NOTHING is filed. | |
| attachmentSha256 | No | The SHA-256 of the ORIGINAL file as 64 hex chars — optional second check alongside attachmentBase64, so corrupted bytes are rejected instead of filed. | |
| confirmNewVendor | No | Set to true ONLY after the user has EXPLICITLY confirmed this vendor is genuinely new. It skips the near-miss guard (which rejects a name that looks like shorthand for an existing vendor). Never set it to push a near-miss through — ask the user first. | |
| attachmentFilename | No | Optional original filename for the attachment, e.g. bill.pdf. | |
| attachmentMediaType | No | The attachment's MIME type, e.g. 'image/jpeg' or 'application/pdf'. Required when attachmentBase64 is given. It must match the file's bytes — a mismatch is refused and nothing is filed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the non-destructive, non-idempotent mutating profile, and the description adds substantial context beyond them: the draft does NOT post, it only posts after user approval (receive stock, moving-average cost blend, input SST as non-recoverable), the server recomputes quantities and totals authoritatively, and unknown line keys are refused by name rather than silently dropped. Attachment lifecycle (rides the draft, lands on the posted bill, mutually exclusive paths) is also disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The workflow is front-loaded well, but the description is very long for a tool whose schema already carries much of the same detail — the payment-term discussion and attachment guidance largely restate schema descriptions, and the all-caps/emoji/date-stamped warning block ('⛔ LINE KEYS ARE STRICT (2026-09-09)') adds bulk. Most sentences carry real information, but the formatting and redundancy cost it.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 19-parameter, no-output-schema, non-idempotent create tool, the description covers the full operational picture: prerequisites, draft-vs-post semantics, authoritative recomputation, reviewer note expectations, attachment size limits and the sandbox/allowlist fallback, and exclusion routing. Nothing an agent needs in order to call it safely is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 58%, so the schema already documents the highest-risk fields (term, notes, quantity, unitCost, tally, attachment*). The description reinforces the reasoning behind `term` and `notes` and warns about strict line keys, but several parameters with no schema description (vendor, vendorId, reference, printedTotal, paymentMethod, taxCode, working, discount) are not explained anywhere, so the coverage gap is only partly compensated.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb + resource + target system ('File a supplier-bill DRAFT into Taokeh from a bill you've read') and immediately scopes it ('INVENTORY-ONLY'). It names the siblings it is not for (manual bill form, create_expense_draft) and distinguishes itself from update_bill_draft by making clear it only creates a pending draft.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use (inventory bills you've read), when-not-to-use (services, non-stock, mixed, already-paid), and named alternatives for each exclusion. It also lists prerequisites in order (intake_contract(doc_type:'bill') → resolve_vendor → resolve_product) and when to set needsReview, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_contact_draftFile contact draft from a name cardAInspect
File a pending CONTACT draft from a business card YOU have read. Read the card image yourself and pass the fields — personName (required), company, role, phones, emails, address — AND the card image itself, which is REQUIRED: the owner checks your fields against the picture before approving, so a draft without the image cannot be verified and is refused. This does NOT add anyone to the address book: it creates a draft the owner reviews and approves in Taokeh, and only that tap files the contact person (under the company, matched to an existing customer/vendor or created as a new one) with the card image kept on the record. BE HONEST about what you could not read: leave a field EMPTY and say so in notes — never guess a phone digit, an email spelling or a company name. If the photo is blurry, is not a business card, or lists two people, say that in notes and set needsReview. Attach the image with attachmentBase64 + attachmentMediaType (a card photo is usually small enough to inline; send attachmentBytes with it so a truncated base64 is rejected instead of filed), or for a large photo or a PDF use request_attachment_upload → PUT the bytes → pass the returned attachmentToken (never both). Say which side of the book it is with partyKind ('customer' for someone you sell to, 'vendor' for someone you buy from) — default customer — and pass partyName if the user tells you the company is really an existing one under a different spelling. Report the fields back to the user in chat so they can spot a misread before they tap.
| Name | Required | Description | Default |
|---|---|---|---|
| role | No | The person's job title / role as printed, e.g. 'Sales Manager'. | |
| notes | No | A SHORT reviewer note: one or two plain sentences, written in the reviewer's language, flagging what the human should double-check against the image before approving — a phone number you could not read cleanly, two people on one card, uncertainty about which line is the company. The reviewer reads this on a small approval card, so keep it brief and human. Leave it empty when there is nothing to flag. | |
| emails | No | The email addresses printed on the card (up to 3). Never guess a spelling; leave it out and flag it in notes instead. | |
| phones | No | The phone numbers printed on the card, as printed (up to 3). Leave out any digit-group you cannot read cleanly and say so in notes — a half-guessed phone number is worse than none. | |
| address | No | The address printed on the card, as one string. | |
| company | No | The company name printed on the card. Leave it out if the card does not show one. | |
| partyKind | No | Which side of the book the card belongs to: 'customer' (someone you sell to) or 'vendor' (someone you buy from). Defaults to customer. | |
| partyName | No | The company to file the contact under, when the user tells you it differs from what is printed on the card (e.g. the card shows a brand but the books use the registered name). Omit to use the company printed on the card — the reviewer can still change it before approving. | |
| personName | Yes | REQUIRED — the person's name exactly as printed on the card. If the name is genuinely illegible, do NOT guess and do NOT file: tell the user the card is unreadable. | |
| needsReview | No | Set true when something about the card gave you pause — a blurry photo, two people on one card, a field you could not read. It flags the draft for the reviewer. | |
| attachmentBytes | No | The decoded byte size of the ORIGINAL file on disk — send it alongside attachmentBase64 and the server rejects a truncated paste instead of filing a corrupt file. | |
| attachmentToken | No | The token from request_attachment_upload, AFTER you have PUT the image bytes to its uploadUrl. Use this instead of attachmentBase64 for a large photo or a PDF — it carries the file out-of-band (no base64 in this call). Mutually exclusive with attachmentBase64. The card lands on the created contact on approval, exactly as an inline one does. | |
| attachmentBase64 | No | The business-card IMAGE as base64 — REQUIRED (unless you pass attachmentToken). A card photo is normally small enough to inline. A bad type/oversize file is rejected and NOTHING is filed. | |
| attachmentSha256 | No | The SHA-256 of the ORIGINAL file as 64 hex chars — optional second check alongside attachmentBase64, so corrupted bytes are rejected instead of filed. | |
| attachmentFilename | No | Optional original filename for the card image, e.g. card.jpg. | |
| attachmentMediaType | No | The card image's MIME type, e.g. 'image/jpeg' or 'image/png'. Required when attachmentBase64 is given. It must match the file's bytes — a mismatch is refused and nothing is filed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the safety profile (readOnly=false, destructive=false). The description adds the review/approval flow, that the draft is refused without the image, how truncation and MIME mismatches are rejected, mutual exclusivity of the two attachment paths, and the owner-verification step — substantial context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the two hard requirements (image required, no address-book write) and is well ordered, but it is a very dense block that repeats some schema guidance (never guess a phone/email, leave empty and flag) rather than purely adding new information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 16-parameter, no-output-schema write tool, the description supplies everything an agent needs: the required input, both attachment routes, the refusal conditions, the honesty/notes policy, and the downstream review consequence. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already documents most fields, so the baseline is 3; the description earns extra credit by explaining cross-parameter flow the schema cannot (attachmentBytes guards against truncated base64, attachmentToken must be used after the PUT, never both, partyName overrides printed company).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('File a pending CONTACT draft from a business card') and explicitly distinguishes itself from the address-book mutation it is not ('This does NOT add anyone to the address book'), which separates it from update_contact_draft and the other create_*_draft siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use conditions (agent has read the card, image mandatory), the two mutually exclusive attach paths (inline base64 vs request_attachment_upload → PUT → attachmentToken), defaulting rules for partyKind, and when to leave fields empty. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_credit_note_draftFile credit-note draftAInspect
File a CREDIT NOTE draft into Taokeh — a sales return, short delivery, price correction or allowance that reduces what a customer owes. This does NOT post to the books: it creates a pending draft the user reviews and approves in Taokeh, and only then does it reduce the customer's balance, reverse the revenue and SST, and (if the user says the goods came back) put stock back — and then only for the lines the ORIGINAL invoice actually shipped, and never more than it shipped: a line that moved no goods is money only, and claiming otherwise is refused at approval rather than posted. Shape the fields with intake_contract(doc_type:'credit_note') first. RESOLVE THE ORIGINAL INVOICE FIRST: a credit note is issued AGAINST an invoice — find it with search_documents and pass its id as originalDocId with kind:'against_invoice'. Only a credit with NO source invoice is kind:'allowance', and an allowance is never linked to an invoice, never capped by one, inherits no SST codes and never restocks — so do not file a real return as one; ask the user which invoice it is against. Send all amounts POSITIVE (Taokeh stores the credit note negative itself). A DISCOUNTED invoice line: send its own unit price (before discount) and Taokeh shares that line's discount out by quantity itself — you cannot send a discount, and the result lists each shared one under sharedDiscounts. A line can only credit back as MANY units as that invoice actually sold, net of earlier credit notes — an over-quantity line is refused naming what is left, so read the invoice's own unit of measure rather than converting it (1 carton is not 100 pieces). Whether goods physically came back into stock is the USER's decision at approval — goodsReturned is only a hint that pre-ticks their checkbox on the full review page, and a one-tap approval always posts money-only — and only the figure the user saw: if the draft now works out to a different total (for example an SST change since you filed it), the one tap is refused and the user posts it from the full review page. Set needsReview and add a SHORT reviewer note in notes (one or two sentences naming what to double-check) for any doubt. Filed it wrong? Use revise_draft (kind: 'credit_note') rather than filing a second one. IS A CREDIT NOTE EVEN THE RIGHT DOCUMENT? Only when something CHANGED AFTER the sale — goods came back, a price was renegotiated, a discount was agreed later. If the sale simply never happened (keyed twice, wrong company, cancelled before delivery), the owner VOIDS the invoice instead: Sales → the invoice → Void, in Taokeh. There is deliberately no AI lane for voiding. And if the sale did happen but the invoice was typed wrong — wrong line, wrong quantity or price, wrong date — that is a correction, not a credit: use update_invoice_draft. An invoice already REVERSED from a Shopee, TikTok or Shopify Returns page is refused when you file (its reversing journal entry already credits it in full, so a credit note would credit it twice). And if a credit note ALREADY POSTED was itself typed wrong (a wrong line, quantity, price or date), do not file a second one on top — correct it with update_credit_note_draft. ⛔ LINE KEYS ARE STRICT (2026-09-09): a key this schema does not list is REFUSED BY NAME — with the key it probably meant — and NOTHING is filed. Unknown keys used to be dropped in silence, which let a line through with its price or its tax code missing.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | REQUIRED. 'against_invoice' = a return / short delivery / price correction on a SPECIFIC invoice (the usual case — give originalDocId or originalDocNumber). 'allowance' = a standalone money credit with NO source invoice (give the customer instead). They are different documents; when it isn't clear, ask the user which invoice it is against. | |
| lines | Yes | ||
| notes | No | A SHORT reviewer note: one or two plain sentences, in the reviewer's language, naming what the human should double-check before approving — whether the goods actually came back, an ambiguous quantity, which invoice you matched it to. Not lengthy reasoning. | |
| cnDate | Yes | The credit-note date, YYYY-MM-DD in Malaysia time (MYT). For an against_invoice note it is never before that invoice's date: an earlier date is refused, naming the invoice's date. | |
| reason | No | Why the credit is given — 'damaged goods returned', 'short delivery', 'agreed price adjustment'. It prints on the credit note. | |
| customer | No | allowance ONLY: the customer name, resolved with resolve_customer first. A credit note reduces an EXISTING customer's balance and can never create a customer. For an against_invoice credit note the customer comes from the invoice — leave this out. | |
| reference | No | The credit-note number already printed on paper, if any. Leave it out and Taokeh numbers it (CN…). | |
| customerId | No | allowance: the resolved customer id instead of the name. | |
| needsReview | No | ||
| printedTotal | No | The total printed on the paper, if any. Cross-check only — the server computes the real total and flags a mismatch on the approval screen. | |
| goodsReturned | No | An ADVISORY hint only: it pre-ticks the reviewer's 'the goods came back into stock' checkbox. It does NOT decide the stock movement — the human does, at approval, and even then only the lines the ORIGINAL invoice actually shipped can come back, up to the quantity it shipped; claiming more is refused rather than posted. Ignored for an allowance (always money-only). | |
| originalDocId | No | against_invoice: the ORIGINAL invoice's id, from search_documents. The server verifies it exists, is an INVOICE and whose it is — it never guesses. | |
| originalDocNumber | No | against_invoice: the printed invoice number, when you have no id. Refused if more than one document carries it — find the right one with search_documents. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say it is not read-only, not idempotent and not destructive; the description goes far beyond by explaining that nothing posts to the books, that the draft is human-approved, that restocking depends on the reviewer's checkbox, that an over-quantity line is refused with the remainder named, and that an already-reversed sales-channel invoice is refused. The refusal conditions and side effects are exactly the behavioural context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded and the ordering (purpose → prerequisites → pricing rules → review semantics → alternatives) is logical, so it is navigable. But it is a ~600-word wall with several multi-clause sentences, and meaningful chunks restate the input-schema descriptions nearly verbatim (positive amounts, goodsReturned as a hint, the strict line keys already implied by additionalProperties: false), so not every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 13-parameter mutation tool with no output schema, the description covers everything an agent needs: prerequisites, kind selection, amount sign convention, discount handling, stock-movement authority, approval/refusal behaviour, and the correct tool for every adjacent situation. No material gap remains for a correct first invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 85%, so the baseline is 3; the description earns above that by adding cross-parameter rules the schema cannot state — resolve the invoice before filing, keep the invoice's own unit of measure instead of converting cartons to pieces, and pass pre-discount prices because Taokeh apportions the discount itself. It does duplicate some schema text (POSITIVE amounts, goodsReturned being advisory), which keeps it from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence gives a specific verb and resource ("File a CREDIT NOTE draft") and immediately defines the business meaning (sales return, short delivery, price correction, allowance). It also explicitly separates itself from the neighbouring documents an agent might confuse it with — debit notes, invoice voids, invoice corrections and a second credit note on top of a posted one.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Routing guidance is unusually explicit: use update_invoice_draft for a mis-typed invoice, void the invoice in Taokeh when the sale never happened, use revise_draft for a misfiled draft, and update_credit_note_draft for a posted note that was wrong. It also names the prerequisite chain (intake_contract, search_documents, then originalDocId) and the against_invoice vs allowance decision rule.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_debit_note_draftFile debit-note draftAInspect
File a SELL-SIDE DEBIT NOTE draft into Taokeh — an ADDITIONAL CHARGE on an invoice this business already issued (an undercharge, a price revised upward after delivery, a surcharge that was missed). This does NOT post to the books: it creates a pending draft the user reviews and approves in Taokeh, and only then does it increase what the customer owes and the output SST. Shape the fields with intake_contract(doc_type:'sales_debit_note') first. THE ORIGINAL INVOICE IS REQUIRED: find it with search_documents and pass its id as originalDocId. There is no allowance or standalone shape here — unlike a credit note, a debit note with no invoice behind it is not a document; if there is nothing to correct upward, the right document is a NEW INVOICE (create_invoice_draft), so say that rather than filing this. CHARGE THE SHORTFALL, NOT THE NEW PRICE: unitAmount is the amount being ADDED per unit, and quantity defaults to 1 because an additional charge is usually one line of money. Every line must name a product that actually appears on that invoice; anything else is refused. MONEY ONLY, ALWAYS — a debit note moves no stock, touches no COGS, and has no restock option anywhere in Taokeh (there is no goodsReturned field on this tool and no checkbox on its review screen), so a one-tap approval is money-only by construction and not by fence — and posts only the figure the user saw: if the draft now works out to a different total (for example an SST change since you filed it), the one tap is refused and the user posts it from the full review page. If EXTRA GOODS were delivered, raise a new invoice instead. There is NO cap on the charge, so the approver is shown the original invoice's own total beside yours — keep the figure defensible. An invoice already REVERSED from a Shopee, TikTok or Shopify Returns page is refused when you file and again at approval (its reversing journal entry has cancelled the order in the books, so a debit note would charge an order that no longer exists). Set needsReview and add a SHORT reviewer note in notes (one or two sentences naming what to double-check) for any doubt. Filed it wrong? Use revise_draft (kind: 'sales_debit_note') rather than filing a second one. This is the SELL side; Taokeh's /debit-notes page is the separate BUY side (a purchase return against a supplier bill), which has no AI lane for CREATING one — a buy-side note already posted can only be corrected, with update_supplier_debit_note_draft. A sell-side debit note already posted has no correction lane at all: the owner voids it in Taokeh and a new one is filed. ⛔ LINE KEYS ARE STRICT (2026-09-09): a key this schema does not list is REFUSED BY NAME — with the key it probably meant — and NOTHING is filed. Unknown keys used to be dropped in silence, which let a line through with its price or its tax code missing.
| Name | Required | Description | Default |
|---|---|---|---|
| lines | Yes | ||
| notes | No | A SHORT reviewer note: one or two plain sentences, in the reviewer's language, naming what the human should double-check before approving — which invoice you matched it to, where the figure came from. Not lengthy reasoning. | |
| dnDate | Yes | The debit-note date, YYYY-MM-DD in Malaysia time (MYT). Never before the date of the invoice it corrects: an earlier date is refused, naming the invoice's date. | |
| reason | No | Why more is charged — 'price revised upward after delivery', 'surcharge missed'. It prints on the note. | |
| customer | No | OPTIONAL CROSS-CHECK: the customer you believe the invoice belongs to. The customer comes FROM the invoice; sending a name that does not match is refused, which is exactly what it is for. | |
| reference | No | The debit-note number already printed on paper, if any. Leave it out and Taokeh numbers it (DN…). | |
| customerId | No | The resolved customer id, as the same cross-check. | |
| needsReview | No | ||
| printedTotal | No | The total printed on the paper, if any. Cross-check only — the server computes the real total and flags a mismatch on the approval screen. | |
| originalDocId | No | REQUIRED (this or originalDocNumber): the ORIGINAL invoice's id, from search_documents. The server verifies it exists, is an INVOICE and whose it is — it never guesses, and it will not file a debit note without one. | |
| originalDocNumber | No | The printed invoice number, when you have no id. Refused if more than one document carries it — find the right one with search_documents. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the generic mutation profile (readOnly=false, destructive=false, idempotent=false); the description adds substantial non-obvious behaviour: it creates a pending draft and does not post to the books, approval increases customer balance and output SST, one-tap approval is refused if the total shifted since filing, reversed Shopee/TikTok/Shopify orders are rejected at file time and at approval, no cap on the charge so the approver sees the original invoice total, no stock/COGS/restock semantics, and no correction lane exists for an already-posted sell-side note. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded well: the purpose, the no-posting behaviour, the prerequisite, and the invoice requirement all land in the first few sentences. But it is a very long block for 11 parameters, with all-caps digressions (the buy-side /debit-notes page, the posted-note correction lane, the line-keys changelog) that are useful for disambiguation yet could be compressed. Redundancy exists between the description's money-only paragraph and the schema's own unitAmount/description warnings.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, and the description compensates by explaining what filing produces (a pending draft, a review screen, a one-tap approval that is refused if the total changed). For a high-risk financial mutation with 11 parameters, it covers failure modes, prerequisites, downstream effects and recovery paths thoroughly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 82% and the schema itself already documents unitAmount, quantity, productRef, taxCode and originalDocId well, so much of the description's field guidance is reinforcement. It does add real value beyond the schema on cross-parameter semantics ('the ORIGINAL INVOICE IS REQUIRED', 'every line must name a product that actually appears on that invoice') and supplies usage guidance for needsReview, which is an undocumented bare boolean in the schema. The strict-line-keys warning also constrains the whole lines object rather than a single field.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('File a SELL-SIDE DEBIT NOTE draft') and immediately defines the concept as an additional charge on an invoice already issued, with three concrete examples of when that applies. It explicitly distinguishes itself from the two nearest siblings: create_credit_note_draft ('unlike a credit note...') and create_invoice_draft ('the right document is a NEW INVOICE'). An agent can route correctly without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit prerequisites (run intake_contract with doc_type:'sales_debit_note' first; locate the invoice via search_documents), explicit exclusions ('if there is nothing to correct upward, the right document is a NEW INVOICE... so say that rather than filing this'; extra goods delivered -> new invoice), and named alternatives for error recovery (revise_draft with kind 'sales_debit_note' rather than filing a second one). The buy-side vs sell-side routing is also spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_expense_draftFile expense draftAInspect
File a paid-expense DRAFT into Taokeh from a receipt you've read. This does NOT post to the books — it creates a pending draft the user reviews and approves in Taokeh; only then does it hit the ledger. Shape the fields with intake_contract + expense_accounts first. Set amountUncertain when any digit of the printed total is uncertain; set needsReview and add a SHORT reviewer note in notes (one or two sentences naming what the human should double-check — not lengthy reasoning) for any other doubt. If you have the ORIGINAL receipt image/PDF, attach it — it rides the draft and lands on the posted entry automatically on approval, so the user never has to re-upload it. Small files: pass attachmentBase64 + attachmentMediaType inline. Send attachmentBytes (the original file’s decoded size) with it so a truncated base64 is rejected instead of filed. Anything bigger: request_attachment_upload → PUT the bytes → pass the returned attachmentToken — unless your shell cannot reach taokeh.my (a sandboxed client behind a network allowlist), in which case inline it anyway, with attachmentBytes; never both.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | ||
| memo | No | ||
| notes | No | A SHORT reviewer note: one or two plain sentences, written in the reviewer's language, flagging what the human should double-check before approving — a smudged or ambiguous total, a vendor/customer you weren't sure of, a judgment call you made. The reviewer reads this on a small approval card, so keep it brief and human. This is NOT a place to dump lengthy reasoning, your working, or boilerplate — just the one thing to check. Leave it empty when there is nothing to flag. | |
| amount | Yes | ||
| currency | No | ||
| inputTax | No | ||
| reference | No | ||
| needsReview | No | ||
| shoeboxItemId | No | The id of a SHOEBOX photo (from shoebox_items) that this draft was read from — the paper someone in the business snapped and sent in. Taokeh attaches ITS OWN stored copy of that photo to the draft, so DO NOT also send the image: no re-upload, no base64, nothing to truncate. Mutually exclusive with attachmentBase64 and attachmentToken. The item must still be waiting: one already booked, set aside, or already carrying a pending draft is refused with the reason. Approving the draft closes that shoebox item against the document it became. | |
| amountUncertain | No | ||
| attachmentBytes | No | The decoded byte size of the ORIGINAL file on disk — send it alongside attachmentBase64 and the server rejects a truncated paste instead of filing a corrupt file. | |
| attachmentToken | No | The token from request_attachment_upload, AFTER you have PUT the file bytes to its uploadUrl. Use this instead of attachmentBase64 for any real photo/PDF — it carries the file out-of-band (no base64 in this call). Mutually exclusive with attachmentBase64. The uploaded file rides the draft and lands on the posted entry on approval, exactly as an inline one does. | |
| attachmentBase64 | No | The ORIGINAL receipt as base64 — SMALL files only (a few KB). Base64 inside a tool call is costly, so for a real receipt photo or a multi-page PDF use request_attachment_upload instead (attachmentToken). Rides the draft and lands on the posted entry on approval — no re-upload. A bad type/oversize file is rejected and NOTHING is filed. | |
| attachmentSha256 | No | The SHA-256 of the ORIGINAL file as 64 hex chars — optional second check alongside attachmentBase64, so corrupted bytes are rejected instead of filed. | |
| debitAccountCode | Yes | ||
| creditAccountCode | Yes | ||
| attachmentFilename | No | Optional original filename for the attachment, e.g. receipt.jpg. | |
| attachmentMediaType | No | The attachment's MIME type, e.g. 'image/jpeg' or 'application/pdf'. Required when attachmentBase64 is given. It must match the file's bytes — a mismatch is refused and nothing is filed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare non-read-only, non-destructive, non-idempotent, closed-world, but the description goes well beyond: it describes the draft→approval→posted lifecycle, that the attachment 'lands on the posted entry automatically on approval', refusal behavior (truncated base64, mismatched MIME type, shoebox item already booked or carrying a pending draft), and mutual-exclusivity rules ('never both'). This is real behavioral context an agent cannot get from the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded correctly — purpose and the critical non-posting caveat come first, with operating instructions after. It is long, and the final sentence stacking the token flow, the sandbox-allowlist exception and 'never both' is dense, but nearly every sentence carries distinct operational content rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 18-param mutation tool with no output schema, the description covers the genuinely hard parts — attachment ingestion, review flags, shoebox state constraints, and validation failure modes. The residual gaps are the plain fields (date, currency, inputTax, reference, account codes) that an agent would still be guessing at.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 44% across 18 params, so the description must compensate. It does explain amountUncertain, needsReview, notes intent, and the three attachment-parameter paths in useful detail, but date, memo, currency, inputTax, reference, debitAccountCode, and creditAccountCode remain undocumented in both places (account codes are only indirectly pointed at via expense_accounts). Partial compensation for a sizeable gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific verb and resource ('File a paid-expense DRAFT into Taokeh from a receipt you've read') and immediately clarifies the non-effect ('does NOT post to the books'). It cleanly separates itself from posting/ledger tools, though it never names a create_*_draft sibling (e.g. create_receipt_draft or create_bill_draft) as an alternative, so same-family disambiguation is left to inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Strong operational guidance: 'Shape the fields with intake_contract + expense_accounts first', explicit conditions for amountUncertain and needsReview, and a decision path for attachments (inline base64 for small files vs request_attachment_upload → attachmentToken for real photos/PDFs). It stops short of saying when to choose this tool over create_bill_draft/create_receipt_draft for a given document, so it is clear context without full alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_invoice_draftFile invoice draftAInspect
File a sales-invoice DRAFT into Taokeh from a sales note you've read. This does NOT post to the books — it creates a pending draft the user reviews and approves in Taokeh; only then does it post (and move stock). Shape the fields with intake_contract(doc_type:'invoice') + resolve_customer + resolve_product first. The server re-computes every line quantity from the tally and the grand total (with SST) — so present your working, but the server's figures are authoritative. Set needsReview and add a SHORT reviewer note in notes (one or two sentences naming what to double-check — not lengthy reasoning) for any doubt. If you have the ORIGINAL sales note image/PDF, attach the original document you extracted from — the owner sees it beside the draft at review (s.82 record-keeping) and it lands on the posted invoice automatically on approval, so the user never has to re-upload it. Small files: attachmentBase64 + attachmentMediaType inline. Send attachmentBytes (the original file’s decoded size) with it so a truncated base64 is rejected instead of filed. Anything bigger: request_attachment_upload → PUT the bytes → pass the returned attachmentToken — unless your shell cannot reach taokeh.my (a sandboxed client behind a network allowlist), in which case inline it anyway, with attachmentBytes; never both. ⛔ LINE KEYS ARE STRICT (2026-09-09): a key this schema does not list is REFUSED BY NAME — with the key it probably meant — and NOTHING is filed. Unknown keys used to be dropped in silence, which let a line through with its price or its tax code missing. ⛔ STOCK (stock-modes S1): on a tracked (perpetual) company an approve that would take an item below zero is refused and nothing posts, so check stock_level first; the owner records the bill or a stock count, then approves.
| Name | Required | Description | Default |
|---|---|---|---|
| term | No | The payment term exactly as the paper states it ('Due on Receipt', 'Net 7', 'Net 14', 'Net 30', 'Net 60'). Anything else the note actually says is kept verbatim and shown to the reviewer as-is — do not round it to the nearest familiar term. Leave it out when the paper states none. | |
| lines | Yes | ||
| notes | No | A SHORT reviewer note: one or two plain sentences, written in the reviewer's language, flagging what the human should double-check before approving — a smudged or ambiguous total, a vendor/customer you weren't sure of, a judgment call you made. The reviewer reads this on a small approval card, so keep it brief and human. This is NOT a place to dump lengthy reasoning, your working, or boilerplate — just the one thing to check. Leave it empty when there is nothing to flag. | |
| refNo | No | The CUSTOMER's own document number as printed on the note — their delivery-order number or purchase-order number ("DO 1074", "PO 88231"). NOT the invoice number: that is `reference`. Leave it out when the paper shows none; never copy the invoice number into it. | |
| dueDate | No | The due date when the paper states one outright. For one of the five preset terms you can leave it out — Taokeh fills the due date on the draft (invoice date + 0/7/14/30/60 days) and the owner sees it on the approval screen. Send it to override that, or when the paper's due date does not follow the term. A term Taokeh does not recognise derives NOTHING, so state the due date yourself on those or the invoice posts due on its own issue date. | |
| customer | No | ||
| saleDate | Yes | ||
| reference | No | ||
| customerId | No | ||
| needsReview | No | ||
| printedTotal | No | ||
| paymentMethod | Yes | ||
| shoeboxItemId | No | The id of a SHOEBOX photo (from shoebox_items) that this draft was read from — the paper someone in the business snapped and sent in. Taokeh attaches ITS OWN stored copy of that photo to the draft, so DO NOT also send the image: no re-upload, no base64, nothing to truncate. Mutually exclusive with attachmentBase64 and attachmentToken. The item must still be waiting: one already booked, set aside, or already carrying a pending draft is refused with the reason. Approving the draft closes that shoebox item against the document it became. | |
| attachmentBytes | No | The decoded byte size of the ORIGINAL file on disk — send it alongside attachmentBase64 and the server rejects a truncated paste instead of filing a corrupt file. | |
| attachmentToken | No | The token from request_attachment_upload, AFTER you have PUT the file bytes to its uploadUrl. Use this instead of attachmentBase64 for any real photo/PDF — it carries the file out-of-band (no base64 in this call). Mutually exclusive with attachmentBase64. The uploaded file rides the draft and lands on the posted invoice on approval, exactly as an inline one does. | |
| attachmentBase64 | No | The ORIGINAL sales note as base64 — SMALL files only (a few KB). For a real photo or multi-page PDF use request_attachment_upload instead (attachmentToken). Rides the draft and lands on the posted invoice on approval — no re-upload. A bad type/oversize file is rejected and NOTHING is filed. | |
| attachmentSha256 | No | The SHA-256 of the ORIGINAL file as 64 hex chars — optional second check alongside attachmentBase64, so corrupted bytes are rejected instead of filed. | |
| attachmentFilename | No | Optional original filename for the attachment, e.g. sales-note.jpg. | |
| confirmNewCustomer | No | Set to true ONLY after the user has EXPLICITLY confirmed this customer is genuinely new. It skips the near-miss guard (which rejects a name that looks like shorthand for an existing customer). Never set it to push a near-miss through — ask the user first. | |
| attachmentMediaType | No | The attachment's MIME type, e.g. 'image/jpeg' or 'application/pdf'. Required when attachmentBase64 is given. It must match the file's bytes — a mismatch is refused and nothing is filed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses that this only creates a pending draft and does not post, that the server recomputes line quantities and totals authoritatively, that strict line keys are refused by name, and that stock/approval constraints can block posting. These are meaningful behavioral details not captured by the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose and non-posting behavior, then moves into prerequisites and warnings. It is long, but the complexity and high-risk validation rules justify most of its length; some attachment and validation detail overlaps with schema descriptions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 20-parameter create tool with no output schema, the description covers the critical workflow, review, attachment, and stock behaviors well. It is not fully complete because it does not describe the return value or clarify several bare parameters, though the schema partially compensates.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 60% schema description coverage, the schema already documents many parameters, but the description adds important cross-parameter semantics: strict line-key validation, server-derived quantities, attachment mutual exclusivity, and guidance on needsReview/notes. Some bare parameters such as customer, customerId, saleDate, reference, printedTotal, and paymentMethod receive little or no added meaning in the description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: filing a sales-invoice DRAFT into Taokeh from a sales note. It immediately distinguishes the action from posting to the books and from approving/posting behavior, so an agent can tell what this tool creates and what it does not.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit prerequisites (intake_contract, resolve_customer, resolve_product) and conditional guidance (check stock_level first on tracked companies). It also names the attachment alternative request_attachment_upload, explains the fallback for sandboxed shells, and warns never to send both inline and token attachments.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_journal_draftFile adjusting journal draftAInspect
File an ADJUSTING JOURNAL ENTRY as a draft in Taokeh — the accountant's entry, not a document: accruals, prepayments, depreciation, corrections, reclassifications, year-end adjustments, and REVERSING entries. This does NOT post to the books: it creates a pending draft with the debits and the credits laid out for the user to see, and only their approval writes it to the ledger. Shape the fields with intake_contract(doc_type:'journal') first — it returns this company's REAL chart of accounts, which is what every accountRef must match. IT MUST BALANCE TO THE SEN: debits equal credits or the draft is refused, with the difference named. Each line carries exactly one side (debit OR credit), and at least two lines are required. Accounts are matched by CODE (best) or by their exact name; an unknown ref is refused and an ambiguous name reports the candidates rather than picking. THIS TOOL NEVER CREATES AN ACCOUNT — if the adjustment needs one that does not exist, tell the user to add it in Taokeh and file again. FIXING A WRONG ENTRY THAT IS ALREADY POSTED: file a REVERSING entry here. Taokeh's connector never edits and never deletes posted history — that is deliberate and it is the point: a ledger you can rewrite is not a ledger, and every entry must stay auditable. So find the wrong entry with search_journal, file a reversing journal dated today (its debits become credits and its credits become debits, same accounts, same amounts), then file the correct entry. Tell the user plainly that this is what you are doing and why, rather than reporting that you 'cannot' fix it. CONTROL ACCOUNTS ARE ALLOWED BUT NEVER SILENT: a line on accounts receivable, accounts payable, inventory, the SST control or opening-balance equity is reconciled to a subledger, so a journal there moves the control with no document behind it and the aging-vs-control tie-out will show the difference. Such a draft is filed and flagged — it can be approved ONLY on its full review page in Taokeh, never by a one-tap email approval or a deck swipe. Say in notes why the control line is right. THE SHAREHOLDER'S LOAN: approve refuses a journal that would leave a posted owner withdrawal over-repaid (the loan in debit on some date), naming the withdrawals and the steps. When that is already so as the books stand, the draft is still filed, the reply says why (ownerLoanRefusal), and it can be approved only on its full review page. WHAT NOT TO USE THIS FOR: anything that has a real document. A supplier bill is create_bill_draft, a sale is create_invoice_draft, a paid expense is create_expense_draft, a customer payment is create_receipt_draft. Those carry party, SST and stock consequences a raw journal silently skips. Attach the WORKING PAPER you read the adjustment off — the depreciation schedule, the accrual computation, the bank letter. It rides the draft and the approver sees it beside your figures. Prefer request_attachment_upload → attachmentToken; if your shell cannot reach taokeh.my (a sandboxed client behind a network allowlist) send attachmentBase64 + attachmentMediaType inline instead — correct even for a full PDF — always with attachmentBytes, the file’s decoded size on disk, so a truncated paste is rejected instead of filed. Never both. NEVER A LINE ON A ZERO-BALANCE CLEARING ACCOUNT: Contra Clearing (1016 in the default chart) and Refund Clearing are Taokeh's own conduits and stay at zero, so a line on either is REFUSED. A customer-supplier set-off is a contra (the owner records it under Banking); a refund out of the drawer is the credit note's Refund page. Set needsReview and add a SHORT reviewer note in notes for any doubt. Filed it wrong? Use revise_draft (kind: 'journal') rather than filing a second one.
| Name | Required | Description | Default |
|---|---|---|---|
| memo | No | What the entry is FOR, in one line — "accrue December electricity", "reverse the duplicated August rent". It prints on the entry and is the first thing the approver reads. | |
| lines | Yes | At least two lines. The debits must equal the credits TO THE SEN — an unbalanced entry is refused with the difference named. ⛔ LINE KEYS ARE STRICT: a key this schema does not list is REFUSED BY NAME and NOTHING is filed. | |
| notes | No | A SHORT reviewer note: one or two plain sentences, in the reviewer's language, naming what the human should double-check before approving — which schedule the figure came from, which entry this reverses, why a control-account line is right. Not lengthy reasoning. | |
| entryDate | Yes | ||
| reference | No | Your own reference for the adjustment, if there is one (a schedule number, a working-paper ref). | |
| clientTotal | No | Your own arithmetic for the entry total — carried onto the review screen for the human to compare against, never used to compute anything. | |
| needsReview | No | ||
| attachmentBytes | No | The decoded byte size of the ORIGINAL file on disk — send it alongside attachmentBase64 and the server rejects a truncated paste instead of filing a corrupt file. | |
| attachmentToken | No | The token from request_attachment_upload, AFTER you have PUT the file bytes to its uploadUrl. Mutually exclusive with attachmentBase64. | |
| attachmentBase64 | No | The supporting WORKING PAPER as base64 — SMALL files only. For a real schedule or a multi-page PDF use request_attachment_upload instead (attachmentToken). A bad type/oversize file is rejected and NOTHING is filed. | |
| attachmentSha256 | No | The SHA-256 of the ORIGINAL file as 64 hex chars — optional second check alongside attachmentBase64, so corrupted bytes are rejected instead of filed. | |
| attachmentFilename | No | The original filename, for the reviewer. | |
| attachmentMediaType | No | The attachment's MIME type, e.g. 'application/pdf'. Required when attachmentBase64 is given. It must match the file's bytes — a mismatch is refused and nothing is filed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnly=false and destructive=false; the description adds extensive behavioral context: the entry never posts to the books, requires approval, must balance to the sen, refuses unknown or ambiguous account refs, never creates accounts, flags control-account lines, restricts approval for control-account and owner-loan cases, refuses zero-balance clearing accounts, and explains attachment handling. This is far beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose and key refusal rules are front-loaded, and the description uses heading-like phrases such as WHAT NOT TO USE THIS FOR to organize a complex workflow. It is very long and repeats some balance and strict-key warnings already present in the schema, but most of the density is justified by the tool's many failure modes.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and 13 parameters, the description still covers the critical context: draft versus posting behavior, approval constraints, account matching, balancing rules, attachment requirements, control-account and owner-loan edge cases, and correction paths. It is complete enough for an agent to call the tool correctly in complex accounting scenarios.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 85%, so the baseline is 3, but the description adds meaningful semantics: accountRef must match the real chart by code or exact name, each line carries exactly one side, at least two lines are required, line keys are strict, and the attachmentToken versus attachmentBase64 workflow is explained. It still leaves some details, such as entryDate format and reference/clientTotal behavior, mostly to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'File an ADJUSTING JOURNAL ENTRY as a draft,' and immediately clarifies it is the accountant's entry, not a document. It lists the entry types it covers and later names the sibling alternatives it is not for, so an agent can distinguish it from create_bill_draft, create_invoice_draft, and others.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance, a detailed WHAT NOT TO USE THIS FOR section with named alternatives, a workflow for correcting already-posted entries via reversing journals, and prerequisites such as using intake_contract first. It also tells the agent when to use revise_draft instead of filing a second draft.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_payment_draftFile a pending supplier-payment draft (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. File a SUPPLIER-PAYMENT draft ("we paid Ah Seng RM5,000 from Maybank on Tuesday against bills 12 and 14"). This does NOT post: only the owner's approval writes a real bank payment and settles the bills, and when it does, accounts payable and the bank move exactly as they would if the owner had matched the line in Banking. A payment settles an EXISTING supplier's open bills: resolve the supplier (resolve_vendor) and see what is owed (ap_aging, search_documents) first, then give EITHER a lump total (auto-allocated oldest-first) OR explicit per-bill allocations. PREFER purchaseId over reference in an allocation: a supplier's own bill number is NOT unique in Taokeh, so a reference that matches two open bills is refused naming both rather than guessed. MYR only — a foreign-currency bill is refused and routes to Banking → Match, which handles the exchange difference. A payment dated BEFORE a bill it pays is accepted — an advance to the supplier: it sits on the supplier's account until the bill's date and pays the bill that day; the answer says so in paidBeforeBill. Except an OPENING bill when the payment is dated on or before the accounting start date: that bill is what was still owed on the start date, so the money is already counted in it — explicit allocations naming one are refused at filing, and a lump total is never poured into one. Amounts are RINGGIT, the same figures ap_aging and search_documents show you — never sen, never a foreign figure converted by you. A CREDIT CARD can't be the paying account here: paying a supplier on a card is a spend that arrives on the card's own statement — import that statement instead. IF THIS COMPANY IMPORTS BANK STATEMENTS and the payment is already sitting on an imported line, do NOT file here — propose the allocation on that line with draft_bank_classification. Filing it here would put the same money in Taokeh twice, and the server refuses when it can see the imported line. The server re-derives the allocation against live outstanding at create AND at approval, so its figures are authoritative and a bill settled in the meantime makes the draft refuse rather than over-pay. A bill's outstanding is NET of the live debit notes raised against it (a RM 432 bill with a RM 363.13 debit note owes 68.87; ap_aging lists the note as its own negative row, so the supplier's total there is already net): a lump total auto-allocates into those net figures only. An explicit per-bill amount may go up to the bill's open amount BEFORE its debit notes — paying the whole bill is allowed, and the excess then stays on the debit note as the supplier's refundable credit — so propose the net figure unless the user says they paid the full bill. Set needsReview and add a SHORT reviewer note in notes (one or two sentences naming what to double-check — not lengthy reasoning) for any doubt. Filed it wrong? Use revise_draft (kind: 'payment') rather than filing a second one. If you have the payment proof (transfer slip, remittance advice), attach the original document you extracted from — the owner sees it beside the draft at review (s.82 record-keeping) and it lands on the settlement entry automatically on approval. Small files: attachmentBase64 + attachmentMediaType inline. Send attachmentBytes (the original file’s decoded size) with it so a truncated base64 is rejected instead of filed. Anything bigger: request_attachment_upload → PUT the bytes → pass the returned attachmentToken — unless your shell cannot reach taokeh.my (a sandboxed client behind a network allowlist), in which case inline it anyway, with attachmentBytes; never both.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note: one or two plain sentences, written in the reviewer's language, flagging what the human should double-check before approving — a smudged or ambiguous total, a supplier you weren't sure of, a judgment call you made. The reviewer reads this on a small approval card, so keep it brief and human. This is NOT a place to dump lengthy reasoning, your working, or boilerplate — just the one thing to check. Leave it empty when there is nothing to flag. | |
| total | No | A lump ringgit amount to auto-allocate oldest-first across the supplier’s open bills. Give this OR explicit allocations — never both. | |
| vendor | No | The supplier name, resolved with resolve_vendor first. A payment settles an EXISTING supplier's bills and can never create a supplier. | |
| vendorId | No | The resolved vendor id, instead of the name. | |
| allocations | No | The explicit per-bill split, at most 50 lines. Each amount is capped by the server at that bill’s live open amount before its debit notes. | |
| needsReview | No | ||
| paymentDate | Yes | ||
| printedTotal | No | The total printed on the payment advice, if any. Cross-check only — the server computes the real total and flags a mismatch on the approval screen. | |
| bankAccountId | No | The PAYING bank/cash account (from intake_contract(doc_type:'payment') → payingAccounts). Optional — omit it and the reviewer picks at approval. A credit-card account is refused by name. | |
| attachmentBytes | No | The decoded byte size of the ORIGINAL file on disk — send it alongside attachmentBase64 and the server rejects a truncated paste instead of filing a corrupt file. | |
| attachmentToken | No | The token from request_attachment_upload, AFTER you have PUT the file bytes to its uploadUrl. Use this instead of attachmentBase64 for any real photo/PDF — it carries the file out-of-band (no base64 in this call). Mutually exclusive with attachmentBase64. The uploaded file rides the draft and lands on the settlement entry on approval, exactly as an inline one does. | |
| attachmentBase64 | No | The ORIGINAL payment proof (transfer slip / remittance advice) as base64 — SMALL files only (a few KB). For a real photo or multi-page PDF use request_attachment_upload instead (attachmentToken). Rides the draft and lands on the settlement entry on approval — no re-upload. A bad type/oversize file is rejected and NOTHING is filed. | |
| attachmentSha256 | No | The SHA-256 of the ORIGINAL file as 64 hex chars — optional second check alongside attachmentBase64, so corrupted bytes are rejected instead of filed. | |
| attachmentFilename | No | Optional original filename for the attachment, e.g. transfer-slip.jpg. | |
| attachmentMediaType | No | The attachment's MIME type, e.g. 'image/jpeg' or 'application/pdf'. Required when attachmentBase64 is given. It must match the file's bytes — a mismatch is refused and nothing is filed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare a non-destructive, non-idempotent write; the description goes far beyond: the draft has no ledger effect until human approval, allocation is re-derived at create AND at approval, a bill settled in the meantime causes refusal rather than over-payment, credit-card accounts are refused by name, foreign currency is refused and routed to Banking → Match, opening-bill and pre-dated-payment semantics are spelled out, and ambiguity in `reference` is refused by naming both bills. This is unusually thorough behavioral disclosure that the annotations alone could not convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical constraint is front-loaded in the first sentence, which is correct, but the remaining block is ~600 words of dense all-caps prose with no paragraph or bullet structure, and the attachment paragraph repeats the schema's attachmentToken/attachmentBase64/attachmentBytes guidance almost verbatim. Much of it earns its place, but the wall-of-text format makes the per-bill-vs-lump and debit-note rules harder to extract than necessary.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 15-parameter mutation tool with no output schema, the description covers the full decision surface an agent needs: sourcing the supplier and bills, allocation strategy, currency, date-before-bill behavior, opening-bill edge case, debit-note netting, review-note discipline, attachment pathway selection, and the response field `paidBeforeBill`. Nothing material is left unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 87%, yet the description still adds real meaning: lump `total` auto-allocates oldest-first while explicit `allocations` cap at the bill's open amount before debit notes, prefer `purchaseId` over `reference` because bill numbers are not unique, amounts are ringgit never sen, and `attachmentBase64`/`attachmentToken` are mutually exclusive with a sandbox fallback. It stops short of 5 only because the attachment paragraph largely restates what the schema property descriptions already say.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (file a pending supplier-payment draft) and immediately scopes it: 'FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH.' It also names what it is not (not a posting, not the path for imported bank lines) and gives a concrete example payload, so it is distinguishable from create_bank_clearing_draft and draft_bank_classification at a glance.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use with prerequisites ('resolve the supplier (resolve_vendor) and see what is owed (ap_aging, search_documents) first'), explicit when-NOT-to-use ('IF THIS COMPANY IMPORTS BANK STATEMENTS and the payment is already sitting on an imported line, do NOT file here — propose the allocation on that line with draft_bank_classification'), and an error-recovery alternative (revise_draft kind:'payment'). Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_payment_match_draftPending draft: match a posted payment to open invoices or bills (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose how ONE bank line already posted as a customer payment (money in) or a supplier payment (money out) settles open invoices or bills — the owner's "this deposit paid these three invoices". An admin or bookkeeper reviews it and approves; only that tap allocates, exactly as Banking → Match → Save does, and the documents then read as paid. Pass bankTransactionId (a POSTED line from bank_review_queue with status "posted") and allocations: [{saleId, amount}] for money in, [{purchaseId, amount}] for money out, ringgit, adding up to the WHOLE line to the sen (the in-app pane's ±0.10 rounding allowance does not apply to a proposal). At most 50 documents. WHICH DOCUMENTS: only the ones Banking → Match offers this line — the open invoices of the customer the line is posted against (or, when that customer has none open, the invoices of the company's walk-in customer — the one the owner marked on its customer page, else a customer named 'CASH SALES'), or the supplier's open bills. A line with no customer at all is offered nothing. A document of ANY other party is refused, because no screen in Taokeh offers it: if the payment was posted against the wrong customer, or no customer, the owner unposts the line in Banking, sets the right customer, and posts it again — then file this. A refusal lists the documents the line can take, with their ids. ALSO REFUSED BY NAME, at filing and again at approval: a line not posted, not a customer/supplier payment, already matched (a match someone saved is never replaced — that includes a line applied only IN PART: the rest of it is applied in-app, on Banking → Match or Sales → Official receipts → Receive payment, never by this draft), or on a foreign-currency account (Taokeh does not post or match those lines yet); a line whose posted entry booked an exchange difference on a foreign-currency invoice or bill (the owner unposts it in Banking, then matches it and posts it again); a document not open; an amount over what a document still owes; a FOREIGN-currency invoice or bill dated after the payment (it is matched by hand on Banking → Match); an OPENING invoice or bill paid by a line dated on or before the accounting start date (the opening balance already counts that money); a match whose line AND document are both dated on or before the lock date (it would apply inside the locked period, whose books are filed: an admin unlocks in Opening balances → Review & lock, matches, then locks again); the same document twice; a pending match already filed for the line. A ringgit INVOICE or BILL dated after the payment is allowed — a customer's deposit, or an advance paid to a supplier: the money sits on that party's account until the document's date and pays it then, and the review page and this answer (paidBeforeInvoice / paidBeforeBill) say so. If the line or a document changes before approval (paid, credited, re-tagged, matched), approval refuses and nothing is allocated. A payment holding an official receipt (recorded with Receive payment) can only pay that customer's invoices — never the walk-in customer's or anyone else's; to move it, the owner undoes it in Banking and records it again. note: one short line for the owner saying how you know (the remittance, the customer's message). A supplier DEBIT NOTE's credit used as a negative line here is refused when the bank line is dated before the debit note (its credit did not exist yet). And approving a new allocation that takes money OFF a bill is refused while one of that bill's debit notes has credit above what the bill owed that was already refunded or used on another payment — the refusal names the note; that refund or payment is undone first, in Taokeh.
| Name | Required | Description | Default |
|---|---|---|---|
| note | No | A SHORT note for the owner: how you know which documents it paid. | |
| lines | No | NOT a filter on this tool. Pass the documents as `allocations`: [{saleId or purchaseId, amount}]. | |
| notes | No | NOT a filter on this tool. Pass the owner note as `note`. | |
| bankLineId | No | NOT a filter on this tool. Pass the bank line as `bankTransactionId`. | |
| allocations | Yes | Where the money goes. An unknown key is refused by name. | |
| needsReview | No | Set true when something gave you pause. | |
| transactionId | No | NOT a filter on this tool. Pass the bank line as `bankTransactionId`. | |
| bankTransactionId | Yes | REQUIRED — the posted bank line, from bank_review_queue (status "posted"). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say it is a non-read-only, non-destructive, non-idempotent write; the description adds the crucial behavioral fact that nothing changes until a human approves in Taokeh, plus approval-time staleness refusal, draft-already-filed refusal, and the fact that a saved match is never replaced. It also discloses permission-free requirements (unpost/re-post workflow) and edge-case economics (debit-note credit ordering, refund interaction). No contradiction with destructiveHint=false.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The single most important constraint (draft only, human approves) is front-loaded in caps, which is correct. However, the description is a very long unbroken block that mixes core contract, refusal catalogue, and rare edge cases (supplier debit note ordering, refunded credit) — a shorter core plus listed exclusions would read much better.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, approval-gated matching tool with no output schema, the description covers input contract, eligibility rules, refusal behavior (including that refusals name the candidate documents and their ids), and even the returned `paidBeforeInvoice`/`paidBeforeBill` signals. An agent has essentially everything needed to call it correctly or to explain why it cannot.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds constraints not present in the schema: allocations must sum to the whole line to the sen with no ±0.10 rounding tolerance, at most 50 documents, ringgit only, and the saleId/purchaseId split tied to money direction. It also explains the `note` field's intent (how you know). The `needsReview` parameter is not explained beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource (files a pending draft that matches one posted bank line to open invoices or bills) and scopes it precisely: money-in via customer payments, money-out via supplier payments. It draws the line against the in-app Banking → Match / Receive payment flows, so an agent can tell this apart from create_payment_draft or create_receipt_draft.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use (a posted line that should settle open documents), when-not-to-use with named alternatives (unpost in Banking and re-post if the wrong customer; use Banking → Match or Receive payment for in-app matching; use Opening balances → Review & lock for locked periods). Refusal conditions are enumerated up front rather than left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_purchase_order_draftFile purchase-order draftAInspect
File a PURCHASE-ORDER draft into Taokeh — an order you intend to PLACE with a supplier, typically read off their quotation. It POSTS NOTHING and MOVES NOTHING: a purchase order is an intent to buy, so approving it creates no journal entry, no stock movement and no amount owing. The books move later, when the goods arrive and the user converts the order into a bill on its page in Taokeh — a step this tool never takes for them. Approval also saves the order as a DRAFT; issuing it to the supplier is the user's own tap, and Taokeh emails nobody. INVENTORY-ONLY — every line must be a product the company stocks; a service or one-off non-stock charge belongs on the manual purchase-order form. NO SST on this draft: it files the order with no tax code and no tax line, and input SST is settled on the bill against the supplier's tax invoice (an SST code the user adds on Taokeh's own purchase-order form taxes the order by its lines' codes, rounded per code on the subtotal, the same as the bill it becomes). Shape the fields with intake_contract(doc_type:'purchase_order') + resolve_vendor + resolve_product first. The server re-computes every line quantity from the tally and the order total — so present your working, but the server's figures are authoritative. Set needsReview and add a SHORT reviewer note in notes (one or two sentences naming what to double-check — not lengthy reasoning) for any doubt. Filed it wrong? Use revise_draft (kind: 'purchase_order') rather than filing a second one. Goods that have ALREADY arrived are not this door — use create_bill_draft. ⛔ LINE KEYS ARE STRICT (2026-09-09): a key this schema does not list is REFUSED BY NAME — with the key it probably meant — and NOTHING is filed. Unknown keys used to be dropped in silence, which let a line through with its price or its tax code missing.
| Name | Required | Description | Default |
|---|---|---|---|
| term | No | The payment/delivery term line, e.g. 'Net 30'. | |
| lines | Yes | ||
| notes | No | A SHORT reviewer note: one or two plain sentences, written in the reviewer's language, flagging what the human should double-check before they order — a quantity you weren't sure of, a supplier name you guessed at, a judgment call you made. The reviewer reads this on a small approval card, so keep it brief and human. Leave it empty when there is nothing to flag. | |
| poDate | Yes | ||
| vendor | No | ||
| vendorId | No | ||
| reference | No | The BUYER'S OWN purchase-order number, if they already have one — it becomes this order's document number. NOT the supplier's quotation number (put that in `notes`). Usually omit it: Taokeh numbers the order itself (POxxxx). | |
| needsReview | No | ||
| expectedDate | No | When the goods are expected, YYYY-MM-DD. Optional — omit it rather than guess. | |
| printedTotal | No | ||
| confirmNewVendor | No | Set to true ONLY after the user has EXPLICITLY confirmed this supplier is genuinely new. It skips the near-miss guard (which rejects a name that looks like shorthand for an existing vendor). Never set it to push a near-miss through — ask the user first. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes far beyond the annotations: no journal entry, no stock movement, no amount owing, approval leaves it a DRAFT, no supplier email, no SST applied, and the server recomputes quantities so its figures are authoritative. Also discloses a hard failure mode (unknown line keys are refused by name and nothing is filed). Docked one point because the SST parenthetical is dense and partly redundant with the schema's tax notes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the most decision-critical fact (posts nothing, moves nothing) and the routing exclusions, but the block is long and mixes uppercase emphasis, em-dashes and a nested SST tangent that is harder to parse than it needs to be. Most sentences earn their place; the tax-code aside and the 'working' aside could be tightened.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 11-parameter draft-creation tool with no output schema, it covers creation contract, prerequisites, review workflow, correction path, exclusion cases, and the authoritative-quantity behavior. An agent has everything needed to call it correctly or to know it should not.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 45%, so the description must compensate; it does add real meaning for the review parameters ('Set needsReview and add a SHORT reviewer note in `notes`... one or two sentences naming what to double-check') and for the quantity-precedence rule. It does not explain term, vendor vs vendorId, or printedTotal, leaving several undocumented parameters without narrative guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('File a PURCHASE-ORDER draft') and immediately scopes it as an intent to buy with no posting or movement. It explicitly separates itself from create_bill_draft ('goods that have ALREADY arrived are not this door') and from update/revise paths, so an agent can route correctly without reading another schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete when-to-use prerequisites (shape fields with intake_contract(doc_type:'purchase_order') + resolve_vendor + resolve_product first), an explicit exclusion (services/non-stock charges belong on the manual form), a correction path (revise_draft kind:'purchase_order' rather than filing twice), and the alternative for received goods (create_bill_draft).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_quote_draftFile quote draftAInspect
File a quotation DRAFT into Taokeh from a request you've read or been told. A quote is an ESTIMATE — it does NOT post to the books or move stock; it creates a pending draft the user reviews and approves in Taokeh, which posts a real quotation they can then convert to an invoice or delivery order. Shape the fields with resolve_customer + resolve_product first. The server re-computes every line quantity from the tally and the grand total (with SST) — so present your working, but the server's figures are authoritative. A quote may have no customer (a walk-in estimate). Set needsReview and add a SHORT reviewer note in notes (one or two sentences naming what to double-check — not lengthy reasoning) for any doubt. If you read the request off a document, attach the original document you extracted from — the owner sees it beside the draft at review (s.82 record-keeping) and it lands on the posted quote automatically on approval. Small files: attachmentBase64 + attachmentMediaType inline. Send attachmentBytes (the original file’s decoded size) with it so a truncated base64 is rejected instead of filed. Anything bigger: request_attachment_upload → PUT the bytes → pass the returned attachmentToken — unless your shell cannot reach taokeh.my (a sandboxed client behind a network allowlist), in which case inline it anyway, with attachmentBytes; never both. ⛔ LINE KEYS ARE STRICT (2026-09-09): a key this schema does not list is REFUSED BY NAME — with the key it probably meant — and NOTHING is filed. Unknown keys used to be dropped in silence, which let a line through with its price or its tax code missing.
| Name | Required | Description | Default |
|---|---|---|---|
| term | No | Payment term carried onto the quote, e.g. 'Net 30'. Defaults to 'Due on Receipt'. | |
| lines | Yes | ||
| notes | No | A SHORT reviewer note: one or two plain sentences, written in the reviewer's language, flagging what the human should double-check before approving — a smudged or ambiguous total, a vendor/customer you weren't sure of, a judgment call you made. The reviewer reads this on a small approval card, so keep it brief and human. This is NOT a place to dump lengthy reasoning, your working, or boilerplate — just the one thing to check. Leave it empty when there is nothing to flag. | |
| customer | No | ||
| quoteDate | Yes | ||
| reference | No | ||
| customerId | No | ||
| validUntil | No | Optional "valid until" date for the quotation (YYYY-MM-DD). | |
| needsReview | No | ||
| printedTotal | No | ||
| attachmentBytes | No | The decoded byte size of the ORIGINAL file on disk — send it alongside attachmentBase64 and the server rejects a truncated paste instead of filing a corrupt file. | |
| attachmentToken | No | The token from request_attachment_upload, AFTER you have PUT the file bytes to its uploadUrl. Use this instead of attachmentBase64 for any real photo/PDF — it carries the file out-of-band (no base64 in this call). Mutually exclusive with attachmentBase64. The uploaded file rides the draft and lands on the posted quote on approval, exactly as an inline one does. | |
| attachmentBase64 | No | The ORIGINAL request/quote document as base64 — SMALL files only (a few KB). For a real photo or multi-page PDF use request_attachment_upload instead (attachmentToken). Rides the draft and lands on the posted quote on approval — no re-upload. A bad type/oversize file is rejected and NOTHING is filed. | |
| attachmentSha256 | No | The SHA-256 of the ORIGINAL file as 64 hex chars — optional second check alongside attachmentBase64, so corrupted bytes are rejected instead of filed. | |
| attachmentFilename | No | Optional original filename for the attachment, e.g. rfq.pdf. | |
| confirmNewCustomer | No | Set to true ONLY after the user has EXPLICITLY confirmed this customer is genuinely new. It skips the near-miss guard (which rejects a name that looks like shorthand for an existing customer). Never set it to push a near-miss through — ask the user first. | |
| attachmentMediaType | No | The attachment's MIME type, e.g. 'image/jpeg' or 'application/pdf'. Required when attachmentBase64 is given. It must match the file's bytes — a mismatch is refused and nothing is filed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses that this creates a pending draft rather than posting to the books, that approval posts a real quotation, that the server re-computes quantities and grand total authoritatively, that unknown line keys are refused by name, and how attachment handling works. These are substantive behavioral details an agent needs in order to invoke the tool correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but it is front-loaded with purpose and structured around prerequisites, authoritative figures, review notes, attachments, and strict line keys. Most sentences earn their place given the tool's complexity, though the density and single-paragraph format make it less scannable than it could be.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 17-parameter create tool with no output schema and only partial schema description coverage, the description is substantially complete: it covers the draft lifecycle, prerequisite resolution, review behavior, attachment paths, and strict line-key handling. It does not explain what the tool returns, such as a draft identifier or status, which is a minor remaining gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 59% schema description coverage, the description adds important meaning for critical parameters: line quantity derivation from tally/thickness/width, the server's authoritative figures, needsReview and notes, and the attachment workflow. It still leaves some parameters thinly explained, notably customer versus customerId, reference, printedTotal, and quoteDate, so it does not fully compensate for every gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'File a quotation DRAFT into Taokeh from a request you've read or been told.' It clearly distinguishes the draft from a posted quotation and from downstream invoice/delivery-order conversion, so an agent can tell what this tool is for without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear usage context: use it for a request you have read or been told, and shape fields with resolve_customer + resolve_product first. It also notes a quote may have no customer. However, it does not explicitly name alternatives such as update_quote_draft for revisions or create_invoice_draft for invoices, so the when-not guidance is implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_receipt_draftFile receipt draftAInspect
File a customer-payment DRAFT into Taokeh ("customer paid X"). This does NOT post — it creates a pending draft the user reviews and approves in Taokeh; only then does it write a real bank receipt, settle the invoices and issue the official receipt (the next OR number, dated as the payment) — exactly as Sales → Receive payment does. A receipt settles an EXISTING customer's open invoices: resolve the customer (resolve_customer) and see what they owe (open_invoices) first, then give EITHER a lump total (auto-allocated oldest-first) OR explicit per-invoice allocations. MYR only — a foreign-currency invoice is refused and routes to Banking → Payments. A customer who paid in CASH is not this lane (its approval books a bank): tell the owner to record it on Sales → Official receipts → Receive payment, with Received into = Cash. The server re-derives the allocation against live outstanding, so its figures are authoritative. Set needsReview and add a SHORT reviewer note in notes (one or two sentences naming what to double-check — not lengthy reasoning) for any doubt. If you have the payment proof (bank-in slip / remittance advice), attach the original document you extracted from — the owner sees it beside the draft at review (s.82 record-keeping) and it lands on the settlement entry automatically on approval, beside the official receipt. Small files: attachmentBase64 + attachmentMediaType inline. Send attachmentBytes (the original file’s decoded size) with it so a truncated base64 is rejected instead of filed. Anything bigger: request_attachment_upload → PUT the bytes → pass the returned attachmentToken — unless your shell cannot reach taokeh.my (a sandboxed client behind a network allowlist), in which case inline it anyway, with attachmentBytes; never both. A receipt dated on or before the accounting start date never settles an OPENING invoice — that invoice is what was still owed on the start date, so the money is already counted in it: explicit allocations naming one are refused at filing, and a lump total is never poured into one.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note: one or two plain sentences, written in the reviewer's language, flagging what the human should double-check before approving — a smudged or ambiguous total, a vendor/customer you weren't sure of, a judgment call you made. The reviewer reads this on a small approval card, so keep it brief and human. This is NOT a place to dump lengthy reasoning, your working, or boilerplate — just the one thing to check. Leave it empty when there is nothing to flag. | |
| total | No | ||
| customer | No | ||
| customerId | No | ||
| allocations | No | ||
| needsReview | No | ||
| receiptDate | Yes | ||
| printedTotal | No | ||
| bankAccountId | No | ||
| attachmentBytes | No | The decoded byte size of the ORIGINAL file on disk — send it alongside attachmentBase64 and the server rejects a truncated paste instead of filing a corrupt file. | |
| attachmentToken | No | The token from request_attachment_upload, AFTER you have PUT the file bytes to its uploadUrl. Use this instead of attachmentBase64 for any real photo/PDF — it carries the file out-of-band (no base64 in this call). Mutually exclusive with attachmentBase64. The uploaded file rides the draft and lands on the settlement entry on approval, exactly as an inline one does. | |
| attachmentBase64 | No | The ORIGINAL payment proof (bank-in slip / remittance advice) as base64 — SMALL files only (a few KB). For a real photo or multi-page PDF use request_attachment_upload instead (attachmentToken). Rides the draft and lands on the settlement entry on approval — no re-upload. A bad type/oversize file is rejected and NOTHING is filed. | |
| attachmentSha256 | No | The SHA-256 of the ORIGINAL file as 64 hex chars — optional second check alongside attachmentBase64, so corrupted bytes are rejected instead of filed. | |
| attachmentFilename | No | Optional original filename for the attachment, e.g. bank-in-slip.jpg. | |
| attachmentMediaType | No | The attachment's MIME type, e.g. 'image/jpeg' or 'application/pdf'. Required when attachmentBase64 is given. It must match the file's bytes — a mismatch is refused and nothing is filed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say non-readonly, non-idempotent, non-destructive; the description adds far more: the draft is pending and only posts on human approval, approval writes the bank receipt and issues the next OR number, the server re-derives allocations as authoritative, and the opening-invoice refusal behavior. This is exactly the context annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Content is front-loaded with purpose and exclusions, but it is a single dense wall of text mixing purpose, workflow, currency rules, cash-lane routing, attachment protocol and the opening-invoice edge case. A few sentences (e.g. the attachment shell/allowlist aside) could be trimmed or split into scannable clauses.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 15-parameter mutation tool with no output schema, the description covers what the tool does NOT do, who must approve, currency constraint, attachment mechanics and the one non-obvious accounting edge case. An agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With only 47% schema coverage the description must compensate, and it does for the hardest params — total (auto-allocation), allocations (per-invoice), needsReview plus a short reviewer notes rule, and the attachmentBase64/attachmentBytes/attachmentToken protocol including when to fall back to inline. It leaves customer/customerId/bankAccountId/printedTotal and the receiptDate formatting unaddressed, so not fully compensating.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('File a customer-payment DRAFT'), names the target system, and immediately contrasts with the posting action and with the cash lane. An agent can distinguish this from create_payment_draft / create_bank_clearing_draft without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly sequences prerequisites (resolve_customer, open_invoices), gives the two mutually exclusive input styles (lump total oldest-first vs explicit allocations), states the MYR-only exclusion, names the cash alternative with its exact UI path, and explains the opening-invoice date rule. No inference left.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_recurring_invoice_draftFile a pending recurring-invoice draft (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING IS CREATED UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose a NEW recurring invoice — a billing schedule that issues the same invoice to the same customer every month, quarter or year (a retainer, a subscription, a maintenance contract, rent). This does NOT create anything: it files a pending DRAFT the owner reviews and approves; only that tap saves the schedule. Give the customer (customerId from resolve_customer, or customer as an EXACT existing customer name — this tool never creates a customer), the lines (each a real catalogue product by productId or exact sku, with the quantity and the agreed unit price that will be billed EVERY cycle), the cadence, and the startDate — whose day of the month becomes the billing anchor (a 31st anchor lands on the 28th/29th/30th in shorter months). Optionally endDate, maxOccurrences, title, notes and paymentMethod (CREDIT = the customer owes it and pays later, CASH = settled at issue; these book differently, so ask rather than guess). A CASH schedule's money is booked where the schedule's "Paid into" says: the owner picks it on the schedule's own page, never this tool, and a schedule proposed here starts on the default (the Cash account for a walk-in customer, otherwise the account the company books cash sales to, usually its main bank). ⛔ WHAT IT CANNOT DO: it cannot set issueMode: "auto" — a schedule you propose files an invoice DRAFT each cycle for a person to approve, and nothing proposed through this connector may buy itself the right to post invoices unattended; the owner switches that on themselves if they want it. It cannot set MFRS 15 revenue spreading (recognition), which is an accounting-policy decision on the schedule's own page. It cannot set the next issue date directly — that is derived from the start date and the cadence, and the review page tells the owner the exact date the FIRST invoice would be issued before they approve. BE HONEST: never guess a contracted price or a start date — leave the schedule unproposed and ask.
| Name | Required | Description | Default |
|---|---|---|---|
| lines | Yes | What gets billed every cycle. Every line must be a real catalogue product. A line carrying Taokeh's printed per-foot or per-piece note (like "(400' @ RM 1.60/ft)") is refused: a schedule holds no size or tally, so it would bill without its rounding line every cycle. Raise those invoices with create_invoice_draft in their filed form. On an update, such a line already on the schedule may be sent back unchanged. ⛔ LINE KEYS ARE STRICT: a key this schema does not list is REFUSED BY NAME and NOTHING is filed. | |
| notes | No | A SHORT reviewer note, in the reviewer's language: what this schedule bills and what they should double-check. | |
| title | No | A short label for the schedule, e.g. 'Monthly retainer — Acme'. Optional; it never prints on the invoice. | |
| cadence | Yes | ||
| endDate | No | Stop issuing after this date. Optional. | |
| customer | No | An EXACT existing customer name. A name that matches nobody is refused — this tool never creates a customer. | |
| startDate | Yes | The first billing date. Its DAY OF THE MONTH becomes the anchor for every later cycle. A date in the past does not backfill — the first invoice lands on the first cycle on or after today. | |
| customerId | No | The customer id from resolve_customer. Give this OR `customer`. | |
| needsReview | No | Set true when something gave you pause — a price the user was unsure about, a start date you inferred. | |
| paymentMethod | No | CREDIT (default) = the customer owes it. CASH = settled at issue. Ask the user; these book differently. | |
| maxOccurrences | No | Stop after this many invoices. Optional. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare non-readOnly, non-destructive, non-idempotent, so the description correctly spends its words on the human-approval gate ('nothing is created until a human reviews and approves') and on downstream effects annotations cannot express — CASH money booked to a default Cash account, no backfill for past start dates, anchor-day clamping in short months. It stops short of saying whether resubmitting identical input creates a duplicate schedule, which matters given idempotentHint=false.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical constraint is front-loaded in the first clause, and each later sentence carries new information (customer resolution, line requirements, anchor behavior, payment booking, the three prohibitions). It loses a point for moderate redundancy and shouty formatting — 'FILES A PENDING DRAFT ONLY' is immediately restated as 'This does NOT create anything', and the ⛔/BE HONEST markers add noise without adding meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 11-parameter mutation with no output schema and real accounting consequences, the description covers the draft-filing lifecycle, the required pre-resolution steps, the strict line-key refusal, the prohibited settings, and the honesty rule on prices and dates. Nothing an agent needs to invoke this correctly or avoid the common failure modes is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 91%, so the baseline is 3; the description still earns above that by explaining derived semantics the schema omits — startDate's day-of-month becoming the billing anchor and rolling to the 28th/29th/30th, and that paymentMethod CASH vs CREDIT book differently so the agent should ask. Customer and line constraints (exact existing name, never creates a customer, never creates a product) are reinforced beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('propose a NEW recurring invoice') plus a concrete definition of what a recurring invoice is (billing schedule issuing the same invoice on a cadence). It distinguishes itself from the per-invoice sibling by naming create_invoice_draft as the tool for lines with printed per-foot notes, and from update_recurring_invoice_draft by framing itself as proposing new schedules.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells the agent to use resolve_customer and resolve_product first, to route per-foot/per-piece lines to create_invoice_draft instead, and to leave the schedule unproposed and ask when price or start date is unknown. The 'WHAT IT CANNOT DO' block names the exact fields (issueMode auto, recognition, next issue date) that must be set by the owner on the schedule page rather than here.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
customer_statementCustomer statementARead-onlyInspect
One customer's statement of account — the same rows, running balance and closing figure as the Statement PDF on the customer's page. Give customerId (from resolve_customer) or customer (a name, resolved like open_invoices: an ambiguous name returns candidates — ask which one). Optional from / to (YYYY-MM-DD). Each line in lines is what the PDF prints, in its order: document (an invoice, credit note or debit note — charge is what it put on the customer's account, which can be LOWER than the invoice total, e.g. a marketplace order's net payout, and is negative on a credit note), payment (money applied to the document above it, on or before to), and on_account (a payment received but not yet applied — in payment — or a refund paid out and not applied — in charge). balance is the running balance after each line and closingBalance the TOTAL OUTSTANDING the PDF prints. With no from, the statement covers the whole history and its closing equals the customer's A/R aging total as of to (except where a bank line was matched to invoices for more than it booked to A/R — an internal fix on that bank line, never printed for the customer). With from, it lists only documents and on-account payments dated from then, with NO balance brought forward, so its closing is the period's own figure — say so. With no to, the statement runs to the later of today and the customer's latest document or payment; period states the dates used. Lines are capped at 300; when more is true the closing and totals are still exact — give a later from to see the rest.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | ||
| from | No | ||
| customer | No | A customer name, resolved against this company's customers. | |
| customerId | No | The customer id from resolve_customer. Give this OR `customer`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnlyHint=true, openWorldHint=false), yet the description still adds substantial behavior: the 300-line cap with `more` true still yields exact closing/totals, no-`from` vs with-`from` produce a balance-brought-forward figure vs a period-only figure, and no-`to` defaults to later of today or latest activity. It even discloses the one known discrepancy case (bank line matched to invoices for more than booked to A/R) and that it is never printed for the customer.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first clause and no sentence is pure filler, but the middle is heavily nested with parentheticals (the marketplace net-payout aside, the internal-fix caveat) that make it slow to parse. One or two of the corrections could be a short structured list rather than comma-stacked prose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only, 4-parameter, no-output-schema tool this is fully sufficient: the description documents the returned fields (`lines`, `document`, `payment`, `on_account`, `balance`, `closingBalance`, `period`, `more`), their ordering, the sign convention for credit notes, and pagination behavior — so the agent can consume the result without an output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50% (from/to have pattern but no description), and the description fully compensates: `from`/`to` are YYYY-MM-DD with explicit inclusion rules and the open-ended defaults, (`customerId` vs `customer`) are given as an either/or with a resolution path for the name variant. A reader does not need the schema to understand the parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opening clause gives a specific verb+resource with a crisp equivalence: 'One customer's statement of account — the same rows, running balance and closing figure as the Statement PDF on the customer's page.' This distinguishes it from ar_aging (aggregate aging) and open_invoices (outstanding docs only) without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It names the required precondition (give `customerId` from resolve_customer, or a name resolved like open_invoices) and tells the agent what to do on ambiguity ('ask which one'). It also spells out the conditional behaviors of `from` and `to`. What's missing is explicit routing versus siblings — nothing says when to prefer ar_aging or open_invoices over this statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
daily_briefDaily briefAInspect
Your morning brief in one call: today's and this-month's sales & expenses, cash position (cash.total is LIQUID money only — cash + banks + gateway float + the Shopee Wallet (held by Shopee); any owner advance comes back separately as cash.ownerFunding (for a sole proprietor or partnership only a pre-business-type Shareholder Loan balance, flagged fromBeforeBusinessType — never money the business owes the owner), and any credit-card debt as cash.cards / cash.cardsOwing — both are liabilities that must never be added to, or netted against, the cash total), who owes you (top debtors + overdue buckets — the buckets count invoices only; receivables.unmatchedPayments, when present, is customer money received but not yet applied to an invoice (a payment applied only in part counts what is left), and receivables.depositsForLaterInvoices, when present, is customer money received before the invoice it is already matched to (a deposit: it sits on their account until the invoice's date and pays it then — do not suggest matching it); both are negative and already netted inside receivables.total), what's waiting for your approval (approvals.total counts DECISIONS, not rows: a real approval contributes its item count, while an operational to-do like "listings to link" counts as 1 however long its backlog — read each item's own count for the size. A STAGED SET (staged_documents, staged_products, …) is one decision too, so its count is always 1: read rows for how many are actually waiting, flagged for how many need a human look, and fenced for how many cannot be entered while the accounting start date stands where it is (they are dated on or before it, so the opening balances already carry them). A fenced row is not work the owner can finish on this queue: the remedies are to leave it out, or to move the accounting start date — after which Taokeh re-checks the fence and the row becomes enterable), tax position, and low stock. Assembles the same figures as the individual tools (business_snapshot, cash_position, ar_aging, tax_position, low_stock) plus your pending-approval queue, so "brief me" is a single read.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description repeatedly frames the tool as read-only: 'single read,' 'assembles the same figures,' and 'brief me.' However, annotations declare readOnlyHint=false, directly contradicting that safety profile. This inconsistency overrides the otherwise detailed field caveats.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a dense wall of parenthetical caveats for a zero-parameter tool, with key semantics buried mid-sentence. It is front-loaded with the morning-brief purpose, but the structure makes it hard to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description must carry return-value semantics, and it does document many fields and caveats such as liquid cash, liabilities, unmatched payments, approval counts, and staged sets. It still does not describe the overall response shape or clarify open-world behavior, so it falls short of 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero input parameters, so the baseline is 4. The lengthy notes concern output fields and accounting caveats rather than parameter meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific aggregated resource, the morning brief, and enumerates the figures it assembles: sales and expenses, cash position, debtors, approvals, tax, and low stock. It also distinguishes itself from siblings by naming business_snapshot, cash_position, ar_aging, tax_position, and low_stock as the individual tools it replaces.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a clear trigger with phrases like 'Your morning brief in one call' and 'brief me is a single read,' and it names the individual alternatives it replaces. It does not state explicit when-not-to-use conditions or limitations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
draft_bank_classificationSuggest bank-row categories (human reviews in Taokeh)AInspect
Propose a category (and optionally a contact) for up to 50 bank rows still waiting for review — imported statement lines AND rows the owner typed in by hand, which land as UNCATEGORIZED — get their ids from bank_review_queue. PROPOSALS ONLY: this writes a suggestion the owner sees as an 'AI suggestion' badge on the Banking → Review screen; it never confirms, never posts, and nothing you send here moves money — the owner reviews and posts every line in /banking. Only SUGGESTED/UNCATEGORIZED rows accept a proposal (already-decided CONFIRMED/POSTED/IGNORED rows are rejected, and so is a statement line the owner paired with a book entry on Clear transactions — it is already in the books). A row that looks like a book entry still on no statement is accepted but comes back with possibleTwin (kind 'bookEntry') and a warning: propose a clearing (create_bank_clearing_draft) for it instead. possibleTwin is {entryId, ref, name, date, kind}, as on bank_review_queue: the warning already names the entry as the owner knows it; call it by name (null: "a bank entry" when ref starts BT- or REV-BT-, else "a book entry" — for kind 'cardLinePosted', "a card statement line"), never by a BT- code. A credit-card line that looks like the card bill already paid into the card comes back with possibleTwin (kind 'cardBill') and a warning: the owner should exclude it — there is no clearing for a card. A card payment whose card-statement line is already posted comes back with kind 'cardLinePosted': posting both would count the bill twice. category must be one of the review screen's own set. contactName applies only to CUSTOMER_PAYMENT (a customer) or SUPPLIER_PAYMENT (a vendor) and must match an EXISTING contact exactly — an unresolvable name rejects that row (create the contact first, or omit it). Send contactName whenever you know the payer: a CUSTOMER_PAYMENT or SUPPLIER_PAYMENT proposed without one is accepted, but Taokeh will not POST it until the owner picks the customer or supplier, because a payment with nobody on it would sit in receivables or payables under nobody. accountCode proposes the LEDGER ACCOUNT for the row (e.g. a hosting bill to 6500) and is accepted ONLY for the three categories whose account the owner picks by hand — EXPENSE, OTHER and INTERNAL_TRANSFER. EXPENSE is a plain expense paid by card or bank, no contact (it is not a supplier payment and never touches payables), and its accountCode must be one of the expense accounts expense_accounts lists. On a money-IN row EXPENSE is an EXPENSE REFUND — money that came back from something bought (a merchant refund on a card statement, say) — and books the card or bank against that same expense account, reducing the expense; propose it only when the line really is a refund of a purchase, never for a customer's payment. OTHER and INTERNAL_TRANSFER never take a receivables/payables control, a contra or clearing account (1016 Contra Clearing, Accumulated Depreciation, the gateway clearing account), Inventory, Opening Balance Equity or Retained Earnings — the owner's picker does not offer them either; OTHER never takes a bank, card or cash account (that is an INTERNAL_TRANSFER), and INTERNAL_TRANSFER takes ONLY one of them (a sales, expense or equity account is OTHER, or EXPENSE for money spent). On a money-OUT row OTHER never takes an account a loan on the Financing register uses: money out to a loan is recorded only through the loan register, so propose LOAN_PAYMENT with no accountCode and the owner picks the loan and the interest on that line's Loan payment panel (money IN to a loan's account, a loan received, is taken). An OWNER_WITHDRAWAL on money out is refused while the account Taokeh would repay first (2400, when no Shareholder Loan account is mapped) carries a loan on the register (never for a sole proprietor or partnership, which books it whole to Owner's Drawings, and an OWNER_DEPOSIT to Capital Contribution) — the owner's Post refuses one that would repay that loan — and the reply names the loan and the two proposals that work: LOAN_PAYMENT (no accountCode) if the money repays that loan, or OTHER with the drawings account if it is a drawing. An Owner withdrawal you proposed before that state is shown on Banking → Review as the loan fence places it (bank_review_queue's yourProposal.shownOnReviewAs) and is never batch-accepted. INTERNAL_TRANSFER never takes a credit-card account for money going out of a bank line — paying a card off is CARD_PAYMENT with that card's code — and on a card's own statement, money in from a bank account is the card bill being paid: it is never posted from the card side, so the owner excludes that line (the bank line that paid it posts as CARD_PAYMENT). A balance transfer between two cards is posted from the CHARGE line (money out, on the card that paid); the paid-off card's money-in line is never posted either, so the owner excludes it. Every other category already books against a fixed account (a customer payment to receivables, a bank charge to bank charges, and so on), so sending accountCode with one is rejected and the reply names the account that category already carries. The code must exist in this company's chart (expense_accounts lists the expense side); the line's own bank or card account is refused, because that would post the money against itself (under OTHER every bank, card and cash account is refused — that move is an INTERNAL_TRANSFER). CARD_PAYMENT is the one category that REQUIRES accountCode, and takes only a credit-card account: it means "this bill settles that card", so propose it with the card's code or not at all — a card payment is never an expense, and naming an expense account (or naming a card account under OTHER) is refused, because the card's spending was already booked line by line from the card statement. The account you propose arrives PRE-SELECTED in the owner's account dropdown on Banking → Review, marked as proposed by you — it saves them hunting the chart, it does not decide anything: if they have already picked an account themselves, theirs stands. allocations proposes WHICH invoices or bills the money settles — the question Taokeh's own matcher hands to a human whenever the payment is partial, spans several documents, or could clear more than one combination, and the one you may already know from a remittance advice or the payer's own message. Send it ONLY with CUSTOMER_PAYMENT on a money-IN row or SUPPLIER_PAYMENT on a money-OUT row, and ONLY together with contactName, because every line is checked against THAT contact's live open documents: up to 20 entries of { docNumber, amount }, where docNumber is the document number as Taokeh shows it (open_invoices / ap_aging list them). EVERY amount is in RINGGIT, and specifically the outstanding balance as Taokeh reports it on those tools — never the document's own foreign-currency figure, and never its gross total where the two differ; a foreign-currency invoice is settled in Banking, not here. The amounts must add up to the WHOLE line within 0.10 — a proposal that leaves part of the deposit unexplained is refused with the exact shortfall, never trimmed to fit — no line may exceed what its document still owes, and an unknown number, a duplicate, a FOREIGN-currency document dated AFTER the payment, or an OPENING invoice or bill for a line dated on or before the accounting start date, is refused too (a ringgit INVOICE or BILL dated after it is accepted: a customer's deposit, or an advance paid to a supplier, which sits on that party's account until the document's date and pays it then); the refusal names that contact's own open documents with their outstanding amounts, so you can correct it in one more turn. What it does is PRE-FILL: the owner's Match pane opens with those documents ticked and those amounts entered, under a banner saying you proposed it and quoting your note. It settles nothing — the owner reads it and clicks Save — and if a document has been paid or part-paid since you proposed it, the pane says your proposal no longer fits and fills in nothing rather than allocating a stale figure. It never reaches the one-tap Accept on the payments list either; that button only ever applies Taokeh's own exact match. note is your stated reason (≤300 chars), shown to the owner — write it well, because the owner reads it when deciding. Your proposals are also collected into a 'Proposals from your AI' panel on Banking → Review, where the owner can accept a whole batch of them in one tap after reading them grouped by category with your reasons; that acceptance sets the category and contact only and is still the owner's decision — it never posts to the ledger. If a row's existing suggestion says "You set this rule on ", the OWNER has written a standing rule for that payer (What Taokeh has learned → Bank rules) — proposing something different is allowed and is sometimes right, but say in your note that you are contradicting their own rule, and expect them to keep the rule. A proposal on a foreign-currency account's line is saved but comes back with a warning: Taokeh does not post or match those lines yet. An accountCode that is a foreign-currency bank or card (as a transfer or card-payment target) is rejected with the reason: Taokeh posts only ringgit accounts for now. Per-row outcomes are reported — nothing is silently skipped. A proposal that books the line to the Shareholder's Loan (OTHER with the loan's code) can be refused at Post when it would leave a posted owner withdrawal over-repaid (or, for a sole proprietor or partnership, a reclassified date no longer nil): the row then comes back applied with that sentence as a warning.
| Name | Required | Description | Default |
|---|---|---|---|
| suggestions | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations mark this as a non-read-only, non-destructive write, and the description adds substantial safety context beyond that: it writes only an 'AI suggestion' badge, never confirms or posts, never moves money, and reports per-row outcomes rather than silently skipping. It also discloses warnings, rejection reason patterns, and owner-review behavior, which is unusually complete for a write-style tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and safety constraints are front-loaded, but the body is an enormous single paragraph mixing drafting rules, refusal conditions, UI behavior, and edge cases. Much of the content is valuable for this complex tool, but the wall-of-text structure and repeated re-statements of 'proposals only' and owner-review behavior make it harder to scan than necessary.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, and the description compensates by explaining per-row outcomes, possibleTwin shapes, warnings, and what the reply names on rejection. Given the complexity of accounting-side effects and owner review, it leaves very little an agent would need to infer before calling the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry all parameter meaning; it covers category constraints, contactName applicability and matching rules, accountCode requirements by category, allocations format and ringgit/outstanding-balance semantics, note length, and the transactionId source from bank_review_queue. It also explains the 50-row cap and allocation limits beyond what the bare schema shows.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: propose a category/contact for up to 50 bank rows waiting for review, sourced from bank_review_queue. It explicitly distinguishes the tool from create_bank_clearing_draft for twin book entries, and from the owner's posting action, so an agent can tell exactly what this tool does and does not do.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use and when-not-to-use rules: only SUGGESTED/UNCATEGORIZED rows accept proposals, confirmed/posted/ignored rows are rejected, paired statement lines are rejected, and alternative paths such as create_bank_clearing_draft or owner exclusion are named. It also routes specific twins and card scenarios to the correct alternative behavior rather than leaving them ambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
employer_cost_estimateEmployer cost estimateARead-onlyInspect
Cost a HYPOTHETICAL hire: give a monthly wage and get back what that employee would really cost the employer each month — the wage plus employer EPF, SOCSO, EIS and the HRD Corp levy at this company's own rate — and what the employee would actually take home after employee EPF, SOCSO, EIS, SKBBK and PCB/MTD. Use it for "if I hire someone at RM4,000, what does it really cost me?" and "what would they take home?". SKBBK (PERKESO Lindung 24 Jam) is included for every hire: mandatory for a foreign worker, and deducted by default for local staff (citizens, permanent residents, temporary residents), who could opt out with PERKESO only until 31 Aug 2026. Optional: residency (RESIDENT, the default, or NON_RESIDENT — a non-resident is taxed at a flat 30%), citizenship (MALAYSIAN or FOREIGNER — for a FOREIGNER who is not a permanent resident, EPF is 2% employer + 2% employee (KWSP Third Schedule Part F, from October 2025 wages) and there is no EIS unless they are a temporary resident, worked out on at least the RM1,700 minimum wage; it also drives the HRD Corp levy; it does not change tax residency, so set residency too), permanentResident (FOREIGNER only: true for a Malaysian permanent resident, who pays the Malaysian EPF rates (KWSP Third Schedule Part C from 60: 5.5% employee, 6.5% / 6% employer, not a citizen's 0% / 4%) and EIS), temporaryResident (FOREIGNER, not a PR: true for a temporary resident holding a MyKAS, the green temporary-resident IC — EPF stays on Part F, but EIS applies at the Malaysian rates), ageBand (UNDER_60 or 60_AND_OVER — reduced EPF (0% / 4% for a citizen, Part C for a permanent resident) and SOCSO Category 2), and epfEmployeeRate if the employee elects a reduced EPF rate (for a foreign worker on Part F, only a rate above 2% elected on KWSP Form 17A changes their share; the employer stays at 2%). The PCB comes from Taokeh's implementation of LHDN's official computerised MTD method — the calculation IRBM confirmed in writing on 13 August 2026 (letter ref 2026-256) — not from a simplified formula. The defaults are RESIDENT, single, no children and NO TP1 reliefs, because reliefs are per-employee paperwork nobody has filled in for a person who does not exist yet; every assumption is spelled out in the reply and MUST be repeated to the user. Present it as an estimate on stated assumptions, never as a quote or as tax advice. READ-ONLY — it creates nothing, hires nobody and files nothing. ADMIN AND BOOKKEEPER + PAYROLL ONLY, like every payroll tool here.
| Name | Required | Description | Default |
|---|---|---|---|
| ageBand | No | ||
| residency | No | ||
| citizenship | No | ||
| monthlyWage | Yes | ||
| epfEmployeeRate | No | ||
| permanentResident | No | ||
| temporaryResident | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint and closed-world, and the description reinforces rather than contradicts this: "creates nothing, hires nobody and files nothing." Beyond that it discloses the default assumption set (RESIDENT, single, no children, no TP1 reliefs) and why, and imposes a binding behavioural rule that every assumption in the reply must be restated to the user and not framed as a quote or tax advice.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first sentence, but the body is a single dense run-on block with nested parentheses, em-dashes and statutory references, and no formatting to separate defaults, parameters and conduct rules. Much of the tax-schedule detail earns its place given zero schema coverage, but the packaging makes it hard to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a seven-parameter calculation tool with no output schema, the description supplies both halves of the return value (employer cost components and employee net pay), the PCB methodology and its provenance, all defaults, and the access and framing obligations. Nothing an agent needs to call it and present the result correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description carries the full burden, and it does: every one of the seven parameters is explained — residency taxation effect, citizenship's effect on EPF/EIS/HRD levy, permanentResident's Third Schedule Part C rates, temporaryResident's MyKAS/EIS treatment, ageBand's reduced EPF and SOCSO Category 2, and epfEmployeeRate's Form 17A constraint. It also flags the non-obvious interaction that citizenship does not change tax residency, so both must be set.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — cost a hypothetical hire, returning employer monthly outlay (wage + EPF, SOCSO, EIS, HRD Corp levy) and employee take-home after statutory deductions. It is unambiguously distinct from the sibling payroll tools (payroll_summary, update_employee_draft), which concern actual payroll data rather than hypothetical costing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete triggering questions ("if I hire someone at RM4,000, what does it really cost me?") and states the audience restriction (admin/bookkeeper + payroll only) and that results must be presented as an estimate with assumptions repeated. It stops short of naming an alternative tool or an explicit when-not-to-use condition, so it is clear context without full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
expense_accountsExpense accountsARead-onlyInspect
The expense accounts you can post to in this company — pick a category (what the money was for) and a paid-from account (where it came from) by code, for shaping a paid-expense draft. expenseCategories is a CURATED list: expense and asset accounts only, with cash/bank, the A/R and Inventory controls and contra accounts left out, because a cost never lands in one of those — and without the accounts only Taokeh's own engines book to (depreciation, foreign-exchange losses, loss on disposal, cash rounding, and salaries/employer statutory once the company runs payroll in Taokeh). It is the everyday set, not the whole rule. When the draft is filed, a CATEGORY code is rejected only if it is not an expense or asset account, or is a cash/bank, control or contra account; an engine account left off the list (e.g. depreciation) is still accepted by code, so use one only when the receipt really is that cost. The paid-from code has its own rule: any asset, liability or equity account except the A/R and A/P control accounts, Taokeh's own zero-balance clearing accounts (Contra Clearing, Refund Clearing — left out of paidFromAccounts and refused) and a foreign-currency bank or card (Taokeh posts only ringgit accounts for now, so paidFromAccounts leaves those out and a draft naming one is refused). MONEY THE OWNER PAID PERSONALLY ("I paid with my own money"): use ownerPocketCode as the paid-from — Capital Contribution for a sole proprietor or partnership, the Shareholder's Loan otherwise. A sole proprietor or partnership has no Shareholder's Loan: paidFromAccounts leaves it out and a draft naming it is refused. When ownerPocketCode is null, ownerPaidRefusal says what the owner must fix first — file no owner-paid draft until then.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint=false; the description goes far beyond by disclosing the curation rule for expenseCategories and paidFromAccounts, the exact conditions under which a code is rejected, the rationale for omitted engine/clearing/foreign-currency accounts, and the owner-paid refusal semantics via ownerPocketCode/ownerPaidRefusal.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded, but the body is a dense multi-clause run-on with heavy parentheticals and some restatement (clearing/refused accounts are explained in more than one place). Most content is rule-relevant, yet a sentence or two could be cut without losing correctness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no parameters and no output schema, the description carries the full burden and does so: it explains what the tool yields, which accounts are included/excluded and why, how codes are validated, and how owner-paid drafts are handled. An agent needs nothing else to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema is empty (0 params), so the baseline is 4. The description does not add input-parameter meaning, but it compensates by explaining the semantics of the returned members (expenseCategories, paidFromAccounts, ownerPocketCode, ownerPaidRefusal), which is useful context rather than parameter documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource — 'the expense accounts you can post to in this company' — and its use: picking a category and paid-from account by code for shaping a paid-expense draft. An agent can tell it apart from the draft/creation siblings, but it never states explicitly that this is a read-only lookup returning lists, leaving the action slightly inferred.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear context (use for shaping a paid-expense draft; use an engine account only when the receipt really is that cost) and strong when-not guidance on excluded accounts and refused codes. It does not explicitly name a sibling tool or say 'call this before create_expense_draft', so routing is implied rather than spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
expenses_summaryExpenses summaryARead-onlyInspect
How much you have spent: your paid expenses today and this month so far.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint=true, and the description adds useful scope information: only paid expenses are counted, and the time window is today and month-to-date. However, it does not describe the return format, whether the result is a single total or separate numbers, or behavior when there are no expenses.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence, front-loaded with the core question 'How much you have spent', and contains no filler or redundant wording. Every word contributes to meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter read-only summary tool, the description provides sufficient detail to understand the tool's purpose and invoke it correctly. The lack of an output schema means the description could ideally clarify the return shape (e.g., two totals vs. one), but this is a minor gap for such a simple tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and schema description coverage is 100%, so there is no parameter burden for the description to carry. The baseline of 4 applies because the schema fully covers an empty parameter set.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool as a summary of paid expenses, with a specific scope (today and this month so far). It uses a clear 'have spent' action on the 'expenses' resource and is distinguishable from siblings like create_expense_draft or search_expenses, though it does not explicitly name alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for checking how much has been spent today and this month so far, but it does not state when to use this tool versus alternatives, nor does it provide any exclusion criteria. The context is clear enough but not explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
financial_timeseriesFinancial time series (monthly)ARead-onlyInspect
A MONTH-BY-MONTH series of this company's figures across a window you name — the one call to make when you are asked to analyse a year or three years of trading. NEVER call income_statement 36 times and stitch the months together yourself: ask here once. You get, per month: revenue, cogs, grossProfit, opex, netIncome, cashClose, arClose, apClose and netCashMovement, in MYR. Give from and to as YYYY-MM (inclusive). The window is capped at 36 months — ask for a narrower window, or several windows, rather than expecting more. metrics narrows what is computed: any subset of pnl, cash, ar, ap, cashflow (default all); a metric you leave out has its fields OMITTED from every month, never returned as zero. THREE THINGS YOU MUST READ BEFORE YOU INTERPRET IT. First, a month that has not finished yet comes back with partial: true and a through date: its figures cover only the days up to and including that date — TODAY — and not the rest of the month, so they are month-to-date. Say so, and do not let the last point bend a trend. Second, months that ended BEFORE this company's accounting start date are left OUT OF THE SERIES, not zeroed: that history is still in the old system, and a note names the cutover date — this is a rule about the series, not about the ledger, because back-dating is allowed here, so a report run over an earlier window on /reports may still show stray entries before that date — and the month CONTAINING that date is partial: true with startsAt, because its trading only begins there; it is incomplete on the left the way a running month is on the right, never a weak month. Third, months that have not started yet are ABSENT too. An absent month is missing or unlived history, never a month with no business — never read one as a collapse in trading. EQUAL BASIS: every figure is produced by the same engine the web report uses for the exact dates stated (the month, or the month up to through when it is partial) — the P&L from /reports, cashClose from the cash-position liquid set (cash, banks, gateway clearing; owner advances and credit-card debt are not cash and are not in it), AR/AP from the aging reports' totals as of month end, netCashMovement from the cash-flow report's net. One caveat to carry: netCashMovement and the month-on-month change in cashClose are measured on DIFFERENT account sets, so they can legitimately differ — neither is a check on the other. ONE CALL ALSO HAS A TIME BUDGET: a very wide window with every metric can exceed it, in which case the call is REFUSED (it says how many months it managed) rather than answered with a short series — ask again for fewer months or fewer metrics. This tool returns FIGURES ONLY. It computes no ratios, no growth rates, no trend labels and no commentary — the reading is yours to write, from these numbers and the notes.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | ||
| from | Yes | ||
| metrics | No | Which families of figures to compute. OMIT for all five; do not send an empty list. A family you leave out is omitted from every month, not zeroed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description is exceptionally transparent: it explains partial months with `partial: true` and `through`, absent months and the cutover rule, the equal-basis engine behind the figures, the netCashMovement vs cashClose discrepancy, the time-budget refusal behavior, and the fact that it returns figures only with no interpretation. The readOnlyHint annotation is consistent with this being a read-only computation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but the length is justified by the tool's many edge cases. It is well-organized into labeled sections (THREE THINGS YOU MUST READ, EQUAL BASIS, TIME BUDGET, FIGURES ONLY), which helps an agent parse the critical caveats. A few phrases could be tightened, but nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema, the description names every field returned, explains how each is computed, clarifies partial/absent/cutover behavior, states currency, and documents failure modes. An agent has everything needed to interpret the response correctly and avoid common misinterpretations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 33%, so the description carries the burden for `from`/`to` and it does: YYYY-MM format, inclusive, 36-month cap, and MYR currency. For `metrics`, it adds that leaving a metric out omits those fields entirely and never zeroes them, going beyond the schema's basic enum description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific statement: a month-by-month series of company figures across a user-named window. It also differentiates itself from income_statement by explicitly saying to request this once instead of stitching 36 months together, making the tool's role unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives direct when-to-use guidance: use this for analyzing a year or three years of trading, and never call income_statement 36 times. It also advises asking for narrower windows or fewer metrics when the time budget is exceeded, covering the main alternative and constraint behavior.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_in_taokehFind in TaokehARead-onlyInspect
A FEATURE FINDER, NOT A RECORD SEARCH. It searches Taokeh's own SCREENS, FEATURES and SETTINGS — never the books. Use it to tell the user WHERE in Taokeh to do something or change a setting: it returns the exact page(s) and what each one does, grounded in Taokeh's LIVE feature catalogue, so you never guess or invent navigation. ⛔ It does NOT look up documents, invoices, customers, products or any other RECORD, and it cannot tell you it failed to — a document number, a customer's DO/PO number, a person's name or an amount typed in here will still rank some page (the closest-worded screen), and that page will be a wrong answer wearing a right one's clothes. Records live elsewhere: search_documents (invoices, credit notes, quotes, delivery orders, bills, POs — and a customer's own Ref No), search_expenses (posted paid expenses), search_journal (ledger entries), resolve_customer / resolve_vendor / resolve_product (a named party or item), open_invoices (what one customer owes). Give a natural-language query about a CAPABILITY ("where do I turn on payment reminders", "how do I connect Shopee", "where's the SST setting") and it ranks the real features and hands back the top matches, each with the page's absolute deep-link URL(s), a one-line description of what the page does, an optional configHint (Taokeh keeps a feature's options on its OWN page, not in a global Settings menu), and an addOn flag (a paid add-on this company may not have). It GUIDES to the UI only — it changes nothing: no setting is ever toggled by the connector, that stays a human action in-app. If nothing matches it says so honestly — then tell the user to browse the left sidebar or contact support; never make up a path.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | What the user wants to DO or the setting they want to CHANGE, in words — not a document number, a customer name or an amount (those are records; use search_documents / resolve_customer instead). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint=true annotation, the description discloses the tool's failure mode (it will rank some page even for record-like input and return a wrong answer), its output shape (deep-link URLs, one-line page description, configHint, addOn flag), and guarantees that no setting is ever toggled. This is exactly the contextual behavior an agent needs to trust results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core distinction and each sentence carries substantive guidance, but it is long and repeats the 'not a record' warning more than once. The density and warning are justified by the tool's deceptive failure mode, though it could be tightened slightly without loss.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema present, the description fully compensates by enumerating the return elements (page URLs, descriptions, configHint, addOn flag), covers edge behavior (nothing matches), and provides alternative routing. Nothing an agent needs to use it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents the query parameter at 100% coverage, giving the baseline 3. The description adds value by clarifying it must be a natural-language CAPABILITY query, providing concrete examples, and reinforcing what it must NOT be (document/customer/amount), so the parameter semantics are further strengthened.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb phrase ('A FEATURE FINDER, NOT A RECORD SEARCH') and names its exact scope: Taokeh's screens, features, and settings. It explicitly distinguishes itself from record-search siblings by stating what it is not, so an agent can immediately tell find_in_taokeh apart from search_documents, search_expenses, etc.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance ('tell the user WHERE in Taokeh to do something or change a setting') and equally explicit when-not-to-use with a detailed list of alternative tools for records, including what each alternative covers. It even instructs the fallback behavior when nothing matches.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
general_ledgerGeneral ledgerARead-onlyInspect
The general ledger for a date range — every account, or ONE account with accountCode — the same figures as Reports → General Ledger and its CSV. Per account: opening (its balance before from, from the first entry — there is no year-end close), the posted rows in date order (each: date, reference, memo, debit, credit, the running balance after it, and journalEntryId), totalDebit / totalCredit for the range, movement and closing. Balances are signed by the account's statement section (positive = a debit balance for assets and expenses, a credit balance for liabilities, equity and revenue), so closing equals that account's balance in trial_balance as of to. An account with no line in the range and a zero opening is left out and counted in accountsOmitted. ROWS ARE CAPPED: at most 300 rows come back across all accounts, in account-code then date order. When more is true the rows were cut short — but every account's opening, totals and closing are STILL EXACT, and hiddenRows on an account says how many of its lines are not shown. Never add up the rows you got as if they were the whole range; narrow the dates or ask for one accountCode to see the rest.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | ||
| from | Yes | ||
| accountCode | No | One account code (exact, e.g. "1010"). Omit for every account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes far beyond the readOnly/openWorld annotations: discloses the 300-row cap, the `more` flag, per-account `hiddenRows`, `accountsOmitted` for empty accounts, the fact that totals stay exact under truncation, sign conventions by statement section, and that there is no year-end close. This is exactly the kind of non-obvious behavior an agent cannot get from the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense but front-loaded: the what, the per-account shape, then the row-cap caveat and the corrective action. Nearly every sentence carries real information; the return-shape enumeration is long, though it is offset by there being no output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully specifies the return shape (opening, rows, totalDebit/totalCredit, movement, closing) and the truncation contract, so an agent can both call and correctly interpret the result. Nothing material is missing for correct use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 33% (only accountCode is documented), so the description carries the burden: it explains that omitting accountCode returns every account, that one accountCode narrows results, and it gives meaning to `from`/`to` via the explanation of `opening` (balance before `from`) and totals for the range. It stops short of stating the date format or inclusive/exclusive boundary behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource (general ledger) with a precise scope (a date range, all accounts or one via accountCode) and anchors it to a known surface (Reports → General Ledger and its CSV). It also distinguishes itself from trial_balance by stating closing equals that account's trial_balance balance as of `to`.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives actionable routing advice — narrow the dates or pass one accountCode to see truncated rows — and implied context for when to use it (per-account detail with running balances). It does not explicitly contrast with siblings like trial_balance or balance_sheet for choosing between them, which keeps it below 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_attachmentGet original document linkARead-onlyInspect
Get a short-lived link to the ORIGINAL DOCUMENT filed against something in the books — the receipt behind a posted expense, or the receipt/sales note/supplier bill/bank-in slip/name card riding a PENDING draft. Use it to CHECK a document is on file, or to re-read one you filed earlier. Find what to ask for first: search_expenses reports hasAttachment and an attachments list (with an attachmentId) on every posted expense it returns, and each create_*_draft / revise_draft result reports whether an original rides that draft. Give owner plus the id of the thing that owns it — for a posted expense that is the expenseId from search_expenses, plus the attachmentId when the entry carries more than one file; for a draft it is the draftId. Returns a signed URL (about 15 minutes, works for anyone holding it — so treat it as you would the document itself), the filename, the media type and the size in bytes; the bytes themselves are NEVER inlined here, because a base64 blob in a tool result costs about one token per character. Download it and read it yourself: Taokeh does not read, OCR or interpret the file for you — your own AI does that, on your own subscription. Read-only; it changes nothing, emails nobody, and no link it hands out can reach another company. An APPROVED draft honestly reports no original: on approval the file moves onto the posted document, so ask for it there instead.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | The owner's id — the expenseId from search_expenses for 'expense', otherwise the draftId a create_*_draft or revise_draft call returned. | |
| owner | Yes | What the original hangs off: 'expense' = a POSTED paid expense (use the expenseId from search_expenses); 'expense_draft' / 'invoice_draft' / 'bill_draft' / 'quote_draft' / 'receipt_draft' / 'payment_draft' / 'contact_draft' = a PENDING draft (use its draftId); 'shoebox' = a photo someone in the business sent in from their phone and nobody has read yet (use the id from shoebox_items). | |
| attachmentId | No | Which file, when a posted expense carries more than one — the `attachmentId` from that expense's `attachments` list in search_expenses. Omit for a draft (a draft carries at most one original), and omit for an expense with exactly one file. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
It discloses that the returned link is short-lived (about 15 minutes) and that anyone holding it can access the document, adding a security caution beyond the readOnlyHint annotation. It also identifies the return items (signed URL and filename). No contradiction with the read-only annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is verbose and ends with a nonsensical repeated phrase 'the filename, the filename, ...' which wastes tokens and confuses the return-value statement. While the main content is ordered logically, the malformed tail and excessive examples hurt conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers purpose, prerequisites, parameter sourcing, and a security caveat, which is fairly complete for a read-only retrieval tool. However, the corrupted tail leaves the exact return payload unclear (only 'the filename' is restated), and error behavior (e.g., no original on file) is not addressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already describes all three parameters at 100% coverage, and the description adds cross-tool guidance on where to find the correct ids (expenseId from search_expenses, draftId from drafts, attachmentId from attachments list). This enriches the schema beyond generic field descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence explicitly states the tool's action: 'Get a short-lived link to the ORIGINAL DOCUMENT filed against something in the books.' It also distinguishes between posted expenses and pending drafts, making the target resource unambiguous. Even with the malformed tail, the central purpose is clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description tells the agent when to invoke it ('to CHECK a document is on file, or to re-read one you filed earlier') and how to prepare inputs using search_expenses and create_*_draft results. It lacks an explicit 'when not to use' clause or named alternatives, but the usage context is strong.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_document_pdfGet document PDF linkARead-onlyInspect
Get a shareable link to the ACTUAL PDF of one invoice, quote or credit note — so you can hand the user (or their customer) the real document, e.g. "here's your invoice". Identify the document by its docId (from search_documents) OR its printed docNumber (e.g. the invoice number) — give one; if a number matches several documents I'll list them so you can pick the id. Returns a short-lived signed URL (works for ~15 minutes, for anyone who has it — so share it deliberately), the document number, and when it expires. Covers invoices, quotes and credit notes (they share one document layout). A CANCELLED document still has a PDF, stamped: a voided quote or a voided CREDIT NOTE comes back with a large diagonal VOID across every page, so it can be handed over as the record of what was cancelled but never passed off as live — say so when you share it, and never quote its amount as money owed or credited. On a voided credit or debit note the PDF also states it in WORDS, not only the watermark (2026-09-14): its header box reads Status VOID with no payment term or due date, the total is labelled what it WAS ('Total credited' / 'Total charged') in a muted bar rather than the usual Grand Total, and a line beneath reads 'Nothing is credited now — this credit note was voided on '. So the document says the thing you must say; quote THAT, not the figure. search_documents finds a voided credit note too and reports its status as "Credit note (VOID)"; read that before you describe the document. (A voided INVOICE is the exception — voiding one removes it from the books entirely, so there is no link left to give and search_documents will not find it either.) LANGUAGE: the PDF's printed words (Bill to, due date, column headings, totals) follow the company's Document language setting — the same setting covers the company's invoice, quote, delivery order, credit and debit note and purchase order PDFs — — English unless the owner chose Bahasa Malaysia on Settings → Preferences — so a voided note's sentence may read in Malay; names and descriptions always print as typed. It is not the app language, and you cannot change it per link. This does not email anyone — it hands YOU a link to pass on.
| Name | Required | Description | Default |
|---|---|---|---|
| docId | No | The document id (from search_documents). Give this or docNumber. | |
| docType | Yes | Which document: 'invoice', 'quote' or 'credit_note'. | |
| docNumber | No | The printed document number (e.g. the invoice/quote reference). Give this or docId; resolved case-insensitively and must match exactly one document. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint; the description adds substantial behavior the agent could not otherwise know: the URL is signed, short-lived (~15 min), and shareable by anyone holding it. It discloses that voided documents still produce stamped PDFs (diagonal VOID, 'Status VOID' header, 'Total credited/charged' labels, an explicit sentence in the PDF), that voided invoices have no link at all, and that printed language follows a company setting. This far exceeds the annotation bar.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and no section is entirely off-topic, but the mass is disproportionate: the voided-document behavior is restated across several long clauses, there is a dangling dated parenthetical ('2026-09-14') and a doubled em-dash typo. The same facts could be conveyed in roughly a third of the words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates by naming the return payload (signed URL, document number, expiry) and covers the real edge cases: ambiguous numbers, voided invoices being unfindable, voided credit notes still resolving, and the language setting. Nothing needed to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description matches and slightly extends the schema: docId comes 'from search_documents', docNumber is the printed number, and it adds the ambiguity behavior — 'if a number matches several documents I'll list them so you can pick the id' — which the schema does not state. DocType is implicit in the description's enumeration of invoice/quote/credit note.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource+scope: 'Get a shareable link to the ACTUAL PDF of one invoice, quote or credit note.' It distinguishes itself from search_documents (the identifier source) and states exactly what it does not do ('This does not email anyone — it hands YOU a link'). An agent can pick it apart from siblings immediately.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear context for use — hand the real document to a user or their customer — and two explicit identification paths (docId from search_documents or docNumber). It also routes the agent to search_documents to read a document's VOID status before describing it. It does not contrast against other link/attachment siblings (get_attachment, request_attachment_upload), so it stops short of full alternatives coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
import_bank_statementImport bank statement into the review queue (nothing posts)AInspect
Import a bank statement into Taokeh for reconciliation, from a statement you've read (PDF/CSV/image). This does NOT post to the books — every row lands in Banking → Review for the user to categorize and post line by line. FOREIGN-CURRENCY ACCOUNTS: a statement for an account whose currency is not ringgit still imports, but Taokeh books in ringgit and refuses to post or match that account's lines for now (intake_contract(doc_type:'statement') lists each account's currency) — say so instead of telling the user to post them. A statement is the bank's own list of transactions for ONE account over a period: use intake_contract(doc_type:'statement') first to see the tenant's bank accounts + the arithmetic law. Give the account id, the printed opening + closing balances, the printed statement date (statementDate) and beginning-balance date (beginningBalanceDate) when the statement shows them, and every transaction row (signed amount: + money in, − money out). The server checks opening + the rows tie to the closing balance and rejects with the exact delta if they don't — never invent or omit rows to force it to balance. CREDIT CARDS: a card statement can be filed here too, but ONLY with signConvention declared, because a card prints the opposite convention (a purchase INCREASES what you owe). Pass 'PRINTED_CARD' when the figures are exactly as the card statement prints them (the usual case) or 'STORED' if you deliberately converted them to Taokeh's convention (− = a spend). Never guess: declaring the wrong one books the whole month backwards and the arithmetic check cannot catch it, because negating every figure still ties out — the server runs a separate directional check and refuses a declaration that reads as a card which never owed anything. signConvention is refused on a bank account. Each card line is then expensed on its OWN statement date against the card, and the "payment received" line that settles the card bill is excluded automatically (the bank statement's lump owns that movement). Single receipts, invoices or bills go through their own draft doors, not here. ARCHIVING THE ORIGINAL: this import archives the statement file WHEN you send one — call request_attachment_upload FIRST, PUT the statement's raw bytes to its uploadUrl, then pass the returned attachmentToken here; the original is then kept on the statement (s.82 record-keeping) and the user can download it from the statement page, exactly as the web upload at Banking → Import does. Without a token nothing is archived and the books carry rows with no source document behind them. Already filed one bare? Call this tool AGAIN with the IDENTICAL account, balances and rows plus attachmentToken — the identical reading matches the same statement, so the file is ADOPTED onto it and no rows are staged twice. Filed one before without statementDate / beginningBalanceDate? The same identical call with the two dates added records them on that statement, again staging nothing twice.
| Name | Required | Description | Default |
|---|---|---|---|
| rows | Yes | ||
| bankAccountId | Yes | ||
| statementDate | No | The statement date printed on the statement (the ENDING BALANCE date), YYYY-MM-DD — it is the end of the period the statement covers, even when the last transaction is earlier. Omit it only if the statement prints none. | |
| closingBalance | Yes | ||
| confirmOverlap | No | Set to true ONLY after the user has confirmed this is a genuinely new, separate statement whose period overlaps one already imported. It bypasses the overlap guard that stops the same transactions being counted twice. Never set it to push an overlap through — ask the user first. | |
| openingBalance | Yes | ||
| signConvention | No | REQUIRED when bankAccountId is a CREDIT CARD, and refused on a bank account. Which convention you signed the rows AND the opening/closing balances in. 'PRINTED_CARD' = exactly as the card statement prints them: a purchase is POSITIVE (what you owe goes up), a payment or refund is NEGATIVE, and the balances are the printed balances owed — this is the right choice when you read the card's own statement. 'STORED' = already converted to Taokeh's stored convention: a spend is NEGATIVE, a payment or refund POSITIVE, and the balances negative while money is owed. Do not guess between them: the wrong declaration books the entire month backwards, and the arithmetic check cannot catch it because negating every figure still ties out. Ask the user if unsure. | |
| attachmentBytes | No | The decoded byte size of the ORIGINAL file on disk — send it alongside attachmentBase64 and the server rejects a truncated paste instead of filing a corrupt file. | |
| attachmentToken | No | The token from request_attachment_upload, AFTER you have PUT the statement bytes to its uploadUrl. This is the normal way to archive a statement PDF — it carries the file out-of-band (no base64 in this call). Mutually exclusive with attachmentBase64. The file is archived on the statement and the user downloads it from the statement page in Taokeh. | |
| attachmentBase64 | No | The ORIGINAL statement as base64 — SMALL files only (a few KB). A real bank statement PDF is never that small, so in practice use request_attachment_upload + attachmentToken instead. Mutually exclusive with attachmentToken. A bad type/oversize file is rejected and NOTHING is imported. | |
| attachmentSha256 | No | The SHA-256 of the ORIGINAL file as 64 hex chars — optional second check alongside attachmentBase64, so corrupted bytes are rejected instead of filed. | |
| attachmentFilename | No | The original filename, for the user browsing their statements. | |
| attachmentMediaType | No | The attachment's MIME type, e.g. 'application/pdf'. Required when attachmentBase64 is given. It must match the file's bytes — a mismatch is refused and nothing is filed. | |
| beginningBalanceDate | No | The date on the BEGINNING (opening) BALANCE line, YYYY-MM-DD — the start of the period. Every row must fall between it and statementDate. Omit it only if the statement prints none. | |
| confirmSignConvention | No | Set true ONLY after the user confirms that the card genuinely sat in CREDIT for the whole period. It bypasses the directional check that refuses a statement reading as a card which never owed anything — the usual cause of which is a printed statement declared 'STORED' (or the reverse). Never set it to push a refusal through: ask the user first. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are only coarse defaults (readOnlyHint=false, destructiveHint=false, openWorldHint=false, idempotentHint=false), so the description carries the real behavioral burden and does: nothing posts, the server validates opening+rows against closing and rejects with the exact delta, foreign-currency accounts are refused for posting/matching, the card directional check runs separately because negating every figure still ties out, the card 'payment received' line is auto-excluded, and confirmOverlap/confirmSignConvention are guards not to be used to force a pass. The only nuance is the claim that an identical reading stages nothing twice, which reads as content-level dedup rather than the blanket idempotentHint flag — a soft tension, not a contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded well (purpose and 'nothing posts' come first) and every paragraph is on-topic, but it is a very long single block with heavy parentheticals and no structural breaks. Substantial content duplicates the schema almost verbatim, notably the signConvention PRINTED_CARD/STORED explanation and the confirmOverlap/attachmentToken guidance, which the rubric says earns no credit.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 15-parameter, non-read-only import with no output schema, the description covers the full lifecycle: intake_contract prerequisite, required reading of the statement, the arithmetic tie-out, credit-card convention traps, foreign-currency refusal, the archiving workflow, overlap handling, and re-call adoption. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 73% and the per-parameter descriptions are already rich (signConvention, confirmOverlap, statementDate, beginningBalanceDate, and the whole attachment family are documented in the schema), so the baseline is high. The description does add meaning the schema lacks: rows are 'signed amount: + money in, − money out', the balances are the printed opening/closing figures, and the validity constraint that opening + rows must tie to closing. Those additions are real but incremental relative to the schema's own text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource (import a bank statement) and states the destination and the key consequence up front: 'This does NOT post to the books — every row lands in Banking → Review.' It also distinguishes itself from siblings by explicitly excluding them: 'Single receipts, invoices or bills go through their own draft doors, not here.' An agent can tell this apart from create_expense_draft, stage_document, or draft_bank_classification without opening another schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is spelled out with prerequisites and named alternatives: call intake_contract(doc_type:'statement') first, call request_attachment_upload first when archiving, and use signConvention='PRINTED_CARD' vs 'STORED' depending on how the figures were read. It gives explicit when-not cases (single receipts/invoices/bills go elsewhere; foreign-currency accounts must be flagged rather than told to post) and describes the re-call/adoption path for already-filed statements.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
income_statementIncome statementARead-onlyInspect
Your profit and loss for a period: revenue, cost of goods sold, gross profit, expenses and net income. COMPARISON (optional): to answer "how does this month compare with last?" ask for it here — NEVER call this tool twice and subtract the figures yourself. Set compare to 'previous_period' (the window of EQUAL LENGTH immediately before this one) or 'same_period_last_year' (the same dates one year earlier), OR give an explicit earlier window with BOTH compareFrom and compareEnd. You get both periods' full statements plus, for every account line and every total, deltaCents and deltaPct computed server-side in integer cents. deltaPct is NULL whenever the earlier figure is zero — a percentage change from zero is undefined, so report it as "no comparable base", never as ∞% or 100%. Percentages are measured against the ABSOLUTE earlier figure, so a movement out of a loss keeps the sign of the movement. windows states both windows, each one's day count, whether they are the same length, and whether either is still open (running to today or beyond, so its figures can still move); notes says all of this in plain words. READ notes BEFORE quoting a variance: if equalLength is false you are looking at a partial period against a full one and must say so. PERIODIC STOCK: when the company is on periodic stock, sold goods that have a cost in the window, has cost of goods sold of RM 0 and has not counted its stock this financial year, the answer carries a notes line saying so (with or without a comparison) — its gross profit is then overstated, so never quote that margin as the real one; tell the owner a stock take (or switching to recording cost on every sale in Settings → Accounting) fixes it.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | ||
| from | Yes | ||
| compare | No | Named comparison window. 'previous_period' = the equal-length window immediately before from…to; 'same_period_last_year' = the same dates one year earlier. Omit for no comparison. | |
| compareEnd | No | End of an explicit comparison window (use WITH compareFrom). | |
| compareFrom | No | Start of an explicit comparison window (use WITH compareEnd; overrides `compare`). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Although annotations declare readOnlyHint=true and openWorldHint=false, the description adds rich behavioral context beyond safety: it details server-side delta computation in integer cents, the null handling of deltaPct when the earlier figure is zero, sign behavior when moving out of a loss, the presence of `windows` and `notes` fields, and periodic-stock edge-case warnings. These go well beyond what annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description front-loads the core purpose then moves to comparison guidance and edge cases. It is dense and information-rich, with every sentence serving a purpose, but the long periodic-stock paragraph and nested warnings make it slightly sprawling for an at-a-glance read.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description fully compensates by explaining the structure of returned fields (`deltaCents`, `deltaPct`, `windows`, `notes`) and how to interpret them. It also covers edge cases like zero-base percentages and periodic stock, making it complete for correct invocation and reporting.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 60%, so the description compensates by explaining the semantics of `compare` (with definitions of 'previous_period' and 'same_period_last_year') and how `compareFrom`/`compareEnd` form an explicit earlier window. It adds meaningful interpretation of comparison parameters, though `from`/`to` are not described beyond the schema patterns.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Your profit and loss for a period' and then enumerates the exact line items returned (revenue, COGS, gross profit, expenses, net income). This clearly distinguishes it from siblings like balance_sheet or cash_forecast, which cover different financial statements.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance for comparisons — 'to answer "how does this month compare with last?" ask for it here — NEVER call this tool twice and subtract the figures yourself.' It also prescribes how to set comparison parameters and warns to read `notes` before quoting a variance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
intake_contractIntake contractARead-onlyInspect
The shape a Taokeh document needs, so you can turn a paid receipt, a sales note, a supplier bill, a customer payment, a payment you made to a supplier, a sales return, a bank statement or a whole set of OPENING BALANCES into a correctly-formed submission: required fields, how to read totals / measurements / SST, and a worked example grounded in this company. Pass doc_type 'expense' (default), 'invoice', 'bill', 'purchase_order', 'statement', 'receipt', 'payment', 'credit_note', 'sales_debit_note', 'journal', or — when this company is SWITCHING from another accounting system — 'historical_document' (ONE already-issued invoice or supplier bill being brought across in bulk with stage_document), 'trial_balance', 'aged_receivables' or 'aged_payables', which are staged in one call each with stage_opening_balances, and 'accounts', 'products', 'contacts', 'settings' or 'employees' (its CHART OF ACCOUNTS, its item list, its customer/supplier book, its COMPANY SETUP and its STAFF LIST), which are staged in one call each with stage_master_data — do 'accounts' first, since the trial balance matches against the chart. The 'settings' contract lists every stageable key, its meaning and current value, plus every owner-only setting the AI may never write and the reason/page for each. The 'employees' contract is ADMIN-ONLY. Nothing posts on its own — the user reviews everything in Taokeh.
| Name | Required | Description | Default |
|---|---|---|---|
| doc_type | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, and the description reinforces this with 'Nothing posts on its own.' It adds extra behavioral context: admin-only for employees, owner-only settings the AI may never write, and the review flow. These go beyond the annotations, though not exhaustively.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but front-loaded with the core purpose. It is dense with essential information and avoids filler. While a more structured format (e.g., bullets) could improve readability, every sentence earns its place given the tool's role as a contract reference.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool that defines contracts across many document types, the description covers all necessary context: purpose, doc_type variants, staging relationships, precedence, and permission constraints. There is no output schema, but the tool's output (the contract) is sufficiently implied.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only a bare string for doc_type with no enum, and schema coverage is 0%. The description fully compensates by listing every valid doc_type value and explaining its meaning and usage, making the parameter self-documenting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states the tool's purpose: defining the shape a Taokeh document needs for various transaction types, and enumerates all doc_type values. It clearly distinguishes itself from siblings by positioning itself as a reference contract rather than a creation or staging tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage guidance: it explains when to use this tool (to understand the document shape) and when to use alternatives like stage_document, stage_opening_balances, and stage_master_data for specific doc types. It also prioritizes 'accounts' first and notes admin-only restrictions, leaving no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
low_stockLow stockARead-onlyInspect
Which products are at or below their reorder point right now — the "what do I need to reorder" list. Each row: sku, name, on-hand quantity, reorder point and unit. Capped, with the true count so you know if more exist. Services ("Service — no stock") never appear, nor do the placeholders Taokeh makes to carry document lines (SHOPEE-ITEM / TIKTOK-ITEM / SHOPIFY-ITEM, POS summary items, items added during migration, scanned lines): they are not stock. Uses the same rule as the dashboard low-stock chip (reorder point set AND on-hand ≤ reorder point). If this company uses Counter (the till), quantities are as at the last day-close: counter sales move stock once, when the day is closed, so an open trading day shows more on hand than the shelf does.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover read-only and closed-world; the description adds substantial context beyond that: results are capped but return the true count, service items and placeholder SKUs are excluded, the filter mirrors the dashboard chip's rule, and Counter/till companies report quantities as of last day-close. That last note materially affects how an agent should interpret the numbers.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core answer is front-loaded in the first clause and the row shape follows immediately. The closing Counter/till paragraph is long, but it carries real interpretive value rather than restating the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description supplies the row fields (sku, name, on-hand, reorder point, unit), the truncation behavior plus true count, and the exclusion and timing caveats. An agent has everything needed to call it and to narrate the result correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing to disambiguate; baseline 4 applies. The description spends its words on result semantics instead, which is the right allocation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names the exact resource and filter in one stroke: "products at or below their reorder point right now" plus the colloquial framing "what do I need to reorder". This clearly separates it from stock_level (all levels) and stock_movements (history) without needing to open either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a strong usage context (the reorder-decision list) and implicitly positions itself against level/movement siblings via "right now" and "at or below". It does not explicitly name when to prefer stock_level or stock_movements instead, so it falls short of a full when/when-not/alternatives statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
my_workMy workAInspect
YOUR OWN QUEUE in this company, in one call — what you filed and what happened to it. Answers four questions: (1) PENDING — everything of yours still waiting on the owner's approval, across every lane (expense, invoice, bill, quote, receipt, supplier payment, contact, credit note, debit note, purchase order, adjusting journal, your bank-category proposals), each with its reference, party, amount, how many days it has waited, and the exact next step in words plus the owner's review link (a bank proposal the review row shows differently — an OWNER_WITHDRAWAL the loan fence places as LOAN_PAYMENT or OTHER — also carries shownOnReviewAs {category, reason}); (2) APPROVED — what the owner approved since since (default: the last 7 days) and WHAT IT BECAME, with the posted document's own reference, so you can say "that one is now invoice INV-0042"; (3) REJECTED — what the owner or the engine refused, with the reason VERBATIM whenever one is stored. Every reject door in Taokeh offers the owner an optional "Why?" box, and staged migration rows carry the engine's own refusal text; where a reason is present, reason holds those exact words and reasonRecorded is true — READ IT and fix precisely what it names rather than re-filing the same thing. The box is never compulsory, so a refusal with an empty box reports reason: null and reasonRecorded: false; read that as "unknown", ask the owner what was wrong, and do NOT guess why before re-filing; (4) STAGED — your migration work (historical documents, opening balances, master data) sitting at a commit door. Params: since (YYYY-MM-DD or ISO, optional), kind (optional single-lane filter), limit (default 50, max 200 — every bucket reports total, returned and an honest truncation note; nothing is silently dropped). READ-ONLY. It changes nothing, it cannot approve anything, no tool can approve on the owner's behalf, and no tool of any kind moves money — every pending item is one human tap in Taokeh.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | ||
| limit | No | ||
| since | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations it discloses a lot of behavior: per-bucket truncation is reported honestly (total, returned, truncation note, "nothing is silently dropped"), `reason`/`reasonRecorded` semantics for refusals, and the `shownOnReviewAs` remapping for OWNER_WITHDRAWAL rows. The one weakness is that the emphatic "READ-ONLY, it changes nothing" claim sits in tension with readOnlyHint=false; the description is doing the work the annotation should do, and an agent comparing the two gets a mixed signal.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The four numbered buckets and front-loaded scope sentence are good structure, but the text is dense and run-on, with heavy parentheticals such as the OWNER_WITHDRAWAL/LOAN_PAYMENT aside and the repeated read-only disclaimer. Several clauses restate the same point (reasons are optional, empty means unknown) and could be compressed without losing meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description takes on the return-shape burden and largely meets it: it names the per-item fields (reference, party, amount, days waited, next step, review link), the approval-to-document linkage, and the truncation accounting. The only gap is ordering/pagination across buckets, which is not addressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry the load, and it does: `since` format (YYYY-MM-DD or ISO, default last 7 days), `limit` (default 50, max 200), and `kind` as a single-lane filter. It does not enumerate the 37 allowed `kind` values, though it does list most lane names inline while describing the PENDING bucket.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
"YOUR OWN QUEUE in this company, in one call — what you filed and what happened to it" states a specific subject (the caller's own submissions) and enumerates the four questions it answers (pending, approved, rejected, staged). That ownership scope distinguishes it from org-wide siblings such as bank_review_queue, shoebox_items or ap_aging without the agent opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete follow-up guidance: read `reason` verbatim and fix precisely what it names rather than re-filing, and when `reasonRecorded` is false treat it as unknown and ask the owner instead of guessing. It never names a competing tool to use instead (e.g. search_expenses for a broader org-wide sweep), so the when-not-to-use side is left implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
open_invoicesOpen invoicesARead-onlyInspect
The unpaid (or part-paid) invoices for one customer, oldest first — search by customer name. Each invoice's outstanding is what it still owes net of payments AND of the live credit notes raised against it (a refunded note no longer counts), which is the figure to quote and to propose. grossOutstanding is the invoice before those notes: a payment may be allocated up to it (a customer who paid the whole invoice), and anything above outstanding then stays on the credit note as the customer's refundable credit. An invoice its credit notes fully cover is not listed. outstandingBalance is the customer's NET balance — these invoices less any payment received but not yet matched to one — so it can be lower than the invoices add up to (see balanceNote).
| Name | Required | Description | Default |
|---|---|---|---|
| customer | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered. The description adds real behavioral context beyond that: results are ordered oldest-first, invoices fully covered by credit notes are excluded from the list, and outstandingBalance is a net customer figure that can be lower than the listed invoices sum. It does not mention pagination, result limits, or match semantics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and ordering are front-loaded in the first clause, and each subsequent sentence explains a distinct figure (outstanding, grossOutstanding, outstandingBalance). It is nonetheless a dense block of prose for a one-parameter read tool, and the payment-allocation aside in the middle is longer than needed to support correct invocation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the burden of explaining the returned figures and does so thoroughly, including the relationship between outstanding, grossOutstanding, and outstandingBalance, and the balanceNote caveat. Remaining gaps are match semantics and pagination/result limits.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single required parameter is undocumented in the schema (0% coverage). The description clarifies that it is a customer name to search by rather than an ID, which is genuinely useful, but it does not say whether matching is exact, partial, or fuzzy, nor what happens on ambiguity (resolve_customer exists as a sibling, suggesting ambiguity is possible).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource+scope: 'The unpaid (or part-paid) invoices for one customer, oldest first', which an agent can distinguish from balance-level siblings like ar_aging or ap_aging. It stops short of naming an alternative tool or stating what this is NOT, so sibling differentiation is implied rather than explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'search by customer name' implies the intended invocation, and 'for one customer' implies the scope versus a portfolio-level aging report. However, there is no explicit when-to-use versus when-not guidance and no alternative named (e.g. ar_aging for all customers), so usage is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
payer_historyPayer historyARead-onlyInspect
How this company has treated a given bank counterparty (a payer or payee, named as printed on the statement line) before: the historical bank rows a human already CONFIRMED or POSTED for that name — grouped by category, contact and GL account, with a count, date range and total amount each — plus the live suggestion Taokeh would now make for it. That live suggestion carries a basis: 'LEARNED' means it was worked out by tallying the rows among those that are still POSTED — a confirmed-but-unposted row, or a post the owner undid, teaches nothing — and timesSeen says how many; 'OVERRIDE' means the OWNER has written a standing rule for this payer on the memory page (What Taokeh has learned → Bank rules), which BEATS the tally — ruleSetOn is the date they set it, and timesSeen is 0 because a rule was never "seen" any number of times. Never report an override as history: the owner said so, the books did not. This is the tenant-scoped PRECEDENT behind a suggestion, so you can explain WHY a row is being categorized a certain way. READ-ONLY; the owner still confirms every categorization in Taokeh.
| Name | Required | Description | Default |
|---|---|---|---|
| counterparty | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true and openWorldHint=false, and the description goes well beyond them: it defines what 'teaches' the tally (a confirmed-but-unposted row or an undone post teaches nothing), explains that OVERRIDE beats the tally, that timesSeen is 0 for rules, and that ruleSetOn is the date set. This is exactly the kind of semantic disclosure annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded and almost every sentence carries needed semantics (basis meanings, override precedence). It is dense and runs long for a one-parameter tool, with heavy emphasis capitalization (CONFIRMED, POSTED, LEARNED, OVERRIDE, BEATS) that borders on noise, keeping it off a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description carries the return-value burden itself, and it does: it enumerates the returned fields (count, date range, total amount, grouping keys) and the suggestion's basis/timesSeen/ruleSetOn. An agent has enough to call it and to interpret the result correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% for the single counterparty parameter, so the description must compensate. It does so partially by clarifying the value is 'named as printed on the statement line', which tells the agent to pass the raw statement text, but it gives no detail on matching, casing, or unmatched names. Adequate but leaves a real gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: it returns the historical, human-confirmed bank rows for a named bank counterparty, grouped by category/contact/GL account, plus the live suggestion. This is clearly distinguishable from sibling resolvers (resolve_vendor, resolve_customer) and from draft_bank_classification, which draft rather than report precedent.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a clear context for use ('the tenant-scoped PRECEDENT behind a suggestion, so you can explain WHY a row is being categorized a certain way') and a usage directive ('Never report an override as history'). It stops short of naming alternatives or stating when NOT to call it, so it does not reach a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
payroll_summaryPayroll summaryARead-onlyInspect
A month's payroll from gross to net, as it was actually run: total gross (basic, allowances, overtime, public-holiday pay and any bonus), what was deducted from staff (EPF, SOCSO, EIS, SKBBK, PCB/MTD, zakat), what the employer contributed on top (EPF, SOCSO, EIS, HRD Corp levy), the total net pay, and the employer's TRUE total cost for the month — wages plus every employer contribution. Also lists who is on approved leave in the next 14 days, so "what does payroll look like this month" and "who is out next week" are one call. With no month it reads the latest FINALIZED or POSTED run; give a month as YYYY-MM for a specific one. A DRAFT run carries run.needsRecompute: when true, an early banner says its figures may be out of date (it was reopened, or employee details, pay before Taokeh, a TP1 sign-off switch or the payroll rules changed after it was computed) and the owner must press Save & recompute in Payroll → Payroll Runs. Even when false, Finalize re-runs the recompute and refuses if any money would move (e.g. a TP1 approval or declaration changed) — so a DRAFT's figures are never final; say so rather than quoting them as final. Set perEmployee: true for the per-person breakdown — those are individual salaries, so ask for them only when the user actually wants them; each entry carries that person's real employeeId. Set staff: true for the full employee ROSTER with ids (each row also carries skbbkExemptReason, the current Lindung 24 Jam setting: null = deducted here), which works even when payroll has never been run — that is where the employeeId for update_employee_draft comes from, and it is the only place an id is published: never key an employee by name. Every figure is a sum of the STORED payslips of that run — the same numbers the staff were paid on and the payroll journal was posted from — never a recalculation. If nothing has been run, it says so plainly rather than returning a month of zeros. ADMIN AND BOOKKEEPER + PAYROLL ONLY: payroll sits behind its own role in Taokeh and this tool refuses on any other connection (a plain Bookkeeper included). READ-ONLY — it cannot create a run, pay anybody, or file anything with LHDN, KWSP or PERKESO.
| Name | Required | Description | Default |
|---|---|---|---|
| month | No | ||
| staff | No | Return the full employee ROSTER (id, name, staff no., status, designation, basic salary, and skbbkExemptReason — null when Lindung 24 Jam (SKBBK) is deducted here, else OPTED_OUT or OTHER_EMPLOYER) alongside the summary — independent of any run, so it works even before the first payroll. This is where the employeeId for update_employee_draft comes from. Individual salaries: ask for it only when the user actually needs it. | |
| perEmployee | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint/openWorldHint; the description adds much more — the ADMIN AND BOOKKEEPER + PAYROLL ONLY gate with an explicit refusal on plain Bookkeeper connections, the empty-run behavior (says so plainly instead of zeros), and the fact that figures are sums of stored payslips, never a recalculation. The DRAFT semantics (`run.needsRecompute`, Finalize refusing if money would move) tell the agent not to present draft numbers as final, which is exactly the behavior beyond the annotations that matters.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the payload definition before the mode flags and caveats, which is the right order. It is long, and some of the DRAFT passage explains in-product UI steps ('press Save & recompute in Payroll → Payroll Runs') that go beyond what an agent needs, though the core warning about non-final draft figures earns its place. Dense but not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With three optional parameters and no output schema, the description carries the return-shape burden and does: it enumerates the reported figures, the roster's fields, and the per-employee entry's `employeeId`. It also covers access requirements, empty-state behavior, and draft validity — no gap an agent would need to fill before calling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 33% — `month` and `perEmployee` carry no schema descriptions at all — and the description fully compensates: `month` format YYYY-MM plus what happens when omitted, `perEmployee` meaning (per-person breakdown with real `employeeId`) plus a privacy caution, and `staff` covering roster fields and the SKBBK exemption semantics. Nothing about parameter meaning is left to inference.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening clause names the exact resource and scope (a month's payroll, gross to net, as actually run) and then enumerates the components returned — gross breakdown, staff deductions, employer contributions, net pay, true employer cost — so an agent knows precisely what comes back. It also explicitly bundles the next-14-days leave list, distinguishing the call from a plain payroll or an HR leave query.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Concrete triggers are given for each mode: no `month` reads the latest FINALIZED/POSTED run, `month` as YYYY-MM picks a specific one, `perEmployee: true` only when the user wants individual salaries, `staff: true` when ids or a roster are needed. It also routes to the canonical id source ('that is where the employeeId for update_employee_draft comes from') and warns never to key an employee by name, which is real routing guidance against a sibling tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
profit_driversProfit driversARead-onlyInspect
The DIAGNOSTIC breakdown of WHY net profit moved — deterministic, not a model's arithmetic. Compares the current month-to-date against the SAME day-span of the previous month (day 1 through min(today's day, the prior month's last day)), so a partial month is never compared against a full one. Returns: the net-profit figure for both windows and the delta; a drivers bridge (revenue delta, COGS delta, then the operating-expense accounts that moved most — top 5 by absolute change plus an other roll-up); oneOffs (disposal and FX gains/losses, pulled out of the movers so a lumpy asset sale doesn't read as an operating trend — empty if you have no such accounts); the prior month's FULL-month net profit as a stated secondary reference (priorMonthFull); and an assumptions list. Each driver's direction is its effect on PROFIT ('improving'/'worsening'/'flat'). All amounts are integer CENTS (RM = cents ÷ 100). Optional month (YYYY-MM) picks a month other than the current one — a completed past month compares its whole length; omit for the current month-to-date. On periodic stock with no stock take this financial year and COGS of RM 0 for the current window, a notes line says the gross-profit story is incomplete — COGS is not a real driver there, so do not present its absence as a margin gain.
| Name | Required | Description | Default |
|---|---|---|---|
| month | No | Month to analyse, YYYY-MM. Omit for the current month-to-date. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover read-only/open-world, so the description carries the behavioral load and does so richly: exact comparison windows, the drivers bridge structure, oneOffs handling of disposal/FX so a lumpy asset sale isn't read as a trend, integer-cents units, `direction` semantics relative to profit, and the periodic-stock COGS caveat that warns against misreading a missing COGS as margin gain.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is a long single paragraph but front-loads the core diagnostic purpose and then layers needed detail. Given there is no output schema, enumerating the return shape earns its place, though the prose is dense enough to be slightly heavy for a one-parameter tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully enumerates the return shape (both net-profit windows, delta, drivers bridge, oneOffs, priorMonthFull, assumptions, notes), and covers edge cases and units, leaving nothing an agent needs in order to call and interpret it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and there is only one optional param, so the schema already documents `month`'s format. The description adds genuine semantic value beyond it by clarifying that a completed past month compares its whole length while the default is current month-to-date against the same day-span.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('diagnostic breakdown of WHY net profit moved') and explicitly frames it as deterministic decomposition rather than a generative model, which distinguishes it from siblings like income_statement or financial_timeseries that report rather than explain movement.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear conditional guidance on the one parameter (omit `month` for current month-to-date; supply it to analyse a completed past month), and explains the comparison baseline. It does not, however, explicitly name an alternative sibling tool or say when to prefer this over income_statement, so it falls short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
request_attachment_uploadRequest attachment uploadAInspect
Get a one-time link to upload a receipt/bill/statement/document that is too large to inline as attachmentBase64 (a real photo or a multi-page PDF — base64 inside a tool call is very costly). Returns an uploadUrl and an attachmentToken: PUT the file's RAW bytes to uploadUrl within the time limit, then pass attachmentToken IN PLACE OF attachmentBase64 to create_expense_draft / create_invoice_draft / create_bill_draft / create_quote_draft / create_receipt_draft / create_contact_draft / create_journal_draft — the file rides that draft and lands on the posted document on approval, exactly as an inline attachment does — or to import_bank_statement, which archives it as the statement's original. CALL THIS FIRST, BEFORE the create/import call: filing bare and adding the file after is the harder path. If you DID already file bare, the repair on the five document lanes (expense, invoice, bill, quote, receipt) is to call the same create tool AGAIN with the IDENTICAL reference + date + amount plus attachmentToken — while the draft is still pending the file is ADOPTED onto it (the result says duplicateAttachment:'added') and no second draft is created; the same repair works on import_bank_statement with the identical account, balances and rows. The link is single-use, expires quickly, and works only for this company. Use this for anything bigger than a few KB; keep attachmentBase64 for tiny files only. If the PUT is blocked (403) from a sandbox such as Claude Cowork, the user's Claude network allowlist is refusing taokeh.my: tell them to add taokeh.my under Settings → Capabilities → Allow network egress and start a new task, and meanwhile send the file inline as attachmentBase64.
| Name | Required | Description | Default |
|---|---|---|---|
| filename | No | Optional original filename, e.g. receipt.jpg — travels with the upload and shows on the review card. | |
| mediaType | No | Optional hint of the file's MIME type, e.g. 'image/jpeg'. Advisory only — the server determines the real type from the file's own bytes (magic bytes), so a wrong hint changes nothing. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say it is a non-read-only, non-destructive, non-idempotent call; the description adds far more: the link is single-use, expires quickly, is company-scoped, requires a raw-bytes PUT, returns uploadUrl plus attachmentToken, and how duplicateAttachment:'added' adoption behaves on retry. It even documents a concrete failure mode (403 from a sandboxed client) with remediation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and the returned artifacts are front-loaded and every sentence carries operational information. However it is delivered as one long run-on block with heavy em-dashes and shouting capitals, making the repair path and the 403 workaround harder to scan than a short enumerated structure would be.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, and the description compensates by naming the two returned values and describing the required follow-up PUT. It also covers the full lifecycle (request, upload, attach, repair-on-retry) that an agent needs to complete this two-step flow, which is unusually complete for a two-parameter tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both optional parameters (filename, mediaType) are fully documented in the schema, so the baseline is 3. The description adds no detail on these two parameters and instead spends its text on the returned token and downstream flow, which is a schema-independent concern.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource — 'Get a one-time link to upload a receipt/bill/statement/document' — and immediately distinguishes the tool from the inline alternative (attachmentBase64). It also names the exact downstream consumers (create_expense_draft, import_bank_statement, etc.), so the agent can separate it from every sibling create/import tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use ('anything bigger than a few KB'), an explicit when-not ('keep attachmentBase64 for tiny files only'), a required call ordering ('CALL THIS FIRST, BEFORE the create/import call'), and a fallback repair path for each affected lane. Alternatives and exclusions are both stated, not inferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolve_customerFind customerARead-onlyInspect
Resolve a customer name for an invoice draft against this company's real customers. Statuses: resolved (with id + the customer's current A/R balance, outstandingBalance — the credit-check-in-chat moment. It is NET: open invoices less any payment posted to this customer but not yet matched to an invoice, so it can be lower than the invoices add up to, and zero or NEGATIVE when they have paid ahead — say so rather than calling it a debt), ambiguous (with candidates), or none. When there is no exact/prefix match but existing customers look CLOSE (nearMiss:true), the name is likely salesperson shorthand for one of them ("JJ Dungun" for "PERNIAGAAN JJ") — ask the user which one; only file as new if they confirm it is genuinely new. Never guess among candidates — ask the user.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint=false, but the description goes well beyond that: it enumerates the three possible statuses, defines outstandingBalance as NET (open invoices less unmatched payments, can be zero or negative when paid ahead), and warns against calling a negative balance a debt. That is precisely the extra context an agent needs before speaking a number to a user.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose and each subsequent clause carries distinct operational detail, but the dense nesting of parentheticals (the credit-check-in-chat aside, the balance arithmetic) makes it heavier than it needs to be. Nothing is wasted, though.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description must describe returns — and it does, covering all three statuses and the fields returned for each (id, outstandingBalance, candidates, nearMiss). Nothing needed to call or interpret it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% for the single param, so the description must carry the load. It conveys that the input is a customer name as written/abbreviated and that prefix and near-miss matching are attempted, which adds real meaning beyond the bare string type.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Resolve) and resource (a customer name for an invoice draft) plus the scope (against this company's real customers). An agent can distinguish it from resolve_vendor/resolve_product by the resource alone without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing guidance for each outcome: ask the user which candidate when ambiguous, treat nearMiss as likely shorthand, only file as new on user confirmation, and never guess. This is genuine when/when-not guidance, not inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolve_productFind productARead-onlyInspect
Resolve a product by name, SKU or exact barcode for an invoice line. Returns id, sku, name, unit, the default sell price the form would use, and isService (true = "Service — no stock": selling it never counts stock or posts a cost). Plain names or SKUs work best; a barcode must match the one saved on the product exactly. Statuses: resolved / ambiguous (candidates) / none — never guess among candidates.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the safety profile (readOnlyHint, openWorldHint=false); the description adds the return payload (id, sku, name, unit, default sell price, isService), the meaning of isService for stock/costing, and the three outcome statuses. That is substantial behavior beyond what the annotations declare.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose, then scope, matching rules, return fields, and status semantics. Every sentence is useful, though the isService parenthetical is slightly wordy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and minimal annotations, the description supplies the return fields, matching constraints, and status outcomes an agent needs to call and interpret this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the single 'query' param is undocumented in the schema, so the description carries the burden and does so by specifying acceptable inputs (name, SKU, exact barcode) and their matching strictness.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (resolve) plus resource (product) and the accepted identifiers, scoped to an invoice line. It is clearly distinguishable from resolve_customer and resolve_vendor among the siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives practical selection guidance: plain names/SKUs work best, a barcode must match exactly, and it warns to never guess among ambiguous candidates. It stops short of naming alternative tools or stating when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolve_vendorFind vendorARead-onlyInspect
Resolve a supplier / vendor name for a bill draft against this company's real vendors. Statuses: resolved (with id), ambiguous (with candidates), or none. When there is no exact/prefix match but existing vendors look CLOSE (nearMiss:true), the name is likely shorthand for one of them — ask the user which one; only file as new if they confirm it is genuinely new. Never guess among candidates — ask the user.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses important behaviors beyond the readOnlyHint annotation: return statuses (resolved, ambiguous, none), the nearMiss flag, and the rule to ask the user rather than guess among candidates. The phrase 'only file as new if they confirm' could sound like a write operation, but readOnlyHint is not contradicted because it is guidance for downstream action, not a claim that this tool mutates data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with its purpose and uses four sentences, each carrying useful information. There is slight redundancy between 'ask the user which one' and 'Never guess among candidates — ask the user,' but it reinforces a critical safety rule without becoming bloated.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the main resolution paths: resolved, ambiguous, and nearMiss, along with explicit user-confirmation requirements. The main gap is the lack of explicit guidance for the status 'none' when no close vendors exist, although the nearMiss rule implies the general principle. Since there is no output schema, the status descriptions are essential and mostly sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the description compensates by explaining the single 'name' parameter is a supplier/vendor name from a bill draft that should be matched against the company's real vendors. This gives the parameter meaningful context that the bare schema does not provide.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Resolve a supplier / vendor name for a bill draft against this company's real vendors.' It clearly distinguishes from sibling tools like resolve_customer and resolve_product by being vendor-specific. The statuses it mentions further clarify its role as a resolution rather than a creation tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context: use this when reading a vendor name from a bill draft and needing to match it to existing vendors. It gives strong behavioral guidance for ambiguous and nearMiss cases, including 'ask the user' and 'never guess.' It does not explicitly name alternatives or state 'do not use for customers/products,' but the vendor-specific wording makes that implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
revise_draftRevise a pending draftAInspect
Correct a draft you already filed, instead of filing it again. Pass the draft's kind and id plus a patch of just the fields that were wrong — the server re-validates them exactly as it did at filing (same account rules, same date rules, same line derivation, same totals) and updates the draft in place. Use this whenever you realise a filed draft is wrong: for an expense, re-calling create_expense_draft with corrected fields is refused as a duplicate of the very draft you are trying to fix, and for every kind a second create files a CONFUSING SECOND DRAFT the human then has to notice and reject — revise the one you filed instead. ONLY a PENDING draft can be revised; a draft the user has already approved or rejected (or one that has expired) is refused, naming its status — file a fresh draft in that case. Fields you do not send are left exactly as filed. REVISING DOES NOT APPROVE ANYTHING: the draft stays pending, nothing posts, and the human still taps Approve in Taokeh — the same one-tap link from the original filing still works and shows the revised figures. The reviewer is TOLD it changed: a short 'revised by your AI' line naming the changed fields is appended to the note they read on the approval card, so add a plain-language note saying WHY you revised it.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | Which kind of draft to amend — the kind the filing tool returned (each create_*_draft and update_*_draft tool files one kind; 'invoice_update' / 'bill_update' are the posted-invoice and posted-bill corrections). 'sales_debit_note' is the SELL-side debit note create_debit_note_draft files (an additional charge on an invoice you issued); 'journal' is the adjusting entry create_journal_draft files. | |
| note | No | A SHORT plain-language reason for the revision, in the reviewer's language — 'the paid-from account should be the owner's capital account, not petty cash'. It is APPENDED to the note they already have (nothing is overwritten) and shows on the approval card. Keep it to a sentence. | |
| patch | Yes | Only the fields you are correcting. Every key must belong to this draft kind — a misspelled or foreign key is refused rather than silently ignored. The card IMAGE of a contact draft and the original document riding any other draft are deliberately NOT patchable: they are the evidence the approver checks your fields against. | |
| draftId | Yes | The draftId the create_*_draft call returned. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well past the annotations: it says the server re-validates with the same rules as filing, re-derives quantities and totals, that unsent fields are preserved, that nothing is approved or posted, that the original one-tap link still works, and that a 'revised by your AI' line is appended to the reviewer's note. That is exactly the mutation consequence detail annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, and the emphatic sentences (duplicate-create refusal, 'REVISING DOES NOT APPROVE ANYTHING', the reviewer-note disclosure) each carry distinct behavioral information. It is on the long side with heavy capitalization and a slightly sprawling middle sentence, but no sentence is pure filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a non-idempotent mutation with no output schema, it covers state transitions, validation, side effects, and the human-in-the-loop consequence. It never says what the call returns (e.g. the updated draft object or a status payload), which is the one gap an agent might want closed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, and the description adds above that: the patch is defined as minimum-diff ('fields you do not send are left exactly as filed'), foreign or misspelled keys are refused, and `note` is described as appended rather than overwriting. It does not walk through the `kind`/`draftId` pairing or the per-kind field applicability, but leaves that to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Correct a draft you already filed') and immediately contrasts it with re-filing via create_expense_draft. It does not, however, distinguish itself from the sibling update_*_draft tools (update_invoice_draft, update_expense_draft, etc.), which an agent could plausibly pick for the same task — the closest sibling relationship is left unaddressed.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use ('whenever you realise a filed draft is wrong'), explicit when-not ('ONLY a PENDING draft can be revised; approved, rejected or expired is refused — file a fresh draft in that case'), and it names the alternative that fails and why. The agent has no inference left to do.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sales_summarySales summaryARead-onlyInspect
How much you have sold. With no dates: today and this month so far. With a date range: the invoiced sales for that period, broken down by month, customer and product. Everything is MYR — a foreign-currency invoice is converted at the rate frozen on that invoice — and INVOICES only: credit notes are in none of these figures, so a period with returns is not netted here. ⚠ TWO TAX BASES, and they are named in the payload rather than left for you to guess: on byMonth and byCustomer, total is tax-INCLUSIVE and exTax is before SST; byProduct is a per-LINE figure and is always EX-TAX, so it foots to exTax, never to total. Quote one basis, say which, and never present a by-product figure as the same kind of number as a by-month one.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | ||
| from | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover only readOnlyHint and openWorldHint, and the description adds substantial behavioral context beyond them: currency is MYR with foreign invoices converted at the invoice-frozen rate, credit notes are excluded so returns are not netted, and the two tax bases are disclosed with exactly which payload fields are tax-inclusive vs. ex-tax. This is precisely the kind of trap-avoiding detail an agent needs and cannot get from the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core meaning, then layered detail in decreasing priority. Every sentence carries real information (currency basis, credit-note exclusion, tax bases), though the closing imperative about quoting one basis is advisory framing rather than tool semantics and could be trimmed without loss.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description must describe the return shape — and it does, naming byMonth, byCustomer, byProduct and the total/exTax fields and their differing tax bases. Combined with the mode explanation and exclusion rules, an agent has everything needed to invoke and interpret the result correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% for `from` and `to`, so the description carries the semantic burden — and it does explain that omitting them yields today/month-to-date while supplying a range yields that period's invoiced sales. It does not restate the YYYY-MM-DD pattern, but that is already in the schema, so the gap is minor.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('How much you have sold') and immediately defines the two operating modes — no dates yields today plus month-to-date, a date range yields invoiced sales for that period. It further specifies the breakdown dimensions (month, customer, product), so an agent knows exactly what this returns and how it differs from sibling summaries like expenses_summary or income_statement.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly states the two usage contexts (no dates vs. with a date range), which effectively tells the agent when each behavior fires. What it does not do is name an alternative sibling (e.g. income_statement or financial_timeseries) or state when NOT to use this tool, so it stops short of the explicit routing found in the best definitions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_document_linesSearch document linesARead-onlyInspect
Read the LINE ITEMS of documents IN BULK — what was actually sold or bought, line by line, across many documents in one call. search_documents answers WHICH documents; this answers WHAT WAS ON THEM. Use it for any question that needs the detail behind the totals: average price per item across a year, how a price moved between two customers, which sizes or grades actually shift, how much of something was supplied. Without it the only way to see lines was to open one PDF per document — 180 documents is 180 round trips and 180 chances to misread a layout. docType is REQUIRED and must be one of: 'invoice', 'credit_note', 'sales_debit_note' (an additional charge this business issued to a customer), 'quote', 'delivery_order', 'bill', 'debit_note' (the BUY-side one, raised against a supplier), 'purchase_order'. Narrow with any combination of number (partial doc number — on invoices this also matches the customer's own Ref No, their DO/PO number), party (partial customer or vendor name), from/to (document date range) and productText — a case-insensitive contains matched against the PRODUCT NAME or the LINE DESCRIPTION, so "chengal" or "2x4" pulls only those lines. docType on its own is a legitimate whole-book pull. Each row: docId, docNumber, docDate, party, lineNo, product {sku, name}, description, quantity, unit, unitStamped, unitPrice, unitCost, discount, lineTotal, taxCode, docStatus, currency, fxRate, isMeasureDrift — verbatim from what was stored, never re-derived. A taxCode of 'OS' means NO SST was charged on that line (out of scope) — tell the user "no SST", not "OS". CURRENCY: unitPrice, discount and lineTotal are in the DOCUMENT's own currency exactly as stored, NOT converted to ringgit; fxRate is the rate frozen on that document (1 unit of currency = fxRate MYR; 1 on a ringgit document). Convert with it yourself where you need ringgit, and never average or total rows of different currencies together unconverted. (sales_summary and search_documents report ringgit instead — converted once per document — so a foreign document's figures differ between them and here by exactly that conversion.) ⚠ Sell-side unitCost is ALWAYS ringgit (the stock cost). MARGIN — ONE RULE, in ringgit: sell-side line margin (RM) = lineTotal × fxRate − quantity × unitCost. lineTotal is the line AFTER its discount and BEFORE SST; unitCost is already ringgit; fxRate is 1 on a ringgit document. Never use unitPrice − unitCost: it ignores the discount and, on a foreign-currency line, subtracts ringgit from foreign money. On an isMeasureDrift row count its lineTotal but NO cost (it is a money correction, not a unit of goods — the posted cost of goods leaves it out). On the BUY side (bill, buy-side debit note) unitCost is the supplier's price in the DOCUMENT currency, not ringgit — multiply by fxRate for ringgit. MEASURE ROUNDING ROWS: isMeasureDrift true marks the measure engine's rounding correction — a line of quantity 1 at a small negative price (typically described 'Discount') that brings a measured line back to the true per-foot money. It is a real line and belongs in document totals, but EXCLUDE isMeasureDrift rows when computing an average price or quantity per product, or they drag the average down and add a phantom unit. It is null on bills, buy-side debit notes and purchase orders, which store no such row. ⚠ The flag is only reliable from the date each table started storing it — invoice, credit-note and sell-side debit-note lines from 4 Sep 2026, delivery-order lines from 19 Sep 2026, quote lines from 23 Sep 2026. Older rows all read false, and nothing was backfilled, so a line written before that date CAN be an unmarked rounding row; Taokeh does not guess which (a qty-1 negative line may equally be a real discount or fee). Say so if your answer leans on older lines. ⚠ DIMENSIONS AND SPECIFICATIONS ARE TEXT, NOT FIELDS. Taokeh has no column for a timber size, a grade, a colour or a variant: they live inside the product name and the line description as the business types them ("Chengal 2x4x8"). So this hands you that text as written and YOU do the parsing and the grouping — the server will not invent a dimension it does not store, and two lines for the same real size may be spelled differently. unit is the unit of measure stamped on the line when it was written; where the line stores none it falls back to the product's CURRENT unit, and unitStamped says which: true = the line's own stamp, false = the product stand-in. A false is right for an ordinary unmeasured line, but a MEASURED line (tons, feet) written before its table could store the unit also reads false and may show the product's unit (often 'ea') — invoice/bill lines before 3 Sep 2026, delivery-order lines before 19 Sep 2026, quote lines before 23 Sep 2026, and purchase-order lines written before the deploy that added their unit column (migration 20260923120000, late September 2026). So never assume pieces, and do not treat a false-unitStamped quantity as a measured one without checking the description. Rows come back newest document first, then in the document's own line order, capped at 200 with total, shown and more — when more is true, narrow by date (from/to) and pull the periods in turn rather than accepting a partial answer as the whole. unitCost is the COST BASIS STAMPED ON THE LINE WHEN IT WAS POSTED — like unit and the tax code beside it — so a historical line reports the cost as of THAT SALE, not the product's cost today. That is what makes per-product margin answerable here, line by line — see MARGIN below. On a BILL, a buy-side debit note or a PURCHASE ORDER there is only one price column, so unitCost and unitPrice are the same figure — what the SUPPLIER charged (on a PO: what was ordered at). On a QUOTE unitCost is null (a quote stores no cost). On a DELIVERY ORDER it is the product's moving-average cost captured when the DO was saved — a dispatch-time reference, NOT the cost of goods: the invoice the DO becomes re-reads cost when it posts, and that is the figure on the invoice line. QUOTES AND DELIVERY ORDERS ARE NOT SALES. Voided ones are left out (as voided posted documents are); every other row carries docStatus — quote OPEN or CONVERTED, delivery order OPEN or INVOICED (null on posted types). ⚠ A CONVERTED quote's lines and an INVOICED delivery order's lines ALSO appear as invoice lines (and a quote converted into a delivery order appears on that DO too), so never add a quote or DO pull to an invoice pull — for what was actually sold, use 'invoice'; for what is still only quoted or delivered-not-billed, keep docStatus OPEN. PURCHASE ORDERS ARE NOT PURCHASES either: a CANCELLED one is left out, every other row carries docStatus DRAFT, SENT (nothing received), PARTIAL (partly received — part delivered, the rest still owed) or CONVERTED (received in full, or the rest closed as never coming), plus quantityReceived (arrived on that line so far, over every delivery bill) and quantityOutstanding (still owed; 0 once CONVERTED). Every delivery is its own bill and the received quantities ALSO appear as those bills' lines — for what was actually bought, use 'bill'. Before you propose a correction to an existing quote, delivery order, purchase order or posted bill (update_quote_draft / update_delivery_order_draft / update_purchase_order_draft / update_bill_draft), read its lines here first: those tools take the FULL replacement line set, and each row's lineNo is what their keep entries (and update_bill_draft's lineNo) refer to. Header-only search: search_documents. Paid expenses (which have no product lines): search_expenses. The ledger postings behind a document: search_journal. Nothing is written, and no draft is created.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Latest DOCUMENT date (YYYY-MM-DD). | |
| ref | No | NOT a filter on this tool. Pass the customer's own Ref No (their DO / PO number) as `number` — that one parameter searches BOTH the invoice's own number and the customer's Ref No, and the row it returns reports each separately as `number` and `refNo`. | |
| sku | No | NOT a filter on this tool. Pass it as `productText` — one case-insensitive contains matched against BOTH the product name and the line description, which is where sizes, grades and variants live (Taokeh stores no column for them). | |
| from | No | Earliest DOCUMENT date (YYYY-MM-DD). The main way to break a >200-line pull into honest slices. | |
| item | No | NOT a filter on this tool. Pass it as `productText` — one case-insensitive contains matched against BOTH the product name and the line description, which is where sizes, grades and variants live (Taokeh stores no column for them). | |
| size | No | NOT a filter on this tool. Pass it as `productText` — one case-insensitive contains matched against BOTH the product name and the line description, which is where sizes, grades and variants live (Taokeh stores no column for them). There is no size field to filter on. | |
| text | No | NOT a filter on this tool. Pass it as `productText` — one case-insensitive contains matched against BOTH the product name and the line description, which is where sizes, grades and variants live (Taokeh stores no column for them). | |
| docId | No | NOT a filter on this tool. This tool reads lines across MANY documents — narrow by `number` (the document number), `party` or a date range instead. For one document, pass its number as `number`. | |
| limit | No | NOT a filter on this tool. The page size is fixed at 200 lines. Narrow with from/to, party or productText and pull the periods in turn. | |
| party | No | Partial customer or vendor name (case-insensitive contains). | |
| refNo | No | NOT a filter on this tool. Pass the customer's own Ref No (their DO / PO number) as `number` — that one parameter searches BOTH the invoice's own number and the customer's Ref No, and the row it returns reports each separately as `number` and `refNo`. | |
| number | No | Partial doc number (case-insensitive). On INVOICES this also matches the customer's own Ref No — their DO or PO number. | |
| vendor | No | NOT a filter on this tool. Pass a customer or vendor name as `party`. | |
| docType | No | REQUIRED. One of: 'invoice', 'credit_note', 'sales_debit_note', 'quote', 'delivery_order', 'bill', 'debit_note', 'purchase_order'. Note the two debit notes: 'sales_debit_note' is one this business ISSUED to a customer; 'debit_note' is the BUY side, against a supplier bill. | |
| product | No | NOT a filter on this tool. Pass it as `productText` — one case-insensitive contains matched against BOTH the product name and the line description, which is where sizes, grades and variants live (Taokeh stores no column for them). | |
| customer | No | NOT a filter on this tool. Pass a customer or vendor name as `party`. | |
| supplier | No | NOT a filter on this tool. Pass a customer or vendor name as `party`. | |
| docNumber | No | NOT a filter on this tool. Pass the document number as `number` (partial matches are fine). | |
| productId | No | NOT a filter on this tool. This search matches product TEXT, not ids — pass the name or size words as `productText`. | |
| reference | No | NOT a filter on this tool. Pass the customer's own Ref No (their DO / PO number) as `number` — that one parameter searches BOTH the invoice's own number and the customer's Ref No, and the row it returns reports each separately as `number` and `refNo`. | |
| description | No | NOT a filter on this tool. Pass it as `productText` — one case-insensitive contains matched against BOTH the product name and the line description, which is where sizes, grades and variants live (Taokeh stores no column for them). | |
| productName | No | NOT a filter on this tool. Pass it as `productText` — one case-insensitive contains matched against BOTH the product name and the line description, which is where sizes, grades and variants live (Taokeh stores no column for them). | |
| productText | No | Case-insensitive text matched against the PRODUCT NAME or the LINE DESCRIPTION — this is where sizes, grades and variants live, because Taokeh stores no column for them. e.g. "chengal", "2x4". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint/openWorldHint; the description adds a large amount of behavioral disclosure beyond them: 'Nothing is written, and no draft is created,' currency of each money column vs. converted siblings, fxRate semantics, margin formula, isMeasureDrift meaning and reliability windows, unit vs unitStamped fallback, and newest-first ordering with a 200-row cap and more/total/shown.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded correctly — the one-line purpose and the sibling contrast come first — but the body is extremely long and repetitive: isMeasureDrift is defined, then re-explained with per-table date windows, margin rules are re-derived at length, and there is a forward reference ('see MARGIN below'). Most content is useful, but it is not economical.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 23-param, no-output-schema tool, the description covers the return shape (explicit row field list), ordering, pagination and next-step guidance, plus edge cases (measure-drift rows, unit-stamp backfill windows, quote/DO/PO not being sales/purchases). Nothing an agent needs to call or interpret this correctly appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real semantics the schema lacks: it spells out each docType value's business meaning (including the sell-side vs buy-side debit note distinction), the productText match against both product name and line description, and that number also matches the customer's own Ref No on invoices.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read the LINE ITEMS of documents IN BULK') with an explicit scope contrast against the sibling: 'search_documents answers WHICH documents; this answers WHAT WAS ON THEM.' An agent can distinguish this from search_documents, search_expenses and search_journal without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use conditions (any question needing detail behind totals), names alternatives and their domains (header-only: search_documents; paid expenses: search_expenses; ledger: search_journal), and even prescribes a required precondition — read lines here before calling update_quote_draft / update_delivery_order_draft / update_purchase_order_draft / update_bill_draft because they take full replacement line sets.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_documentsSearch documentsARead-onlyInspect
Find invoices, credit notes, sell-side debit notes, quotes, delivery orders, bills, buy-side debit notes and purchase orders by any combination of: doc number (partial), party (customer/vendor) name (partial), date range, amount range, due-date window (invoices and bills), and doc type. Returns compact rows (docId, type, number, date, party, total, status), newest first, capped — with a more flag when there are further matches to narrow down. INVOICE rows carry two more fields: refNo, the CUSTOMER's own reference for that job — their delivery-order or purchase-order number, the one they quote back at you on the phone — and dueDate, the date that invoice falls due. refNo is NOT the invoice number; the invoice's own number is number. refNo is null when the invoice was raised without one. dueDate is null on a CASH sale — paid at the counter, so it is not money anyone is waiting on and it deliberately sits outside every due-date window and aging bucket; a null there means CASH, not a missing date. Only CREDIT invoices report one. BILL rows carry dueDate too — the date a supplier bill falls due, from the payment term printed on it, and null on a CASH bill for the same reason. Only invoices carry refNo (a bill's own number IS the supplier's number, so it needs no second field — except a bill converted from a purchase order with no supplier number typed, which carries that purchase order's number), and only invoices and bills carry dueDate — every other type omits them rather than reporting null about a column its document has no such thing as. number searches BOTH, so "find INV-0031" and "find the invoice for their DO 1074" are the same call. An invoice reversed from a marketplace Returns page (Shopee, TikTok or Shopify) reports status 'Reversed (Returns page)' — a reversing journal entry took it out of the books, so it is neither paid nor owed. Invoices, credit notes, bills and quotes also report whether their ORIGINAL document (a scan or upload) is on file: hasAttachment plus an attachments list you can fetch with get_attachment. Delivery orders and purchase orders omit that field because they cannot carry an original. INVOICES, CREDIT NOTES and SELL-SIDE DEBIT NOTES (sales_debit_note — additional charges this business added to an invoice it issued; not to be confused with debit_note, the buy-side one raised against a supplier bill) also carry an einvoice field — this business's MyInvois posture for that document AS RECORDED IN TAOKEH: validated (a MyInvois validation is recorded here, with uuid + validatedAt), consolidated (covered by a consolidated e-invoice, which holds the uuid — the individual document has none by design), platform (a marketplace sale: Shopee/TikTok Shop/Lazada issues the e-invoice, nothing for this business to submit), exported (put into a MyInvois batch export from Taokeh, nothing recorded back yet) or none. Taokeh CANNOT see the MyInvois portal, so say "no validation recorded in Taokeh" — never "never submitted to LHDN". Bills and buy-side debit notes omit the field. Use the docId with get_attachment (the SOURCE document someone filed) or get_document_pdf (the PDF Taokeh generates). PAYMENTS — docType:'payment' — are an OPT-IN type you must ask for BY NAME: they are never included when you omit docType, because a payment is not a document. It is a BANK LINE that invoices or bills point at, so the grain is the movement of money: one receipt that settles three invoices is ONE row carrying three allocations ({docId, docType, docNumber, party, amount, amountForeign, notes}). An allocation's docType is the real kind of the document settled — 'invoice', 'credit_note' (a refund walks a credit note back), 'sales_debit_note', 'bill' or 'debit_note' — not an assumption from which side of the books it sits on, and notes is that allocation's own note rather than one note borrowed for the whole transfer. That makes "which payment covered INV-0031" a single call — number and party here match the ALLOCATED documents, not the bank line, and on the invoice side number also matches the customer's own Ref No (their DO/PO number, or a marketplace order key), exactly as it does for invoices in the ordinary search. Each row: docId (⚠ a BANK TRANSACTION id, NOT a document id — do not hand it to get_document_pdf or get_attachment), date, direction ('in'/'out'), total (the bank line's own magnitude), status (the bank row's status), bankAccount, party (the single customer or supplier when every allocation is to the same one, else null — read the per-allocation party then), source and description (the bank line as stored). source is DERIVED from where the line came from, not stored: 'statement' (it arrived on an imported bank statement and a human matched it in Banking), 'receipt_draft' or 'payment_draft' (an AI-filed draft the owner approved), or 'manual' — recorded by hand in the app, which is EVERYTHING ELSE rather than one door: the receipt door, the /go command bar, the supplier-payment door, a refund against a hand-recorded line. Taokeh does not distinguish them, so do not tell the owner which screen was used. A NEGATIVE allocation amount is a REFUND walking that document back down, not a settlement. CONTRAS ARE NOT PAYMENTS and never appear here: a set-off moves no bank money, and Taokeh books it as two internal clearing lines which are excluded from this read on purpose. To see one, use search_journal or the contra note on the documents themselves.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | ||
| ref | No | NOT a filter on this tool. Pass the customer's own Ref No (their DO / PO number) as `number` — that one parameter searches BOTH the invoice's own number and the customer's Ref No, and the row it returns reports each separately as `number` and `refNo`. | |
| from | No | ||
| text | No | NOT a filter on this tool. This search matches document numbers and party names only — use `number` or `party`. To search memo/line text, use search_journal (`text`) or search_expenses (`text`). | |
| dueTo | No | Latest DUE date (YYYY-MM-DD). Same docType rule as dueFrom — 'invoice' or 'bill'. Pair the two for a window: dueFrom + dueTo across this week with docType:'bill' answers "what do I owe before Friday"; dueTo alone with a past date is everything already due. | |
| party | No | Partial customer or vendor name (case-insensitive contains). | |
| refNo | No | NOT a filter on this tool. Pass the customer's own Ref No (their DO / PO number) as `number` — that one parameter searches BOTH the invoice's own number and the customer's Ref No, and the row it returns reports each separately as `number` and `refNo`. | |
| number | No | Partial doc number / reference (case-insensitive). On INVOICES this also matches the customer's own Ref No — their DO or PO number, whatever they printed on the paperwork. So a customer chasing "our PO 4471" is one call: pass 4471 here. That Ref No is NOT the invoice number; the invoice's own number matches here too, and you cannot tell from a hit which of the two it was — read `number` and `refNo` on the row it comes back with. On MARKETPLACE-IMPORTED invoices the Ref No is the platform's order key (SHOPEE-<order_sn>, TIKTOK-<order id>, SHOPIFY-<order id>), not a customer's number — so a marketplace order id pasted here finds its invoice too. | |
| vendor | No | NOT a filter on this tool. Pass a customer or vendor name as `party`. | |
| docType | No | Restrict to one type: 'invoice','credit_note','sales_debit_note','quote','delivery_order','bill','debit_note','purchase_order','payment'. Omit for all — EXCEPT 'payment', which is opt-in only and is never returned unless you name it. NOTE the two debit notes: 'sales_debit_note' is one this business ISSUED to a customer (additional charges on an invoice — money owed TO this business); 'debit_note' is the BUY side, issued to a supplier against a bill. | |
| dueFrom | No | Earliest DUE date (YYYY-MM-DD) — this is the "what's due this week" filter, and it asks about when the money falls due, not when the document was raised (that's `from`/`to`). INVOICES OR BILLS, one at a time: pass docType:'invoice' (money coming in) or docType:'bill' (money going out) with it, or the call is refused. Nothing else in this search has a due date, and quietly dropping every quote and delivery order would be a wrong answer wearing a right one's clothes; netting receivables and payables into one list would be another. | |
| customer | No | NOT a filter on this tool. Pass a customer or vendor name as `party`. | |
| supplier | No | NOT a filter on this tool. Pass a customer or vendor name as `party`. | |
| docNumber | No | NOT a filter on this tool. Pass the document number as `number` (partial matches are fine). | |
| maxAmount | No | ||
| minAmount | No | ||
| reference | No | NOT a filter on this tool. Pass the customer's own Ref No (their DO / PO number) as `number` — that one parameter searches BOTH the invoice's own number and the customer's Ref No, and the row it returns reports each separately as `number` and `refNo`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true and openWorldHint=false, yet the description discloses far more: nulls carry meaning (dueDate null = CASH, not missing), refNo vs number distinction, status 'Reversed (Returns page)', the einvoice posture vocabulary and Taokeh's visibility limit, and that payments are bank lines with per-allocation grain. It even warns that a payment row's docId is a bank transaction id, not a document id.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded with purpose and return shape, but the body is a very long, densely packed block covering einvoice postures, source derivation, payment grain, refunds and contras. Much of it is genuinely useful given the domain, yet the sheer volume and undifferentiated prose make it a documentation dump rather than a tight tool description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 17 parameters, no output schema and a complex accounting domain, the description carries the full burden and does so thoroughly — it explains the returned fields, their nullability and edge cases, and cross-tool handoffs (get_attachment, get_document_pdf, search_journal). An agent has what it needs to call and interpret this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 76%, so the baseline is 3, but the description adds real meaning beyond the schema: 'number' searches both the invoice number and the customer's Ref No (including marketplace order keys), the two debit-note types, how allocations' docType reflects the settled document, and why null dueDate means CASH. This is well past what the structured fields convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening states a specific verb and resource (find invoices/credit notes/quotes/POs etc. by combined filters) and enumerates both the filter dimensions and the returned row shape. It repeatedly differentiates itself from siblings (search_journal, search_expenses, get_attachment, get_document_pdf), so an agent can select it without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing is everywhere: dueFrom/dueTo require docType 'invoice' or 'bill' or the call is refused; 'payment' is opt-in and never returned when docType is omitted; text search belongs in search_journal/search_expenses; contras live in search_journal. Both when-to-use and when-not-to-use are covered with named alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_expensesSearch paid expensesARead-onlyInspect
Search PAID EXPENSES already posted to the books (the /expenses ledger) by any combination of: reference (partial), text in the memo/description (partial), date range, amount range, and category account. Returns compact rows (expenseId, date, amount, category account code + name, memo, reference), newest first, capped — with a more flag. Every row also says whether the ORIGINAL RECEIPT is on file: hasAttachment plus an attachments list (filename, media type, size) — an empty list means genuinely no receipt is attached, not "unknown". To read one, pass the row's expenseId and attachmentId to get_attachment. ALWAYS check here BEFORE filing an expense draft (create_expense_draft): if the same receipt is already booked, filing again would double-book it. search_documents does NOT cover paid expenses — this tool is the only way to see them.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | ||
| ref | No | NOT a filter on this tool. Pass the supplier/receipt reference as `reference`. | |
| from | No | ||
| memo | No | NOT a filter on this tool. Pass memo text as `text`. | |
| text | No | Partial text to match in the expense memo/description (case-insensitive contains). | |
| refNo | No | NOT a filter on this tool. Pass the supplier/receipt reference as `reference`. | |
| number | No | NOT a filter on this tool. Pass the supplier/receipt reference as `reference` — a posted expense has no document number of its own. | |
| vendor | No | NOT a filter on this tool. A posted expense carries no party field here — search the supplier name as `text` (it matches the memo/description) or as `reference`. | |
| account | No | NOT a filter on this tool. Pass the chart-of-accounts code as `accountCode` (exact). | |
| category | No | NOT a filter on this tool. Pass the category by its chart-of-accounts code as `accountCode` (exact) — get codes from expense_accounts. | |
| supplier | No | NOT a filter on this tool. A posted expense carries no party field here — search the supplier name as `text` (it matches the memo/description) or as `reference`. | |
| maxAmount | No | ||
| minAmount | No | ||
| reference | No | Partial supplier/receipt reference (case-insensitive contains). | |
| accountCode | No | Restrict to one expense category by its account code (exact). | |
| description | No | NOT a filter on this tool. Pass memo text as `text`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true, so the read-only safety is already known. The description goes further by explaining return-row composition, newest-first ordering, capping with a `more` flag, and the exact meaning of an empty `attachments` list versus an unknown state. This is valuable behavioral context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average, but every sentence carries operational meaning: search scope, return shape, attachment semantics, attachment retrieval, double-booking prevention, and sibling-tool distinction. Information is front-loaded and logically ordered, though it could be tightened slightly without losing value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully compensates by specifying the returned fields, ordering, cap behavior, and attachment representation. It also covers the key workflow context (check before create_expense_draft) and the relationship to get_attachment and search_documents. Nothing essential is missing for an agent to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers 75% of parameters with useful descriptions, including NOT-filter disclaimers for ref, memo, vendor, and similar aliases. The description adds the 'any combination' semantics and maps search concepts like reference, text, date range, amount range, and category account onto the intended use. It does not enumerate every parameter name, but the schema plus description together are clear.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action and resource: search expenses that are already posted/paid in the /expenses ledger. It lists the exact filter dimensions and explicitly distinguishes itself from search_documents, which does not cover paid expenses. This gives an agent a precise, non-ambiguous purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: check this tool before creating an expense draft to avoid double-booking a receipt, and use get_attachment to read attachments. It also names search_documents as a non-alternative, making the usage boundary crystal clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_journalSearch journal entriesARead-onlyInspect
Search the general ledger's JOURNAL ENTRIES — every posting, whatever door created it (a manual journal, a scanned receipt, an invoice, a bank row, a paid expense, payroll, depreciation…). Returns each entry WITH its balanced lines (account code + name, debit, credit, line description), so you can see exactly HOW something was booked, not just that it exists. Filter by any combination of: date range, text (case-insensitive, matched against the entry memo AND its line descriptions), accountCode (entries touching that account), source (the door that created it), and an amount range on the entry's total debits. Give at least one filter — this never dumps the whole ledger. Newest first, capped, with a more flag. Pairs with search_expenses for reconciling: search_expenses shows what the /expenses register holds, search_journal shows every ledger entry including journal-era postings the register never covered. Read-only — it changes nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | ||
| ref | No | NOT a filter on this tool. A journal entry has no reference field — search its memo and line descriptions with `text`. | |
| from | No | ||
| memo | No | NOT a filter on this tool. Pass memo text as `text` (it matches the entry memo AND every line description). | |
| text | No | Partial text matched against the entry memo AND its line descriptions (case-insensitive contains). | |
| party | No | NOT a filter on this tool. The ledger has no party column — search the name as `text`, or find the document with search_documents. | |
| refNo | No | NOT a filter on this tool. A journal entry has no reference field — search its memo and line descriptions with `text`. | |
| number | No | NOT a filter on this tool. A journal entry has no document number — search its memo and line descriptions with `text`, or find the document itself with search_documents. | |
| source | No | The door that created the entry (exact, case-insensitive). One of: MANUAL, SALE, PURCHASE, ADJUSTMENT, BANK, CREDIT_NOTE, DEBIT_NOTE, PAYROLL, IMPORTED_SERVICE, FX_REVAL, DEPRECIATION, ASSET_DISPOSAL, EXPENSE, LOAN, REVENUE_RECOGNITION, ASSET_ACQUISITION, BANK_OPENING. Omit for all. | |
| account | No | NOT a filter on this tool. Pass the chart-of-accounts code as `accountCode` (exact). | |
| maxAmount | No | Maximum entry size, measured on the entry's total debits (MYR). | |
| minAmount | No | Minimum entry size, measured on the entry's total debits (MYR). | |
| reference | No | NOT a filter on this tool. A journal entry has no reference field — search its memo and line descriptions with `text`. | |
| accountCode | No | Restrict to entries that have a line on this account code (exact). | |
| description | No | NOT a filter on this tool. Pass the text as `text` (it matches the entry memo AND every line description). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, and the description adds meaningful behavior: results are 'Newest first, capped, with a more flag', and each entry comes with balanced debit/credit lines. The only redundancy is the closing 'Read-only — it changes nothing', which merely restates the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average but front-loads the core purpose, then moves through return shape, filters, constraints, and sibling relationship. The enumeration of creation doors is slightly verbose but reinforces the 'every posting' scope; no wasted sentences overall.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 15-parameter tool with no output schema, the description covers the return payload, ordering, cap indicator, filter semantics, required filter, and sibling distinction. An agent has enough to select and invoke this tool correctly without opening the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 87%, so the baseline is solid; the description still adds value by explaining the filter contract ('any combination of'), clarifying text matches 'the entry memo AND its line descriptions', and requiring at least one filter. It also orients the amount range to 'the entry's total debits', which matches the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description opens with a specific verb-resource combination: 'Search the general ledger's JOURNAL ENTRIES — every posting, whatever door created it'. It further clarifies the output includes balanced lines, which differentiates it from a mere existence-check and from sibling search_expenses.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states 'Give at least one filter — this never dumps the whole ledger' and names the sibling alternative: 'Pairs with search_expenses for reconciling: search_expenses shows what the /expenses register holds, search_journal shows every ledger entry including journal-era postings the register never covered.' This gives clear selection criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
shoebox_itemsShoebox items waitingARead-onlyInspect
List the photos sitting in this company's SHOEBOX — paper the people in the business snapped on their phones and sent in, which NOBODY has read yet. This is the pile to work from when the user asks you to "do the receipts" or "clear the shoebox". Only items still waiting are listed: anything already booked or set aside is settled and deliberately absent. For each item you get its id, what kind of paper it is, the file name, type and size, when it arrived (Malaysia time) and who sent it in — never an amount, because nothing has been read. To SEE one, call get_attachment with owner:'shoebox' and the item's id; you read the photo yourself, on your own subscription — Taokeh does not OCR or interpret it for you. To BOOK one, file the matching draft (create_expense_draft, create_bill_draft or create_invoice_draft) with shoeboxItemId set to that id, and DO NOT re-send the photo: the server attaches its own stored copy, so it rides the draft and lands on the posted document on approval. An item already carrying a pending draft says so (pendingDraft) — file nothing more against it; correct the existing draft with revise_draft instead. An item TAOKEH itself is already reading says so too (beingRead): someone tapped "Book it" or asked Taokeh to book the shoebox, that read is paid for and its draft is waiting for the owner at the reviewPath given — file nothing against it either, and tell the user where it is waiting. Reading this pile costs the company no AI credits.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | How many waiting items to return, newest first. Defaults to 50. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true and the description reinforces a safe, read-only profile, adding a cost disclosure ('Reading this pile costs the company no AI credits'). It transparently discloses return-field semantics — explicitly 'never an amount' — plus the fact that Taokeh does not OCR/interpret photos and the agent must read them itself on its own subscription. The material is rich, though the corrupted run-on in the middle makes some disclosures harder to parse.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is far longer than necessary for a one-parameter list tool, and the middle section is visibly corrupted — repeated phrases, missing punctuation, and a broken 'two-send the photo' fragment. Purpose and cost notes are nicely front-loaded, but the excessive, garbled prose fails the conciseness test.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with one optional parameter and no output schema, the description covers the essential operational facts: exact fields returned, how to view an item, how to book it, and which state flags (pendingDraft, beingRead) to respect before acting. Nothing an agent needs to invoke it correctly is missing; the garbled prose hurts readability but not coverage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%: the single optional `limit` parameter is fully documented with default (50), maximum (200), and ordering ('newest first'). The tool description adds nothing about `limit`, so the baseline of 3 applies — the schema carries the load.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource — 'List the photos sitting in this company's SHOEBOX' — and sharply scopes it to items nobody has read yet. It differentiates from siblings by naming the viewing tool (get_attachment) and booking tools (create_*_draft), so an agent knows this is the unread-inbox list, not the action tool. The garbled middle section does not obscure this core purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit trigger phrases: 'the pile to work from when the user asks you to do the receipts or clear the shoebox'. Routes next steps to named alternatives (get_attachment to view, specific create_*_draft tools to book) and states exclusion rules (don't file against items with pendingDraft or beingRead). This is about as explicit as usage guidance gets.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stage_documentStage a historical documentAInspect
Bring ONE already-issued historical document (a sales invoice, or a supplier bill) across from the system this company is migrating FROM. This is the BULK migration door and it is NOT create_invoice_draft: nothing is posted, nothing is drafted for individual approval, and no approval card is raised per document. Staged documents group into monthly batches the owner reviews and approves together at /switch/documents. Use it ONLY for documents that were genuinely issued in the old system (or written in a paper book) — a NEW document belongs in create_invoice_draft / create_bill_draft. The lane must be OPEN (the owner turns it on at /switch → "Bring over your documents") and it can only be open before the books are locked; if it is closed the call is refused with instructions. IDEMPOTENCY IS ON sourceDocId: staging the same sourceDocId again UPDATES that pending row instead of filing a second one. Once the owner has ENTERED it, or deliberately LEFT IT OUT, nothing you send can change it, re-book it or bring it back — only the owner can. So re-running your whole export is always safe. THE CUTOVER: bring over only documents dated AFTER the company's accounting start date. Anything dated on or before it is already carried by the owner's opening balances, and Taokeh will flag it and refuse to enter it — staging those wastes both our time, so filter them out of your export if you can. ORDER MATTERS: stage the SUPPLIER BILLS for a period before the sales invoices for it, because Taokeh works out cost of sales from the stock that was bought. The server re-computes every quantity and the grand total from the lines (with SST); your own printed total goes in sourceTotal and is used ONLY to show the owner a tie against the server's figure. Anything the server cannot settle — a product that does not resolve, a customer name matching several contacts, a printed total that disagrees — is staged anyway, FLAGGED, and held out of bulk approve for the owner to open individually. The document keeps its ORIGINAL number (reference) and its ORIGINAL date, and posts marked as historical so Taokeh never e-invoices or chases it. A MARKETPLACE ORDER (Shopee, TikTok Shop, Shopify) brought over as an invoice: pass marketplaceOrderKey (e.g. SHOPEE-2608246V81G729) so it posts as that channel's sale and a later import of the same order is not booked twice — one order, one invoice; a key already booked or already staged is refused. PAPER: if you read this document off a photo or a scan, ATTACH IT — request_attachment_upload, PUT the bytes, pass attachmentToken here. The owner then reviews your figures beside the actual slip on that document's own screen, edits anything you misread, and approves it there; the original lands on the posted document. Without it they are approving your arithmetic on your word alone. Show your per-line working, and mark any balancing/catch-all line residual:true — a residual line carrying a material share of the total is flagged for the owner, because that is exactly where a misread hides. NOT EVERY LINE IS A CATALOGUED PRODUCT, and an old document is full of the ones that are not: a delivery or transport charge, a labour or installation line, a service fee, a rounding line. Those still go through as ordinary lines — productRef is REQUIRED and must not be blank, so put the line's OWN wording in it ("Delivery charge"), and Taokeh CREATES that wording as a non-stock item (MD, 2026-09-09) so the document derives normally — the row is flagged, the created item is named for the owner, and it carries no stock and no price of its own. That is the designed path: it is not an error, so do not drop the line, do not fold its amount into another line, and do not invent a SKU for it. Omitting productRef altogether is the one thing that fails — the call is refused at the boundary before anything is staged. ⛔ ROW KEYS ARE STRICT (MD, 2026-09-09): a key the schema does not list is REFUSED BY NAME — with the key it probably meant, e.g. sell_price → unitPrice — and NOTHING is staged. Send the contract's apiField, not the spreadsheet header. Unknown keys used to be dropped in silence, so a set could stage "successfully" with its prices missing; that is the bug this refusal closes. NOT IN THE CATALOGUE? IT IS NOW: a line whose productRef matches NOTHING is created as a NON-STOCK item named after the line's own wording (no stock, no price of its own, marked as created by the migration), so the document derives and the owner reviews figures instead of a blocked row. Tell the owner which items were added — the reply names them. A ref that matches SEVERAL products is still flagged rather than duplicated. ⚠ NOT ON A DOCUMENT THE CUTOVER FENCE REFUSES: Taokeh will not grow the catalogue for a document it can never enter, so a pre-cutover row stages with no figures and an unresolved-product note ALONGSIDE its fence. The fence is the blocker; the product note is a consequence of it. Do not call resolve_product for such a row, do not ask the owner to add a catalogue item for it, and never route it to the manual bill form or the expense door — the only answers are to leave it out or to have the owner move the accounting start date.
| Name | Required | Description | Default |
|---|---|---|---|
| lines | Yes | ||
| notes | No | A SHORT note for the owner about this document — one or two sentences naming anything they should check. Not a place for lengthy reasoning. | |
| party | No | The customer (sales invoice) or supplier (purchase bill) name as printed. Matched against the existing contacts; a name matching several, or one that looks like shorthand for an existing contact, is flagged for the owner rather than guessed. Omit for a walk-in cash sale with no named customer. | |
| docDate | Yes | The date the document was ORIGINALLY issued, YYYY-MM-DD. Never today unless it really was today — the whole point is that these documents keep their real dates. | |
| docKind | Yes | 'sales_invoice' = an invoice this company ISSUED to a customer. 'purchase_bill' = a bill a supplier issued TO this company (inventory lines only, same rule as create_bill_draft). Other kinds are not supported yet. | |
| partyId | No | The Taokeh customer/vendor id, when you already resolved it with resolve_customer / resolve_vendor. Wins over `party`. | |
| reference | No | The document number exactly as printed in the old system (e.g. INV1603). Taokeh keeps it, so the customer's copy and yours still match. It must be unique in this company: a number already on the books is refused by name. Leave it out only if the document genuinely has no number. Before the first such invoice is entered, the owner must confirm where the real invoice series continues; Taokeh never silently starts migrated books at INV1. | |
| needsReview | No | Set true when you want the owner to look at this one specifically. It is then held out of bulk approve and gets its own door. | |
| sourceDocId | Yes | The id this document has in the system you are migrating FROM — the primary key, row id or export id. THIS IS THE DEDUPE KEY: staging the same sourceDocId twice updates one row instead of booking the document twice, which is what makes re-running your export a no-op. If the old system has no id, use the document number, but use it consistently across the whole export. | |
| sourceTotal | No | The grand total PRINTED on the source document. Advisory only — Taokeh re-derives its own figure and shows the two side by side, so the owner can see a whole batch tie to the old system's report. Send it whenever the paper states one; it is the single most useful check in the whole migration. | |
| paymentMethod | No | 'CREDIT' (default) = it was on account, so it carries into receivables/payables. 'CASH' = it was settled at the counter. Get this right: it decides whether the document leaves a balance behind. | |
| attachmentToken | No | The ORIGINAL this document was read off — the photo of the handwritten cash-sale slip, the scanned invoice page. Call request_attachment_upload FIRST, PUT the raw bytes to its uploadUrl, then pass the token here (there is no inline base64 on this door: a backlog export would blow the context). The file rides the staged row, the owner sees it BESIDE your figures on the document's own review screen, and it lands on the posted invoice or bill automatically when they approve. Re-staging the same sourceDocId WITH a token replaces the file; re-staging without one leaves the file already there untouched, so a bare filing is repairable. | |
| marketplaceOrderKey | No | SALES INVOICES ONLY, and only when the invoice WAS a marketplace order: the channel plus the order's own number, exactly as Taokeh's channel import keys it — 'SHOPEE-<order number>' (e.g. SHOPEE-2608246V81G729), 'TIKTOK-<order ID>' (digits), or 'SHOPIFY-<order ID>' (the numeric id in the Shopify admin address /admin/orders/<id>, NOT the '#1001' order name). It is posted as the sale's order reference and channel, so the invoice counts as a Shopee/TikTok/Shopify sale everywhere and a later live import of the same order is recognised instead of booked twice. A malformed key, a key already on an invoice in the books, or a key another staged document already carries is REFUSED and nothing is staged. A Shopee order number starts with the order's date (YYMMDD); when that is not docDate the document is FLAGGED for the owner (not refused) — check which date is right. Keep `reference` for the invoice's own number. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the coarse safety profile (readOnlyHint=false, destructiveHint=false, openWorldHint=false), so the description carries real weight here and delivers: the open-lane gate, the cutover fence, per-line residual flagging, strict row-key refusal by name, marketplace-order dedupe, and the attachment review flow. The only blemish is that 'IDEMPOTENCY IS ON sourceDocId ... staging the same sourceDocId again UPDATES that pending row' sits uneasily with idempotentHint=false; the two are reconcilable (the row is mutated, not a no-op) but the tension is never acknowledged.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and the sibling exclusion are front-loaded, but the body is roughly 900 words in a near-unbroken block, dense with ALL-CAPS and emoji markers. Guidance is materially repeated: the non-catalogued-product path is explained once under 'NOT EVERY LINE IS A CATALOGUED PRODUCT' and again under 'NOT IN THE CATALOGUE? IT IS NOW', and the pre-cutover rule recurs in two places. An agent has to read the whole thing to be sure nothing unique is buried in the second telling.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 13-parameter, no-output-schema mutation tool that gates a financial migration, the description closes nearly every operational gap: lane state, cutover, ordering, error/refusal modes, attachment workflow, and what happens to flagged rows. The one thing left thin, given there is no output schema, is what a successful response returns — it is only glimpsed in passing ('the reply names them', staged-with-a-note), which an agent needs to report results back to the owner.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 92% schema coverage the baseline would be 3, but the description adds adjudication the schema cannot: productRef is REQUIRED and must carry the line's own wording for non-stock lines so Taokeh creates an MD item, strict row keys are refused by name with a suggested correction ('sell_price' → 'unitPrice'), and sourceTotal is advisory-only tie-out against the server's re-derived figure. It also explains the marketplaceOrderKey grammar and the paymentMethod consequence ('it decides whether the document leaves a balance behind') in terms of outcome rather than field definition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb and resource ('Bring ONE already-issued historical document ... across from the system this company is migrating FROM') and immediately draws the boundary against the sibling an agent would most plausibly confuse it with ('it is NOT create_invoice_draft: nothing is posted, nothing is drafted'). The sales-invoice vs supplier-bill scope, the monthly-batch grouping, and the /switch/documents review surface make it unmistakable among the ~70 siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('documents that were genuinely issued in the old system'), when-not ('a NEW document belongs in create_invoice_draft / create_bill_draft'), and preconditions (the lane must be OPEN and pre-lock, otherwise 'the call is refused with instructions'). It goes further with process-level guidance an agent would otherwise get wrong: filter to post-cutover dates, stage supplier bills before sales invoices for a period, and do not route pre-cutover rows to resolve_product or the expense door.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stage_master_dataStage master dataAInspect
Bring this company's MASTER DATA across from the system it is switching FROM: its CHART OF ACCOUNTS (every ledger code it keeps its books in), its PRODUCT CATALOGUE (every item it buys, stocks or sells, with the stock each was carrying at the cutover) or its CONTACT BOOK (every customer and supplier). Call intake_contract(doc_type: 'accounts' | 'products' | 'contacts') FIRST — it gives the exact fields, the rules and this company's own current counts. This WRITES NOTHING: the rows land on the owner's existing Import screens, where they confirm the columns and import them. THE UNIT IS THE WHOLE LIST: send every row of one kind in ONE call, and calling again for the same kind REPLACES the entire staged set rather than adding to it. Found a mistake? Send the corrected list in full. UNLIKE stage_opening_balances there is NO 'already imported' refusal, because master data is a CATALOGUE and not a ledger: the product upsert key is the SKU (an existing SKU is updated in place and ONLY the fields you actually sent are changed — a corrected three-field price list leaves its unit, barcode and buying-unit conversion untouched — its on-hand quantity is never touched by an import, and an opening quantity on a SKU that already exists is ignored), and contacts are de-duplicated case-insensitively by name — so staging a corrected list after an import is normal and double-counts nothing. TWO PARTS TOUCH THE BOOKS: OPENING STOCK — a NEW SKU with both an opening quantity and a cost posts a real opening-stock journal entry (Dr Inventory / Cr Opening balance equity) when the owner approves, dated at this company's accounting start date while it is still migrating — and A CHANGED COST on a SKU that already exists, on perpetual moving-average stock (on FIFO stock that row's cost is refused and left as it is, except a migrating item's opening stock — tell the owner to correct the bill's unit cost instead): Taokeh books the change in the stock's value (against cost of goods sold, dated that day; or, for an item that so far has only its opening stock while the company is still migrating, against Opening balance equity on the opening stock's date), and the review screen shows each amount before the owner imports. Send the cutover quantity and the cutover cost, never a guess — a zero or missing cost still seeds the quantity but posts nothing. PRESENTATION FIELDS DO NOT MIGRATE: web addresses, storefront visibility and product images stay with the website, there is no field for them here, and you should TELL THE OWNER to keep those before the old system is switched off. A NEW product starts HIDDEN from this company's own Taokeh web shop; the owner shows it from its product page. Bill-of-materials links (combination SKUs built from components) are a separate sheet with their own review step and are NOT staged here — the owner uploads that sheet on the Import products page. Every contact row must say which side it is on (isCustomer / isVendor, or defaultRole for the whole list); Taokeh names any row that says neither straight back to you, so fix those before the owner sees the screen. THE CHART OF ACCOUNTS ('accounts') is the step to do FIRST, because the trial balance and every document afterwards land on its codes — and it is the quietest of the three: it writes NO journal line and carries NO figures, so opening balances still come across separately with stage_opening_balances(kind:'trial_balance'). Its key is the account CODE: an existing code is updated in place and keeps every posting attached to it, a new code is created, a code is never rewritten, and NOTHING IS EVER DELETED OR DEACTIVATED — an account missing from your list is simply left alone, so do not imply to the owner that it will disappear. A RENAME is always allowed on every account, including the ones Taokeh posts to automatically. A CHANGE OF TYPE OR NORMAL BALANCE IS REFUSED, row by row, on any account that already has postings or that a system role, a linked bank account or card, or a live fixed asset or loan depends on, because it would silently restate reports the owner has already read and filed; intake_contract(doc_type:'accounts') marks exactly which accounts those are, so shape the list to avoid the refusal, and when a reclassification really is wanted, tell the owner the honest route — a new account plus a dated reclassification journal (create_journal_draft). THE COMPANY SETUP ('settings') is the LAST MILE: once the books are across, this is how the company itself stops being a form the owner types — its registered name, SSM registration number, TIN, address, state, statutory officer, the language it reads in, the measurement profile it prices by, and the date its books start. IT IS GOVERNED BY THE STRICT ALLOWLIST returned by intake_contract(doc_type:'settings'), and nothing outside it can be created: call intake_contract(doc_type:'settings') for the list, each key's meaning, its current value, and the settings an AI may NEVER write — payment credentials (a secret is never AI-written), publishing a storefront and advertising consent (outward and consent acts the owner takes), the AI and legal terms acceptances (an AI must never accept terms on the owner's behalf, least of all its own), the books lock (a money-integrity control) and every e-Invoice setting (they change statutory filing behaviour and follow the owner's own LHDN status). Those are refused BY NAME with the reason so you can tell the owner what to set rather than retrying. SEND ONLY WHAT THE OWNER TOLD YOU OR WHAT THE OLD SYSTEM PRINTS — an inferred TIN, registration number or employer number passes this import and fails at LHDN months later on a document that has already gone out; a blank is honest. The accounting start date is the highest-consequence key (it defines what counts as before the books started) and is refused if the company already has posted entries or stock movements dated earlier; the measurement divisor is a NUMBER, not a label. Nothing is ever cleared: a setting missing from your list is left alone, and a row with a blank value is refused rather than read as 'erase this'. THE STAFF LIST ('employees') is the fifth kind and the one that behaves differently in two ways you must say out loud to the owner. It is ADMIN-ONLY — salaries and IC numbers are the most sensitive data in a company, and a non-admin connection is refused with the reason, deliberately. And ITS IMPORT CREATES RATHER THAN UPSERTS: employees have no unique key in Taokeh, so every ready row becomes a NEW person, anyone already in Taokeh must be left OFF the list, and a change to somebody who exists is made on their own employee page instead, or proposed by your AI with update_employee_draft. Use it for a whole payroll register being migrated AND for a single new hire the employer just described. SEND ONLY WHAT THE EMPLOYER ACTUALLY TOLD YOU: never construct or infer an IC number, a salary, a bank account, a date of birth or a statutory reference — a blank field is honest and gets filled in, a guessed one passes silently and then follows that person into EPF, SOCSO and LHDN submissions that have already gone out. A foreign employee has a passport and countryCode instead of an IC, with isMalaysian:false. The statutory flags DEFAULT ON when omitted (EPF, SOCSO, EIS, PCB, Malaysian, resident), exactly as the new-employee form does, so send false only on the employer's own word. Salary is MONTHLY and in ringgit. A date spoken loosely ('starting Monday') is yours to resolve to YYYY-MM-DD and to confirm back to them. Only ACTIVE staff consume a payroll seat: rows past the plan's allowance are reported as skipped at import rather than created, and historical leavers come across with status RESIGNED. This creates records only — it pays nobody, posts no journal line and files nothing; a payroll run is a separate act the owner takes afterwards. ⛔ ROW KEYS ARE STRICT (MD, 2026-09-09): a key the schema does not list is REFUSED BY NAME — with the key it probably meant, e.g. sell_price → unitPrice — and NOTHING is staged. Send the contract's apiField, not the spreadsheet header. Unknown keys used to be dropped in silence, so a set could stage "successfully" with its prices missing; that is the bug this refusal closes.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | 'products' = the item list / catalogue. 'contacts' = the customer and supplier book. 'accounts' = the chart of accounts. 'settings' = the company setup; its complete stageable and owner-only classification is returned by intake_contract(doc_type:'settings'). 'employees' = the staff list; admin-only and create-only. | |
| rows | Yes | EVERY row of the list, in the order the export prints them. This replaces any previously staged set for this kind. | |
| sourceName | No | What you read — the export's file name or the report's own title, e.g. 'Item list export (Financio) — 412 items'. Shown to the owner as the evidence for what they are approving. | |
| defaultRole | No | contacts only: the side a row that carries neither isCustomer nor isVendor lands on — the same choice the upload page offers as a Customers / Suppliers radio. Send it when the export is a single-sided list. Per-row flags always win over it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only supply readOnlyHint=false and idempotentHint=false; the description goes far beyond them, disclosing that staging writes nothing until the owner imports, that a second call REPLACES the whole staged set, that unknown row keys are refused by name with a suggested key, that type/normal-balance changes are refused row by row, and exactly which settings an AI may never write and why. It also spells out the consequences of a changed cost (journal entries, FIFO refusal) and what presentation fields do not migrate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The single most important constraint (call intake_contract first, whole-list replacement) is front-loaded, but the body is a very long unbroken block using all-caps, bold and an emoji, with all five kinds and dozens of edge rules threaded into one paragraph. It is dense with genuinely useful content, yet it is roughly an order of magnitude longer than needed and would benefit from being split per kind.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with five heterogeneous kinds and a very large row schema with no output schema, the description covers sequencing, refusals, defaults, what must never be inferred, what the owner must be told, and what the tool does not do (pays nobody, posts no journal line, files nothing). Nothing an agent needs in order to call it correctly appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, but the description adds set-level meaning the schema cannot: the unit is the whole list, a repeat call replaces rather than appends, the contract's apiField must be used rather than the spreadsheet header, and rows with neither isCustomer nor isVendor fall to defaultRole. It also explains the SKU/code upsert keys and their non-destructive merge behaviour.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (stage MASTER DATA across from the old system) and enumerates the five kinds it covers: chart of accounts, product catalogue, contact book, company settings and staff list. It explicitly separates itself from stage_opening_balances, stage_document and update_employee_draft, so an agent can route without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit prerequisite ("Call intake_contract(...) FIRST"), an ordering rule ("THE CHART OF ACCOUNTS is the step to do FIRST"), and named alternatives with the condition that selects them (stage_opening_balances for trial balance, create_journal_draft for reclassification, update_employee_draft for changes to existing staff). It also names when NOT to include rows (anyone already in Taokeh must be left OFF the employee list).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stage_opening_balancesStage opening balancesAInspect
Bring this company's OPENING BALANCES across from the accounting system it is switching FROM: its closing TRIAL BALANCE, its AGED RECEIVABLES (invoices customers still owed) or its AGED PAYABLES (bills still owed to suppliers). Call intake_contract(doc_type: 'trial_balance' | 'aged_receivables' | 'aged_payables') FIRST — it gives the exact fields, the rules, and for the trial balance this company's own chart of accounts. This posts NOTHING: the rows land on the owner's existing "Switch to Taokeh" review screens, where they match each account to their chart (trial balance) or check each party (aged lists) and post it themselves. THE UNIT IS THE WHOLE SET: send every row of one kind in ONE call, and calling again for the same kind REPLACES the entire staged set rather than adding to it — a trial balance only balances as a whole, so a partial patch would produce a set that ties to no report. Found a mistake? Send the corrected set in full. Taokeh re-derives every total from the rows you sent and shows it beside the total you say was printed on the report, so the owner can see the set tie to the cent; send that printed total in sourceTotal, and never adjust a row to make a total work — report the gap instead. THESE ARE THE CUTOVER, so unlike stage_document there is no "too old" fence: an aged invoice dated before the accounting start date is exactly right, and that is what makes it an opening document. REFUSED while the books are LOCKED, while the owner is part-way through entering the staged set, and for an AGED list whose opening documents are ALREADY POSTED (staging over them would double-count — only the owner can undo them). A TRIAL BALANCE may be staged when opening balances are already saved (a starting-money save counts): the owner's approval REPLACES the whole saved opening entry, the review screen says so first, and the answer tells you how many saved lines that is — say it to the owner. A trial-balance row on a bank whose opening balance is already in Banking is refused when the owner posts (the same money twice): send it anyway and tell the owner to skip it. A trial-balance row with a figure on a FOREIGN-CURRENCY bank or card (its code or name is that account's) is REFUSED here: Taokeh posts only ringgit accounts for now, so take the row out and tell the owner to post its ringgit value as a journal against Opening Balance Equity. A trial-balance row with a figure NAMED like one of Taokeh's own zero-balance clearing accounts (Contra Clearing, Refund Clearing) is REFUSED too: take it out and ask the owner which account its amount belongs on. A CREDIT balance on an aged list (an overpayment, an unapplied credit note) is not an opening invoice or bill: Taokeh will not enter it, so leave those rows out and TELL THE OWNER they exist, naming the party and the amount, so they can enter them by hand. ⛔ ROW KEYS ARE STRICT (MD, 2026-09-09): a key the schema does not list is REFUSED BY NAME — with the key it probably meant, e.g. sell_price → unitPrice — and NOTHING is staged. Send the contract's apiField, not the spreadsheet header. Unknown keys used to be dropped in silence, so a set could stage "successfully" with its prices missing; that is the bug this refusal closes.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | 'trial_balance' = the closing balances of every account in the old system. 'aged_receivables' = the invoices customers still owed at the cutover, one row each. 'aged_payables' = the bills still owed to suppliers, one row each. | |
| rows | Yes | EVERY row of the set, in the order the report prints them. This replaces any previously staged set for this kind. | |
| sourceName | No | What you read — the export's file name or the report's own title, e.g. 'Trial Balance as at 31-12-2025 (Financio)'. Shown to the owner as the evidence for what they are approving. | |
| sourceTotal | No | The grand total PRINTED on the report (for a trial balance, the DEBIT total). Advisory: Taokeh re-derives its own figure and shows the two side by side. This is the single most useful check in the whole migration — send it whenever the report states one. | |
| sourceTotalCredit | No | trial_balance only: the CREDIT total printed on the report, if it is stated separately. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations present the bar is lower, and the description adds a great deal beyond them: replace-not-append semantics, refusal conditions, foreign-currency and clearing-account row handling, credit-balance exclusion, and the row-key strictness. However several of these (replace semantics for a non-idempotent tool, refusal-on-post for banks) are already implied or partially covered by the annotations' mutation profile, and the sheer volume of edge cases, while valuable, partly overlaps the intake_contract contract it tells the agent to fetch. Solid additive context, but not leanly delivered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and the mandatory intake_contract step, then proceeds through refusals and edge cases in a logical order. The prose is dense and heavily capitalized for emphasis, and a few clauses restate the same point (whole-set/partial-patch is said twice), but nearly every sentence carries actionable information for a genuinely complex tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a 5-parameter mutation tool with no output schema and a nested rows array, the description covers the prerequisites, the whole-set replacement model, the exact refusal matrix, currency and clearing-account exclusions, and what happens after staging. An agent could call this correctly without further documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3 and the schema already defines every field precisely. The description still adds real meaning beyond it: sourceTotal is framed as the single most useful migration check, printed totals must never be reconciled by fudging rows, and row keys must be the contract's apiField rather than spreadsheet headers. It complements rather than repeats the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (stage) and resource (opening balances) with the precise scope: closing trial balance, aged receivables, or aged payables carried over from the previous system. It explicitly distinguishes itself from stage_document (no 'too old' fence) and routes the agent to intake_contract, so an agent can tell it apart from siblings without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit sequencing ('Call intake_contract(doc_type: ...) FIRST'), explicit when-not conditions (refused while books are locked, mid-entry, or aged documents already posted), and an explicit contrast with stage_document for old-dated rows. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stock_levelStock levelsARead-onlyInspect
How many units of a product you have on hand right now — search by product name, SKU or exact barcode. An item marked "Service — no stock" comes back with isService:true and onHand:null — it holds no stock, so never report a quantity for it. A placeholder Taokeh made to carry document lines (SHOPEE-ITEM / TIKTOK-ITEM / SHOPIFY-ITEM, a POS summary item, an item added during migration, a scanned line) comes back with isPlaceholder:true and onHand:null the same way: it is not stock, whatever figure it holds. onHand is the company total. On a company that keeps stock at more than one place, product.locations lists each location ({location, isDefault, onHand}); a location with none of it reads 0, and the list is omitted entirely on single-location books. The locations need not add up to onHand. LESS than onHand: the difference is on record at no location (stock booked before locations were set up). MORE than onHand: a sale took more than the location it was made from held, so the extra came off the total but no shelf; onHand is the figure the books value. Say which, rather than picking one. If this company uses Counter (the till), the figure is as at the last day-close: counter sales move stock once, when the day is closed, not at each scan.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=true and openWorldHint=false; the description goes well beyond that, disclosing that figures are stale to the last day-close on Counter-enabled books, that locations may not reconcile to onHand in either direction, and that isService/isPlaceholder rows must never be reported as quantities. That is real behavioral context an agent could not get from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded, which is good, but the body is a dense single block covering six or seven distinct rules with no structure, and includes an instruction to the agent ('Say which, rather than picking one'). Substantively each point earns its place, but the packaging makes the important edge cases (isService, isPlaceholder, day-close) hard to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description rightly carries the return-field semantics (onHand, isService, isPlaceholder, product.locations and its omission on single-location books) and does so thoroughly. The remaining gap is result cardinality — how many matches come back for a name search and what an empty result looks like.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% for the single `query` parameter, so the description must compensate — and it does, stating that the string may be a product name, a SKU, or an *exact* barcode. The 'exact' qualifier is meaningful (it tells the agent barcode matching is not fuzzy), though it doesn't say what happens on multiple or zero matches.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource: 'How many units of a product you have on hand right now', and adds the lookup keys (name, SKU, exact barcode). It never names a sibling (low_stock, stock_movements, resolve_product) so the distinction from those tools rests on inference, but the purpose itself is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The lookup keys imply how to search, and the Counter/day-close note hints at when the figure is stale, but there is no explicit 'use this when X, use stock_movements when Y'. Ambiguity handling (multiple name matches, no match) is also unaddressed, leaving usage to be inferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stock_movementsStock movementsARead-onlyInspect
Read the STOCK MOVEMENT LEDGER in bulk — every recorded change to on-hand quantity, across products, in one call (and every changed cost: a revaluation row has qtyDelta 0 and carries the NEW cost in unitCost — the owner changed the item's average cost and Taokeh booked the difference that day). stock_level says how many you have RIGHT NOW for one product; this says WHAT MOVED, WHEN, WHY and WHAT IT LEFT BEHIND. Use it for stock-turn analysis, shrinkage hunting, reorder timing, or reconstructing how a balance got where it is. Every filter is optional and a bare call is a legitimate "what moved lately" pull: narrow with product (case-insensitive contains against the product name OR SKU), from/to (a window on each row's docDate) and sourceType (exactly one cause — see the list). Each row: ts (when it was posted), docDate (the document's own date, YYYY-MM-DD — a voided or edited bill's reversal keeps the bill's date, while a voided credit or debit note's reversal is dated the day of the void, like its journal entry; where no document survives, the Malaysian day it was posted), product {sku, name}, qtyDelta (signed — negative took stock out), balanceAfter (on-hand immediately after that move, as the posting path recorded it), unitCost, reason (free text the person typed, where there was one), sourceType and sourceId (the id of the document that moved it — pair it with search_documents to see which one), and editReversal: true on a purchase_void / sale_void row written by an EDIT, not a void — never count it as one. When the edit kept the product, its repost is the purchase / sale row at the same ts; when the edit removed it, there is no repost and the quantity really left. ⚠ WHICH MOVEMENTS EXIST AT ALL DEPENDS ON THIS COMPANY'S STOCK MODE, and the answer says which mode it is in (stockMode) with a note. In modified_periodic — the mode of companies created before 28 Sep 2026 unless they switched; newer companies start perpetual — selling does NOT move stock: invoices, delivery orders and credit notes write no movement row, and stock is trued up at stock take. Seeing no 'sale' rows there means the company is periodic; it does NOT mean nothing was sold, and it is NOT shrinkage. Only a perpetual company ORIGINATES sale / credit_note rows — but a periodic company can still HOLD them, and can still gain new ones: rows written while it ran perpetual stay, and a VOID takes its truth from the document's own movement rows rather than from today's setting, so voiding a perpetually-booked invoice or supplier return writes a fresh sale_void / debit_note_void row in a company that is periodic now. So a periodic company with movement rows is not a contradiction and not a bug. Read the mode before you interpret the rows, and read a row's own sourceType before you attribute it to the mode. balanceAfter is the total across the whole company, not per location; on a multi-location company each row also carries location. A stock take of ONE location can write a PAIR of stock_take rows with the same sourceId and opposite qtyDelta — one at the location, one with no location. Its reason is 'Stock take · : ' followed by 'N sold earlier from no shelf', 'N placed from no location', 'N written off from no location', or 'P placed from no location, W written off from no location'. Whatever its words, any such stock_take pair (opposite qtyDelta under one sourceId and product) nets to zero: it corrects where the stock sits on the shelves, not how much there is — the take's other rows carry its real change. Never count the pair as stock coming in or going out; sum qtyDelta per sourceId and product. An adjustment row with no location on a multi-location company can be a write-off of stock that was on record at no location, a move made before the company had locations, or a move at a location that has since been deleted. Rows come back newest first, capped at 200 with total, shown and more — when more is true, narrow by date and pull the periods in turn rather than treating a partial page as the whole. Values are verbatim as recorded at the time, never re-derived. ⛔ WHAT IT WILL NOT DO: it does not value your inventory (balance_sheet does), does not compute COGS or margin (income_statement, profit_drivers), and does not tell you what to reorder (low_stock). Nothing is written, and no draft is created.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Latest docDate (YYYY-MM-DD; the document date, else the Malaysian posting day). | |
| sku | No | NOT a filter on this tool. Pass it as `product` — one case-insensitive contains matched against BOTH the product name and the SKU. | |
| date | No | NOT a filter on this tool. Pass a date WINDOW as `from` and/or `to` (YYYY-MM-DD). A single date is `from` and `to` set to the same day. | |
| from | No | Earliest docDate (YYYY-MM-DD; the document date, else the Malaysian posting day). The main way to break a >200-row pull into honest slices. | |
| item | No | NOT a filter on this tool. Pass it as `product` — one case-insensitive contains matched against BOTH the product name and the SKU. | |
| name | No | NOT a filter on this tool. Pass it as `product` — one case-insensitive contains matched against BOTH the product name and the SKU. | |
| type | No | NOT a filter on this tool. Pass the movement cause as `sourceType`. | |
| limit | No | NOT a filter on this tool. The page size is fixed at 200 movements. Narrow with from/to, product or sourceType and pull the periods in turn. | |
| query | No | NOT a filter on this tool. Pass it as `product` — one case-insensitive contains matched against BOTH the product name and the SKU. | |
| dateTo | No | NOT a filter on this tool. Pass the end of the window as `to`. | |
| reason | No | NOT a filter on this tool. `reason` is free text the person typed and is RETURNED on each row, not a filter. To narrow by cause use `sourceType` (e.g. 'adjustment'). | |
| source | No | NOT a filter on this tool. Pass the movement cause as `sourceType`. | |
| product | No | Case-insensitive contains matched against the product NAME or SKU. Omit for every product. | |
| dateFrom | No | NOT a filter on this tool. Pass the start of the window as `from`. | |
| productId | No | NOT a filter on this tool. This search matches product TEXT, not ids — pass the name or SKU as `product`. | |
| sourceType | No | Exactly one movement cause. One of: purchase, purchase_void, debit_note, debit_note_void, adjustment, stock_take, import_opening, assembly_out, assembly_in, disassembly_out, disassembly_in, sale, sale_void, credit_note, transfer_out, transfer_in, revaluation. Note that sale, sale_void, credit_note are only ever CREATED by a perpetual company — a company that has since switched to periodic still holds the ones it wrote (and a void of one of those documents still writes its reversal leg). | |
| productName | No | NOT a filter on this tool. Pass it as `product` — one case-insensitive contains matched against BOTH the product name and the SKU. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true and openWorldHint=false. The description goes far beyond, disclosing field semantics (qtyDelta signed, balanceAfter as recorded), edge cases (editReversal on void rows, stock_take pairs netting to zero, adjustment ambiguity), stock-mode caveats (modified_periodic vs perpetual), pagination behavior ('capped at 200 with total, shown and more'), and the guarantee that 'Nothing is written, and no draft is created.'
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but front-loads the core purpose and then organizes caveats into clear sections. Given the absence of an output schema, much of the length is justified to explain row semantics and edge cases. However, some parenthetical details (e.g., date nuances for voided documents) could be tightened, so it is not maximally concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully explains the return value: field-by-field row structure, ordering (newest first), pagination (capped at 200 with total/shown/more), and the meaning of stockMode. It also clarifies what the tool does not return and routes to siblings for those needs, leaving no essential gap for an agent to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all filter parameters; baseline is 3. The description repeats the filtering semantics ('product' case-insensitive contains name or SKU, 'from'/'to' as a docDate window, 'sourceType' exactly one cause) without adding syntax or format details beyond the schema. It does not mention the many decoy parameters the schema warns about, but those are schema-level concerns.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Read the STOCK MOVEMENT LEDGER in bulk — every recorded change to on-hand quantity, across products, in one call.' It explicitly distinguishes from sibling stock_level ('says how many you have RIGHT NOW for one product; this says WHAT MOVED, WHEN, WHY and WHAT IT LEFT BEHIND') and names other non-overlapping siblings (balance_sheet, income_statement, profit_drivers, low_stock) in its exclusions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete use cases (stock-turn analysis, shrinkage hunting, reorder timing, reconstructing balances) and states that every filter is optional with a bare call being a legitimate 'what moved lately' pull. It explicitly names when NOT to use it and which sibling to use instead: 'does not value your inventory (balance_sheet does), does not compute COGS or margin (income_statement, profit_drivers), and does not tell you what to reorder (low_stock).'
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tax_positionTax positionARead-onlyInspect
Your current tax posture in one read: (1) SST — the current bi-monthly period's SST payable if you're SST-registered (registration 'none' ⇒ not registered, nothing to remit); and (2) e-invoice consolidation — whether monthly consolidation is on, the open month, last month's filing due date, and whether last month's consolidated document is generated / LHDN-validated. Basis: SST-02 return figures + the consolidated-e-invoice register.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true, and the description reinforces this with 'one read' and 'Basis: SST-02 return figures + the consolidated-e-invoice register.' It adds meaningful conditional behavior about unregistered users and the e-invoice consolidation statuses, going beyond what the annotation alone conveys.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a front-loaded summary sentence and numbered components. Every clause adds useful information, and the parenthetical clarifications are compact rather than redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only status tool with no output schema, the description is complete: it names both reporting areas, the source basis, the conditional SST behavior, the open period, due date, and validation status. An agent has enough to know what this tool returns and when the data is meaningful.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and the schema coverage is effectively 100%, so there is no parameter-meaning burden for the description to carry. The baseline for a no-parameter tool is 4, and the description does not need to add anything further.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear verb and target ('Your current tax posture in one read') and then enumerates the two specific components covered: SST payable and e-invoice consolidation. This makes the tool's scope unmistakable and distinct from the many summary siblings like cash_position or business_snapshot.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly implies this is the tool for a consolidated tax-status check, and it clarifies the conditional handling for SST registration ('registration 'none' ⇒ not registered, nothing to remit'). However, it does not explicitly name alternative tools or state when not to use it, so it misses the explicit exclusion guidance that would earn a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
trial_balanceTrial balanceARead-onlyInspect
Your trial balance as of a date (default today, Malaysia time) — the same figures as Reports → Trial Balance. Each row is an account with a posting on or before asOf: code, name, type, debit / credit (the account's NET posting placed on the side it landed — a contra account shows on its natural side, so the debit column adds up to the credit column) and balance (the account's balance signed by its statement section — positive is normal: a debit balance for assets and expenses, a credit balance for liabilities, equity and revenue; it equals general_ledger's closing for that account from the first entry to asOf). Accounts with nothing posted are left out, as on the page, and counted in accountsWithNoPostings. totals gives the debit and credit column sums and balanced (true when they agree to the sen). An entry dated after asOf is never included. There is no year-end closing journal in these books, so revenue and expense accounts carry their balance since the first entry — the balance_sheet tool closes the year by arithmetic; this tool does not.
| Name | Required | Description | Default |
|---|---|---|---|
| asOf | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint/openWorldHint; the description adds substantial behavioral detail beyond them — default date and timezone, exclusion of accounts with no postings (and their count in accountsWithNoPostings), the hard rule that entries after asOf are never included, no year-end closing journal, and the sen-level balancing guarantee. It also explains contra-account placement so the debit/credit columns are interpretable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and default, then row shape, then totals, then exclusions, then the balance_sheet contrast — a sensible ordering. It is dense and the contra-account parenthetical plus the closing year-end note are long, though both earn their place by preventing misreading of the numbers.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, and the description compensates fully by enumerating every row field (code, name, type, debit/credit, balance) with its sign convention, plus totals, balanced, and accountsWithNoPostings. An agent can both call this tool and interpret its response without further documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must carry the parameter, and it does: asOf is described as a date with a default of today in Malaysia time, which the bare pattern in the schema does not convey. The exact date format is only implied by the schema pattern, not restated, keeping this just short of a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource (trial balance) with an explicit scope (as of a date, default today, Malaysia time) and anchors it to a concrete UI equivalent (Reports → Trial Balance). It is immediately distinguishable from balance_sheet, general_ledger, and income_statement, which are named directly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly contrasts behavior with two siblings: it says balance_sheet closes the year arithmetically while this tool does not, and that each balance equals general_ledger's closing for that account. It stops short of a prescriptive 'use this tool when X, use that when Y' routing sentence, so an agent still has to infer the choice rather than being told it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_bill_draftFile a pending correction draft for a posted bill (admin approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose a correction to a supplier bill ALREADY POSTED in Taokeh — the door for a bill that went onto the books with a wrong line, a wrong quantity or cost, the wrong date, the wrong payment term or the wrong payment method. To change, add or remove LINES, read them first with search_document_lines (docType 'bill') and NAME each line you keep with its lineNo (or send { keep: } for a line that is already right): a line with a lineNo IS that line of the bill, a line without one is a NEW line, and a line you leave out is REMOVED — on a bill that received goods against a purchase order, a removed line's goods go back to being owed on the order (see lines). It files a pending DRAFT an ADMIN reviews as a DIFF (current → proposed) and approves; only that tap rewrites the bill, and it keeps the SAME bill number, the original attached document, and the link to the purchase order the bill was converted from. Pass purchaseId, the bill's REAL id from search_documents — a bill number is refused, because two suppliers can print the same number and correcting the wrong one is worse than asking. ROUTING — three different mistakes, three different doors. (1) THE PURCHASE NEVER HAPPENED (keyed twice, entered on the wrong company, the wrong supplier's bill): the owner VOIDS it — Purchasing → the bill → Void, in Taokeh. There is deliberately NO AI lane for voiding. Tell them where to click. (2) THE PURCHASE HAPPENED BUT THE BILL IS WRONG (a wrong line, quantity or unit cost, the wrong date, term, due date or payment method): THIS tool. (3) SOMETHING CHANGED AFTER THE PURCHASE (goods went back to the supplier, the supplier credited a price difference later): a DEBIT NOTE against the bill, which the owner raises in Taokeh (Purchasing → Debit notes) — a real event that belongs on the books as its own document. WHAT YOU CAN CHANGE: purchaseDate, paymentMethod ('CASH' paid on the spot, or 'CREDIT' on account), term (the supplier's payment term, stored as written — changing an on-account bill to one of the five presets, Due on Receipt / Net 7 / Net 14 / Net 30 / Net 60, re-derives the due date from the bill date unless you also send dueDate), dueDate (only when the due date ITSELF is wrong — correcting just the bill date already moves the due date by the same number of days), and lines — the FULL REPLACEMENT SET, so send every line the bill should have, including the ones that were already right. Everything you do NOT send is carried across exactly as the bill has it: the supplier, the currency and its frozen exchange rate, the location the goods were received into, the batch (lot) each line was received into, and each line's SST code. On a bill with no purchase order each replacement line keeps the UNIT it has on the bill: a line identical to one of the bill's own lines keeps that line's unit, any other line takes the product's one unit on the bill (a product new to the bill: its own unit). Here unit is only the price basis — it never sets a line's unit — and a line measured from its size and tally is booked in the measurement's unit (tons for timber). When the product is on the bill in more than one unit, a changed line of it that is not measured is refused, and the user makes that change on the bill's Edit screen in Taokeh. Batches: without line numbers, a replacement line of a product the bill already carries is received into the same batch as the original line of that product; with line numbers, a named line keeps its own batch and a NEW line of a product the bill already received into stock goes into that product's lot on the bill, by lot number (the lot keeps its own expiry) — refused when the bill received it into more than one lot (raise the quantity on the right line instead). WHAT YOU CANNOT, each refused by name: the SUPPLIER (a different supplier is a different document — the owner voids and re-enters); the BILL NUMBER (preserved across an edit; Taokeh's own Edit screen cannot change it either); the CURRENCY or the RATE (frozen at posting); and a bill posted with a whole-document tax rate instead of per-line SST codes (Taokeh does not store that rate, so a correction from here would drop the input tax — the owner corrects it on the app's Edit screen). WHAT BLOCKS AN EDIT OUTRIGHT, checked when you file and again when the admin taps, each refusal naming the real next step: the document is not a bill (an opening balance or a debit note has its own page); it is validated on MyInvois as a self-billed e-invoice; a live debit note has been raised against it; a payment has been allocated to it; or it is dated inside a period the company has locked. On perpetual moving-average stock a correction that changes the bill's stock (a quantity, a price, a product, the date) keeps the bill's place in each item's history, and is refused by name when Taokeh cannot replay an item's stock history (a cost typed before cost changes were booked, a FIFO period, stock with no proven earlier cost) — a correction that names lines when you file it, any other at the admin's tap; relay the sentence: a debit note takes goods or money off, another bill adds, and a header-only correction still works. A proposal that changes nothing is refused rather than filed as an empty diff. If the bill is edited by someone else after you read it, the admin is shown BOTH versions and the one-tap approval is refused — that is by design; call revise_draft (kind 'bill_update') to re-read and re-propose. An edit in Taokeh re-books the bill under a NEW id with the same number: a pending draft FOLLOWS it, and calling this tool with the old id is refused with the new id named — re-read the bill before proposing against it. SHAREHOLDER'S LOAN: approve refuses a correction that would leave a posted owner withdrawal over-repaid (the loan in debit on some date) — or, for a sole proprietor or partnership, a date a manual journal reclassified no longer nil — and names them and the steps. MANY BILLS WITH THE WRONG DATE (a batch keyed a day early): send dateShift ALONE instead of one draft per bill — ONE pending draft moves up to 200 bills (by days, or each to its own date), counts as one against the queue, and the admin approves the whole list on one page (all of them or none); only the date and a stored due date move.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note in the reviewer's language: what was wrong, what you changed, and anything they should double-check before they approve a rewrite of a posted bill. | |
| proposed | No | REQUIRED unless you send dateShift. ONLY what you want changed. An unknown or not-patchable key is refused by name; a proposal the bill already agrees with is refused rather than filed. | |
| dateShift | No | USE THIS INSTEAD OF ONE DRAFT PER BILL when many bills carry the wrong date (keyed a day early, a whole batch dated wrong): ONE pending draft moves all of them, and it counts as ONE against the 50-draft queue. Send it ALONE — no purchaseId, no `proposed`. Only the date moves; a stored due date moves by the same number of days; nothing else (payment method, term, lines) changes. Every bill is checked now with the same guards as a single correction: if ANY is refused, NOTHING is filed and every refusal is listed — leave those out and send the rest. An admin approves the whole list on one page in Taokeh; they can drop rows there, and approving moves all of them or none. | |
| purchaseId | No | REQUIRED (unless you send dateShift) — the posted bill's real id, from search_documents. A bill number is refused: correcting the wrong document silently is worse than asking which one they mean. | |
| needsReview | No | Set true when something gave you pause — it flags the draft for the reviewer. | |
| printedTotal | No | The grand total the corrected bill should show, if the user told you one — cross-checked against the server's own derivation and flagged on the review screen. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the generic profile (not read-only, not destructive, not idempotent); the description goes far beyond by disclosing the approval workflow (admin reviews a current→proposed diff), what is preserved (bill number, attached document, purchase-order link, currency and frozen rate), what blocks an edit outright (MyInvois self-billed, live debit note, allocated payment, locked period), and what happens on concurrent edits. The non-destructive claim in annotations is consistent with the repeated 'NOTHING CHANGES UNTIL A HUMAN APPROVES' framing, so no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical warning is properly front-loaded in caps, but the body is enormously overlong and repetitive: the lineNo/keep/removal rule appears in the description body and again twice in the schema, and the batch-resolution rules are re-derived in both places. Much of the detail (unit conversion, tally parsing, shareholder loan edge cases) is already carried at full length by per-property schema descriptions, so it does not earn its place in the tool description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with no output schema, six top-level parameters and deep nesting, this covers everything an agent needs: prerequisites (read lines first), refusal conditions, batch-vs-single mode, concurrency recovery, and the human approval loop. Nothing material about calling it correctly is left unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description still adds real semantics on top — that dateShift must be sent ALONE with no purchaseId or proposed, that a proposal changing nothing is refused, that everything unsent is carried across, and that a bill number is refused in favor of the real id. It falls short of 5 because the line-numbering rule and the 'keep' semantics are restated three to four times across description and schema rather than adding new information each time.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening lines state a specific verb and resource (propose a correction to an already-posted supplier bill) and immediately bound the scope: it files a PENDING DRAFT, not a live change. It explicitly names what it is not — voiding (no AI lane), debit notes, and the Taokeh Edit screen — so an agent can distinguish it from update_purchase_order_draft, create_bill_draft and create_debit_note_draft without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The ROUTING section gives an explicit three-way decision table (purchase never happened → void in Taokeh; purchase happened but bill wrong → this tool; something changed after the purchase → debit note), which is exactly the disambiguation an agent needs. It also names the sibling fallbacks revise_draft (kind 'bill_update') and search_document_lines (docType 'bill') for the required prior read, and states the dateShift alternative for batches.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_contact_draftFile a pending contact-change draft (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose changes to a customer or vendor ALREADY in Taokeh — the correction door for an address book with missing tax ids, stale phone numbers or wrong e-Invoice flags (e.g. after a migration). This does NOT change anything: it files a pending DRAFT the owner reviews as a DIFF (current → proposed, changed fields only) and approves with one tap; only that tap writes. Pass partyKind ('customer' or 'vendor'), the contact's REAL contactId from resolve_customer / resolve_vendor — a name is refused, because a near-match would edit the wrong company — and proposed, an object holding ONLY the fields you want changed. Settable fields, mirroring the contact screen exactly: name, email, phone, address, tin (Tax ID), brn (registration no.), idType ('BRN' or 'NRIC'); on a CUSTOMER also requiresEinvoice, einvCity, einvPostcode, einvState (LHDN 2-digit code), remindersMuted; on a VENDOR also selfBill, alwaysExpense, state. Pass null on a text field to CLEAR it. Anything else is refused by name — do not invent fields. You cannot move a contact between customer and vendor here; they are separate records. A field the contact already agrees with is dropped, and a proposal that changes nothing is refused rather than filed as an empty diff for a human to tap. One contact per call — loop for a sweep, because 100 reviewable diffs is the point: the owner sees each change before it lands. BE HONEST: never guess a tax id or a phone digit; if you are unsure, leave the field out and say so in notes with needsReview. If the record has been edited since you read it, the owner is shown BOTH values and the one-tap approval is refused — that is by design; call revise_draft (kind 'contact_update') to re-read and re-propose.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note, in the reviewer's language: what you changed and why, and anything they should double-check. Leave empty when there is nothing to flag. | |
| persons | No | Changes to the PEOPLE filed under this contact (the individuals you deal with there). Each entry is one change. A removal names only personId. A person needs a name to be added. These ride the SAME draft and the SAME single approval as the field changes — the owner sees a removal shown as a removal, in words. | |
| proposed | No | ONLY the fields you want changed. A field the contact already agrees with is dropped; an unknown or wrong-side field is refused by name. Omit it entirely when you are only changing the PEOPLE below. | |
| contactId | Yes | REQUIRED — the contact's real id from resolve_customer / resolve_vendor. A name is refused: editing the wrong company silently is worse than asking. | |
| partyKind | Yes | Which side of the book the contact is on: 'customer' (someone you sell to) or 'vendor' (someone you buy from). They are separate records — this tool cannot move one to the other. | |
| needsReview | No | Set true when something gave you pause — a tax id you read off a blurry document, a phone number the user was unsure about. It flags the draft for the reviewer. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only convey readOnly=false, openWorld=false, idempotent=false, destructive=false, so the description carries the behavioral burden — and it exceeds it. It discloses the draft-only semantics ('nothing changes until a human approves'), the diff review flow, rejection behaviors (empty proposals refused, agreeing fields dropped, unknown fields refused by name), null-clears-text semantics, one-contact-per-call, and the concurrency design where simultaneous edits force re-proposal. Nothing in the description contradicts the annotations; in fact, the draft-only framing explains why destructiveHint=false is appropriate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long (~350 words) and repeats the core draft-only message three times (title, opening sentence, third sentence). However, it is front-loaded with the most decision-critical fact and the remaining length is dense with safety-critical semantics — particularly the 'never guess a tax id' honesty rule and the concurrency behavior. A small trim of the redundant draft-only restatements would make it exemplary, but every substantive paragraph earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
This is a high-complexity tool — 6 parameters, two nested objects (proposed, persons), conditional customer/vendor fields, concurrency edge cases, no output schema — and the description still leaves nothing essential unexplained. It covers field eligibility rules, the people sub-object, failure modes (name refused, empty diff refused, stale-record refusal), looping guidance for sweeps, and honesty requirements. No output schema exists, so some return-value details are absent, but for a tool whose semantics are this intricate the description is remarkably complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real semantic value on top: 'Pass null on a text field to CLEAR it', 'Anything else is refused by name — do not invent fields', the consolidated customer-vs-vendor field grouping ('on a CUSTOMER also requires...'), and 'Omit it entirely when you are only changing the PEOPLE below'. These operational meanings are not fully inferable from the schema's property descriptions alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb-resource pair: 'Propose changes to a customer or vendor ALREADY in Taokeh' and 'FILES A PENDING DRAFT ONLY'. It clearly distinguishes itself from the create sibling by scoping to existing contacts ('the correction door'), and the title adds the human-approval workflow. An agent can immediately tell this is the update/amend tool, not the creation tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use context (missing tax ids, stale phone numbers, wrong e-Invoice flags after a migration) and explicit exclusions: 'You cannot move a contact between customer and vendor here' and 'A name is refused'. It also routes to alternatives: resolve_customer/resolve_vendor for the real ID and 'call revise_draft (kind 'contact_update')' for the concurrency case. This is actionable routing guidance, not just a purpose statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_credit_note_draftFile a pending correction draft for a posted credit note (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose a correction to a SALES CREDIT NOTE already posted in Taokeh — the door for a credit note that went onto the books with a wrong line, quantity, price, date or reason. An ADMIN reviews it as a DIFF (current → proposed) and approves; only that tap re-books the note, and it keeps the SAME credit-note number, the invoice it credits and any attached document. Pass creditNoteId, the note's REAL id from search_documents (docType credit_note) — a credit-note number is refused. ROUTING — three different mistakes, three different doors. (1) THE CREDIT NEVER SHOULD HAVE EXISTED (keyed twice, wrong customer, raised against the wrong invoice): the owner VOIDS it — Credit notes → the note → Void, in Taokeh; voiding keeps the note on the register marked VOID, and there is deliberately no AI lane for it. A note VALIDATED on MyInvois cannot be voided: any further correction goes through LHDN against the validated document. (2) THE CREDIT IS RIGHT BUT TYPED WRONG: THIS tool. (3) SOMETHING NEW HAPPENED LATER (more goods came back, a further allowance was agreed): a NEW credit note with create_credit_note_draft — a later event is its own document, not a rewrite of this one. WHAT YOU CAN CHANGE: cnDate, reason, and lines — the FULL REPLACEMENT SET (send every line the note should have, including the ones that were already right). ⛔ EVERY LINE MUST ANSWER goodsReturned (true = these goods physically came back into stock, false = money only). There is no default. Your answer only PRE-FILLS the admin's per-line question on the full review page — the admin's answer is what posts — and a ONE-TAP approval never moves stock: it is refused whenever the note already put goods back or any line says goods came back, so those corrections are approved on the review page. Leave taxCode out and the line inherits the note's own SST code for that product; a NEW product on a taxed note has nothing to inherit and is refused by name. A NOTE ON A WHOLE-DOCUMENT SST RATE (the note, or the invoice it credits, has no SST codes at all, and that rate is not 0) is refused unless EVERY line you send carries a taxCode — the app's own Edit applies the same rule, so the two never post different tax. A NOTE WITH A DISCOUNTED LINE (search_document_lines shows discount on it): its lines cannot be corrected here at all — any lines proposal on it is refused, and the owner changes them in Taokeh (Credit notes → Edit), where the Discount box is; a date or reason correction (no lines) still works and reposts the note's own lines, discounts included. A NOTE THAT CARRIES THE INVOICE'S ROUNDING OR ADJUSTMENT LINE (a line with a NEGATIVE unit price — the web form brings it along on a whole return): the same — any lines proposal is refused, because a line you send cannot carry it, and the owner changes the lines in Taokeh (Credit notes → the note → Edit); a date or reason correction still works and reposts that line as it is. WHAT YOU CANNOT, each refused by name: the CUSTOMER, the credit-note NUMBER, and the INVOICE it credits. THINGS THAT BLOCK AN EDIT OUTRIGHT, checked when you file (the server runs the real correction and rolls it back, so every refusal approval would give you is given NOW) and again when the admin taps: the note is voided; it is validated on MyInvois (it cannot be voided either: any further correction goes through LHDN against the validated document); exported for MyInvois, or submitted to LHDN (the owner voids it instead — the void clears the export and says what to do at MyInvois); a customer refund is allocated to it; the invoice it credits is gone or is not an invoice; its date is inside a LOCKED period; it put goods back under the company's previous stock setting; or the corrected note would credit more than the invoice has left, or put back more than the invoice shipped. A proposal that changes nothing is refused rather than filed. If the note is edited by someone else after you read it, the one-tap approval is refused — call revise_draft (kind 'credit_note_update') to re-read and re-propose.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note in the reviewer's language: what was wrong, what you changed, and — above all — whether the goods really came back. | |
| proposed | Yes | ONLY what you want changed. An unknown or not-patchable key is refused by name. | |
| needsReview | No | Set true when something gave you pause. | |
| creditNoteId | Yes | REQUIRED — the posted credit note's real id, from search_documents (docType credit_note). A credit-note number is refused. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false; the description adds the crucial behavioral fact that nothing posts until a human approves a diff, that one-tap approval never moves stock, and a full enumerated list of refusal conditions checked both at filing and at approval. No contradiction with the non-destructive annotation — filing a pending draft is a write but not a destructive one.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The key constraint is front-loaded, but the body is a dense all-caps wall with duplication — the MyInvois validated/void rule appears both in the routing section and again in the blockers list, and the discounted-line and negative-price cases restate the same 'lines refused, date/reason still works' pattern. Much of it earns its place given the complexity, but the repetition and shouting reduce scannability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a nested-object mutation with no output schema, the description is unusually complete: it covers permitted fields, per-line requirements, refusal conditions, stock effects, and the approval lifecycle. The only gap is that it never says what a successful call returns (draft id, status, review link), which an agent calling with no output schema would benefit from.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline would be 3, but the description adds real semantics: creditNoteId must be the real id from search_documents (a note number is refused), lines is a FULL replacement set, goodsReturned has no default and only pre-fills the admin's answer, and taxCode omission inherits the note's SST code. These are rules the schema alone cannot convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a precise verb, resource and scope: 'FILES A PENDING DRAFT ONLY' for 'a correction to a SALES CREDIT NOTE already posted'. It explicitly distinguishes itself from create_credit_note_draft (new document) and from the void path, so an agent can route without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit three-way routing rule: void in Taokeh when the credit never should have existed, THIS tool when the credit is right but typed wrong, and create_credit_note_draft when a later event occurred. It also names revise_draft for the concurrent-edit case, so both when-to-use and when-not-to-use are covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_delivery_order_draftFile a pending delivery-order correction draft (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose a correction to a DELIVERY ORDER that already exists and has not been invoiced — a wrong quantity or price, a missing or extra line, the wrong date or notes. An admin or bookkeeper reviews a plain current → proposed DIFF and approves. A delivery order posts nothing and moves NO stock (stock moves when it is invoiced), so the correction touches no ledger and no stock. ⚠ Approving re-captures each line's cost from the products' CURRENT average cost — exactly what Taokeh's own Edit screen does; it is a dispatch-time reference that posts nothing. ROUTING — (1) the delivery never happened: the owner voids the DO in Taokeh (Delivery orders → the DO → Void); there is no AI lane for voiding. (2) the DO is wrong and still OPEN: THIS tool. (3) the DO has already been INVOICED: it is closed to edits — correct the invoice instead (update_invoice_draft). WHAT YOU CAN CHANGE: doDate, notes (null clears them) and lines. WHAT YOU CANNOT, each refused by name: the CUSTOMER, the DO NUMBER, the CURRENCY or exchange rate, and invoicing or voiding. ONLY AN OPEN DELIVERY ORDER can be corrected — checked when you file and again when the reviewer taps. Leave a line's taxCode out and it inherits this DO's own SST code for its product — unless the DO carries that product under more than one code, when you must state it. ⛔ STOCK (stock-modes S1): on a tracked (perpetual) company a correction that RAISES a product's quantity past what is on hand (counting the other delivery orders not yet invoiced) is refused at approve; keeping or cutting a quantity is never refused for stock. WHAT HAPPENS ON APPROVAL: the document is updated IN PLACE — same id, same number — through the SAME update Taokeh's own Edit screen runs. Every field you leave out is carried across exactly as it stands (the party, the attention contact, the currency and its frozen exchange rate, the tax) and is never reset. LINES: lines is the FULL replacement set. READ THE LINES FIRST with search_document_lines — each row's lineNo is what a { keep: } entry refers to, and keep is how a line that is already right survives untouched (a measured line keeps its frozen unit and its rounding row). A changed or new line is written in full, in the create tool's line shape, and the server re-derives its quantity and amount. A proposal that changes nothing is refused rather than filed as an empty diff. STALE: if a person edits the document after you read it, the one-tap approval is refused and the reviewer is shown both versions — call revise_draft with this draft's kind to re-read and re-propose. Pass the document's REAL id from search_documents; a document number is refused. A line Taokeh converted from a per-foot or per-piece price prints in tons at a whole-ringgit rate under a note like "(150' @ RM 0.50/ft)" (older documents print "/feet"): send such a line in its FILED form — thickness, width, tally, unit 'feet' or 'pcs', the per-foot or per-piece price (on a purchase, the supplier's cost) and only the grade as the description, never the printed note. On a correction, a PURCHASE line may instead be sent back exactly as it stands on the document; a SELLING line sent back exactly as it stands is read back into its filed form only when its note's price reproduces it (otherwise keep it with { keep: } where offered, or correct the header only); any other line carrying the note follows the rule for a new line — a selling line read back only at its own tons and rate with no discount, a purchase line refused.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note in the reviewer's language: what was wrong, what you changed, and anything they should check before approving. | |
| proposed | Yes | ONLY what you want changed. An unknown or not-patchable key is refused by name; a proposal the delivery order already agrees with is refused rather than filed. | |
| needsReview | No | Set true when something gave you pause — a quantity you inferred, a price the user was unsure about. It flags the draft for the reviewer. | |
| printedTotal | No | The grand total the corrected delivery order should show, if the user told you one — cross-checked against the server's own figure and flagged on the review screen. | |
| deliveryOrderId | Yes | REQUIRED — the delivery order's real id, from search_documents. A DO number is refused. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses that filing only creates a pending human-reviewed draft, that nothing hits stock or ledger until invoicing, that approval re-captures current average cost, and that stock-raising corrections can be refused at approval. It also covers stale approval behavior, in-place update, no-op refusal, and strict line-key refusal.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical draft-only warning and routing are front-loaded and sectioned, which helps. However, the description is very long and repeats the per-foot/per-piece line rule almost verbatim from the schema, so it is not tightly concise despite being well organized.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex nested mutation, it covers the full lifecycle, stock constraints, line replacement, stale handling, and approval effects, which is enough to invoke it correctly. It does not describe the immediate return payload or draft identifier, and there is no output schema, leaving a minor completeness gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already explains doDate, notes, lines, keep, taxCode inheritance, and strict line keys in detail. The description reinforces what can and cannot change, but adds little parameter syntax or format meaning beyond the structured schema, so the high-coverage baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening states a specific verb and resource — 'Propose a correction to a DELIVERY ORDER' — and makes the draft-only scope explicit. It distinguishes this from voiding an unfulfilled DO and from invoiced DOs, so the agent can identify the operation without consulting siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The ROUTING section explicitly gives the three cases: never happened → void in Taokeh (no AI lane), open and wrong → THIS tool, already invoiced → update_invoice_draft. It also names search_document_lines and revise_draft for supporting flows, so alternatives and exclusions are clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_employee_draftFile a pending employee-change draft (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose corrections to an employee ALREADY in Taokeh's payroll — a wrong IC or passport number, a missing EPF/SOCSO/income-tax number, a salary keyed a digit out, a stale designation, the statutory applicability flags. This does NOT change anything: it files a pending DRAFT an admin (or a Bookkeeper + payroll login) reviews as a DIFF (current → proposed, changed fields only) and approves with one tap; only that tap writes. Pass the employee's REAL employeeId — from payroll_summary with staff: true (the whole roster, works even before the first payroll run) or perEmployee: true — and proposed, an object holding ONLY the fields you want changed. A name is refused: two people can share one, and editing the wrong person's salary silently is much worse than asking. ⚠ FORWARD ONLY, and you must tell the user this: an approved change applies to FUTURE payroll runs. It never re-opens, recomputes or restates a payslip or payroll run that has already been filed — including the year-to-date PCB, which is summed from payslips already submitted. If a filed month is wrong, that is a human decision taken in Payroll → Payroll Runs, not something this tool can do. Settable fields, mirroring the employee form exactly: staffNo, name, icNo, passportNo, countryCode, dateOfBirth, designation, joinDate, resignDate, status ('ACTIVE' or 'RESIGNED'), email, isMalaysian, isPermanentResident, isTemporaryResident, basicSalary, epfEmployeeRate, bankName, bankAccountNo, epfNo, socsoNo, incomeTaxNo, epfApplicable, socsoApplicable, eisApplicable, pcbApplicable, skbbkExemptReason (SKBBK is deducted by default; 'OPTED_OUT' for a local who opted out with PERKESO by 31 Aug 2026, 'OTHER_EMPLOYER' where another employer deducts it, null to deduct here; the old skbbkEnrolled key is refused), maritalStatus ('SINGLE', 'MARRIED' or 'SINGLE_PARENT'), spouseWorking, numChildren, taxResident, residencyChangeMonth ('YYYY-MM'), zakatMonthly, employmentStatus (CP8D code '1'–'6'), contractEndDate, holidayState, notes. NOT settable here, each refused by name with the screen that owns it: the TP3 prior-employer figures (a signed declaration, and requesting one EMAILS the employee), the TP2 benefits-in-kind values (owned by the approved election), the flat-15% tax regime (approval-conditional — an AI cannot verify it, and halving somebody's tax is not a diff-door change), and 'pay before Taokeh this year' (the company's own opening year-to-date from its old payroll system — entered on the employee's page, because it seeds PCB and prints on the EA). bankName and bankAccountNo ARE settable because they print on the payslip and on the giro list the owner uploads at their own bank — TAOKEH NEVER PAYS ANYONE; the owner authorises every payment at their own bank, and nothing on this connector can move money. A field the record already agrees with is dropped, and a proposal that changes nothing is refused rather than filed as an empty diff. One employee per call — loop for a sweep, because each reviewable diff is the point. BE HONEST: never guess an IC, a bank account or a salary; leave the field out and say so in notes with needsReview. ADMIN AND BOOKKEEPER + PAYROLL ONLY: payroll sits behind its own role and this tool refuses on any other connection (a plain Bookkeeper included).
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note, in the reviewer's language: what you changed, why, and what they should double-check. | |
| proposed | Yes | ONLY the fields you want changed. A field the record already agrees with is dropped; an unknown or deliberately-excluded field is refused by name. | |
| employeeId | Yes | REQUIRED — the employee's real id, from payroll_summary (`staff: true` for the roster, or `perEmployee: true` for a run). A name is refused. | |
| needsReview | No | Set true when something gave you pause — an IC read off a blurry photo, a salary the user was unsure about. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (readOnlyHint=false, destructiveHint=false), the description discloses the full write path: a diff is filed, an admin or Bookkeeper+payroll reviews it, only the approval tap writes. It adds critical forward-only semantics — an approved change never re-opens or restates a filed payslip or YTD PCB — plus the role gate (refuses on a plain Bookkeeper connection) and the payroll-seat constraint on reactivating someone. None of this contradicts the annotations; the write-with-approval model is consistent with readOnlyHint=false and destructiveHint=false.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The headline and the forward-only warning are correctly front-loaded, but the body is extremely long, uses sustained all-caps emphasis, and repeats material already carried by the schema (the bank/giro explanation appears in the description and in the bankName and bankAccountNo schema descriptions). Much of the content earns its place, but a third of the words duplicate structured data.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a high-stakes payroll mutation with a nested 35-field proposed object and no output schema, the description covers what an agent needs: id acquisition, field eligibility and refusals, no-op dropping, reviewer notes/needsReview, and the forward-only consequence it must relay to the user. Nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, but the description adds meaning the schema cannot: the complete settable-field list mirroring the employee form, the named refusals (skbbkEnrolled is retired and rejected, TP3/TP2/flat-15%/prior-YTD are not settable at all), the null-vs-value semantics for skbbkExemptReason, and the requirement that `proposed` hold only changed fields. That is substantive guidance well past the schema baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence states a specific verb and resource and pins the scope: 'FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT'. It also distinguishes itself from sibling draft tools (create_*_draft) by framing the operation as a proposal against an employee already on payroll, and names payroll_summary as the source of the identifier.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use cases are enumerated (wrong IC, missing EPF/SOCSO/tax number, mis-keyed salary, stale designation, statutory flags) and an explicit when-not list follows: TP3 figures, TP2 benefits-in-kind, the flat-15% regime, and opening year-to-date are each refused by name with the screen that owns them. It also routes the agent to payroll_summary (staff/perEmployee) for the id, and states one employee per call with loop guidance for sweeps.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_expense_draftFile a pending correction draft for a posted paid expense (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose a correction to a PAID EXPENSE already posted in Taokeh (an entry on the Expenses page — find its id with search_expenses). The owner or bookkeeper reviews it as a DIFF (current → proposed) and approves; only that tap re-books the entry, exactly as Expenses → Edit does, and the receipt already on it carries across. ROUTING — (1) THE EXPENSE NEVER HAPPENED (keyed twice, the wrong company's receipt): the owner DELETES it in Taokeh (Expenses → the expense → Delete); there is deliberately no AI lane for that. (2) IT HAPPENED BUT WAS KEYED WRONG (the date, the amount, the input SST, the category, the account it was paid from, the payee, the reference): THIS tool. (3) SOMETHING NEW HAPPENED LATER (a refund came back, a second payment was made): a new entry — create_expense_draft for a further payment, or a reversing journal via create_journal_draft for money that came back. WHAT YOU CAN CHANGE, sending ONLY what is wrong (everything you leave out is carried from the entry as it stands): date, amount (the gross ringgit paid), inputTax (the input SST inside that amount), debitAccountCode (the category) and creditAccountCode (paid from) — both from expense_accounts —, memo and reference. Ringgit only; the currency and the receipt are not changeable here. BLOCKED OUTRIGHT, checked when you file (the server runs the real correction and rolls it back) and again at approval: the entry is not a paid expense; its date is inside a LOCKED period (or your new date is); a category that is not a cost account; a paid-from account that cannot pay; or an entry split across more lines than the expense form can show. A proposal that changes nothing is refused. If the expense is edited after you read it, the one-tap approval is refused — call revise_draft (kind 'expense_update'). SHAREHOLDER'S LOAN: approve refuses a correction that would leave a posted owner withdrawal over-repaid (the loan in debit on some date) — or, for a sole proprietor or partnership, a date a manual journal reclassified no longer nil — and names them and the steps; when that is already so as the books stand, the reply says it (ownerLoanRefusal) and the one-tap approval holds back.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note: what was wrong and where the corrected figure came from. | |
| entryId | Yes | REQUIRED — the paid expense's journal-entry id, from search_expenses. | |
| proposed | Yes | ONLY what you want changed. An unknown or not-patchable key is refused by name. | |
| needsReview | No | Set true when something gave you pause. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (which only say not-read-only, not-destructive, not-idempotent): it discloses the pending/human-approval gate, the pre-flight roll-back check, the enumerated blocked conditions (locked period, non-cost category, non-paying account, over-split entry), the no-op refusal, the stale-read refusal, and the shareholder's-loan / ownerLoanRefusal behavior. This is unusually rich mutation disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the critical caveat in caps and organized into routing, changeable-fields, blocked-conditions, and edge-case blocks. Dense and long, but nearly every sentence carries decision-relevant content; the capital-letter emphasis is heavy-handed but not wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex nested mutation with no output schema, the description covers approval flow, what is patchable, refusal conditions, and the specific error signal (ownerLoanRefusal) an agent may receive. Nothing an agent needs to call this correctly or interpret a refusal is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real patch semantics the schema does not: 'sending ONLY what is wrong (everything you leave out is carried from the entry as it stands)', the ringgit-only constraint, and that currency and receipt are not changeable here. It stops short of restating per-field syntax, which the schema already covers.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb and resource ('files a pending correction draft for a paid expense already posted') plus scope ('nothing changes until a human approves'). The routing section explicitly separates it from delete-in-Taokeh, create_expense_draft, create_journal_draft, and revise_draft, so an agent can pick it out of a large sibling set without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The ROUTING block gives three explicit when-to-use cases with named alternatives (the expense never happened → delete in Taokeh; keyed wrong → this tool; something new happened later → create_expense_draft or create_journal_draft). It also names revise_draft (kind 'expense_update') for the stale-read case, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_invoice_draftFile a pending correction draft for a posted invoice (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose a correction to an invoice ALREADY POSTED in Taokeh — the door for an invoice that went onto the books with a wrong line, a wrong date or the wrong payment method. This does NOT change anything: it files a pending DRAFT an ADMIN reviews as a DIFF (current → proposed) and approves; only that tap rewrites the invoice, and it keeps the SAME invoice number, the original attached document and any payment-reminder history. Pass saleId, the invoice's REAL id from search_documents — an invoice number is refused, because two documents can carry the same printed number and correcting the wrong one is worse than asking. ROUTING — three different mistakes, three different doors, and picking the wrong one is expensive. (1) THE SALE NEVER HAPPENED (keyed twice, entered on the wrong company, cancelled before delivery): the owner VOIDS it — Sales → the invoice → Void, in Taokeh. There is deliberately NO AI lane for voiding; erasing a document from the books is a person's decision at the screen. Tell them where to click. (2) THE SALE HAPPENED BUT THE INVOICE IS WRONG (a wrong line, a wrong quantity or price, the wrong date, the wrong payment method): THIS tool. (3) SOMETHING CHANGED AFTER THE SALE (goods came back, a price was renegotiated, a discount was agreed later): create_credit_note_draft — a real event that belongs on the books as its own document, not an erasure of history. WHAT YOU CAN CHANGE: saleDate, paymentMethod ('CASH' or 'CREDIT'), refNo (the customer's own reference / PO number), term (the payment term, stored as written — changing an on-account invoice to one of the five presets, Due on Receipt / Net 7 / Net 14 / Net 30 / Net 60, re-derives the due date from the invoice date unless you also send dueDate, and the admin sees the resulting due date before approving), dueDate (only when the due date ITSELF is wrong — correcting just the invoice date already moves the due date by the same number of days, so the agreed credit period survives), and lines — the FULL REPLACEMENT SET, so send every line the invoice should have, including the ones that were already right. ON A MEASUREMENT COMPANY, do NOT send the small negative rounding line the engine adds under a per-foot line: it is Taokeh's own line, not one of the invoice's, it is preserved untouched by a header-only correction, and it is re-derived from whatever timber line you do send. You cannot mark a line as one either — that is a fact Taokeh reads off the document, never something a caller asserts. WHAT YOU CANNOT, each refused by name: the CUSTOMER (a different customer is a different document — credit this one and raise a new invoice to the right party); the INVOICE NUMBER (preserved across an edit; Taokeh's own Edit screen cannot change it either); and the lines of an invoice that spreads its revenue over time under MFRS 15 (which new line inherits which schedule is a guess, and a guess about deferred revenue is not something a reviewer can check on a diff — header-only corrections still work WHILE the schedule is only part-way through, and keep it intact). On a deferred invoice whose revenue has ALREADY been recognised — any period posted, up to and including a finished schedule — the invoice is closed to edits altogether, header fields included, because rewriting it would re-state revenue in periods that may already be closed and filed; the remedy is a credit note, and the refusal says so. THESE BLOCK AN EDIT OUTRIGHT, checked when you file and again when the admin taps, and each refusal names the real next step: the document is not an invoice; it is validated on MyInvois; it has been submitted to LHDN; it was consolidated from delivery orders; a payment has been allocated to it; it was reversed from a Shopee, TikTok or Shopify Returns page (its reversing journal entry already takes it out of the books, so it can be neither edited nor voided); it belongs to a sales channel — imported from Shopee, TikTok, Shopify, the online store or another channel import, or a migrated invoice attached to a marketplace order through refNo (an edit would drop its channel and its platform fees; a credit note, or the Returns page for that channel, corrects it); it is a closed Counter day's sale, or a RM10,000+ till receipt's own sale (the close split its money into cash, QR and rounding, which an edit cannot rebuild — a credit note corrects it). A LIVE CREDIT OR DEBIT NOTE against the invoice blocks only a lines change: a header-only correction (date, payment method, refNo, term, due date) still files and approves, and the note keeps its number and is re-linked to the corrected invoice. It still refuses if that note has gone to LHDN, or if the new date would fall after the note's date. AND A LOCKED PERIOD BLOCKS THE APPROVAL (2026-09-19): an edit reposts the invoice and removes the original entry, and removing a line from a month the company has already locked and filed is refused — so if the invoice is dated on or before the lock date, the admin's tap is refused by name, even when your proposal re-dates the invoice into an open month. It is checked at APPROVAL, not at filing, so a draft can still be filed against a locked invoice; do not do it — tell the owner instead that the correction has to be a credit note dated today (create_credit_note_draft), which leaves the filed months as filed, or that an admin has to unlock in Opening balances → Review & lock first. A field the invoice already agrees with is dropped, and a proposal that changes nothing is refused rather than filed as an empty diff for a human to tap. If the invoice is edited by someone else after you read it, the admin is shown BOTH versions and the one-tap approval is refused — that is by design; call revise_draft (kind 'invoice_update') to re-read and re-propose. An edit in Taokeh re-books the invoice under a NEW id with the same number: a pending draft FOLLOWS it (it shows as changed since you read it, and revise_draft on the draft still works), and calling this tool with the old id is refused with the new id named — re-read the invoice before proposing against it. ⛔ STOCK (stock-modes S1): on a tracked (perpetual) company a correction that would take an item below zero is refused at approve and nothing changes. MANY INVOICES WITH THE WRONG DATE (a batch keyed a day early): send dateShift ALONE instead of one draft per invoice — ONE pending draft moves up to 200 invoices (by days, or each to its own date), counts as one against the queue, and the admin approves the whole list on one page (all of them or none); only the date and a stored due date move.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note in the reviewer's language: what was wrong, what you changed, and anything they should double-check before they approve a rewrite of a posted document. | |
| saleId | No | REQUIRED (unless you send dateShift) — the posted invoice's real id, from search_documents. An invoice number is refused: correcting the wrong document silently is worse than asking which one they mean. | |
| proposed | No | REQUIRED unless you send dateShift. ONLY what you want changed. An unknown or not-patchable key is refused by name; a proposal the invoice already agrees with is refused rather than filed. | |
| dateShift | No | USE THIS INSTEAD OF ONE DRAFT PER INVOICE when many invoices carry the wrong date (keyed a day early, a whole batch dated wrong): ONE pending draft moves all of them, and it counts as ONE against the 50-draft queue. Send it ALONE — no saleId, no `proposed`. Only the date moves; a stored due date moves by the same number of days; nothing else (payment method, term, lines) changes. Every invoice is checked now with the same guards as a single correction: if ANY is refused, NOTHING is filed and every refusal is listed — leave those out and send the rest. An admin approves the whole list on one page in Taokeh; they can drop rows there, and approving moves all of them or none. | |
| needsReview | No | Set true when something gave you pause — a quantity you inferred, a price the user was unsure about. It flags the draft for the reviewer. | |
| printedTotal | No | The grand total the corrected invoice should show, if the user told you one — cross-checked against the server's own derivation and flagged on the review screen. Reconcile a disagreement in chat; never quietly average the two. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say non-readonly, non-destructive, non-idempotent, but the description discloses far more: nothing changes until human approval, an edit re-books under a new id, refusals are named (MyInvois/LHDN/channel imports/locked periods/stock-negative), and header-only vs line corrections differ. This is behavioral context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical constraint is front-loaded in caps, which is good, but the description is very long and dense with routing rules that overlap the schema's own guidance. Much of it earns its place, yet several passages (repeated refusal enumerations) could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex write tool with no output schema, the description covers approval flow, refusal conditions, idempotency-like behavior (re-booking under a new id), and the batch path. Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description adds real semantics — that saleId must be the real id (not invoice number), that lines is the FULL replacement set, that the engine's negative rounding line must be omitted, and that term/dueDate/days interact. It mostly aligns with the schema, so not a full 5, but meaning is added beyond the structured fields.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource — filing a pending correction draft against an ALREADY POSTED invoice — and immediately distinguishes it from the other correction doors (void, credit note). An agent can tell exactly what this tool does without reading any sibling schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit three-way routing rule (sale never happened → void manually; invoice is wrong → this tool; something changed after sale → create_credit_note_draft), plus a batch alternative (dateShift) and when to escalate to an admin. Alternatives and exclusions are named, not implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_logo_draftFile a pending logo-change draft (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose a new COMPANY LOGO for this company — the mark Taokeh prints on every invoice, quote, receipt and statement it generates, and that fronts the online storefront. This does NOT change anything: it files a pending DRAFT an ADMIN reviews by looking at the logo they have now beside the one you are proposing, and approves with one tap; only that tap sets it. Send the image one of two ways, never both. Lane 1 (default, any size): call request_attachment_upload, PUT the raw bytes to its uploadUrl, and pass the returned attachmentToken here. Lane 2 (fallback): if your shell cannot reach taokeh.my — sandboxed clients sit behind a network allowlist and the PUT fails 403/blocked; that is YOUR sandbox, not Taokeh — send attachmentBase64 + attachmentMediaType inline instead, always with attachmentBytes (the file's decoded size on disk) so a truncated paste is rejected rather than filed. PNG or JPEG only: those are the only formats Taokeh can embed in a generated PDF, and anything else is refused by name. Taokeh SHRINKS oversized artwork for you rather than sending you away to resize it — a 5000×5000 export is accepted and stored at 2000px, and the review screen says so; only an image so large it is no longer a logo is refused. A LOGO IS ONE VALUE, so the proposal REPLACES the whole thing rather than patching it, and there is only ever ONE logo proposal waiting: filing a second one replaces the first, which is rejected as superseded. If the company already has a logo the review page shows both images side by side; if it has none, this is the first one. If an admin sets or removes a logo in Taokeh after you file, the one-tap doors refuse and send them to the full review page — a human's own choice is never overwritten by a proposal that never saw it. ⛔ THIS IS THE COMPANY LOGO ONLY. It is not the Pioneer program's testimonial artwork, and nothing on this connector can touch that. BE HONEST: propose only an image the user actually gave you for this purpose. Never generate a logo and file it as though they had chosen it, and tell them in the same breath that nothing on their paperwork changes until they tap Approve.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note, in the reviewer's language: where this image came from and what they should check before it goes on their invoices. | |
| needsReview | No | Set true when something gave you pause — an image you are not certain is the right mark, or one the user sent in passing rather than chose. | |
| attachmentBytes | No | The decoded byte size of the image on disk — send it alongside attachmentBase64 and the server rejects a truncated paste instead of filing half a logo. | |
| attachmentToken | No | The token from request_attachment_upload, AFTER you have PUT the image bytes to its uploadUrl. The preferred lane for any real logo file. Mutually exclusive with attachmentBase64. | |
| attachmentBase64 | No | The logo image as base64 — the fallback lane, for when your shell cannot reach taokeh.my. PNG or JPEG only. Mutually exclusive with attachmentToken. | |
| attachmentSha256 | No | The SHA-256 of the image as 64 hex chars — optional second integrity check alongside attachmentBase64. | |
| attachmentFilename | No | The original file name, e.g. 'acme-logo.png'. Shown on the review page so the admin recognises what they were sent. | |
| attachmentMediaType | No | The image's MIME type — 'image/png' or 'image/jpeg'. Required when attachmentBase64 is given. It must match the file's bytes — a mismatch is refused and nothing is filed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations covering only the safety profile, the description carries the behavioral burden and delivers: replacement-not-patch semantics, only-one-proposal-waiting with supersede-on-second-file, admin-override protection after a human sets/removes a logo, and automatic downscaling instead of file rejection. These traits are not recoverable from the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The crucial constraint (pending draft only, human approves) is front-loaded in caps, and the lane/format/replacement details follow in a logical order. It is longer and more emphatic than strictly necessary, but nearly every sentence earns its place by conveying a distinct rule.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-param mutation-style drafting tool with no output schema, the description supplies the full behavioral picture: submission lanes, format/size handling, replacement semantics, and the human-approval gate. An agent has everything needed to call it correctly and to inform the user.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, and the description adds genuine meaning beyond it: the mutual exclusivity of the two lanes, why attachmentBytes accompanies base64 (rejects truncated pastes), and the PNG/JPEG-only constraint. It stops short of explaining every field, but meaningfully enriches the key ones.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb+resource: filing a pending draft proposing a new COMPANY LOGO, and immediately scopes that nothing changes until human approval. It cleanly distinguishes itself from the sibling create_*_draft/update_*_draft tools and explicitly rules out the Pioneer testimonial artwork.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternative lanes explicitly (Lane 1 request_attachment_upload default, Lane 2 base64 fallback) and the exact condition that selects the fallback (sandbox 403/blocked reach to taokeh.my). It also states the honesty precondition and the exclusions, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_product_draftFile a pending product-change draft (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose corrections to a product ALREADY in Taokeh's catalogue — a selling price keyed wrong, a name that came across from the old system mangled, a missing barcode, the wrong unit, a reorder point nobody set, an item filed under the wrong category. This does NOT change anything: it files a pending DRAFT the owner reviews as a DIFF (current → proposed, changed fields only) and approves with one tap; only that tap writes. Name the product with productId (the id resolve_product returns) or with its EXACT sku — a SKU is unique inside a company, so it is an exact key; a product NAME is not, and is refused. If you pass both and they disagree, the call is refused rather than guessing. proposed holds ONLY the fields you want changed: name, unit, unitPrice, reorderPoint, barcode, and the category (pass categoryId, or category as a name matched against the categories the company ALREADY has — this tool will never create a category, because the owner is approving a change to a product, not new master data). ⛔ WHAT IT CANNOT DO, each refused by name with the screen that owns it: it cannot change STOCK ON HAND — a quantity change moves inventory and cost of goods sold together, so Taokeh only takes it at Products → Adjust stock where a counted reason is required; it cannot change the AVERAGE COST, which the purchases that set it own and which is what the inventory is worth on the balance sheet, so an edit here would restate that value with no journal behind it; it cannot change the SKU, the key everything else resolves on; it cannot set a description, which the product page has no control for at all (the catalogue import does); it cannot switch "Service — no stock" (isService) on or off — the owner ticks it on the product page; and it cannot publish or unpublish the item to the online shop, because what strangers can see and buy is not something an AI proposal should decide. A field the record already agrees with is dropped, and a proposal that changes nothing is refused rather than filed as an empty diff. One product per call — loop for a sweep, because each reviewable diff is the point. BE HONEST: never guess a price or a barcode; leave the field out and say so in notes with needsReview.
| Name | Required | Description | Default |
|---|---|---|---|
| sku | No | The EXACT SKU, as an alternative to productId. Exact only — a near miss is refused, and a product NAME is never accepted. | |
| notes | No | A SHORT reviewer note, in the reviewer's language: what you changed, why, and what they should double-check. | |
| proposed | Yes | ONLY the fields you want changed. A field the record already agrees with is dropped; an unknown or deliberately-excluded field is refused by name. | |
| productId | No | The product's real id, from resolve_product. Give this OR `sku`. | |
| needsReview | No | Set true when something gave you pause — a price the user was unsure about, a barcode read off a blurry photo. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false but destructiveHint=false; the description explains exactly why — nothing writes until a human taps approve on a diff. It adds behavior annotations cannot express: unchanged fields are dropped, a no-op proposal is refused, one product per call, and refusal-by-name for stock, average cost, SKU, description, isService, and publish state.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Excellent front-loading — the draft-only constraint is the first clause, and the refusal list is grouped in one block. It is long, though, and the "nothing changes until a human approves" point is restated three times, which costs some density even if the repetition is partly deliberate for safety.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a nested-object mutation tool with no output schema, the description covers input selection, refusal semantics, excluded fields with their owning screens, no-op handling, and batching advice. An agent has everything needed to call it correctly and to explain the result to a user.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, yet the description still adds real meaning: `productId` provenance from resolve_product, SKU being exact-only and names refused, the both-passed-and-disagree refusal, and the guarantee that `category` resolves to existing master data rather than creating it. It also tells the agent to omit uncertain values and use `notes`/`needsReview`.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ("FILES A PENDING DRAFT") and immediately scopes it to corrections on a product already in the catalogue, enumerating concrete examples (mispriced selling price, mangled name, missing barcode). It is clearly distinguishable from create/update siblings and from stage_master_data, since it explicitly refuses master-data creation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
States when to use it, how to identify the product (productId or exact SKU, never a name), what to pass in `proposed`, and what happens on conflicting identifiers. It also names explicit when-NOT cases with the owning surface for each (Products → Adjust stock, product page, catalogue import, online shop), which is the strongest form of routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_purchase_order_draftFile a pending purchase-order correction draft (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose a correction to a PURCHASE ORDER that already exists — a wrong quantity or cost, a missing or extra line, the wrong date, term or notes. An admin or bookkeeper reviews a plain current → proposed DIFF and approves. A purchase order posts nothing and moves no stock, so the correction touches no ledger and no stock; a SENT order stays SENT, and Taokeh emails nobody — sending the corrected order to the supplier is the user's own step (an approved change stops the WhatsApp link the supplier was sent, so they need the order again). ROUTING — (1) the order should not stand: the owner cancels it in Taokeh (Purchase orders → the PO → Cancel); there is no AI lane for cancelling. (2) the order is wrong and not yet received in full: THIS tool — a DRAFT, a SENT or a PARTIAL (partly received) order all qualify. On a PARTIAL order a line that has received goods must keep its product and cannot go below the quantity already received (send it at least that), and it cannot be dropped from lines — the approval is refused by name otherwise; the supplier and currency are frozen once anything has arrived. (3) the order is CONVERTED (received in full, or the rest closed as never coming) or CANCELLED: it is closed to edits — the delivery bills are the documents now, corrected in Taokeh (Purchases → the bill → Edit). A NEW order is create_purchase_order_draft. WHAT YOU CAN CHANGE: poDate, term (null clears it), notes (null clears them) and lines. A purchase order is taxed from its lines' SST codes (rounded per code on the subtotal; an order entered before 7 Oct 2026 keeps the per-line rounding it was saved with), the same as the bill it becomes: a new line inherits the code this order already stores for its product; a new product takes the order's code when every line carries the same one; otherwise it carries no SST (the review shows the new total). An order with NO SST code on any line is taxed at a whole-document rate Taokeh stores only as an amount, so when such a taxed order's LINES change you must also send taxRate (see its field). WHAT YOU CANNOT, each refused by name: the SUPPLIER (an order to someone else is a different order), the PO NUMBER, the CURRENCY or exchange rate, the TAX, and marking sent, cancelling or converting. INVENTORY-ONLY lines, as on create_purchase_order_draft. WHAT HAPPENS ON APPROVAL: the document is updated IN PLACE — same id, same number — through the SAME update Taokeh's own Edit screen runs. Every field you leave out is carried across exactly as it stands (the party, the attention contact, the currency and its frozen exchange rate, the tax) and is never reset. LINES: lines is the FULL replacement set. READ THE LINES FIRST with search_document_lines — each row's lineNo is what a { keep: } entry refers to, and keep is how a line that is already right survives untouched (a measured line keeps its frozen unit and its rounding row). A changed or new line is written in full, in the create tool's line shape, and the server re-derives its quantity and amount. A proposal that changes nothing is refused rather than filed as an empty diff. STALE: if a person edits the document after you read it, the one-tap approval is refused and the reviewer is shown both versions — call revise_draft with this draft's kind to re-read and re-propose. Pass the document's REAL id from search_documents; a document number is refused. A line Taokeh converted from a per-foot or per-piece price prints in tons at a whole-ringgit rate under a note like "(150' @ RM 0.50/ft)" (older documents print "/feet"): send such a line in its FILED form — thickness, width, tally, unit 'feet' or 'pcs', the per-foot or per-piece price (on a purchase, the supplier's cost) and only the grade as the description, never the printed note. On a correction, a PURCHASE line may instead be sent back exactly as it stands on the document; a SELLING line sent back exactly as it stands is read back into its filed form only when its note's price reproduces it (otherwise keep it with { keep: } where offered, or correct the header only); any other line carrying the note follows the rule for a new line — a selling line read back only at its own tons and rate with no discount, a purchase line refused.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note in the reviewer's language: what was wrong, what you changed, and anything they should check before approving. | |
| proposed | Yes | ONLY what you want changed. An unknown or not-patchable key is refused by name; a proposal the purchase order already agrees with is refused rather than filed. | |
| needsReview | No | Set true when something gave you pause — a quantity you inferred, a price the user was unsure about. It flags the draft for the reviewer. | |
| printedTotal | No | The grand total the corrected purchase order should show, if the user told you one — cross-checked against the server's own figure and flagged on the review screen. | |
| purchaseOrderId | Yes | REQUIRED — the purchase order's real id, from search_documents. A PO number is refused. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Far exceeds the annotations (readOnly=false, destructive=false, idempotent=false): it discloses the human-approval gate, that the update is in place (same id, same number), that omitted fields are carried across and never reset, that a no-op diff is refused, that stale documents are refused with both versions shown, and that the supplier WhatsApp link is invalidated on approval. It also enumerates each refusal by name, which is exactly the behavioral context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical constraint is front-loaded in capitals, which is good, but the body is a dense ~700-word block with long ALL-CAPS clauses and no structural breaks beyond inline dashes. The per-foot/per-piece conversion paragraph is reproduced almost verbatim in the schema's `keep` field description, so a meaningful share of the text does not earn its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a nested, non-idempotent mutation with no output schema and partial-order edge cases, nothing material is missing: tax derivation, frozen supplier/currency after partial receipt, line-level replacement semantics, staleness, and the reviewer-visible diff are all covered. An agent could call this correctly from the description alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real semantic value on top: it names the changeable set (poDate, term, notes, lines), establishes that 'lines' is a FULL replacement set, mandates reading lineNo via search_document_lines before proposing, and states the conditional requirement for taxRate on whole-document-taxed orders. It also clarifies that purchaseOrderId must be the real id, not a document number.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource ('Propose a correction to a PURCHASE ORDER that already exists') and immediately bounds it ('FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT'). It names the sibling it is not ('A NEW order is create_purchase_order_draft'), so an agent can route without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The ROUTING block gives explicit when-to-use and when-not-to-use branches keyed to document state: cancel → no AI lane, DRAFT/SENT/PARTIAL → this tool, CONVERTED/CANCELLED → correct the bill in Taokeh, new order → create_purchase_order_draft. It also names the recovery path for a stale read ('call revise_draft with this draft's kind').
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_quote_draftFile a pending quote-correction draft (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose a correction to a QUOTE that already exists — a wrong price or quantity, a missing or extra line, the wrong date, validity, term or notes. An admin or bookkeeper reviews a plain current → proposed DIFF and approves. A quote posts nothing, so the correction touches no ledger, no stock and no e-invoice. ROUTING — (1) the quote should not exist at all: the owner voids it in Taokeh (Quotes → the quote → Void); there is no AI lane for voiding. (2) the quote is wrong and still OPEN: THIS tool. (3) the quote was already CONVERTED into an invoice or a delivery order: it is closed to edits — correct the document it became (update_invoice_draft or update_delivery_order_draft). A NEW quote is create_quote_draft. WHAT YOU CAN CHANGE: quoteDate, validUntil (null clears it), term, notes (null clears it) and lines. WHAT YOU CANNOT, each refused by name: the CUSTOMER (a quote to someone else is a different quote), the QUOTE NUMBER, the CURRENCY or exchange rate, and converting or voiding. ONLY AN OPEN QUOTE can be corrected — checked when you file and again when the reviewer taps. Leave a line's taxCode out and it inherits this quote's own SST code for its product — unless the quote carries that product under more than one code, when you must state it. A quote taxed at a whole-document rate (no SST codes on its lines) needs taxRate whenever its LINES change (see that field). WHAT HAPPENS ON APPROVAL: the document is updated IN PLACE — same id, same number — through the SAME update Taokeh's own Edit screen runs. Every field you leave out is carried across exactly as it stands (the party, the attention contact, the currency and its frozen exchange rate, the tax) and is never reset. LINES: lines is the FULL replacement set. READ THE LINES FIRST with search_document_lines — each row's lineNo is what a { keep: } entry refers to, and keep is how a line that is already right survives untouched (a measured line keeps its frozen unit and its rounding row). A changed or new line is written in full, in the create tool's line shape, and the server re-derives its quantity and amount. A proposal that changes nothing is refused rather than filed as an empty diff. STALE: if a person edits the document after you read it, the one-tap approval is refused and the reviewer is shown both versions — call revise_draft with this draft's kind to re-read and re-propose. Pass the document's REAL id from search_documents; a document number is refused. A line Taokeh converted from a per-foot or per-piece price prints in tons at a whole-ringgit rate under a note like "(150' @ RM 0.50/ft)" (older documents print "/feet"): send such a line in its FILED form — thickness, width, tally, unit 'feet' or 'pcs', the per-foot or per-piece price (on a purchase, the supplier's cost) and only the grade as the description, never the printed note. On a correction, a PURCHASE line may instead be sent back exactly as it stands on the document; a SELLING line sent back exactly as it stands is read back into its filed form only when its note's price reproduces it (otherwise keep it with { keep: } where offered, or correct the header only); any other line carrying the note follows the rule for a new line — a selling line read back only at its own tons and rate with no discount, a purchase line refused.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note in the reviewer's language: what was wrong, what you changed, and anything they should check before approving. | |
| quoteId | Yes | REQUIRED — the quote's real id, from search_documents. A quote number is refused: correcting the wrong document silently is worse than asking which one they mean. | |
| proposed | Yes | ONLY what you want changed. An unknown or not-patchable key is refused by name; a proposal the quote already agrees with is refused rather than filed. | |
| needsReview | No | Set true when something gave you pause — a quantity you inferred, a price the user was unsure about. It flags the draft for the reviewer. | |
| printedTotal | No | The grand total the corrected quote should show, if the user told you one — cross-checked against the server's own figure and flagged on the review screen. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses that this files a pending draft only, that approval updates the document in place with the same id and number, and that omitted fields are carried across rather than reset. It also explains stale-document behavior, line replacement semantics, tax inheritance, refusal cases, and the need to re-propose with revise_draft.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The definition is long, but it is front-loaded with the most important constraint — pending draft only — and uses clear sectioning for routing, mutable fields, approval behavior, lines, and stale handling. Some line-conversion detail is dense and repetitive with the schema, but nearly every sentence carries operational meaning for this complex mutation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, nested, non-idempotent draft-filing tool with no output schema, the description is unusually complete: it covers approval flow, in-place update behavior, allowed and forbidden fields, line replacement, tax-rate handling, stale edits, and the correct id source. An agent has enough context to select the tool and prepare a valid proposal.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaningful semantics: quoteId must be the real id from search_documents and not a document number, lines are a full replacement set using { keep: lineNo } for unchanged lines, and taxCode may be inherited or must be stated in specific cases. It does not explain every top-level field such as needsReview or printedTotal, but the schema covers those and the description materially improves the critical parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: it files a pending correction draft for an existing quote, and explicitly clarifies that nothing changes until human approval. It also distinguishes this from create_quote_draft, voiding, and correcting converted documents, so an agent can identify the tool without opening sibling schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit routing: if the quote should not exist, the owner voids it in Taokeh; if it is wrong and still open, use this tool; if it was converted, correct the invoice or delivery order instead; if new, use create_quote_draft. It also names the conditions under which the tool refuses changes, such as non-open quotes and non-patchable fields.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_recurring_invoice_draftFile a pending recurring-invoice change draft (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose changes to a recurring invoice ALREADY set up in Taokeh — a price rise across a subscription, a changed quantity, a new end date, a cadence correction, or PAUSING the billing. This does NOT change anything: it files a pending DRAFT the owner reviews as a DIFF (current → proposed, changed fields only) and approves; only that tap writes. Name the schedule with recurringId, its real id — there is NO name fallback, because a schedule title is free text and one customer often has two schedules (a monthly retainer and an annual licence), so guessing between them would re-bill the wrong contract. proposed holds ONLY what you want changed: title, customerId/customer, lines, cadence, paymentMethod, startDate, endDate, maxOccurrences, notes, and status ("paused" to stop future invoices, "active" to resume). ⚠ lines is WHOLE-REPLACE: send every line the schedule should have, including the unchanged ones — for a price rise, send the same lines with the new prices. ⚠ APPROVING CAN MOVE THE BILLING DATE even when you change only a price: Taokeh re-derives the next issue date from the start date on every save, so the review page states the exact date the next invoice would go out, and whether that has moved. ⛔ WHAT IT CANNOT DO, each refused by name: it cannot set issueMode: "auto" — that would let one approval remove the human tap from every future invoice; it cannot set status: "ended", a ONE-WAY door no later draft could undo (propose "paused" instead, which stops the billing just as completely and can be resumed); it cannot set MFRS 15 revenue spreading, an accounting-policy decision on the schedule's own page; it cannot set the schedule's "Paid into" account (where a CASH schedule's money is booked — picked on the schedule's own page; an approved change keeps the one the schedule has, and moving the schedule to CREDIT clears it); and it cannot touch the invoices this schedule has ALREADY issued — a change reaches the next firing and no earlier one. If a past invoice was wrong, credit it (create_credit_note_draft); the schedule only decides what happens next. A field the record already agrees with is dropped, and a proposal that changes nothing is refused rather than filed as an empty diff. One schedule per call — loop for a sweep across several, because each reviewable diff is the point.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note, in the reviewer's language: what you changed, why, and what they should double-check. | |
| proposed | Yes | ONLY the fields you want changed. A field the record already agrees with is dropped; an unknown or deliberately-excluded field is refused by name. | |
| needsReview | No | Set true when something gave you pause — a price the user was unsure about, a date you inferred. | |
| recurringId | Yes | The recurring invoice's real id. Required — there is no name fallback. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say non-destructive/non-idempotent; the description goes far beyond by disclosing that approval is human-gated, that approving can re-derive the next billing date even for a price-only change, that lines are whole-replace, and by naming each refused field (issueMode auto, status ended, MFRS 15, Paid into) with the reason. This is unusually rich behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical constraint ('FILES A PENDING DRAFT ONLY') is front-loaded ahead of all detail, and the sections progress logically. However it is dense and repetitive in places — the 'nothing changes' point is asserted twice and the warnings are heavy on capitalization, which costs some readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a nested-object schema, no output schema, and a human-approval mutation path, the description covers everything an agent needs: what is filed, what is refused, side effects on billing dates, line-replacement semantics, and single-schedule scoping.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline is 3, but the description adds meaning the schema does not: why recurringId has no name fallback (free-text titles, duplicate schedules), that `proposed` should carry only changed fields, and that `status: paused` is the reversible alternative to the refused `ended`. Much of it restates schema enums, which keeps it from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource+scope: files a pending draft that changes nothing until human approval. It clearly distinguishes itself from create_recurring_invoice_draft ('ALREADY set up') and from direct-write siblings, so an agent can select it without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes to alternatives under named conditions: create_credit_note_draft when a past invoice was wrong, create_invoice_draft for per-foot/per-piece lines, and 'loop for a sweep' for multiple schedules. It also states what to do when a proposal changes nothing (refused, not filed).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_supplier_debit_note_draftFile a pending correction draft for a posted supplier debit note (human approves in Taokeh)AInspect
FILES A PENDING DRAFT ONLY — NOTHING CHANGES UNTIL A HUMAN REVIEWS AND APPROVES IT IN TAOKEH. Propose a correction to a BUY-SIDE DEBIT NOTE already posted in Taokeh — a purchase return raised against a SUPPLIER's bill (Taokeh's /debit-notes page). This is NOT the sell-side debit note that create_debit_note_draft files (an additional charge on your own invoice); that document has no correction lane at all — the owner voids it and issues a new one. An ADMIN reviews this as a DIFF (current → proposed) and approves; only that tap re-books the note, and it keeps the SAME debit-note number, the bill it was raised against and any attached document. Pass debitNoteId, the note's REAL id from search_documents (docType debit_note). ROUTING — (1) THE RETURN NEVER HAPPENED (keyed twice, wrong bill): the owner VOIDS the note in Taokeh (Debit notes → the note → Void); voiding keeps it on the register marked VOID. A note VALIDATED on MyInvois cannot be voided: any further correction goes through LHDN against the validated document. (2) THE RETURN IS RIGHT BUT TYPED WRONG (a wrong line, quantity, cost, date or reason): THIS tool. (3) MORE GOODS WENT BACK LATER: a new debit note, raised by the owner in Taokeh — there is no AI lane for creating a buy-side debit note. WHAT YOU CAN CHANGE: dnDate, reason, and lines — the FULL REPLACEMENT SET. ⛔ EVERY LINE MUST ANSWER goodsReturned (true = these goods physically went back to the supplier and leave stock, false = money only). There is no default. Your answer only PRE-FILLS the admin's per-line question on the full review page; a ONE-TAP approval never moves stock and is refused whenever the note already took goods off the shelf or any line says goods went back. Stock only ever leaves for a product the BILL actually received, never more than the bill still has standing (a goods line that sends back more is refused with both quantities named, when you file and when the admin taps; a money-only line is not capped by quantity), and never more than is on hand (see STOCK below). SST is inherited from the bill — there is no tax code to send. A NOTE WITH A DISCOUNTED LINE (search_document_lines shows discount on it): its lines cannot be corrected here at all — any lines proposal on it is refused, and the owner changes them in Taokeh (Debit notes → Edit); a date or reason correction (no lines) still works and reposts the note's own lines, discounts included. WHAT YOU CANNOT, each refused by name: the SUPPLIER, the debit-note NUMBER, the BILL it was raised against. BLOCKED OUTRIGHT, checked when you file (the server runs the real correction and rolls it back) and again when the admin taps: the note is voided; it is validated or exported for MyInvois as a self-billed document; a supplier refund is allocated to it, or its credit was used on a payment to the supplier; the bill it was raised against is gone; its date is inside a LOCKED period; or the corrected note would credit more than the bill still owes — UNLESS the note already carries the supplier's own credit note number (it then may credit up to the bill's value less its other debit notes, and the extra is money the supplier owes back). This tool cannot add that number: the owner enters it on the note's Edit form in Taokeh (Supplier’s credit note no.); say so instead of filing a correction that goes over. A proposal that changes nothing is refused. If the note is edited after you read it, the one-tap approval is refused — call revise_draft (kind 'supplier_debit_note_update'). ⛔ STOCK (stock-modes S1; sweep 18a): on a tracked (perpetual) company a correction that returns more than is on hand is refused at approve. On a not-tracked (periodic) company it is refused when you file and again when the admin taps, naming each short item with what it sends back and what is on hand. The way out, in this order: if the goods really went back, put the extra on a money-only line (goodsReturned false) so the note keeps its value; if fewer went back, reduce the line to what really went back. The note's own earlier take counts as on hand, so an ordinary note's unchanged quantity passes; a note posted before 2 Nov 2026 that took fewer units than its lines say is refused even unchanged.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | A SHORT reviewer note: what was wrong, what you changed, and whether the goods really went back. | |
| proposed | Yes | ONLY what you want changed. An unknown or not-patchable key is refused by name. | |
| debitNoteId | Yes | REQUIRED — the posted BUY-side debit note's real id, from search_documents (docType debit_note). | |
| needsReview | No | Set true when something gave you pause. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the safety profile (readOnly=false, destructive=false, idempotent=false), and the description goes far beyond them: nothing changes until a human approves, the admin sees a diff, the note keeps its number/bill/attachments, and the post-approval behaviour of stock is spelled out. It also discloses the unusual validation model — the server runs the real correction and rolls it back at file time, then re-checks on approval — plus the full list of named refusal conditions (voided, MyInvois validated, allocated refund, locked period, bill deleted, over-credit) and the optimistic-concurrency failure mode.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loading is excellent — the 'FILES A PENDING DRAFT ONLY' constraint and the three-way routing come first — and the block headings (ROUTING, WHAT YOU CAN CHANGE, WHAT YOU CANNOT, BLOCKED, STOCK) give it scannable structure. But the body is a very long wall of conditionals with deep parenthetical nesting and repeated re-statements of stock and refusal rules, which raises the risk an agent skims past a load-bearing clause such as the goodsReturned-per-line requirement.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with nested line objects, no output schema and unusual stock/approval semantics, the definition covers every decision an agent must make: which ids to pass and where to get them, what is patchable, what is refused by name, when to escalate to the owner's Edit form, and the ordered fallback when stock is short. Nothing material is left to inference, including the SST-is-inherited and cannot-add-supplier-credit-note-number limits.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so 3 is the floor, and the description genuinely adds meaning on top: 'dnDate, reason, and lines — the FULL REPLACEMENT SET' tells the agent lines replaces rather than patches, and the schema's own note that lines may be omitted for date-only/reason-only corrections is reinforced. The goodsReturned mandate and the discounted-line refusal are also stated behaviourally, not just as field docs. It stops short of 5 only because most per-field meaning is already carried by the unusually rich schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — 'Propose a correction to a BUY-SIDE DEBIT NOTE already posted in Taokeh' — and explicitly disambiguates from the sell-side sibling create_debit_note_draft, which 'has no correction lane at all'. It also anchors the document to a concrete location (Taokeh's /debit-notes page) and to the purchase-return meaning, so an agent can tell it apart from every other draft tool without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The ROUTING block gives three mutually exclusive situations with the exact tool or action for each: void in Taokeh when the return never happened, THIS tool when the return is right but typed wrong, and a new owner-raised note when more goods went back. It further names revise_draft (kind 'supplier_debit_note_update') as the alternative when the note was edited after reading, which is the kind of exclusion most definitions omit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
working_capitalWorking capital (days + who pays slowly)ARead-onlyInspect
Working capital in DAYS, month by month, plus who is slow to pay — the deterministic answer to "the profit and loss says I made money, so where is the cash?". Give from and to as YYYY-MM, inclusive; the window is capped at 36 months, the same cap financial_timeseries carries. TWO HALVES COME BACK IN ONE CALL. First, months[]: per month revenue, cogs, arClose, apClose, inventoryClose and four ratios — debtorDays (arClose ÷ revenue × days: days of sales sitting unpaid in customers' hands), creditorDays (apClose ÷ cogs × days: days of cost sitting unpaid in yours), inventoryDays (inventoryClose ÷ cogs × days: days of cost sitting on the shelf) and cashConversionCycle (debtorDays + inventoryDays − creditorDays). Every row states days, the calendar days it covers, because a days ratio without its denominator cannot be checked. Second, customers and vendors: per party avgDaysToPay (amount-weighted, from each document's own date to the date the payment APPLIED: a customer's deposit paid before its invoice, or an advance paid to a supplier before the bill, applies on that document's date, so it counts as 0 days and falls in the document's month), avgDaysPastDue (the same weighting measured from the due date), invoices, paid and withTerms — slowest first, up to 50 a side with total, shown and more. NULL IS AN ANSWER. A ratio whose denominator is zero comes back null — no comparable base — never 0 and never an enormous number; cashConversionCycle is null whenever any one of its three legs is, and is never the sum of the legs that happen to exist. Relay a null as "there is no base to measure that against", never as zero. ⚠ INVENTORY DAYS IS WITHHELD unless this company runs PERPETUAL costing. On Taokeh's default mode (modified periodic) a sale does not move stock and cost of goods sold is recognised only at a stock take, so the inventory balance sits still and then jumps while monthly COGS is zero in most months — a monthly inventory-days figure on that basis would measure something else. inventoryClose is still returned, because it is a real ledger balance; inventoryDays and cashConversionCycle are null and a note says why. Do NOT compute it yourself from inventoryClose and cogs: that is precisely the number the server declined to state. WHAT PAYMENT SPEED EXCLUDES, and say so when you relay it: cash sales and cash bills (they settle on their own date and would average zeros into every party), opening invoices and opening bills (those dates belong to the old system, not to this company's paper), refunds (a negative allocation is money walking back out, not a customer paying) and contra set-offs (a set-off moves no bank money). MONTHS THAT DO NOT EXIST ARE ABSENT, NOT ZERO: months ending before this company's accounting start date, and months after today. A month still running is partial: true with through = today, and its days counts only the days that have happened, so both sides of every division are month-to-date — say so, and never let that row bend a trend. The cutover month is partial with startsAt, and its days, revenue and cogs all count from there — so its revenue can be lower than financial_timeseries shows for the same month, which runs from the 1st. EQUAL BASIS: revenue and cogs are the /reports income statement, arClose and apClose the A/R and A/P aging reports' own totals as of month end, inventoryClose the ledger balance of the mapped inventory account. Nothing is re-summed, and basis states every formula with its exact inputs. DRILL DOWN RATHER THAN GUESS: a slow customer here → ar_aging for the buckets, open_invoices for that customer's unpaid documents, search_documents with docType 'payment' for the settlements behind the average, get_document_pdf for the paper itself. FIGURES ONLY — no verdict, no benchmark, no trend label, no "healthy" and no "improving". The reading is yours to write. A very wide window can exceed this call's time budget, in which case it is REFUSED (naming how far it got) rather than answered with a short series. Nothing is written.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | ||
| from | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint already covering safety, the description still adds substantial behavior: null-vs-zero semantics for every ratio, inventoryDays/cashConversionCycle withheld under modified periodic costing, partial months and cutover months, the 36-month cap, and time-budget refusal. It also forbids re-deriving the withheld ratio, which prevents a specific downstream failure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and the two output halves are front-loaded before the caveats, and sections are separated by labeled clauses (NULL IS AN ANSWER, WHAT PAYMENT SPEED EXCLUDES, MONTHS THAT DO NOT EXIST). It is long 500-plus words for a two-parameter tool, but nearly every sentence carries decision-relevant content, so the length is defensible rather than padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description carries the entire return contract — it enumerates months[] fields, the four ratios with formulas, the customers/vendors party fields with pagination (total, shown, more), and the null rules. Nothing an agent needs in order to call it or relay results correctly appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the schema only carries a pattern, so the description must add meaning — and it does: 'inclusive' boundary semantics, YYYY-MM format, and a 36-month window cap tied to financial_timeseries. It stops short of explaining how an out-of-range or pre-accounting-start month is treated at the parameter level (that is only covered at the output level), so not quite a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb-and-resource pair ('Working capital in DAYS, month by month, plus who is slow to pay') and names exactly what the output contains. It explicitly contrasts itself with financial_timeseries (same 36-month cap, but cutover-month differs because that tool runs from the 1st), so an agent can distinguish it from siblings without reading schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives the triggering question ('the P&L says I made money, so where is the cash?') and routes drill-down cases to specific alternatives: ar_aging for buckets, open_invoices for a customer's unpaid documents, search_documents with docType 'payment', get_document_pdf for paper. Also states the window cap and that oversized windows are refused rather than truncated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
- Changed
my_work1 field changed- changed
Input schema / properties / kind / enumPrevious value: -[ - "expense", - "invoice", - "bill", - "quote", - "receipt", - "payment", - "contact", - "contact_update", - "employee_update", - "product_update", - "recurring", - "credit_note", - "debit_note", - "purchase_order", - "journal", - "invoice_update", - "bill_update", - "logo_update", - "quote_update", - "delivery_order_update", - "purchase_order_update", - "credit_note_update", - "supplier_debit_note_update", - "expense_update", - "bank_clearing", - "payment_match", - "date_shift", - "coll_bill", - "coll_amendment", - "coll_slip", - "coll_final", - "coll_register", - "coll_restaurant", - "bank", - "staged_documents", - "staged_balances", - "staged_master" -]New value: +[ + "expense", + "invoice", + "bill", + "quote", + "receipt", + "payment", + "contact", + "contact_update", + "employee_update", + "product_update", + "recurring", + "credit_note", + "debit_note", + "purchase_order", + "journal", + "invoice_update", + "bill_update", + "logo_update", + "quote_update", + "delivery_order_update", + "purchase_order_update", + "credit_note_update", + "supplier_debit_note_update", + "expense_update", + "bank_clearing", + "payment_match", + "date_shift", + "coll_bill", + "coll_amendment", + "coll_slip", + "coll_final", + "coll_register", + "coll_restaurant", + "coll_rate", + "bank", + "staged_documents", + "staged_balances", + "staged_master" +]
1 tool update
- Changed
my_work1 field changed- changed
Input schema / properties / kind / enumPrevious value: -[ - "expense", - "invoice", - "bill", - "quote", - "receipt", - "payment", - "contact", - "contact_update", - "employee_update", - "product_update", - "recurring", - "credit_note", - "debit_note", - "purchase_order", - "journal", - "invoice_update", - "bill_update", - "logo_update", - "quote_update", - "delivery_order_update", - "purchase_order_update", - "credit_note_update", - "supplier_debit_note_update", - "expense_update", - "bank_clearing", - "payment_match", - "date_shift", - "coll_bill", - "coll_amendment", - "coll_slip", - "coll_final", - "bank", - "staged_documents", - "staged_balances", - "staged_master" -]New value: +[ + "expense", + "invoice", + "bill", + "quote", + "receipt", + "payment", + "contact", + "contact_update", + "employee_update", + "product_update", + "recurring", + "credit_note", + "debit_note", + "purchase_order", + "journal", + "invoice_update", + "bill_update", + "logo_update", + "quote_update", + "delivery_order_update", + "purchase_order_update", + "credit_note_update", + "supplier_debit_note_update", + "expense_update", + "bank_clearing", + "payment_match", + "date_shift", + "coll_bill", + "coll_amendment", + "coll_slip", + "coll_final", + "coll_register", + "coll_restaurant", + "bank", + "staged_documents", + "staged_balances", + "staged_master" +]
1 tool update
- Changed
my_work1 field changed- changed
Input schema / properties / kind / enumPrevious value: -[ - "expense", - "invoice", - "bill", - "quote", - "receipt", - "payment", - "contact", - "contact_update", - "employee_update", - "product_update", - "recurring", - "credit_note", - "debit_note", - "purchase_order", - "journal", - "invoice_update", - "bill_update", - "logo_update", - "quote_update", - "delivery_order_update", - "purchase_order_update", - "credit_note_update", - "supplier_debit_note_update", - "expense_update", - "bank_clearing", - "payment_match", - "date_shift", - "coll_bill", - "coll_amendment", - "bank", - "staged_documents", - "staged_balances", - "staged_master" -]New value: +[ + "expense", + "invoice", + "bill", + "quote", + "receipt", + "payment", + "contact", + "contact_update", + "employee_update", + "product_update", + "recurring", + "credit_note", + "debit_note", + "purchase_order", + "journal", + "invoice_update", + "bill_update", + "logo_update", + "quote_update", + "delivery_order_update", + "purchase_order_update", + "credit_note_update", + "supplier_debit_note_update", + "expense_update", + "bank_clearing", + "payment_match", + "date_shift", + "coll_bill", + "coll_amendment", + "coll_slip", + "coll_final", + "bank", + "staged_documents", + "staged_balances", + "staged_master" +]
1 tool update
- Changed
my_work1 field changed- changed
Input schema / properties / kind / enumPrevious value: -[ - "expense", - "invoice", - "bill", - "quote", - "receipt", - "payment", - "contact", - "contact_update", - "employee_update", - "product_update", - "recurring", - "credit_note", - "debit_note", - "purchase_order", - "journal", - "invoice_update", - "bill_update", - "logo_update", - "quote_update", - "delivery_order_update", - "purchase_order_update", - "credit_note_update", - "supplier_debit_note_update", - "expense_update", - "bank_clearing", - "payment_match", - "date_shift", - "bank", - "staged_documents", - "staged_balances", - "staged_master" -]New value: +[ + "expense", + "invoice", + "bill", + "quote", + "receipt", + "payment", + "contact", + "contact_update", + "employee_update", + "product_update", + "recurring", + "credit_note", + "debit_note", + "purchase_order", + "journal", + "invoice_update", + "bill_update", + "logo_update", + "quote_update", + "delivery_order_update", + "purchase_order_update", + "credit_note_update", + "supplier_debit_note_update", + "expense_update", + "bank_clearing", + "payment_match", + "date_shift", + "coll_bill", + "coll_amendment", + "bank", + "staged_documents", + "staged_balances", + "staged_master" +]
3 tool updates
- Changed
payroll_summary1 field changed- changed
Input schema / properties / staff / descriptionPrevious value: -"Return the full employee ROSTER (id, name, staff no., status, designation, basic salary) alongside the summary — independent of any run, so it works even before the first payroll. This is where the employeeId for update_employee_draft comes from. Individual salaries: ask for it only when the user actually needs it."New value: +"Return the full employee ROSTER (id, name, staff no., status, designation, basic salary, and skbbkExemptReason — null when Lindung 24 Jam (SKBBK) is deducted here, else OPTED_OUT or OTHER_EMPLOYER) alongside the summary — independent of any run, so it works even before the first payroll. This is where the employeeId for update_employee_draft comes from. Individual salaries: ask for it only when the user actually needs it."
- Changed
stage_master_data1 field changed- added
Input schema / properties / rows / items / properties / skbbkExemptReasonAdded value: +{ + "description": "employees: SKBBK (PERKESO Lindung 24 Jam) is deducted by default: OMIT = deducted here. \"OPTED_OUT\" only for a local (citizen, permanent resident, temporary resident) the employer says opted out with PERKESO by 31 Aug 2026; a foreign worker cannot opt out. \"OTHER_EMPLOYER\" where the person has another job and that employer deducts it. The old key skbbkEnrolled is refused.", + "enum": [ + "OPTED_OUT", + "OTHER_EMPLOYER" + ], + "type": "string" +}
- Changed
update_employee_draft2 fields changed- removed
Input schema / properties / proposed / properties / skbbkEnrolledRemoved value: -{ - "description": "PERKESO SKBBK (\"Lindung 24 Jam\") standing opt-in — voluntary for Malaysians, permanent residents and temporary residents (MyKAS); foreign workers are always deducted. Only set it when the employee has actually opted in.", - "type": "boolean" -} - added
Input schema / properties / proposed / properties / skbbkExemptReasonAdded value: +{ + "anyOf": [ + { + "enum": [ + "OPTED_OUT", + "OTHER_EMPLOYER" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "PERKESO SKBBK (\"Lindung 24 Jam\"). It is deducted BY DEFAULT (null = deducted here). \"OPTED_OUT\" = a local employee (citizen, permanent resident, temporary resident) opted out with PERKESO by 31 Aug 2026, the last day PERKESO took an opt-out; a foreign worker cannot opt out and is refused. \"OTHER_EMPLOYER\" = the employee has another job and that employer deducts it (only the one employer chosen with PERKESO deducts it, foreign workers included). null = deduct it here, e.g. after they opted back in with PERKESO. Set it only on the employer's word. The old key skbbkEnrolled was retired on 3 Nov 2026 and is refused." +}
1 tool update
- Changed
update_employee_draft1 field changed- changed
Input schema / properties / proposed / properties / skbbkEnrolled / descriptionPrevious value: -"PERKESO SKBBK (\"Lindung 24 Jam\") standing opt-in — voluntary for Malaysians. Only set it when the employee has actually opted in."New value: +"PERKESO SKBBK (\"Lindung 24 Jam\") standing opt-in — voluntary for Malaysians, permanent residents and temporary residents (MyKAS); foreign workers are always deducted. Only set it when the employee has actually opted in."
3 tool updates
- Changed
employer_cost_estimate1 field changed- added
Input schema / properties / temporaryResidentAdded value: +{ + "type": "boolean" +}
- Changed
stage_master_data2 fields changed- changed
Input schema / properties / rows / items / properties / isMalaysian / descriptionPrevious value: -"employees: Malaysian citizen. OMIT = yes, matching the employee form. Send false for a foreign worker — it drives EPF (2% each side from Oct 2025 wages for a non-PR), EIS (none for a non-PR), the HRD Corp levy and the CP8D / EA handling."New value: +"employees: Malaysian citizen. OMIT = yes, matching the employee form. Send false for a foreign worker — it drives EPF (2% each side from Oct 2025 wages for a non-PR), EIS (none for a non-PR unless a temporary resident), the HRD Corp levy and the CP8D / EA handling." - added
Input schema / properties / rows / items / properties / isTemporaryResidentAdded value: +{ + "description": "employees: temporary resident holding a MyKAS (green temporary-resident IC), for a non-citizen who is not a PR. OMIT = no. A temporary resident pays EIS; for EPF they stay on the non-PR 2% each side. Send true only on the employer's word.", + "type": "boolean" +}
- Changed
update_employee_draft2 fields changed- changed
Input schema / properties / proposed / properties / isPermanentResident / descriptionPrevious value: -"Malaysian permanent resident — only meaningful when isMalaysian is false. A non-citizen who is NOT a PR pays EPF at 2% each side from October 2025 wages and has no EIS; a PR pays the Malaysian rates (EPF Part C from age 60). Set it only on the employer's word."New value: +"Malaysian permanent resident — only meaningful when isMalaysian is false. A non-citizen who is NOT a PR pays EPF at 2% each side from October 2025 wages and has no EIS unless they are a temporary resident; a PR pays the Malaysian rates (EPF Part C from age 60). Set it only on the employer's word." - added
Input schema / properties / proposed / properties / isTemporaryResidentAdded value: +{ + "description": "Temporary resident holding a MyKAS (the green temporary-resident IC) — only for a non-citizen who is not a PR. A temporary resident pays EIS; for EPF they stay on the non-PR 2% each side. Moving someone from temporary resident to PR or citizen? Send isTemporaryResident: false with the new citizenship. Set it only on the employer's word.", + "type": "boolean" +}
1 tool update
- Changed
update_purchase_order_draft1 field changed- changed
Input schema / properties / proposed / properties / term / descriptionPrevious value: -"The delivery/payment term as it should read (stored as written). Pass null to clear it."New value: +"The delivery/payment term as it should read (stored as written). Pass null to clear it. Bills made afterwards with Taokeh's \"Receive a delivery\" take it (a preset term such as 'Net 30' sets each one's due date from its own date); bills already made, and scanned bills matched to the order, keep their own."
3 tool updates
- Changed
my_work1 field changed- changed
Input schema / properties / kind / enumPrevious value: -[ - "expense", - "invoice", - "bill", - "quote", - "receipt", - "payment", - "contact", - "contact_update", - "employee_update", - "product_update", - "recurring", - "credit_note", - "debit_note", - "purchase_order", - "journal", - "invoice_update", - "bill_update", - "logo_update", - "quote_update", - "delivery_order_update", - "purchase_order_update", - "credit_note_update", - "supplier_debit_note_update", - "expense_update", - "bank_clearing", - "payment_match", - "bank", - "staged_documents", - "staged_balances", - "staged_master" -]New value: +[ + "expense", + "invoice", + "bill", + "quote", + "receipt", + "payment", + "contact", + "contact_update", + "employee_update", + "product_update", + "recurring", + "credit_note", + "debit_note", + "purchase_order", + "journal", + "invoice_update", + "bill_update", + "logo_update", + "quote_update", + "delivery_order_update", + "purchase_order_update", + "credit_note_update", + "supplier_debit_note_update", + "expense_update", + "bank_clearing", + "payment_match", + "date_shift", + "bank", + "staged_documents", + "staged_balances", + "staged_master" +]
- Changed
update_bill_draft4 fields changed- added
Input schema / properties / dateShiftAdded value: +{ + "description": "USE THIS INSTEAD OF ONE DRAFT PER BILL when many bills carry the wrong date (keyed a day early, a whole batch dated wrong): ONE pending draft moves all of them, and it counts as ONE against the 50-draft queue. Send it ALONE — no purchaseId, no `proposed`. Only the date moves; a stored due date moves by the same number of days; nothing else (payment method, term, lines) changes. Every bill is checked now with the same guards as a single correction: if ANY is refused, NOTHING is filed and every refusal is listed — leave those out and send the rest. An admin approves the whole list on one page in Taokeh; they can drop rows there, and approving moves all of them or none.", + "properties": { + "days": { + "description": "Move every listed bill by this many days (1 = one day later, -1 = one day earlier). Leave it out when each bill carries its own `date`.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "documents": { + "description": "The bills to move, each once — at most 200.", + "items": { + "properties": { + "date": { + "description": "This bill's correct date, YYYY-MM-DD (MYT). Overrides `days` for this one.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "id": { + "description": "The bill's real id from search_documents.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "maxItems": 200, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "documents" + ], + "type": "object" +} - changed
Input schema / properties / proposed / descriptionPrevious value: -"ONLY what you want changed. An unknown or not-patchable key is refused by name; a proposal the bill already agrees with is refused rather than filed."New value: +"REQUIRED unless you send dateShift. ONLY what you want changed. An unknown or not-patchable key is refused by name; a proposal the bill already agrees with is refused rather than filed." - changed
Input schema / properties / purchaseId / descriptionPrevious value: -"REQUIRED — the posted bill's real id, from search_documents. A bill number is refused: correcting the wrong document silently is worse than asking which one they mean."New value: +"REQUIRED (unless you send dateShift) — the posted bill's real id, from search_documents. A bill number is refused: correcting the wrong document silently is worse than asking which one they mean." - removed
Input schema / requiredRemoved value: -[ - "purchaseId", - "proposed" -]
- Changed
update_invoice_draft4 fields changed- added
Input schema / properties / dateShiftAdded value: +{ + "description": "USE THIS INSTEAD OF ONE DRAFT PER INVOICE when many invoices carry the wrong date (keyed a day early, a whole batch dated wrong): ONE pending draft moves all of them, and it counts as ONE against the 50-draft queue. Send it ALONE — no saleId, no `proposed`. Only the date moves; a stored due date moves by the same number of days; nothing else (payment method, term, lines) changes. Every invoice is checked now with the same guards as a single correction: if ANY is refused, NOTHING is filed and every refusal is listed — leave those out and send the rest. An admin approves the whole list on one page in Taokeh; they can drop rows there, and approving moves all of them or none.", + "properties": { + "days": { + "description": "Move every listed invoice by this many days (1 = one day later, -1 = one day earlier). Leave it out when each invoice carries its own `date`.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "documents": { + "description": "The invoices to move, each once — at most 200.", + "items": { + "properties": { + "date": { + "description": "This invoice's correct date, YYYY-MM-DD (MYT). Overrides `days` for this one.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "id": { + "description": "The invoice's real id from search_documents.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "maxItems": 200, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "documents" + ], + "type": "object" +} - changed
Input schema / properties / proposed / descriptionPrevious value: -"ONLY what you want changed. An unknown or not-patchable key is refused by name; a proposal the invoice already agrees with is refused rather than filed."New value: +"REQUIRED unless you send dateShift. ONLY what you want changed. An unknown or not-patchable key is refused by name; a proposal the invoice already agrees with is refused rather than filed." - changed
Input schema / properties / saleId / descriptionPrevious value: -"REQUIRED — the posted invoice's real id, from search_documents. An invoice number is refused: correcting the wrong document silently is worse than asking which one they mean."New value: +"REQUIRED (unless you send dateShift) — the posted invoice's real id, from search_documents. An invoice number is refused: correcting the wrong document silently is worse than asking which one they mean." - removed
Input schema / requiredRemoved value: -[ - "saleId", - "proposed" -]
1 tool update
- Changed
stage_document1 field changed- added
Input schema / properties / marketplaceOrderKeyAdded value: +{ + "description": "SALES INVOICES ONLY, and only when the invoice WAS a marketplace order: the channel plus the order's own number, exactly as Taokeh's channel import keys it — 'SHOPEE-<order number>' (e.g. SHOPEE-2608246V81G729), 'TIKTOK-<order ID>' (digits), or 'SHOPIFY-<order ID>' (the numeric id in the Shopify admin address /admin/orders/<id>, NOT the '#1001' order name). It is posted as the sale's order reference and channel, so the invoice counts as a Shopee/TikTok/Shopify sale everywhere and a later live import of the same order is recognised instead of booked twice. A malformed key, a key already on an invoice in the books, or a key another staged document already carries is REFUSED and nothing is staged. A Shopee order number starts with the order's date (YYMMDD); when that is not docDate the document is FLAGGED for the owner (not refused) — check which date is right. Keep `reference` for the invoice's own number.", + "type": "string" +}
14 tool updates
- Changed
create_bank_clearing_draft2 fields changed- removed
Input schema / properties / items / items / properties / statementLineId / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Input schema / properties / items / items / properties / statementLineId / typeAdded value: +[ + "string", + "null" +]
- Changed
create_bill_draft2 fields changed- removed
Input schema / properties / lines / items / properties / discount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "string" - } -] - added
Input schema / properties / lines / items / properties / discount / typeAdded value: +[ + "number", + "string" +]
- Changed
create_invoice_draft2 fields changed- removed
Input schema / properties / lines / items / properties / discount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "string" - } -] - added
Input schema / properties / lines / items / properties / discount / typeAdded value: +[ + "number", + "string" +]
- Changed
create_purchase_order_draft2 fields changed- removed
Input schema / properties / lines / items / properties / discount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "string" - } -] - added
Input schema / properties / lines / items / properties / discount / typeAdded value: +[ + "number", + "string" +]
- Changed
create_quote_draft2 fields changed- removed
Input schema / properties / lines / items / properties / discount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "string" - } -] - added
Input schema / properties / lines / items / properties / discount / typeAdded value: +[ + "number", + "string" +]
- Changed
revise_draft10 fields changed- removed
Input schema / properties / patch / properties / bankAccountId / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Input schema / properties / patch / properties / bankAccountId / typeAdded value: +[ + "string", + "null" +] - removed
Input schema / properties / patch / properties / clientTotal / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Input schema / properties / patch / properties / clientTotal / typeAdded value: +[ + "number", + "null" +] - removed
Input schema / properties / patch / properties / inputTax / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Input schema / properties / patch / properties / inputTax / typeAdded value: +[ + "number", + "null" +] - removed
Input schema / properties / patch / properties / printedTotal / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Input schema / properties / patch / properties / printedTotal / typeAdded value: +[ + "number", + "null" +] - removed
Input schema / properties / patch / properties / validUntil / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Input schema / properties / patch / properties / validUntil / typeAdded value: +[ + "string", + "null" +]
- Changed
stage_document2 fields changed- removed
Input schema / properties / lines / items / properties / discount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "string" - } -] - added
Input schema / properties / lines / items / properties / discount / typeAdded value: +[ + "number", + "string" +]
- Changed
update_bill_draft2 fields changed- removed
Input schema / properties / proposed / properties / lines / items / properties / discount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "string" - } -] - added
Input schema / properties / proposed / properties / lines / items / properties / discount / typeAdded value: +[ + "number", + "string" +]
- Changed
update_delivery_order_draft2 fields changed- removed
Input schema / properties / proposed / properties / lines / items / properties / discount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "string" - } -] - added
Input schema / properties / proposed / properties / lines / items / properties / discount / typeAdded value: +[ + "number", + "string" +]
- Changed
update_employee_draft2 fields changed- removed
Input schema / properties / proposed / properties / epfEmployeeRate / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Input schema / properties / proposed / properties / epfEmployeeRate / typeAdded value: +[ + "number", + "null" +]
- Changed
update_invoice_draft2 fields changed- removed
Input schema / properties / proposed / properties / lines / items / properties / discount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "string" - } -] - added
Input schema / properties / proposed / properties / lines / items / properties / discount / typeAdded value: +[ + "number", + "string" +]
- Changed
update_product_draft2 fields changed- removed
Input schema / properties / proposed / properties / categoryId / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Input schema / properties / proposed / properties / categoryId / typeAdded value: +[ + "string", + "null" +]
- Changed
update_purchase_order_draft2 fields changed- removed
Input schema / properties / proposed / properties / lines / items / properties / discount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "string" - } -] - added
Input schema / properties / proposed / properties / lines / items / properties / discount / typeAdded value: +[ + "number", + "string" +]
- Changed
update_quote_draft2 fields changed- removed
Input schema / properties / proposed / properties / lines / items / properties / discount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "string" - } -] - added
Input schema / properties / proposed / properties / lines / items / properties / discount / typeAdded value: +[ + "number", + "string" +]
1 tool update
- Changed
update_invoice_draft1 field changed- changed
Input schema / properties / proposed / properties / refNo / descriptionPrevious value: -"The customer's own reference or PO number as it should read. Pass null to clear it."New value: +"The customer's own reference or PO number as it should read. Pass null to clear it. On a MIGRATED invoice (entered as a historical re-entry) with no sales channel, a refNo of SHOPEE-<order number>, TIKTOK-<order ID> or SHOPIFY-<order ID> attaches it to that marketplace order: on approval it joins that channel (channel_summary counts it), and from then on it can only be corrected by a credit note. That also happens when such a migrated invoice already carries a valid one and you file any correction. Setting a malformed one there (a Shopee order number is 14 characters starting with the order date YYMMDD; TikTok and Shopify order IDs are digits), or one already on another invoice, is refused at filing. On any other invoice refNo is stored as written."
3 tool updates
- Added
customer_statement - Added
general_ledger - Added
trial_balance
Related MCP Connectors
IQ Books is an AI-native UK ledger that does the bookkeeping for you. Bank transactions arrive already coded, receipts and bills file themselves, invoices are allocated, the bank is reconciled, and VAT and MTD for Income Tax updates go to HMRC from it. Through this connector Claude can read the reports, raise invoices, record payments and code bank statements, with every change shown as a plan you approve first. UK businesses; the ledger is free for one business, and there is a free demo company to try.
AI accounting: ask about your live AccountsOS books in plain English. Read-only tools for transactions, cash balances, invoices, bills, VAT, filing deadlines and reports across 32 countries. OAuth sign-in; free 14-day trial at accounts-os.com.
Query your books, company data, business health, and ship invoices from your AI assistant.
Connect Claude or Cursor to books, invoices, bills, payroll, and sealed closes.
Related MCP Servers
- AlicenseBqualityBmaintenanceEnables AI assistants to query and manage self-hosted accounting data—invoices, balances, and books—through natural language, with read-only tools by default and optional scoped write operations.3025 PyPI1AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to query a connected accounting platform for organisations, invoices, contacts, chart of accounts, and profit-and-loss reports, supporting bookkeeping, AR/AP, and financial reporting workflows.299 npmMIT
- AlicenseNot gradedqualityDmaintenanceGives AI models native-level control over TallyPrime ERP, covering 169+ tools across all functional modules including masters, vouchers, reports, GST, payroll, and more.30 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to read and write Cynco accounting data, including querying books, creating invoices, reconciling transactions, and generating financial reports.9 npm1MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.