Skip to main content
Glama
ComplyEaze

ComplyEaze Bridge: TallyPrime MCP server for Claude Desktop

Official

vouchers

Read-only

Reads TallyPrime voucher lists or grouped summaries within a date range, filtering by ledger, voucher type, number, reference, narration or amount.

Instructions

Return literal-window voucher evidence with curated metadata and redaction. Reads the full source window before selectors and output pagination; limit does not reduce Tally work. A complete window is held in memory for its later pages: a page after the first (offset above 0) is served from that read, with one marks read instead of the whole window, while the company's two marks (vouchers and masters) are unchanged, and its result carries snapshot (id, master_alter_id, voucher_alter_id, read_at, reused). Pass the first page's snapshot_id on later pages to have the call refused with listing_snapshot_changed (cause book_changed_since_first_page or snapshot_not_held) instead of continuing from a different read. Without it, a later page whose window is no longer held or whose book moved reads the window again, and rows can shift between pages. A partial window is not held. A later page is served only for the same question: the same dates (a date is the same however it is written), the same voucher-type selector, search and grouping, and the ledger argument exactly as typed on the first page (a differently spelled ledger is a different question and reads the whole window again). Without snapshot_id, a page whose held window found the book moved reads afresh and carries earlier_snapshot (offsets_do_not_continue true): start again from offset 0. snapshot_id is read on later pages only. Only a page whose snapshot.reused is true continues the earlier pages: a later page with reused false or no snapshot is a fresh read whose offsets may not continue them. A change that moves neither mark is not seen; not established: whether a company feature or configuration change that alters export content moves a mark, whether a restored copy of the company with the same marks is told apart, and whether a remote writer's save shows in the marks at once. So a held page can be up to ten minutes old after its read finished; a first page is always read fresh. Use narrow dates; dense windows are unqualified and can fail source limits. state is complete only when the window's rows were checked voucher for voucher against a count ComplyEaze Bridge made of the window first, or the window was empty, a read widened by a day on each side was also empty, and the company has no vouchers (reason company_has_no_vouchers); otherwise it is partial, with reason nonempty_window_unqualified when rows were not counted, empty_uncorroborated when an empty window's widened read is also empty but the company has vouchers, and empty_window_wider_read_nonempty when the widened read found rows on the neighbouring days (the window's own read found nothing and those rows cannot count it, so an empty window beside a busy day cannot reach complete today). ComplyEaze Bridge makes that count for every book that has held a voucher, however few, so nonempty_window_unqualified is expected only when the company's mark reads zero while the window holds rows. Selectors apply after the window is labelled, so a zero from a complete window is a checked zero. A voucher created, altered or deleted between the count and the read refuses with voucher_window_part_not_admitted, cause part_census_mismatch; call again. Each item carries cancelled and optional (always booleans; a source that omits or cannot assert either fails the whole read rather than guess). post_dated behaves differently: Tally asserts it on every voucher when the fetch list asks for it, and the key is left out only when a source does not report it (one old capture did, taken before the tag was asked for), so it is boolean only when Tally asserted Yes/No and is absent from the item when Tally did not report it. Absent is not evidence of false — it means ‘Tally did not say’, not ‘Tally said no’, and a caller must branch on key presence, not on falsiness, before treating a voucher as not post-dated. Neither this tool's listing nor voucher_presence filters out post-dated (or optional/cancelled) vouchers; the caller decides what a non-posting or unobserved status means for its own computation. (A summarise_by call is the exception: it sets aside cancelled, optional and entry-less vouchers and counts them, and sums post-dated ones with a count.) reference, is_invoice and party_gstin follow the same absent-means-not-observed convention as post_dated: each key is present only when Tally reported a non-empty value for it, and its absence must not be read as false or as an empty string. is_invoice is a boolean exactly like post_dated; reference and party_gstin are non-empty strings when present. A captured book with no GSTIN recorded against a party's ledger has shown party_gstin absent on every voucher for that party, which is not evidence Tally cannot report one. Filter by voucher type with at most one of: voucher_class (a reserved class such as Purchase, matched however the book has renamed its types, and including their child types, by Tally's own class functions), voucher_type_guid (exactly one type; a GUID that is not this company's is refused as voucher_type_guid_foreign), or voucher_type (one display name, matched ignoring ASCII case as Tally does). Voucher-type names are editable in Tally, so a display name that is a class name, or the reserved name of any type in scope, is refused as voucher_type_ambiguous whenever the types of that name are not exactly the types of that class or reserving that name; the types involved are listed in candidates (bounded to a quarter of the response budget, with candidates_total and candidates_truncated). Use voucher_class or voucher_type_guid instead. That check sees only the vouchers in scope: the window read, after any ledger filter. A type with no voucher in scope is not seen, and when nothing is in scope nothing is ambiguous. When a voucher_type name selects no voucher, one more read lists the book's voucher types: a name no type carries is refused as unknown_voucher_type, with the name as requested and every type in candidates, nearest name first (bounded as above); a name some type carries keeps its zero. A filtered result carries voucher_types: the types included and every type in_scope (name, GUID, own reserved name, class and row count), and each item carries voucher_type_guid, voucher_type_reserved_name and voucher_class (null outside the measured classes). A row whose type Tally cannot resolve, or whose class answers contradict each other or its reserved name, refuses the whole read. A voucher whose amount Tally stored as a foreign-currency composite (-$ 100.00 @ I₹ 86/$ = -I₹ 8600.00) is withheld, not read: it still passes every date, ledger and type check, and is then listed in withheld_vouchers (GUID, date, type, number and cause foreign_currency_amount_unparsed, up to 100) with an exact withheld_total, the same on every page. items and total then exclude it, state is partial with reason vouchers_withheld, and coverage says so; the voucher_types row counts still include it, and the listing is also bounded to a quarter of the response budget. Any other amount Tally did not return as a plain decimal still refuses the whole read, as does a composite whose foreign and base amounts differ in sign while the foreign amount is not zero. A ledger name resolves only when spelled as in the book or differing from it only in ASCII case and spaces; ledger_match names the ledger read, how (matched: exact, or case_or_spacing, which the answer should mention by naming the ledger read) and any similar_ledgers that differ from it only in case or whitespace. Search: voucher_number and reference (each matched whole, ignoring ASCII case and surrounding spaces), narration_contains (a phrase of at least 3 characters, matched ignoring letter case, accent composition and runs of spaces) and amount (a plain unsigned decimal, matched against the absolute value of any ledger entry of a voucher, so it finds an invoice total and a tax line alike) keep only the vouchers that satisfy every criterion given. The search runs last, on the labelled window, so a zero from a complete window is a checked zero; each item then carries matched (which criteria held and, for an amount, the positions in amounts that equalled it). A blank, over-long or invalid criterion is refused (search_criterion_empty, search_criterion_too_long, search_narration_too_short, search_amount_invalid), and a narration phrase is refused as search_narration_redacted where narrations are withheld from the assistant. suspense_tagged (true, only with ledger) keeps the vouchers of that ledger whose narration ends in a suspense tag ComplyEaze Bridge writes and says which in matched.suspense_tag; it classifies by the narration text, so the same text typed by hand matches and a narration edited after the tag is not listed: also read the ledger without it. It is refused as search_suspense_tagged_needs_ledger without ledger and as search_narration_redacted where narrations are withheld. A voucher withheld for a foreign-currency amount is never dropped by an amount search, since its amounts cannot be compared: it stays in withheld_vouchers and the result stays partial. A held window is reused only for the same search, and only for the same listing or summary: a listing and a summary of one question hold separate windows. voucher_types counts are taken before the search, so with a search they do not add up to total. Summaries: summarise_by (ledger, month, voucher_type, group or primary_group) returns buckets instead of items, over the same window and the same selectors and search; offset and limit then page the buckets. Each bucket has group (a ledger name, a YYYY-MM month, a voucher type name or a group name), vouchers (an exact count, each voucher once), debit (zero or negative, as ledger_movement reports it), credit, net (debit plus credit) and voucher_refs (up to 5 vouchers by date, type, number and GUID; voucher_refs_complete says whether that is all of them; a vouchers listing without summarise_by, narrowed with the existing selectors, lists the rest). Debit and credit follow each entry's sign (a zero amount stays on the side Tally flagged it). A ledger bucket adds the entries on that ledger of the vouchers the window, selectors and search selected: with a ledger, type or search given, a counter-ledger's bucket holds only its entries on those vouchers, not that ledger's whole movement. A month or type bucket adds all the entries of its vouchers (debits and credits then equal in size), or only the selected ledger's entries when ledger is given. entries_counted says which: all_entries or selected_ledger. A group or primary_group bucket adds the entries of the ledgers under that group (every entry of the selected vouchers, also when ledger is given). A group bucket holds only the ledgers directly under that group, as the ledger master shows it (a ledger directly under the root is under Primary, with reserved_name null), so a predefined group whose ledgers sit in sub-groups has no bucket or a small one: its whole figure, descendants included, is in subtree_totals (each group on any bucket's chain with covers, depth, vouchers, debit, credit and net over everything under it, never to be added to a bucket of the same group, whose own covers says it holds only the ledgers directly under it, largest movement first, at most 60, with subtree_totals_total exact and subtree_totals_complete; they overlap, so they do not add up to totals). A group bucket carries reserved_name (empty for a group the book's user made), chain (every group from it up to the one under the root, nearest first, each with name and reserved_name) and primary_group. A primary_group bucket holds every ledger under the group directly under the root and is keyed by its reserved name, so a renamed predefined group stays one bucket and shows its current name in group (a group the book's user made there is keyed by its name). Each has members (the ledgers in it with their own debit and credit, largest movement first, at most 10), members_total (exact) and members_complete; a group bucket cannot be narrowed with a selector, so trace its vouchers through a member ledger with ledger. Group names are shown as the book has them (a group may be named after a party); member ledger names are masked when parties are masked. The groups are the book's masters read now, not the grouping in force on each voucher's date: each ledger's group chain is read before the window and again after it and must be the same, or the call refuses (group_snapshot_drifted, or ledger_snapshot_drifted if the set of ledgers changed). A ledger that the ledger list does not hold, or whose group chain cannot be walked, refuses the whole summary (summary_group_unresolved, with cause no_parent, group_absent, group_name_repeated, reserved_name_missing, cycle, exhausted, top_group_not_under_root or ledger_not_in_catalogue; ledger names the ledger that could not be placed, the group is not named, and no_parent also covers a parent group Tally returned that ComplyEaze Bridge will not carry). By construction a group summary adds 30 requests to the window read, with or without ledger (a paired, identity-bracketed read is 6 requests: the company's marks to size the ledger list first, then the ledger list and the group list before the window and again after it); in one live run each call took 64 requests (34 for the window and 30 for the group modes, as counted from the code); a book of thousands of ledgers was not measured: a book whose master-alteration mark is above 11,983 is refused first (summary_group_book_too_large; the limit is provisional and computed, not measured: 16 MiB, a size chosen here and not verified, over the compliance ledger read's estimate of 1,400 bytes a ledger, which that read measured at 1,104 (a book of 1,989 ledgers) and 1,221 (a real book of about 9,500) on its own list without balances, and this list read 1,175 bytes a ledger in one live run (51,698 bytes for a book of 44 ledgers; a small book, so the figure is not a basis to raise the limit); the mark is an upper bound on ledgers, so a smaller book may be refused, and the limit is about half of what the transport's own rule admits for the same list), a real book's group buckets may need several pages (use offset), and on a large book ledger totals are cheaper with trial_balance. Group summaries were checked once against a live TallyPrime 7.1 on one synthetic book of 67 vouchers: each bucket equalled the ledger buckets added up by the trial balance's own parent column and the call took 64 requests; a large book, a group renamed or moved while a window is read and a held later page were not run live. Each bucket has position, its place in the whole ordering (labels of ledgers can read alike when parties are masked, and a masked label cannot be passed back as ledger); to list a month bucket's rest, narrow from and to to that month. totals is the debit and credit the buckets add up to, vouchers_summarised the vouchers behind them, and excluded_from_buckets the cancelled, optional and entry-less vouchers left out (counted, not hidden). Post-dated vouchers are summed, not set aside: post_dated_included counts those Tally flagged Yes and post_dated_flag_absent those with no flag at all (the read asks for the flag and Tally asserts it on every voucher, so that is expected to be 0; only when it is not does a zero in the first prove nothing); a voucher type that does not post (a memorandum, a reversing journal, a sales or purchase order, a delivery or receipt note), if the book uses it and Tally exports it with ledger entries, is not told apart and is summed too: basis says so in the result. Checked once against a live TallyPrime 7.1 on a synthetic book of 67 vouchers (a year, 44 ledgers, a debug build of master at 4c30f3f9f): every ledger, month and voucher-type bucket equalled the sums over the listed vouchers, the 30 ledger buckets equalled trial_balance for the same year, each search returned what the same criterion selects from the listing, and a later page came from the held window in 10 small requests. Not covered: a memorandum, a reversing journal, a voucher withheld for a foreign-currency amount, mask_parties, withheld narrations, an item invoice with stock lines, paging of a summary past one page, ledger name resolution, another TallyPrime edition or release, Windows, a large book. Cost, from that one run: a plain read took 34 requests, about 7 s and about 6 MB from Tally; each ledger, month or voucher_type summary or search call reads the window again at the same cost, and ledger adds 12 requests there, four of them reads of the whole ledger list (a group summary adds 30 with or without ledger, below); for a month or more on a large book read ledger totals with trial_balance (it has no month or voucher-type grouping and no search; the same year's trial_balance took 34 requests and 2.5 s on that book; one day of vouchers took minutes on the largest book measured, a voucher mark of about a million). Bucket debit, credit, net and totals are plain decimals with trailing zeros dropped (1000, -87900.5); an item's amounts keep the two places Tally sent (1000.00). Withheld vouchers are not in any bucket and the result is partial. A voucher whose entries do not balance refuses the whole summary (voucher_entries_unbalanced). Buckets are ordered by the larger movement first (ledger, type, group, primary_group), ties in movement keeping the order in which the bucket was first counted in the window, or by month. A held window serves a later page of buckets as it serves a later page of vouchers. Each ledger entry's bill_allocations carries reference, bill_type and amount; where Tally sends them it also carries bill_date (YYYYMMDD, the original bill's date for an Agst Ref) and credit_period (a value and a unit of days, weeks or months, or unit unrecognised with its text when the text cannot be read); an allocation Tally sent neither for has neither. To ask whether vouchers you mean to post are already in the book, use voucher_presence instead. Each call appends metadata-only receipt lines (tool, company, counts, request and response fingerprints; no book content) to ComplyEaze Bridge's local log on this computer; it writes nothing to Tally.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
toYesThe last day of the date range, included (YYYYMMDD or YYYY-MM-DD).
fromYesThe first day of the date range, included (YYYYMMDD or YYYY-MM-DD).
limitNoThe most rows to return on this page. The default and the maximum are this server's row limit (500 unless it is configured otherwise).
amountNoOptional: an unsigned decimal such as 1180.50; only vouchers with a ledger entry of exactly this amount (debit or credit). Each item says which entries matched.
ledgerNoOptional: only vouchers with an entry on this ledger, spelled as in the book or differing only in ASCII case and spaces. A ledger whose own name differs in case or symbols from the spelling its vouchers carry is reached by either; `ledger_match` shows its own name and, when different, `ledger_row_spelling`, the spelling its vouchers carry.
offsetNoHow many rows to skip before this page: 0, the default, is the first page.
referenceNoOptional: only vouchers whose reference is exactly this, ignoring ASCII case and surrounding spaces. A voucher with no reference never matches.
snapshot_idNoOptional, on a later page only: the first page's snapshot_id, so the call is refused (listing_snapshot_changed) rather than continuing from a different read.
company_guidYesThe company's company_guid, as list_companies returns it.
summarise_byNoOptional: ledger, month, voucher_type, group or primary_group. The result then holds buckets of debit, credit and voucher counts instead of the vouchers, over the same window, selectors and search; offset and limit page the buckets. group and primary_group sum each ledger under its group as the book has it now (see the description).
voucher_typeNoOptional, at most one of the three: one voucher type's display name, matched ignoring ASCII case. A name that could mean more than one class or type is refused (voucher_type_ambiguous).
voucher_classNoOptional, at most one of voucher_class, voucher_type_guid and voucher_type: a reserved class, matched however the book has renamed its types, child types included.
voucher_numberNoOptional: only vouchers with exactly this voucher number, ignoring ASCII case and surrounding spaces. Numbers repeat across voucher types, so more than one voucher can match.
suspense_taggedNoOptional, true only, and only together with `ledger` (the suspense ledger): keep the vouchers on that ledger whose narration ends in a suspense tag ComplyEaze Bridge writes ("Bridge: purpose not confirmed; reclassify", or "UNIDENTIFIED - reallocate from" followed by the name of one of the voucher's own ledgers), allowing one trailing [BRIDGE:...] marker after it. Each item says which tag in `matched.suspense_tag`. This classifies by the narration text: it does not prove ComplyEaze Bridge wrote the voucher (the same text typed by hand matches), and a voucher on the ledger whose narration was edited after the tag is NOT listed, so also read the ledger without this filter and treat every voucher left over as still in suspense. Refused without `ledger` and where narrations are withheld from the assistant.
voucher_type_guidNoOptional, at most one of the three: exactly one voucher type, by its GUID. Another company's GUID is refused (voucher_type_guid_foreign).
narration_containsNoOptional: only vouchers whose narration contains this phrase (at least 3 characters), ignoring letter case, accent composition and runs of spaces. Refused where narrations are withheld from the assistant.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed17 schema fields changedv0.4.3
    • addedInput schema / properties / amount
      Added value: +{
      +  "description": "Optional: an unsigned decimal such as 1180.50; only vouchers with a ledger entry of exactly this amount (debit or credit). Each item says which entries matched.",
      +  "maxLength": 256,
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / company_guid / description
      Added value: +"The company's company_guid, as list_companies returns it."
    • addedInput schema / properties / from / description
      Added value: +"The first day of the date range, included (YYYYMMDD or YYYY-MM-DD)."
    • addedInput schema / properties / ledger / description
      Added value: +"Optional: only vouchers with an entry on this ledger, spelled as in the book or differing only in ASCII case and spaces. A ledger whose own name differs in case or symbols from the spelling its vouchers carry is reached by either; `ledger_match` shows its own name and, when different, `ledger_row_spelling`, the spelling its vouchers carry."
    • removedInput schema / properties / limit / default
      Removed value: -500
    • addedInput schema / properties / limit / description
      Added value: +"The most rows to return on this page. The default and the maximum are this server's row limit (500 unless it is configured otherwise)."
    • addedInput schema / properties / narration_contains
      Added value: +{
      +  "description": "Optional: only vouchers whose narration contains this phrase (at least 3 characters), ignoring letter case, accent composition and runs of spaces. Refused where narrations are withheld from the assistant.",
      +  "maxLength": 256,
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / offset / description
      Added value: +"How many rows to skip before this page: 0, the default, is the first page."
    • addedInput schema / properties / reference
      Added value: +{
      +  "description": "Optional: only vouchers whose reference is exactly this, ignoring ASCII case and surrounding spaces. A voucher with no reference never matches.",
      +  "maxLength": 256,
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / snapshot_id / description
      Added value: +"Optional, on a later page only: the first page's snapshot_id, so the call is refused (listing_snapshot_changed) rather than continuing from a different read."
    • addedInput schema / properties / summarise_by
      Added value: +{
      +  "description": "Optional: ledger, month, voucher_type, group or primary_group. The result then holds buckets of debit, credit and voucher counts instead of the vouchers, over the same window, selectors and search; offset and limit page the buckets. group and primary_group sum each ledger under its group as the book has it now (see the description).",
      +  "enum": [
      +    "ledger",
      +    "month",
      +    "voucher_type",
      +    "group",
      +    "primary_group"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / suspense_tagged
      Added value: +{
      +  "description": "Optional, true only, and only together with `ledger` (the suspense ledger): keep the vouchers on that ledger whose narration ends in a suspense tag ComplyEaze Bridge writes (\"Bridge: purpose not confirmed; reclassify\", or \"UNIDENTIFIED - reallocate from\" followed by the name of one of the voucher's own ledgers), allowing one trailing [BRIDGE:...] marker after it. Each item says which tag in `matched.suspense_tag`. This classifies by the narration text: it does not prove ComplyEaze Bridge wrote the voucher (the same text typed by hand matches), and a voucher on the ledger whose narration was edited after the tag is NOT listed, so also read the ledger without this filter and treat every voucher left over as still in suspense. Refused without `ledger` and where narrations are withheld from the assistant.",
      +  "enum": [
      +    true
      +  ],
      +  "type": "boolean"
      +}
    • addedInput schema / properties / to / description
      Added value: +"The last day of the date range, included (YYYYMMDD or YYYY-MM-DD)."
    • addedInput schema / properties / voucher_class / description
      Added value: +"Optional, at most one of voucher_class, voucher_type_guid and voucher_type: a reserved class, matched however the book has renamed its types, child types included."
    • addedInput schema / properties / voucher_number
      Added value: +{
      +  "description": "Optional: only vouchers with exactly this voucher number, ignoring ASCII case and surrounding spaces. Numbers repeat across voucher types, so more than one voucher can match.",
      +  "maxLength": 256,
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / voucher_type / description
      Added value: +"Optional, at most one of the three: one voucher type's display name, matched ignoring ASCII case. A name that could mean more than one class or type is refused (voucher_type_ambiguous)."
    • addedInput schema / properties / voucher_type_guid / description
      Added value: +"Optional, at most one of the three: exactly one voucher type, by its GUID. Another company's GUID is refused (voucher_type_guid_foreign)."
  2. Changed1 schema field changedv0.4.2
    • addedInput schema / properties / snapshot_id
      Added value: +{
      +  "maxLength": 128,
      +  "minLength": 1,
      +  "type": "string"
      +}
  3. First observedv0.4.1

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare read-only/non-destructive, but the description discloses far more: snapshot holding and reuse, pages up to ten minutes stale, refusals (listing_snapshot_changed, snapshot_not_held, part_census_mismatch, group_snapshot_drifted), foreign-currency withholding, and an explicit statement that the call writes nothing to Tally and only appends metadata-only receipt lines locally. This is well beyond 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.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The text is an unformatted wall of several thousand words with no headings or bullets, mixing essential contract (snapshot rules, refusals, selector semantics) with internal engineering notes (a debug build hash, byte-per-ledger estimates, the provenance of a 16 MiB limit, a single live-run request count). The purpose is front-loaded, but most passages do not earn their place in a tool description an agent must parse on every call.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 16-parameter tool with pagination, snapshot semantics, five summary modes, withholding and numerous refusal codes, the description is more than complete: it covers the window model, selector interactions, summary bucket accounting, subtree_totals, search behavior and error causes. No output schema exists, so coverage of return structure is necessary and is provided.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents all 16 parameters (baseline 3). The description adds genuine semantics on top: ledger name resolution rules, voucher_type ambiguity and the candidate listing, the absent-means-not-observed convention for post_dated/reference/party_gstin/is_invoice, and the meaning of snapshot_id on later pages. It is richer than the schema alone, though much of the same text is duplicated in the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening states a specific verb and resource ("Return literal-window voucher evidence with curated metadata and redaction") and the body makes clear it lists vouchers and can instead return summaries via summarise_by. It explicitly distinguishes itself from voucher_presence and trial_balance. The phrase "literal-window voucher evidence" is dense jargon that a first-time reader must decode, which keeps this just below a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It routes the agent explicitly: use voucher_presence to check whether vouchers are already posted, use trial_balance for ledger totals on large books, use voucher_class or voucher_type_guid instead of voucher_type when ambiguity is possible, and read the ledger without suspense_tagged as a cross-check. It also states when-not (dense windows are unqualified and can fail source limits; narrow dates advised). Nothing about tool selection 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.