refactor(mcp): the eight long tool descriptions move into the handbook (P5)
Agent reason: a tool description is read every time the tool list is, so eight descriptions of 800 to 2,000 characters cost every session for text only a few calls need. books.export, books.export_cpa_package, books.import_bank_csv, books.reconcile, books.set_entry_party, books.set_w9, books.show and books.vendor_1099 now say what the tool is for, what it requires and where the rest is, in at most 700 characters; the rest is in the handbook, verbatim: the CPA package's contents (cpa-handoff), the share rules of a multi-party tag and the W-9 procedure (parties-and-tins), the worksheet's construction (1099), the CSV import and the report list with the IIF limits (files-in-out), the reconciliation's answer (reconciliation) and the targets books.show opens (viewer). tool_descriptions_fit_700_characters now pins zero and its allowance list is deleted: no description may exceed the cap, and none is excused. The period test keeps its sentence about omitted dates in books.export's description. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
1 parent
453abb62ea
commit
70a3f795f4
11 files changed
+49
-24
No files matched your search
@@ -716,23 +716,10 @@ fn tool_text_lint() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tool descriptions are at most 700 characters (plan I-12). The
|
||||
/// ones over it today are listed, each to be shortened in P5 when its
|
||||
/// text moves into the handbook; no new one may join them, and one
|
||||
/// that shrinks under the cap must leave the list.
|
||||
/// Tool descriptions are at most 700 characters (plan I-12); the long
|
||||
/// text lives in the handbook (P5 moved the last eight there).
|
||||
#[test]
|
||||
fn tool_descriptions_fit_700_characters() {
|
||||
// Shorten in P5.
|
||||
const OVER: &[&str] = &[
|
||||
"books.export",
|
||||
"books.export_cpa_package",
|
||||
"books.import_bank_csv",
|
||||
"books.reconcile",
|
||||
"books.set_entry_party",
|
||||
"books.set_w9",
|
||||
"books.show",
|
||||
"books.vendor_1099",
|
||||
];
|
||||
let mut over: Vec<String> = BooksServer::tool_router()
|
||||
.list_all()
|
||||
.iter()
|
||||
@@ -740,7 +727,7 @@ fn tool_descriptions_fit_700_characters() {
|
||||
.map(|t| t.name.to_string())
|
||||
.collect();
|
||||
over.sort();
|
||||
assert_eq!(over, OVER, "tool descriptions over 700 characters");
|
||||
assert!(over.is_empty(), "tool descriptions over 700 characters: {over:?}");
|
||||
}
|
||||
|
||||
/// The tools docs/mcp-interface.md documents: a line (or a bullet)
|
||||
|
||||
@@ -706,7 +706,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.import_bank_csv",
|
||||
title = "Import a bank CSV as drafts",
|
||||
description = "Import a QuickBooks-style bank/credit-card CSV: 3 columns Date,Description,Amount (payments negative), or 4 columns Date,Description, money out, money in (Intuit heads them Credit,Debit). Slash dates must settle month-first or day-first from the file itself or from date_order (\"month_first\" for mm/dd, \"day_first\" for dd/mm); ask the human when the tool says the dates read either way. Creates one DRAFT entry per transaction, all or none: bank account on one side, categorized offset on the other (the longest matching rule pattern, else default_offset_code). Give the CSV as upload_id: upload it with books.begin_upload purpose import and `wanderbooks upload` (a file never passes through you). Review with books.digest, then post. File the export too (books.file_document category import_source) and pass source_attachment_id: it is linked to the bank account, so the import traces to its source (never to the drafts: a bank export is not a receipt).",
|
||||
description = "Import a bank or card CSV (Date, Description, Amount; or Date, Description, money out, money in) as one DRAFT entry per transaction, all or none: the bank account on one side, a categorized offset on the other. Give the CSV as upload_id (books.begin_upload purpose import; a file never passes through you). Ambiguous slash dates need date_order. Review with books.digest, then post. Also file the export (books.file_document category import_source) and pass source_attachment_id. Details in books.handbook section files-in-out.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = false,
|
||||
|
||||
@@ -341,7 +341,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.vendor_1099",
|
||||
title = "1099 worksheet",
|
||||
description = "The 1099 worksheet for a calendar year. Per vendor: what counts toward its 1099 (paid out of the cash accounts, or through a financing account with the method recorded, while not decided not_reportable; an entry with several parties counts only the vendor's share), what was paid by card or payment network (excluded: Form 1099-K's), what is undetermined (a member paid, method not recorded: books.set_entry_instrument), its 1099 decision, and its status (reportable at the threshold: $600 through 2025, $2,000 in 2026, indexed after; under_threshold; not_reportable; undetermined). Lists those payments, W-9s missing, each payee's TIN last four. include_tins adds full TINs from the vault; csv hands it over. cash_accounts and financing_accounts default to the settings' (as books.export_cpa_package's do); through stops the year at a day: given a package's accounts and its as_of, the CSV is the package's 1099 worksheet. A year whose threshold isn't known is refused: pass threshold from the IRS. For the CPA to confirm.",
|
||||
description = "The 1099 worksheet for a calendar year: per vendor, what counts toward its 1099, what was paid by card or network (excluded), what is undetermined, its decision and its status (reportable, under_threshold, not_reportable, undetermined), the payments, W-9s missing and each payee's TIN last four. include_tins adds full TINs from the vault; csv hands it over; through stops the year at a day. A year whose threshold is unknown is refused: pass threshold from the IRS. For the CPA to confirm. How each figure is built is in books.handbook section 1099.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = false,
|
||||
@@ -775,7 +775,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.set_w9",
|
||||
title = "Record W-9 facts",
|
||||
description = "Record what a contractor's or landlord's W-9 says, or clear it (clear: true alone): the date received (received_on), line 3 entity type, line 4 exempt payee and FATCA codes, the TIN's last four digits, and where the form is kept. Give the full TIN as tin with tin_type (ssn, ein, itin) and it goes only to the encrypted TIN vault (its key kept apart from it, created on first use): the books keep its last four (tin_last4 is then optional, and refused if it disagrees) and a hash of the vault record, in the W-9 facts and the party's TIN facts alike (party.tin_set, as books.set_tin), and kept_at defaults to \"wanderbooks vault\". The CPA package carries the full TIN to the CPA. Give the signed form (a PDF, PNG, or JPEG) as w9_upload_id (books.begin_upload purpose w9, then `wanderbooks upload`: a file never passes through you), and it goes to the vault too, never to the receipts and documents; read it back with books.get_w9. Never put a TIN in any other field, note, description, or file: the engine refuses any nine-digit number, and no tool output echoes one. The party needs a 1099 role (contractor or landlord: books.set_party_roles). Record it before paying them. A party with a vendor record also gets its W-9 marked on file there (books.vendor_1099 missing_w9). Clearing W-9 facts whose TIN is in the vault needs books.purge_tin first. Journaled as party.w9_set. Returns the party (the TIN's last four only).",
|
||||
description = "Record what a contractor's or landlord's W-9 says, or clear it (clear: true alone): date received, entity type, exempt codes, the TIN's last four, where the form is kept. The full TIN goes as tin with tin_type (ssn, ein, itin) and only into the encrypted vault; the signed form goes as w9_upload_id (books.begin_upload purpose w9). Never put a TIN in any other field, note or file. The party needs a 1099 role first (books.set_party_roles). Clearing facts whose TIN is in the vault needs books.purge_tin first. Details in books.handbook section parties-and-tins.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = true,
|
||||
@@ -1497,7 +1497,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.set_entry_party",
|
||||
title = "Tag an entry's parties",
|
||||
description = "Tag an entry (draft or posted) with the party it was with (id or name), or pass party null (or omit it) to clear the tag. A party with a vendor record also tags the entry's vendor, so a payment to a contractor or landlord counts in books.vendor_1099; a party without one, or clearing, clears the vendor tag. For an entry with several counterparties pass parties instead: [{party, amount?|percent?}], each share an exact amount (cents), a percent (summing to 100; leftover cents to the first party), or neither (derived: a sole party gets all; members named together get what their capital or distributions lines say, else their [[company.members]] share; once the entry is posted, a derived split among several parties is recorded as the exact amounts it resolves to, so a later change to [[company.members]] never re-splits posted history). Name both members on a joint member transaction (a reimbursement on 2100, a joint contribution) and let the shares derive; give amounts when a split is uneven. Refused: a party twice, mixed kinds of share, two parties with vendor records, and, once the entry has balanced lines, shares that don't add up to what it moved. Metadata only: amounts never change. An entry and its reversal carry the same parties: tagging either tags both, so each party's part nets to zero. Journaled as entry.party_set (one party) or entry.parties_set (parties). Returns the entry (as books.get_entry), each party's share resolved, with its direction and what of it was money or property in kind.",
|
||||
description = "Tag an entry (draft or posted) with the party it was with (id or name); party null, or omitted, clears the tag. For several counterparties pass parties: [{party, amount? | percent?}]. A party with a vendor record also tags the entry's vendor, so a payment to a contractor or landlord counts in books.vendor_1099. Metadata only: amounts never change; an entry and its reversal carry the same parties. Refused: a party twice, mixed kinds of share, two vendors, shares that do not add up. Returns the entry (as books.get_entry) with each share resolved. The rules for shares are in books.handbook section parties-and-tins.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = false,
|
||||
|
||||
@@ -123,7 +123,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.reconcile",
|
||||
title = "Reconcile an account",
|
||||
description = "Reconcile a bank, cash, or card account against a statement. Give statement_attachment (a statement recorded with books.record_statement: its account, period end, and closing balance), or account, statement_date, and statement_balance for a quick check against any balance. Returns the book balance, cleared balance, statement balance, the difference (cleared minus statement; zero means reconciled), the cleared and uncleared lines through the statement date, and the problems that would stop it being recorded. Without confirmed=true it is a preview and records nothing. With confirmed=true and a recorded statement it records the reconciliation (journaled as reconciliation.completed, linked to the statement), refused unless the difference is exactly zero and it comes after the account's last reconciliation. Mark lines cleared with books.mark_cleared first.",
|
||||
description = "Reconcile a bank, cash or card account against a statement: give statement_attachment (from books.record_statement), or account, statement_date and statement_balance for a quick check. Returns the book, cleared and statement balances, the difference (cleared minus statement; zero means reconciled), the cleared and uncleared lines and the problems that would stop it. Without confirmed it previews. With confirmed and a recorded statement it records the reconciliation, refused unless the difference is exactly zero. Mark lines first with books.mark_cleared.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = true,
|
||||
|
||||
@@ -405,7 +405,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.show",
|
||||
title = "Show in the viewer",
|
||||
description = "Show something in the human's viewer (the read-only desktop app): an entry (with its lines, receipt, and history), an account's register (optionally for start/end dates), a report (optionally for start/end dates, split into month/quarter/year columns, compared with the prior period or prior year), a stored receipt or document, a fixed asset (by tag or id), a party (by id or name: who they are and everything the company did with them), a project (by id or name), a reconciliation (by id: its account with its statements and reconciliations), a search of the transactions, or a page. Give exactly one target; work out dates yourself (\"last quarter\" means its first and last day). Use it whenever the human asks to see something, and after posting so they can look at what you did. Changes nothing in the books.",
|
||||
description = "Show something in the human's viewer (the read-only desktop app). Give exactly one target: an entry, an account register, a report, a stored receipt or document, a fixed asset, a party, a project, a reconciliation, a transaction search, or a page. Work out dates yourself (\"last quarter\" is its first and last day). Use it when the human asks to see something, and after posting so they can look at what you did. Changes nothing in the books. The targets and their options are in books.handbook section viewer.",
|
||||
annotations(read_only_hint = true, open_world_hint = false)
|
||||
)]
|
||||
async fn show(&self, params: Parameters<ShowParams>) -> Result<Json<ShowOut>, String> {
|
||||
@@ -929,7 +929,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.export",
|
||||
title = "Export a report",
|
||||
description = "Export a report. Reports: profit_and_loss, balance_sheet, cash_flow (direct method, with its reconciliation and noncash schedules; needs cash_accounts), trial_balance (as_of), members_equity (multi-member LLC; members from the settings), tax_summary, general_ledger (by account: running balances, splits), general_journal (each entry posted in the period; alias journal), fixed_asset_register (as_of: every asset with tag, serial, cost, depreciation, book value, reconciled to the books), ar_aging and ap_aging (as_of: one row per open invoice or bill, with a tie-out line to its control account). The file is handed over (run its download to save it; never its bytes). Formats: pdf and xlsx for the statements, tax_summary, fixed_asset_register and the agings; xlsx, csv, json for every report; iif for general_journal or journal (QuickBooks Desktop: plain ASCII text, CR LF; every posted entry from the first through as_of (start and end are refused), period closes left out, DOCNUM the entry id; names, numbers and memos fit QuickBooks' limits; warnings says what is written differently from the books, memos grouped, to tell the human). Dates: start/end for period reports (omitted: the fiscal year to date), as_of for balance_sheet, trial_balance, fixed_asset_register, ar_aging, ap_aging and iif (omitted: today).",
|
||||
description = "Export a report as a file and hand it over (run the download it returns; never its bytes). Reports: profit_and_loss, balance_sheet, cash_flow, trial_balance, members_equity, tax_summary, general_ledger, general_journal, fixed_asset_register, ar_aging, ap_aging. Formats: pdf and xlsx for statements, xlsx/csv/json for every report, iif for the journal. Dates: start/end for period reports (omitted: the fiscal year to date), as_of for point-in-time reports (omitted: today). Each report's contents and the IIF limits are in books.handbook section files-in-out.",
|
||||
annotations(read_only_hint = true, open_world_hint = false)
|
||||
)]
|
||||
async fn export(&self, params: Parameters<ExportParams>) -> Result<Json<ExportOut>, String> {
|
||||
@@ -1133,7 +1133,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.export_cpa_package",
|
||||
title = "CPA handoff package",
|
||||
description = "Build the CPA handoff package as a zip: the profit and loss, balance sheet, cash flow (direct method, with its reconciliation and noncash schedules), statement of changes in members' equity (multi-member LLCs), tax summary, and trial balance, each as PDF and XLSX; the fixed asset register as PDF and XLSX when an asset was acquired by as_of; the general ledger (by account) and general journal (entry by entry) as XLSX; the journal through as_of as IIF (imports into QuickBooks Desktop; plain ASCII within its limits: warnings says what is written differently from the books, and so does the README); the 1099 worksheet as CSV (payments from January 1 through as_of); with members, the K-1 worksheet (k1-members.csv: per member their party, share, capital account, TIN, and beginning capital, contributions in cash and in property, share of income, distributions, and ending capital for the fiscal year to date) and its tax-basis capital beside the book figures (k1-tax-basis-capital.csv: Schedule K-1 Item L on the tax basis, property at its §723 basis, with the reconciliation; what only the CPA can supply, like tax depreciation, left open and named); every receipt behind the year's entries, filed on them or linked to them (receipts/, with an index.csv tying each file to its entry) and the company's documents (documents/), unless include_receipts is false; and a README with the journal's verified hash. By default (include_tins) the K-1 and 1099 worksheets carry every member's and payee's full TIN from the TIN vault; include_tins: false, last fours only. The zip is handed over: run its download command to save it in a folder. Never file it into the books. A locked vault or a missing TIN doesn't stop the package: tin_gaps lists each one (collect them with books.set_tin or books.set_w9 and export again). cash_accounts (e.g. [\"1000\"]) is required: the cash flow statement reconciles to them. as_of defaults to today; period reports cover the fiscal year to date.",
|
||||
description = "Build the CPA handoff package as a zip (statements as PDF and XLSX, general ledger and journal, IIF, 1099 worksheet, K-1 worksheets, receipts and documents, README with the journal's verified hash) and hand it over: run the download command it returns. cash_accounts is required; as_of defaults to today. Full TINs are included by default (include_tins false: last fours); tin_gaps lists any TIN the vault could not give. Never file the zip into the books. What the package holds, and the order of work, is in books.handbook section cpa-handoff.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = false,
|
||||
|
||||
Reference in new issue
Block a user