Skip to main content
Glama

Server Details

Receipt ingestion, Gmail capture, expense analysis, and reviewed reports for AI-agent workflows.

If you are the author of this connector, you can claim ownership by verifying the domain or GitHub account it belongs to. Claimed connector authors can inspect health checks, view analytics, and manage their listing.
Status
Healthy
Uptime
100.0% over 54 days
Last Tested
Transport
Streamable HTTP · MCP 2025-11-25
URL
Repository
TotesMagotes/expensebot-mcp
GitHub Stars
0
Server Listing
ExpenseBot MCP

TDQS

A3.9/5.0

Scored across 60 tools

Disambiguation4/5

Most tools have distinct purposes and descriptions include explicit 'use only when' guidance, making the set largely navigable. Some overlaps remain, notably fix_compliance vs correct_expenses (same correction workflow), fetch vs get_report_details, and the cluster of summary/analytics tools (get_spending_summary, get_deep_analytics, get_pnl, get_per_tag_pnl, get_monthly_books_review), so occasional misselection is possible.

Naming Consistency5/5

All tool names use a consistent snake_case convention, predominantly verb_noun (add_income, create_report, list_reports) with a few bare verbs (fetch, search) and a consistent whatif_ prefix, without mixing styles.

Tool Count2/5

At 60 tools, the set is very large and exceeds a well-scoped surface, even for a broad expense-management domain. While many tools serve specialized features, the sheer count burdens selection and could likely be consolidated.

Completeness4/5

Coverage is strong across expenses, income, reports, client invoicing, accounting integrations, Gmail scans, mileage, and analytics. Notable gaps exist around delete/void operations for expenses and invoices, and update operations for income and reports, though many such actions are intentionally handled through app handoffs.

Available Tools

60 tools
add_cash_expenseAInspect

Add an expense without a receipt, or record money a client provided in advance for expenses. Writes directly to the user's expense spreadsheet after confirmation. For an ordinary expense, call list_categories first and provide a configured Expense Account. For a client advance, set isClientAdvance=true and clientName; ExpenseBot reuses or creates the canonical client group, applies Cash advance received, and stores the amount as a negative expense. Use this only for money supplied by a client before related spending, never for vendor refunds, reimbursements, earned fees, retainers, or generic deposits. Use the Manual expense link from get_spreadsheet_url instead when the user needs a reviewed form for multiple entries, credits/refunds, tax or tip breakdowns, per diem, or incomplete details.

ParametersJSON Schema
NameRequiredDescriptionDefault
taxNoTax amount
tipNoTip amount
cityNoCity/location
dateYesExpense date (YYYY-MM-DD)
notesNoNotes or description
totalYesPositive amount paid or received. ExpenseBot stores a client advance as negative.
categoryNoA configured Expense Account returned by list_categories. Do not invent a category.
currencyNoCurrency code (default: USD)
merchantNoMerchant/vendor name for an ordinary expense. Not needed for a client advance; ExpenseBot derives it from clientName.
subtotalNoSubtotal before tax
clientNameNoClient name for a client advance, in ordinary language. ExpenseBot resolves the canonical Client – Name group.
isClientAdvanceNoTrue only when a client supplied money in advance for expenses. Requires clientName and does not require category.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
expenseNo
messageYes
successYes
recordIdNo
rowNumberNo
finalAnswerNo
spreadsheetUrlNo
responseGuidanceNo
reviewExpensesUrlNo

TDQS

A4.4/5.0
Behavior4/5

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

Beyond the annotations (readOnlyHint=false, destructiveHint=false, openWorldHint=true), the description discloses that it writes directly to the spreadsheet after confirmation, reuses or creates the canonical client group, applies 'Cash advance received', and stores advance amounts as negative. These are non-obvious side effects an agent could not infer from the schema. It stops short of stating duplication/permission behavior, so not a full 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Purpose and both modes are front-loaded, and every sentence carries routing or behavioral information. The final exclusion sentence is long and list-heavy, but it earns its place by preventing common misrouting.

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 12-parameter write tool with an output schema (so return values need not be explained), the description covers both operating modes, prerequisites, side effects, and the alternative path. An agent has everything needed to call it correctly or route elsewhere.

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

Parameters3/5

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

Schema description coverage is 100%, so all twelve parameters are already documented in the input schema, including the negative-storage convention for total and the clientName/isClientAdvance relationship. The description mostly restates those semantics, adding only the list_categories prerequisite. Baseline 3 is appropriate when the schema carries the parameter burden.

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

Purpose5/5

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

The description names a specific verb and two distinct modes of the resource: 'Add an expense without a receipt' and 'record money a client provided in advance.' It clearly differentiates from siblings like submit_receipt, parse_expense, add_income, and update_expense by scoping itself to receiptless cash entries and client advances.

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 gives explicit preconditions per mode ('call list_categories first and provide a configured Expense Account'; 'set isClientAdvance=true and clientName'), states a strong negative boundary ('never for vendor refunds, reimbursements, earned fees, retainers, or generic deposits'), and names a concrete alternative ('Use the Manual expense link from get_spreadsheet_url instead when...'). This is textbook when/when-not/alternative guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

add_incomeAInspect

Log exactly one user-confirmed income payment manually (cash, check, Stripe payout, etc.). Never loop this tool over a statement, export, table, or multi-row list. For two or more payments, use add_income_from_csv for CSV/TSV/text or add_income_from_file for an image/PDF; those tools stage a duplicate-checked preview and require confirmation before writing. Writes to the Income tab of the user's expense spreadsheet. Useful for income that isn't auto-detected from Gmail or Plaid. Call list_income_categories first and use one of its fixed tax categories; an omitted category defaults to Service income and an unknown category is rejected.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagNoTag for client/project attribution
dateYesIncome date (YYYY-MM-DD). Required. Use the date the user gives; for "today" send today's calendar date in their timezone. If no date was given, ask.
feesNoProcessor/transfer fees deducted
notesNo
amountYesIncome amount (>0)
sourceYesWho paid you (client name, customer, etc.)
categoryNoA category returned by list_income_categories (optional; defaults to Service income)
currencyNoCurrency code (default: home currency)
referenceNoInvoice or transaction reference
descriptionNoWhat the income was for
taxCollectedNoSales tax/GST/HST collected
paymentMethodYesHow you got paid. Canonical rails are Cash, Check, Bank transfer, Wallet app, Credit/Debit card, Payment processor, or Other; common labels such as Stripe and Venmo are normalized.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
dedupedNo
messageYes
successYes
recordIdNo
rowNumberNo
matchedRowNo
tabCreatedNo
finalAnswerNo
destinationsNo
responseGuidanceNo

TDQS

A4.9/5.0
Behavior5/5

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

Annotations only declare the generic write profile (readOnlyHint=false, destructiveHint=false, openWorldHint=true). The description adds real behavioral context: the write target (Income tab of the expense spreadsheet), the single-payment constraint, that sibling tools stage a duplicate-checked preview requiring confirmation, and that an unknown category is rejected while an omitted one defaults to Service income.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

Front-loads the core constraint (exactly one payment) in the first sentence, then the anti-pattern, then alternatives, then destination and category rule. Every sentence carries a distinct instruction with no filler.

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?

An output schema exists so return values need no explanation. For a 12-param mutation tool with annotations, the description covers the cardinality rule, the sibling routing, the write destination, and the category prerequisite — everything an agent needs to call it correctly.

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 92%, so the baseline is 3. The description still adds value beyond the schema by mandating the list_income_categories lookup and stating the rejection/default behavior for the category parameter, plus the paymentMethod normalization note is reinforced in the schema.

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

Purpose5/5

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

States a specific verb and resource ('Log exactly one user-confirmed income payment manually') plus concrete scope ('one', 'manually') and enumerates example rails. It explicitly differentiates itself from add_income_from_csv and add_income_from_file, so an agent can pick 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.

Usage Guidelines5/5

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

Explicit when-to-use ('income that isn't auto-detected from Gmail or Plaid'), explicit when-NOT ('Never loop this tool over a statement, export, table, or multi-row list'), and names the exact alternatives for the two-or-more case along with the condition that selects them.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

add_income_from_csvAInspect

Use this to import the user's CSV/TSV/text income export (max 500 KB). Step 1 stages a duplicate-checked previewId without writing Income rows. Show it and obtain approval. Step 2 uses confirm:true and that previewId to write the approved rows/edits; duplicates require explicit keep approval. Unknown or expired previews never write (15-minute expiry). Return spreadsheetUrl and reviewIncomeUrl after success. Accepts user-provided attachments or inline text, not arbitrary web URLs.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagNoOptional client/project tag for staged or confirmed rows.
confirmNoStep 2: true writes the staged selection only after user approval.
csvTextNoStep 1 inline CSV/TSV/text fallback; ChatGPT should use csvContent.
bulkNoteNoStep 2 note prepended to confirmed rows (max 120 characters).
rowEditsNoStep 2 approved edits; at most 50 selected preview rows.
previewIdNoStep 2 ID returned by the unexpired preview.
csvContentNoStep 1 ChatGPT attachment; securely downloaded and decoded.
paymentMethodNoFallback payment rail only when absent from the parsed row.
keepBothIndexesNoStep 2 duplicate indexes the user explicitly approved keeping.
selectedIndexesNoStep 2 preview row indexes to write; omit for all staged rows.

Output Schema

ParametersJSON Schema
NameRequiredDescription
totalNo
failedNo
dedupedNo
entriesNo
messageYes
previewNo
successYes
writtenNo
warningsNo
confirmedNo
expiresAtNo
previewIdNo
duplicatesNo
entryCountNo
finalAnswerNo
unattemptedNo
rejectedRowsNo
duplicateCountNo
maxChatEntriesNo
refundRowCountNo
spreadsheetUrlNo
reviewImportUrlNo
reviewIncomeUrlNo
tooLargeForChatNo
expiresInMinutesNo
idempotentReplayNo
responseGuidanceNo
totalsByCurrencyNo
projectionTruncatedNo
confirmationContractNo

TDQS

A4.6/5.0
Behavior5/5

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

Goes well beyond the annotations by disclosing that Step 1 writes nothing, that duplicates require explicit keep approval, that unknown/expired previews never write (15-minute expiry), and which URLs are returned. These are the safety-critical behaviors an agent needs before invoking a write path.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loads the purpose and the size limit, then walks the two steps in order, and every sentence carries operational content. It is dense and the long sentences could be split, but nothing is wasted for a tool of this complexity.

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?

Given ten parameters, nested objects, and an output schema, the description covers the full call lifecycle: input sources, the staged-then-confirmed flow, duplicate handling, expiry, and the returned URLs. 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.

Parameters4/5

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 cross-parameter semantics the schema alone lacks: confirm:true must be paired with the previewId from Step 1, and rowEdits/keepBothIndexes operate on staged rows. This meaningfully clarifies how the parameters interact across the two calls.

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

Purpose5/5

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

States a specific verb and resource ('import the user's CSV/TSV/text income export') and bounds the input with a size limit (max 500 KB). The CSV-import scope clearly separates it from add_income and add_income_from_file, so an agent can distinguish 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.

Usage Guidelines4/5

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

It lays out a concrete two-step workflow (Step 1 stage preview, Step 2 confirm) and states an explicit exclusion: accepts user-provided attachments or inline text, not arbitrary web URLs. It never names a sibling alternative such as add_income_from_file, so the routing guidance is strong but not fully comparative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

add_income_from_fileAInspect

Import income from an attached screenshot, image (JPEG, PNG, WebP, HEIC/HEIF), or PDF (payment screenshots, wallet apps, payout or bank statements; max 10 MB). This is a two-step tool. STEP 1: call it with the file and WITHOUT confirm — ExpenseBot parses the file with the same importer as the app's Add Income screen, checks every row against the user's Income tab for duplicates, and returns a preview with a previewId, exact row count, totals by currency, per-row details, duplicate flags, and any rejected rows. NOTHING is saved in step 1; treat the attachment as consent to parse, not consent to write. Show the user the parsed rows and duplicates, then STEP 2: call again with confirm: true and the previewId to write exactly those rows. Only include user-approved changes in step 2 (selectedIndexes, keepBothIndexes, rowEdits, tag, bulkNote). Flagged duplicates are skipped unless the user explicitly asks to keep them (keepBothIndexes). The preview expires after 15 minutes; an expired or unknown previewId never writes. After a successful confirm, show the returned spreadsheetUrl and reviewIncomeUrl. For a visually complex review (mixed income/expense rows, many edits), send the user to the Add Income app link returned by get_spreadsheet_url instead.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagNoOptional client/project tag. In step 1 it pre-fills the staged rows; in step 2 it applies to the confirmed selection.
photoNoAttached image or PDF supplied by ChatGPT for step 1. The server downloads and parses the attachment securely.
confirmNoStep 2 only: true writes the staged rows. Omit it (with previewId absent) to parse and preview without writing.
bulkNoteNoStep 2 only: one note (max 120 chars) prepended to every confirmed row's Notes, same as the app's 'Add note to all entries'.
mimeTypeNoMIME type of the file (default: image/jpeg)
rowEditsNoStep 2 only: user-approved corrections, at most 50 rows. Fields: date (ISO YYYY-MM-DD), source, amount, currency, category, paymentMethod, description, notes, reference, fees, taxCollected, tag.
previewIdNoStep 2 only: the previewId returned by the step-1 call.
photoBase64NoLegacy fallback for MCP clients that send complete image/PDF bytes as base64. ChatGPT should use photo.
paymentMethodNoFallback payment rail for rows where the parser found none (Cash, Check, Bank transfer, Wallet app, Credit/Debit card, Payment processor, or Other).
keepBothIndexesNoStep 2 only: preview indexes of duplicate-flagged rows the user explicitly wants to keep anyway (the app's 'Keep Both').
selectedIndexesNoStep 2 only: preview row indexes to write. Omit to write all staged rows.

Output Schema

ParametersJSON Schema
NameRequiredDescription
totalNo
failedNo
dedupedNo
entriesNo
messageYes
previewNo
successYes
writtenNo
warningsNo
confirmedNo
expiresAtNo
previewIdNo
duplicatesNo
entryCountNo
finalAnswerNo
unattemptedNo
rejectedRowsNo
duplicateCountNo
maxChatEntriesNo
refundRowCountNo
spreadsheetUrlNo
reviewImportUrlNo
reviewIncomeUrlNo
tooLargeForChatNo
expiresInMinutesNo
idempotentReplayNo
responseGuidanceNo
totalsByCurrencyNo
projectionTruncatedNo
confirmationContractNo

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only cover readOnlyHint/destructiveHint/openWorldHint; the description adds substantial context beyond them: nothing is saved in step 1, the preview expires after 15 minutes, an expired or unknown previewId never writes, duplicates are checked against the Income tab, and file size (10 MB) and edit limits (50 rows, 120-char note) are stated. The write-on-confirm behavior is consistent with destructiveHint=false since it adds rows rather than deleting.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loaded with the purpose, then organized as an explicit STEP 1 / STEP 2 sequence, so the density is navigable. It is long, but nearly every sentence carries operational detail (expiry, duplicate handling, which params apply to which step); a small amount of repetition about 'step 2 only' could be trimmed.

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 an 11-parameter, nested-object, two-phase mutation tool with an output schema, the description covers the parse/confirm lifecycle, safety framing, expiry behavior, and follow-up URLs. Nothing an agent needs to call it correctly is missing, and return values are left to the output schema.

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 baseline is 3, but the description adds real meaning: which parameters belong to step 1 versus step 2, the consent framing for the attachment, the 50-row cap on rowEdits, and the semantics of keepBothIndexes and selectedIndexes. It goes beyond restating the schema descriptions.

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

Purpose5/5

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

States a specific verb and resource ('Import income from an attached screenshot, image... or PDF') and immediately scopes it with concrete source examples (payment screenshots, wallet apps, payout/bank statements). An agent can distinguish this from siblings like add_income and add_income_from_csv purely from the first sentence.

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?

Explicitly describes the two-step flow, when to call without confirm (parse/preview only) versus with confirm: true (write), how to treat duplicates (skipped unless keepBothIndexes), and it names a sibling alternative (get_spreadsheet_url) for complex visual reviews. The when-to-use and when-not-to-use conditions are fully spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

add_mileage_entryAInspect

Log a business mileage trip in ExpenseBot. Useful for realtors, consultants, contractors, and anyone who drives for work. Requires the user to have configured their mileage rate (cents/km or cents/mi) and unit (mi/km) in ExpenseBot Settings. The trip writes a row to their expense spreadsheet with the calculated dollar value. Use the Mileage and travel link from get_spreadsheet_url instead when the user needs Google Maps route calculation, mileage settings, repeated trips, calendar/rideshare import, per diem, or visual review.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagNoTag for client/project attribution (optional)
cityNoCity (optional)
dateYesTrip date (YYYY-MM-DD). Required. Use the date the user gives; for "today" send today's calendar date in their timezone. If no date was given, ask.
notesNoOptional user-supplied context stored with the mileage entry.
purposeYesBusiness purpose / description of the trip (e.g., 'Client meeting at 1234 Main St')
categoryNoOverride the user's default mileage category (optional)
distanceYesDistance traveled in the user's configured unit (miles or km)
roundTripNoIf true, doubles the distance (return trip)
destinationNoDestination address or location (optional)

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
messageYes
successYes
settingsNo
rowNumberNo
finalAnswerNo
spreadsheetUrlNo
responseGuidanceNo
reviewExpensesUrlNo

TDQS

A4.4/5.0
Behavior4/5

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

Annotations declare the write safety profile (readOnlyHint=false, destructiveHint=false, openWorldHint=true), so the bar is lower. The description still adds value beyond them: it discloses that a row is written to the user's expense spreadsheet with a calculated dollar value, and that mileage rate/unit settings must exist first. It does not cover duplicate handling or what happens if settings are missing.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Four sentences, front-loaded with the action, then prerequisite, then side effect, then alternative. Each sentence earns its place, though the audience list ('realtors, consultants, contractors') is soft filler that could be trimmed.

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?

With 9 params, an output schema present, and annotations covering the safety profile, the description supplies exactly what structured fields cannot: the prerequisite configuration, the write side effect, and clear routing away from the sibling alternative. An agent has everything needed to invoke it correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so every parameter (including roundTrip's doubling behavior and the date timezone guidance) is already documented in the schema. The description only reinforces the unit dependency for `distance`; baseline 3 is appropriate when the schema carries the semantics.

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

Purpose5/5

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

States a specific verb+resource ('Log a business mileage trip in ExpenseBot') and immediately contrasts it with the sibling route via get_spreadsheet_url. An agent can distinguish this write-tool from add_cash_expense, add_income, and get_trip_suggestions 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.

Usage Guidelines5/5

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

Gives an explicit prerequisite (user must have configured mileage rate and unit), a named alternative tool (`Mileage and travel link from get_spreadsheet_url`) and enumerates the exact conditions that select that alternative (Maps routing, repeated trips, calendar/rideshare import, per diem, visual review). 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.

check_complianceA
Read-only
Inspect

Use only when the user explicitly asks to check an existing report for compliance issues such as missing business purpose or policy violations. Creating a report, excluding Personal expenses, sharing a report, or billing a client is not a compliance request; never call this tool automatically as a preflight or follow-up for those workflows.

ParametersJSON Schema
NameRequiredDescriptionDefault
reportIdYesReport ID to check
analysisRequestedYesSet true only when the user explicitly asked for this report's compliance analysis. Do not ask for or copy their full message, and do not infer consent from report creation, sharing, or billing.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
messageNo
successYes

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is covered. The description adds meaningful behavioral context beyond that: the explicit anti-automation guardrail and the 'do not infer consent' rule, which constrain how the agent may trigger it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

Two sentences, the positive trigger front-loaded and the exclusions following. Every clause is load-bearing for correct invocation; there is no filler.

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?

An output schema exists so return values need no explanation, and annotations cover safety. The description covers the remaining gap — precise trigger conditions and prohibitions — so 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.

Parameters3/5

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

Schema description coverage is 100% and the analysisRequested description already explains the explicit-consent requirement, so the schema carries the parameter burden. The description reinforces but adds no syntax or format detail beyond it; baseline 3 applies.

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

Purpose5/5

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

States a specific verb and resource ('check an existing report for compliance issues') and names what those issues are (missing business purpose, policy violations). It is clearly distinguishable from siblings like create_report and fix_compliance.

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?

Explicit when-to-use ('only when the user explicitly asks') plus an enumerated when-not list (creating a report, excluding Personal expenses, sharing, billing) and a prohibition on automatic preflight/follow-up invocation. Nothing about invocation conditions 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.

check_featureA
Read-only
Inspect

Check whether ExpenseBot supports a specific feature ('does ExpenseBot support X', 'can it integrate with Y'). Searches the public knowledge base and returns a confidence-scored answer + related questions. Works with or without authentication.

ParametersJSON Schema
NameRequiredDescriptionDefault
featureYesFeature or integration to check (e.g., 'Xero', 'mileage tracking', 'Plaid')

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
messageNo
successYes

TDQS

A4.3/5.0
Behavior5/5

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

With annotations already declaring readOnly/openWorld/destructive hints, the description adds useful behavioral context: it queries a public knowledge base, returns a confidence-scored answer plus related questions, and does not require authentication. 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.

Conciseness5/5

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

Two concise sentences with examples front-loadedclause by clause. No filler, every sentence earns its place.

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 simple, read-only, single-parameter tool with rich annotationschedema and a described return shape, the description is fully adequate for an agent to select and invoke it correctly.

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

Parameters3/5

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

Schema covers the single parameter 100%, including a description and example. The description's example queries reinforce semantics but don't add meaningful new information beyond the schema, so baseline 3 is appropriate.

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 description clearly states the tool's purpose: checking whether ExpenseBot supports a specific feature, with examples of natural language queries. It distinguishes from check_compliance and check_tax_deductibility by focusing on feature support, though it doesn't explicitly distinguish itself from the similar 'search' tool.

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

Usage Guidelines4/5

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

Provides clear context with example phrasings ('does ExpenseBot support X', 'can it integrate with Y'). States that it searches the public knowledge base and works with or without authentication. Doesn't explicitly exclude alternatives, but the examples serve as strong usage signals.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

check_tax_deductibilityA
Read-only
Inspect

Use this when the user asks for general deduction guidance for an expense type, such as business meals or home-office expenses. Looks up reference rules using the account's tax-country/home settings and current year. A verified rule may return a percentage and reference; unverified rules return unknown/reviewRequired without a percentage. Read-only: it does not classify saved expenses, prepare a return, or determine the user's actual tax liability. Do not use for tax refunds received as income; use get_income_summary for recorded refunds. General guidance is not a tax professional's determination.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesBrief expense-type deduction question, e.g. 'are business meals deductible?'; no account numbers or unrelated personal context.
categoryNoOptional expense category to match the reference rule.
merchantNoOptional merchant name only when relevant to the expense type.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
messageNo
successYes

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, and the description is consistent ('Read-only: it does not classify saved expenses, prepare a return, or determine the user's actual tax liability'). Beyond that it discloses non-obvious behavior the annotations cannot: the lookup keys off the account's tax-country/home settings and current year, and verified rules return a percentage plus reference while unverified rules return unknown/reviewRequired with no percentage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loads the core 'when to use' before the behavioral and exclusion details, and each sentence carries distinct information (use case, lookup mechanism, return semantics, scope limits, exclusion). It is slightly longer than strictly necessary with some overlapping negative statements ('does not classify... prepare a return... determine liability'), but nothing is filler.

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?

Despite the tool's non-trivial logic, the description covers the trigger, the settings-dependent lookup, the two possible return shapes, the read-only scope, and the sibling alternative. An output schema exists, so return-value documentation is not required, yet the two outcome branches are still flagged, leaving nothing an agent needs missing.

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

Parameters3/5

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 query, category, and merchant, including the 'no account numbers or unrelated personal context' constraint. The description adds little parameter-level detail beyond re-emphasizing the expense-type framing, so the baseline 3 is appropriate.

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

Purpose5/5

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

States a specific verb+resource ('looks up reference rules' for an expense type's deductibility) and scopes it precisely ('general deduction guidance... such as business meals or home-office expenses'). It also explicitly differentiates from a sibling by naming get_income_summary for refunds, so an agent can separate it from the other tools 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.

Usage Guidelines5/5

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

Gives an explicit use case ('when the user asks for general deduction guidance for an expense type') and an explicit exclusion with the alternative named ('Do not use for tax refunds received as income; use get_income_summary for recorded refunds'). The routing decision is fully determined.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

correct_expensesA
Destructive
Inspect

Safely correct the category, business purpose, or explicit attendees on an exact bounded set of recorded expenses. Examples: 'categorize these as Travel', 'add Client kickoff dinner as the business purpose for these meals', or 'add Fred, Rob, and Lamar to last night's business meal'. First use search_expenses to identify the exact rows, then pass their full expenseId values. A grounded preview is automatic: first call with confirm omitted/false, show the exact count and proposed before-to-after changes, and ask once for approval. Only after explicit approval repeat the same operationId, selection, and change with confirm:true. A premature confirm:true is converted to preview. Attendee names must come explicitly from the user; never infer them. attendeeMode add preserves existing attendees, while replace substitutes only the attendee segment. Business purpose and attendees preserve the structured Notes field, including card, description, inbox, and other typed segments. Formula Notes and changes that exceed the Notes limit are skipped safely. The confirmed result reports applied/conflicted/failed counts and supports Undo. This tool does not omit duplicates, change amounts or dates, infer business context, or run broad Calendar matching. For unsupported or more than 100-row cleanup, send the user to https://www.expensebot.ai/review-expenses?source=mcp.

ParametersJSON Schema
NameRequiredDescriptionDefault
undoNoUndo a completed correction within its Undo window.
changeNoExactly one correction. The selected type determines which matching value field is required.
statusNoRead operation status using operationId.
confirmNoOmit/false for preview; true only after explicit approval.
selectionNoExact expenses returned by search_expenses and approved for this bounded correction.
operationIdYesStable idempotency key generated once for preview and reused unchanged.

Output Schema

ParametersJSON Schema
NameRequiredDescription
undoNo
changeNo
statusYes
messageNo
previewNo
successYes
manifestNo
finalAnswerNo
operationIdNo
confirmationNo
responseGuidanceNo
reviewExpensesUrlNo
requiresConfirmationNo

TDQS

A4.8/5.0
Behavior5/5

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

Annotations declare destructiveHint=true and openWorldHint=true, and the description adds far more: an automatic grounded preview with count and before-to-after diffs, premature confirm being downgraded to preview, idempotency via a stable operationId, Notes preservation and Formula Notes/limit skips, applied/conflicted/failed reporting, and Undo support. This is exactly the safety and side-effect detail that matters for a destructive mutation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loaded with purpose then workflow then constraints, and the examples are useful. It is long and dense for a single paragraph, with a few clauses (e.g., the Notes-limit/Formula skip detail) that could be tightened, but almost every sentence carries operational information.

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 destructive, nested-object mutation with an available output schema, the description covers selection provenance, concurrency expectations via 'expected' fields, preview/confirm gating, idempotency, and failure counts, so an agent has everything needed to call it correctly. Return-value detail is appropriately left to the output schema.

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 description coverage is 100%, so the baseline is 3, but the description still adds meaning: 'repeat the same operationId, selection, and change with confirm:true' explains the sequencing contract, and 'attendeeMode add preserves existing attendees, while replace substitutes only the attendee segment' clarifies replace semantics beyond the schema's terse 'Use add unless...'.

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

Purpose5/5

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

States a specific verb and resource bounded precisely ('Safely correct the category, business purpose, or explicit attendees on an exact bounded set of recorded expenses') and enumerates concrete examples. It also carves out what it does NOT do ('does not omit duplicates, change amounts or dates, infer business context, or run broad Calendar matching'), which separates it from the broader update_expense sibling even without naming it.

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?

Prescribes an explicit workflow: first call search_expenses to identify exact rows, then pass full expenseId values, preview automatically, and only confirm after explicit approval reusing the same operationId/selection/change. It also names the exclusion route ('unsupported or more than 100-row cleanup' → review URL), so when-to-use and when-not-to-use are both covered.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_client_invoiceA
Destructive
Inspect

Create the exact invoice previously returned by prepare_client_invoice. This is a confirmed write: it revalidates the report snapshot and client identity, creates a private editable Google Doc plus private PDF and DOCX copies, records the issued invoice, and supersedes an older active invoice for the same report. It does not send email or create an Income row. If tax is positive, confirmTax must be true.

ParametersJSON Schema
NameRequiredDescriptionDefault
confirmTaxNoTrue only after the user confirms the displayed tax label and rate.
operationIdNoOptional retry key. Reuse it after an uncertain response.
preparationIdYesShort-lived ID returned by prepare_client_invoice.

Output Schema

ParametersJSON Schema
NameRequiredDescription
totalNo
pdfUrlNo
statusNo
docxUrlNo
dueDateNo
successYes
currencyNo
invoiceIdNo
issueDateNo
recoveredNo
clientNameNo
grandTotalNo
documentUrlNo
invoiceNumberNo
advanceAppliedNo
sourceReportIdNo
artifactWarningNo

TDQS

A4.9/5.0
Behavior5/5

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

The description goes well beyond the destructiveHint annotation by detailing side effects: revalidating snapshot/client identity, creating Google Doc/PDF/DOCX copies, recording the invoice, and superseding an older active invoice. It also explicitly frames the operation as 'a confirmed write,' giving the agent accurate expectations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

Four tightly written sentences deliver the core purpose first, then side effects, exclusions, and the key conditional rule. Every sentence earns its place with no filler or repetition of schema fields.

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?

Given this is a destructive write operation with an output schema, the description covers all essential context: preconditions, created artifacts, supersession effect, excluded side effects, and the tax confirmation requirement. An agent has enough information to decide when to invoke it and what to expect.

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 parameters. The description adds useful semantic context on top, such as confirmTax being required only when tax is positive and preparationId being the short-lived ID from prepare_client_invoice.

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

Purpose5/5

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

States a specific verb and resource: 'Create the exact invoice previously returned by prepare_client_invoice.' This clearly distinguishes it from read-only siblings like get_client_invoice and list_client_invoices, and from related actions like mark_client_invoice_paid.

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?

The description explicitly ties invocation to a prior call to prepare_client_invoice, and clarifies what the tool does not do ('does not send email or create an Income row'). It also gives a concrete conditional rule: 'If tax is positive, confirmTax must be true.'

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_reportA
Destructive
Inspect

Create an expense report from a clear date, client, project, trip, category, or merchant request. Tags are ExpenseBot's grouping layer for clients, projects, project codes, and trips; call list_tags when the user's intended group is unclear. For requests such as 'all expenses in August except personal', set excludePersonal=true; this excludes both the Personal tag and Personal expense category, matching ExpenseBot's Report Wizard. Returns the report summary, exact report link, Bill Client link, applied filters, and a prefilled Report Wizard fallback for criteria that need visual review. Reports scoped to an existing client/project/trip group include matching expenses that are not already assigned to another ordinary report. Can optionally share with recipients. If no unreported matches remain, create no duplicate report and explain that the expenses are already in Reports. Terminal results also include role-appropriate accountingHandoffUrls from the server capability matrix and the existing reviewed file-export workflow. Use only the exact destination the user requested; the link opens the reviewed app flow and does not mean an export occurred. Complete the requested report directly; do not call check_compliance, get_report_details, or tax/deductibility tools before or after it unless the user explicitly asks for that separate analysis.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagsNoExisting ExpenseBot groups to include, such as a client, project, project code, property, or trip. Use list_tags first when uncertain.
titleNoCustom report title
dateRangeNoTime period filter. Use exactly one variant — pick the shape that matches the user's phrasing.
shareWithNoEmail addresses to share the report with
categoriesNoOptional configured expense categories to include in the report.
excludedTagsNoExisting ExpenseBot groups to exclude from the report.
recipientRoleNoAccess for every shareWith recipient. Use reviewer for one-report review, comments, approval, or change requests. Use accountant only when the user explicitly wants ongoing report management and accounting-software access.reviewer
excludePersonalNoExclude expenses whose Tag is Personal or whose Expense Category is Personal.
excludedCategoriesNoExpense categories to exclude from the report.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
successYes
reportIdNo
reportUrlNo
finalAnswerNo
nextActionsNo
billClientUrlNo
creationStatusNo
responseGuidanceNo
accountingSetupUrlNo
accountingHandoffUrlsNo

TDQS

A4.5/5.0
Behavior4/5

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

Annotations declare destructiveHint=true, openWorldHint=true, readOnlyHint=false, so the safety bar is partly covered. The description adds substantial non-schema behavior: dedup logic (no duplicate report if no unreported matches), the scope rule that grouped reports pull in unassigned matching expenses, sharing/role semantics, and the caveat that the link does not mean an export occurred. It stops short of explicitly framing the mutation/destructive impact on expense assignment.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

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

Purpose is front-loaded and readable, but the description is long and overlaps the schema (excludePersonal, recipientRole, shareWith) and the output schema (returned summary, links, applied filters, handoff URLs). Several clauses restate structured fields rather than adding new signal.

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 9-parameter mutation tool with annotations and an output schema, the description covers the critical gaps: dedup behavior, scope of included expenses, sharing roles, sibling-tool routing, and the meaning of the returned link. An agent has everything needed to invoke it correctly.

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 description coverage is already 100%, so the baseline is 3. The description still adds meaning: it explains that excludePersonal removes both the Personal tag and the Personal category (matching Report Wizard) and clarifies the reviewer vs accountant intent for recipientRole, going beyond the schema text.

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

Purpose5/5

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

States a specific verb (Create) and resource (expense report) plus the scoping inputs (date, client, project, trip, category, merchant). It is clearly distinguishable from siblings like list_reports and get_report_details, which are read/retrieval operations.

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?

Explicitly routes to list_tags when the group is unclear, gives a concrete trigger for excludePersonal ('all expenses in August except personal'), and explicitly forbids calling check_compliance, get_report_details, or tax tools before/after unless asked. This is when-to-use, when-not-to-use, and alternatives all in one.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

export_reportA
Read-only
Inspect

Get a short-lived direct PDF download for one exact authorized expense report, plus the exact highlighted in-app report link. Use the in-app report for CSV, XLSX, receipt ZIP (when available), comments, invoicing, and other visually reviewed actions. Use list_reports first to find the reportId.

ParametersJSON Schema
NameRequiredDescriptionDefault
reportIdYesReport ID from list_reports

Output Schema

ParametersJSON Schema
NameRequiredDescription
urlsYes
notesYes
reportYes
successYes
expiresAtYes
availableInReportYes

TDQS

A4.7/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds meaningful behavioral context: the PDF is 'short-lived', the report must be 'authorized', and the in-app link is 'exact highlighted'. It also clarifies that the tool only provides URLs, not the actual file contents, which is useful behavioral information 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.

Conciseness5/5

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

Three sentences with zero waste. The core purpose is front-loaded in the first sentence, the alternative use case is in the second, and the prerequisite is in the third. Every sentence earns its place.

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 single-parameter read-only tool with an output schema, the description is complete. It covers what the tool returns (PDF URL + in-app link), when to use it, when not to use it, and the prerequisite. The output schema handles return value details, so 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.

Parameters4/5

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

Schema coverage is 100% and the only parameter (reportId) is described as 'Report ID from list_reports'. The description reinforces this by saying 'Use list_reports first to find the reportId', which adds provenance context beyond the schema. With a single parameter and full schema coverage, the description adds just enough extra meaning.

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

Purpose5/5

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

The description states a specific verb ('Get'), a specific resource ('one exact authorized expense report'), and the exact output format ('short-lived direct PDF download' plus 'exact highlighted in-app report link'). It clearly distinguishes itself from sibling tools like get_report_details and share_report by focusing on export URLs rather than details or sharing workflows.

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?

The description explicitly says when to use this tool ('Use the in-app report for CSV, XLSX, receipt ZIP, comments, invoicing, and other visually reviewed actions') and provides a prerequisite ('Use list_reports first to find the reportId'). This gives clear routing guidance and tells the agent what to do before invoking.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fetchA
Read-only
Inspect

Fetch full details for a specific item returned by search. The id encodes the item type: • 'expense:' — the durable Column-Q Receipt ID returned by search, URL-encoded, e.g. 'expense:RR-123%3A%3Adrive%2Ffile%209'. Never a sheet row number: rows move when the sheet is prepended. • 'report:' — the alphanumeric Firestore document id from search or list_reports, e.g., 'report:FQqDglExofsyyQv7aYy4' • 'kb:' — the knowledge-base entry id from search Always use the id exactly as returned by search or list_reports — do not invent or modify the trailing portion. Returns the full text content + metadata for the AI to cite.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesItem ID from a previous `search` or `list_reports` call. Format: 'expense:<URL-encoded Column-Q expenseId>', 'report:<reportId>' (alphanumeric Firestore doc id, ~20 chars), or 'kb:<entryId>' (knowledge-base entry id).

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesEchoed back from the input
urlNo
textYesFull text content for the AI to cite
titleYes
metadataNo

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, destructiveHint=false, openWorldHint=false, so the safety profile is covered. The description adds useful context beyond that: the return payload ('full text content + metadata for the AI to cite') and a concrete failure warning (expense ids are never sheet row numbers because rows shift), which is genuinely non-obvious behavioral information.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loads the core action, then bullets the three id families with examples. The encoded example strings ('expense:RR-123%3A%3Adrive%2Ffile%209') are somewhat dense, but each sentence carries actionable content rather than filler.

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 single-parameter read tool with an output schema, this is complete: it covers every accepted id form, how to obtain each, the exact-usage constraint, and what comes back. 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.

Parameters4/5

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 exceeds that by expanding the id grammar with real examples, the URL-encoding requirement, and the row-number pitfall — meaning the agent understands more than the regex pattern alone conveys.

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?

States a specific verb+resource (fetch full details for a specific item) and constrains it to items previously returned by `search`. It distinguishes itself from the list-oriented `search` sibling, but does not differentiate from detail-oriented siblings like `get_expense_by_id` or `get_report_details`, so an agent may not know which detail tool to pick.

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

Usage Guidelines4/5

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

Gives clear context: use it on an id returned by `search` or `list_reports`, and 'always use the id exactly as returned... do not invent or modify.' It names the producing tools but offers no when-not-to-use guidance or routing against the other detail tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fix_complianceA
Destructive
Inspect

Safely correct compliance issues on an exact bounded set of recorded expenses. Use only after the user explicitly asks to fix issues found by check_compliance. Use search_expenses to obtain each full expenseId, then pass those exact identities; never select every report row or infer a business purpose, category, or attendee. This compatibility tool uses the same reviewed workflow as correct_expenses: the first call always prepares a grounded preview and makes no change, even if confirm:true arrives early. Show the exact proposed changes and ask once for approval. Only after explicit approval repeat the same operationId, selection, and change with confirm:true. The confirmed result reports applied/conflicted/failed counts and supports status and Undo. For tags/groups use group_expenses; for unsupported or more than 100-row cleanup open https://www.expensebot.ai/review-expenses?source=mcp.

ParametersJSON Schema
NameRequiredDescriptionDefault
undoNoUndo a completed correction within its Undo window.
changeNoExactly one correction. The selected type determines which matching value field is required.
statusNoRead operation status using operationId.
confirmNoOmit/false for preview; true only after explicit approval.
selectionNoExact expenses returned by search_expenses and approved for this bounded correction.
operationIdYesStable idempotency key generated once for preview and reused unchanged.

Output Schema

ParametersJSON Schema
NameRequiredDescription
undoNo
changeNo
statusYes
messageNo
previewNo
successYes
manifestNo
finalAnswerNo
operationIdNo
confirmationNo
responseGuidanceNo
reviewExpensesUrlNo
requiresConfirmationNo

TDQS

A4.8/5.0
Behavior5/5

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

Goes well beyond annotations: discloses the preview-first / no-change-on-first-call behavior even if confirm:true arrives early, the idempotency contract (repeat same operationId/selection/change), approval gating, result reporting (applied/conflicted/failed counts), and Undo support. Destructive and openWorld annotations are consistent with the described mutate+Undo profile.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loads scope and safety in the first clauses and stays information-dense, but the single paragraph runs long and mixes workflow, constraints, and fallback routing; a sentence break would improve scannability.

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?

Given a destructive, confirmation-gated, idempotent mutation with an output schema present, the description covers the full agent-relevant surface: preconditions, preview/confirm loop, idempotency, conflict handling, and escape hatches. Nothing critical is missing.

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 carries most parameter detail. Description adds meaningful semantics the schema doesn't: the identity-vs-row-selection distinction on expenseId, never infer business purpose/category/attendee, and the confirm/operationId pairing. Minor value-add over an already complete schema.

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

Purpose5/5

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

States a specific verb+resource (correct compliance issues on a bounded set of recorded expenses) and explicitly positions itself against siblings check_compliance, correct_expenses, group_expenses. 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.

Usage Guidelines5/5

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

Explicit when-to-use ('only after the user explicitly asks to fix issues found by check_compliance'), prerequisites ('use search_expenses to obtain each full expenseId'), and named alternatives ('for tags/groups use group_expenses', overflow link for >100 rows). Exclusions are stated clearly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_accounting_integration_statusA
Read-only
Inspect

Check whether an accounting destination is connected and read-only setup state for the owner. Returns the selected organization/company/business, stored connection state, rollout state, and the single setup link for connecting or reconnecting the destination. When an existing ExpenseBot report is available, pass reportId so the link opens that exact report's provider setup control instead of the general Reports setup. This is read-only and never posts accounting data. Agent-driven posting is currently available only for Zoho Books (zoho_books); QuickBooks Online (quickbooks), Xero (xero), Wave (wave), and FreeAgent (freeagent, beta) are accepted here for connection/setup status only and report writesAuthorized=false until their canonical planning path is shared.

ParametersJSON Schema
NameRequiredDescriptionDefault
providerYesAccounting destination provider. QuickBooks Online, Xero, Wave, FreeAgent (beta), or Zoho Books.
reportIdNoOptional existing ExpenseBot report ID. When supplied, setupUrl opens this exact report and highlights the selected accounting provider's setup control.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNoPer-provider connection and rollout state.
messageNo
successYes

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, so the description's claim of being read-only is redundant but consistent. However, the description adds context beyond annotations: it specifies that the tool returns selected organization/company/business, connection state, rollout state, and the setup link null, and notes that posting is not performed. It also mentions that some providers have writesAuthorized=false, which is useful behavioral context. 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.

Conciseness4/5

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

The description is moderately long but well-structured: it states the core purpose first, then details the return valueschers, then the reportId usage, then the read-only/posting qualifier. It is front-loaded with the most important informationholistic. The latter part about providers is slightly verbose but provides necessary navigation between providers, so it earns its place.

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

Completeness4/5

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

Given the tool has an output schema, the description does not need to list return fields in detail, but it does mention what is returned (organization, connection state, rollout state, setup link). It also clarifies the nuanced provider limitations (e.g., only Zoho Books has writesAuthorized=true). The tool is not overly complex, and with annotations and schema covering safety and parameters, the description provides sufficient context for correct invocation. A 4 is appropriate.

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

Parameters3/5

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 both parameters. The description adds a small amount of meaning beyond the schema: for reportId, it explains that the setup link opens the exact report's provider setup control, which is more specific than the schema's 'opens this exact report'. For provider, the description adds that certain providers are accepted for connection onlyholistic. But overall, since the schema already covers the parameters well, a baseline of 3 is appropriate.

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

Purpose5/5

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

The description clearly states the tool checks connection status and read-only setup state for an accounting destination, with a specific verb (check) and resource (accounting integration status). It distinguishes itself from siblings like request_accounting_integration (which initiates connection) and get_accounting_push_status (which is about posting), by explicitly mentioning connection/setup state and read-only behavior.

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?

The description provides explicit usage guidance: when an existing ExpenseBot report is available, pass reportId to open the exact report's setup control. It also explicitly lists which providers are supported for what purpose—agent-driven posting only for Zoho Books, others for connection only. This clearly tells the agent when to use this tool and how it differs from posting-related tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_accounting_push_statusA
Read-only
Inspect

Read the authoritative posting and reconciliation status for one ExpenseBot report at an accounting destination. Use after a timeout or uncertain response before considering any retry. Returns status, submission time, posting mode, posted receipt count, any public error, and whether reconciliation is required. Never re-post a completed report or a report marked needs_reconciliation; show that state to the owner instead. This tool is read-only and owner-account only.

ParametersJSON Schema
NameRequiredDescriptionDefault
providerYesAccounting destination provider used for the prior posting attempt.
reportIdYesExact ExpenseBot report ID from the prior proposal or posting attempt.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
messageNo
successYes

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false; description adds value by stating the tool is read-only and owner-account only, and by listing the exact fields returned (status, submission time, posting mode, posted receipt count, public error, reconciliation requirement). It also discloses a safety rule (never re-post) which goes beyond annotations. 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.

Conciseness5/5

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

Four sentences, each earning its place: purpose, usage trigger, return summary, and constraint. Front-loaded with the primary action and scope. No filler or redundancy.

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

Completeness4/5

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

For a two-parameter read-only tool with an output schema, the description covers the essential use case (checking status after timeout), the return contents, and the critical behavioral rule (don't re-post). It also notes the owner-account restriction. Nothing critical is missing for correct invocation.

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

Parameters3/5

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

Schema descriptions already fully document both parameters: provider enum with its purpose and reportId with exact identification requirement. The description does not add extra parameter-level meaning beyond what the schema provides, so a baseline of 3 is appropriate given 100% schema coverage.

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

Purpose5/5

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

States a specific verb (read) and resource (posting and reconciliation status for one report at a destination). Distinguishes from siblings like get_accounting_integration_status (integration status) and get_report_details (report details) by specifying 'authoritative' and 'one report'. Clear and unambiguous.

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?

Explicitly says 'Use after a timeout or uncertain response before considering any retry', giving a clear trigger condition. Also provides an exclusion: 'Never re-post a completed report or a report marked needs_reconciliation; show that state to the owner instead.' This is direct guidance on when to use and what not to do, even though it doesn't name specific alternative tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_client_advance_balancesA
Read-only
Inspect

Read the same per-client advance balances shown in ExpenseBot's Client advances section. Use for questions like 'how much of Acme's advance remains?', 'which client floats are still open?', or 'do I owe a client a refund?'. A negative ledger balance means money remains to refund; a positive balance means the client owes the user. Returns the existing app handoff for Refund leftover or Bill Client. Read-only: never records a refund, creates an invoice, or recomputes the ledger in model prose.

ParametersJSON Schema
NameRequiredDescriptionDefault
clientNameNoOptional client name or canonical Client tag. Omit to list every open balance backed by a recorded client advance.
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
messageNo
successYes
balancesNo
homeCurrencyNo

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, and the description reinforces this with a strong behavioral guarantee: 'never records a refund, creates an invoice, or recomputes the ledger in model prose.' It also clarifies the meaning of positive/negative balances, which is not in the schema. This goes beyond annotations by explaining what the tool does NOT do, which is valuable for an agent to avoid accidental side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is concise but information-dense. It starts with the core purpose, then gives examples, then explains the sign convention, then the return value, and ends with the read-only guarantee. No sentence is redundant; each adds actionable detail. The structure is front-loaded with the main purpose, making it easy for an agent to scan.

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

Completeness4/5

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

For a read-only tool with two optional parameters and an output schema, the description covers the essential usage: how to invoke it, what the balance signs mean, and what it returns. It doesn't discuss error handling (e.g., invalid client name) but the output schema likely covers that. It also doesn't mention rate limits or auth, but given the read-only nature and annotations, this is sufficient. Slightly incomplete because it doesn't note any caveats about data freshness or multi-currency, but overall it's quite complete.

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?

The schema covers both parameters with descriptions (coverage 100%), so the baseline is 3. The description adds extra value: for clientName, it clarifies that omitting it lists all open balances; for clientEmail, it restricts usage to accepted ExpenseBot clients. These clarifications are not in the schema and help the agent understand optionality and constraints. This exceeds the baseline.

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

Purpose5/5

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

The description clearly states the tool reads per-client advance balances, references the exact UI section ('Client advances section'), and provides concrete example questions. It also explains the sign convention (negative means refund, positive means client owes), which removes ambiguity. This distinguishes it from any sibling that might deal with invoices or refunds, though it doesn't name a specific alternative.

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

Usage Guidelines4/5

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

The description gives explicit use cases ('Use for questions like...') and explains the optional parameter behavior ('Omit to list every open balance'). It also mentions the return handoff for refund/bill actions. It doesn't explicitly state when not to use it or compare to alternatives, but the examples are clear enough for an agent to select it correctly. A slight gap is the lack of a note about using it vs. other financial tools like get_credits_refunds.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_client_invoiceA
Read-only
Inspect

Get the full financial and delivery summary for one issued client invoice, including subtotal, markup, tax, advance applied, balance due, dates, status, source report, and private Google Doc/PDF/DOCX links when available. Provide invoiceId or invoiceNumber — one is required; invoiceNumber is accepted only when it is unique. Read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
invoiceIdNoStable invoice ID from list_client_invoices. Required unless invoiceNumber is supplied.
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.
invoiceNumberNoHuman invoice number when invoiceId is unavailable; it must identify one unique invoice.

Output Schema

ParametersJSON Schema
NameRequiredDescription
invoiceNoThe invoice record. Absent only on the error branch (not-found), which never carries structuredContent.
messageNo
successYes

TDQS

A4.2/5.0
Behavior4/5

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

Description explicitly states read-only behavior, consistent with the annotation, and adds detail about return contents and the 'when available' caveat for private document links.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loaded purpose, enumerates the key returned fields, states the input requirement and read-only nature. Dense but efficient; no filler.

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?

The description explains what data is returned, how to identify the invoice, availability caveats, and read-only safety. Combined with schema and annotations, nothing essential is missing for a getter.

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

Parameters3/5

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

Schema descriptions already fully document the parameters; tool description reinforces the one-required rule and uniqueness condition but adds little beyond schema. No contradiction.

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

Purpose5/5

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

Uses a specific verb ('Get') and a clear resource ('full financial and delivery summary for one issued client invoice'), and enumerates the returned fields. This distinguishes it from list/prepare/update invoice siblings.

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

Usage Guidelines4/5

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

Provides clear guidance on the required identifier, the one-required rule, and the uniqueness constraint on invoiceNumber. Does not explicitly name alternatives, but the usage context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_credits_refundsA
Read-only
Inspect

List card refunds, cashback/rewards, and statement credits that ExpenseBot has already recorded — either as negative expenses or matched against the original charge. Examples: 'did my refund come through', 'show my statement credits', 'was that return recorded'. Returns the most recent items (default 25, newest first); narrow with dateRange. Read-only: it never scans cards, changes review decisions, or adds rows.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum results to return (default 25, max 100).
dateRangeNoOptional open-ended or closed window to narrow results. Supply startDate, endDate, or both; supplied bounds are inclusive YYYY-MM-DD dates.
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
creditsNo
messageNo
refundsNo
successYes

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, and the description reinforces this with 'Read-only: it never scans cards, changes review decisions, or adds rows.' This adds behavioral context beyond the annotations, clarifying what the tool does NOT do. It also discloses default behavior (most recent items, default 25, newest first) and the dateRange narrowing option.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is compact and front-loaded: it states the resource and scope in the first sentence, provides example queries, and then gives behavioral and parameter context. Every sentence earns its place, and the read-only note is a useful addition without bloat.

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

Completeness4/5

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

The description is complete for a read-only list tool: it covers what is returned, default behavior, narrowing options, and read-only guarantees. The output schema exists, so return values are documented elsewhere. It could mention the clientEmail parameter's purpose, but the schema already covers that, and the description's focus on usage and behavior is sufficient.

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

Parameters3/5

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 three parameters (limit, dateRange, clientEmail). The description adds context for dateRange ('narrow with dateRange') and limit ('default 25, newest first'), but does not add significant meaning beyond the schema. Baseline 3 is appropriate.

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

Purpose5/5

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

The description states a specific verb ('List') and resource ('card refunds, cashback/rewards, and statement credits that ExpenseBot has already recorded'), and clarifies the scope ('either as negative expenses or matched against the original charge'). It also provides example user queries, which makes the tool's purpose immediately recognizable. It distinguishes itself from sibling tools like get_recent_activity or search_expenses by focusing on recorded credits/refunds.

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

Usage Guidelines4/5

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

The description gives clear context for when to use the tool ('did my refund come through', 'show my statement credits', 'was that return recorded') and notes it returns recent items with a default limit and dateRange narrowing. It does not explicitly name alternative tools or state when not to use it, but the examples and scope make usage conditions clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_deep_analyticsA
Read-only
Inspect

Run deeper, multi-step analytics on the user's expenses. Use for explanatory questions like 'why did my spending increase' or 'compare Q1 vs Q2'. Takes 10-30 seconds (runs as a background job, polled automatically). Returns: { message, data: { ..., sampleMeta? } } where sampleMeta.isTruncated indicates whether the agent saw the full dataset.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesThe analytics question to answer
dateRangeNoTime period filter. Use exactly one variant — pick the shape that matches the user's phrasing.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNoInline branch: computed analytics, or null when no spreadsheet / no data.
typeNoAsync branch only: "analytics_pending" — poll pollEndpoint or call poll_analytics with jobId.
jobIdNoAsync branch only.
messageNoHuman-readable narrative (both branches).
successNoAsync branch only.
responseNoChat alias of message.
pollEndpointNoAsync branch only.
pollIntervalNoAsync branch only: milliseconds.
estimatedTimeNoAsync branch only: seconds.

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, and the description enriches this with important operational behavior: it runs as a background job, takes 10-30 seconds, is polled automatically, and may return truncated data via sampleMeta.isTruncated. This goes beyond the structured annotations and gives the agent accurate expectations for latency and result completeness.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is three sentences with no fluff: purpose, example use cases, latency/background behavior, and return shape are each packed into distinct, front-loaded statements. Every sentence earns its place and the key operational constraint (10-30 seconds) appears early.

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 complex analytics tool, the description covers purpose, invocation context, asynchronous behavior, and important return metadata (sampleMeta.isTruncated). The schema and output schema handle the parameter and return details, so nothing critical is missing for an agent to call this correctly.

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

Parameters3/5

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 both `query` and the `dateRange` variants in detail. The description reinforces the query semantics with examples but does not add substantial new parameter meaning beyond what the schema provides. This is the appropriate baseline.

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

Purpose5/5

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

The description states a specific verb and resource ('Run deeper, multi-step analytics on the user's expenses') and gives concrete example questions that distinguish it from simpler reporting tools like get_spending_summary or get_pnl. The 'deeper, multi-step' phrasing and explanatory-question use cases make the tool's purpose clear and differentiated.

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

Usage Guidelines4/5

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

The description explicitly says 'Use for explanatory questions' and provides examples, giving clear guidance on when to invoke this tool. It does not explicitly name alternatives or state when not to use it, so it stops short of a full when-to-use vs. when-not-to-use matrix, but the context is unambiguous enough for an agent to route correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_expense_by_idA
Read-only
Inspect

Read one existing expense from the authenticated user's ExpenseBot spreadsheet. Supply the exact expenseId returned by search_expenses (Receipt ID in Column Q), or the user's specified rowNumber. Never invent an identifier. Returns sheetName, rowNumber, headers, values, labeled fields and, when available, reviewExpenseUrl. This tool does not edit, submit, delete, share, or scan anything.

ParametersJSON Schema
NameRequiredDescriptionDefault
expenseIdNoExact full ExpenseBot Receipt ID from Column Q
rowNumberNo1-indexed sheet row (row 1 is headers, so ≥ 2)

Output Schema

ParametersJSON Schema
NameRequiredDescription
valuesNo
headersNo
labeledNo
successYes
rowNumberNoResolved sheet row for the expense.
sheetNameNo
reviewExpenseUrlNo

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnlyHint=true, destructiveHint=false, openWorldHint=true), so the bar is lower; the description still adds value by enumerating what it will not do and by listing the returned payload (sheetName, rowNumber, headers, values, labeled fields, reviewExpenseUrl). 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.

Conciseness4/5

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

Four short sentences, front-loaded with the core action and the identifier requirement; the closing negative list is slightly redundant with the readOnly annotations but earns its place as explicit scope bounding.

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?

With an output schema present, the description need not explain returns, yet it still covers the zero-required-parameter nuance (must supply one identifier), the identifier provenance, and the non-destructive scope. Nothing an agent needs 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.

Parameters4/5

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

Schema description coverage is 100%, so the baseline is 3, but the description adds provenance the schema lacks: the expenseId must come from search_expenses and corresponds to Receipt ID in Column Q, and rowNumber is an alternative the user may specify. It also implies that one of the two identifiers must be supplied even though the schema marks neither as required.

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

Purpose5/5

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

States a specific verb+resource ('Read one existing expense from the authenticated user's ExpenseBot spreadsheet') and immediately scopes it against siblings by declaring it 'does not edit, submit, delete, share, or scan anything', distinguishing it from update_expense, submit_receipt, and the scan_* tools.

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?

Gives explicit input-sourcing rules: use the exact expenseId returned by search_expenses (Column Q) or the user's specified rowNumber, and 'never invent an identifier'. It names the alternative tool (search_expenses) that produces the required identifier, so the when-to-use path is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_expense_splitsA
Read-only
Inspect

Show how an expense was split across categories, clients, properties, or business vs personal portions. Returns the single parent payment with its nested allocation lines — split lines are never counted as separate expenses, so totals stay correct. Examples: 'how is that expense split', 'what was the business portion of that bill', 'show the allocation for this receipt'. Read-only — splits are edited in ExpenseBot's Review workspace.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax parent expenses to return (default 25, max 100).
dateRangeNoOptional open-ended or closed window to search for split expenses. Supply startDate, endDate, or both; supplied bounds are inclusive YYYY-MM-DD dates.
onlySplitNoReturn only expenses with active splits (default true). Set false to inspect an unsplit Receipt ID.
receiptIdNoOptional Receipt ID for one expense.
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
splitsNo
messageNo
successYes

TDQS

A4.5/5.0
Behavior5/5

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

Beyond the readOnlyHint and destructiveHint annotations, the description discloses important behavior: split lines are never counted as separate expenses, so totals stay correct, and the tool returns a single parent payment with nested allocation lines. It also tells the agent that splits are edited elsewhere, which clarifies the tool's non-mutating role.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is compact and front-loaded, leading with the core action, then key behavioral nuance, then examples, then a read-only note. No sentence is wasted, and the structure makes the tool's purpose immediately scannable.

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?

The description covers what the tool does, how results are shaped, how totals behave, when to use it via examples, and the read-only context. With a full input schema, annotations, and an output schema present, nothing essential is missing for correct selection and invocation.

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

Parameters3/5

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

Schema description coverage is 100%, so all five parameters are already documented with types, defaults, and constraints. The description adds useful conceptual context but does not need to restate parameter semantics; the baseline of 3 applies because the schema carries the burden.

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

Purpose5/5

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

The description uses a specific verb ('Show') with a clear resource ('how an expense was split') and enumerates the dimensions of splits: categories, clients, properties, business/personal portions. It also clarifies that it returns parent payments with nested allocation lines, distinguishing it from ordinary expense lookup tools like get_expense_by_id or search_expenses.

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

Usage Guidelines4/5

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

The description provides natural-language examples ('how is that expense split', 'what was the business portion of that bill') that signal when the tool is appropriate. It also adds the read-only note and directs editing to ExpenseBot's Review workspace, giving clear context, though it does not explicitly name alternative tools or state when not to use this one.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_income_summaryA
Read-only
Inspect

Use this when the user asks about income already recorded in their ExpenseBot Income tab. Read totals; group by source, category, month, payment method or tag; filter by dateRange/incomeTagPrefix. Query supports period comparisons (e.g. 'income YTD by source', 'income this year vs last'). Returns message and data with total and optional breakdown, comparison, sampleMeta. No scanning, imports, mutations, tax returns or liability determination.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoNatural language income question (e.g., 'income YTD by source', 'rental income last month')
groupByNoHow to group the breakdown
dateRangeNoTime period filter. Use exactly one variant — pick the shape that matches the user's phrasing.
incomeTagPrefixNoOptional tag-prefix shortcut (e.g., 'Prop –' for rental income, 'Client –' for client billings, 'Wedding –' for events). When set, the tool filters to income rows tagged with this prefix.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNoInline branch: computed analytics, or null when no spreadsheet / no data.
typeNoAsync branch only: "analytics_pending" — poll pollEndpoint or call poll_analytics with jobId.
jobIdNoAsync branch only.
messageNoHuman-readable narrative (both branches).
successNoAsync branch only.
responseNoChat alias of message.
pollEndpointNoAsync branch only.
pollIntervalNoAsync branch only: milliseconds.
estimatedTimeNoAsync branch only: seconds.

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare readOnly/openWorld=false/destructive=false, and the description adds real context beyond them: it discloses the return shape (message plus data with total, optional breakdown, comparison, sampleMeta) and draws hard boundaries against scanning, importing, or mutating. It does not discuss pagination or result limits, so it falls short of 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.

Conciseness4/5

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

Front-loaded with the trigger condition, then scope, then return shape, then exclusions — a sensible ordering with no filler sentences. It is fairly dense, packing five groupBy values and multiple exclusions into two sentences, but nothing is wasted.

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?

An output schema exists, so return values need not be explained further; the description covers trigger, scope, grouping/filtering dimensions, comparison support, and explicit non-goals. Nothing an agent needs to invoke this correctly is missing.

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

Parameters3/5

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 query, groupBy, dateRange variants, and incomeTagPrefix in detail. The description largely restates the groupBy enum values and filter fields rather than adding new meaning, so the baseline 3 applies.

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

Purpose5/5

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

States a specific verb (read totals) and resource (income recorded in the ExpenseBot Income tab) and enumerates the grouping and filtering dimensions. It is clearly distinguishable from siblings like get_pnl, get_spending_summary, and get_mileage_summary 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.

Usage Guidelines5/5

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

Explicit trigger ('Use this when the user asks about income already recorded in their Income tab') plus an explicit exclusion list ('No scanning, imports, mutations, tax returns or liability determination') that routes those needs away from this tool. Both when-to-use and when-not are covered.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_last_receipt_resultA
Read-only
Inspect

Check the authoritative final outcome of a receipt image/PDF batch submitted with submit_receipt. After submit_receipt returns submissionId, call this tool with that exact ID using the polling interval and time allowance in the submit_receipt response. A small batch allows at least 5 minutes; larger batches allow longer. A pending result is normal and must not trigger a duplicate resubmission. Returns added, duplicate, skipped, held_for_review, or errored verdicts with exact counts and up to 10 processed receipt summaries. A held_for_review file (withheld for missing details or a possible duplicate) was not added; relay the next step in the message. found:false is pending and has no terminal verdict; found:true is terminal. On completion, report the authoritative per-file outcomes. Show spreadsheetUrl and reviewExpensesUrl for rows actually added, not as the destination for held files. For a held file relay only its message's next step (such as Files to review on Add Expenses); do not invent a missing card or direct held uploads to Gmail Action Needed. Unknown outcomes stay unknown; do not blindly resubmit. This is for uploaded receipts; use get_scan_status for Gmail scans. Read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
submissionIdNoExact submissionId returned by submit_receipt. Strongly preferred because it binds the result to this upload instead of an earlier receipt.
withinSecondsNoFallback lookback window when submissionId is unavailable (30-3600 seconds; default 600).

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
foundYes
stateNoPresent on pending polls for a specific submission.
countsNo
messageYes
successYes
verdictNoTerminal branch only. held_for_review: every file was held for review, so none was added. mixed also covers any batch that includes held files.
receiptsNoProcessed receipt summaries. Held files are not listed here; see data.outcomes.
timestampNo
submissionIdNo
spreadsheetUrlNo
reviewExpensesUrlNo

TDQS

A4.5/5.0
Behavior5/5

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

Annotations only declare readOnlyHint/openWorldHint/destructiveHint, but the description adds substantial behavior: pending is normal and found:false is non-terminal, held_for_review files were not added, unknown outcomes stay unknown, and spreadsheets must not be shown as the destination for held files. This is genuine operational context beyond the safety annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

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

The purpose and trigger are front-loaded, but the description runs long and includes a cluster of guardrail sentences ('do not invent a missing card', 'do not blindly resubmit') that partly repeat the same intent. It is dense and useful but not tightly sized relative to a 2-parameter read-only tool.

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?

An output schema exists, yet the description still supplies the terminal/pending semantics and verdict vocabulary an agent needs to interpret results. For a polling tool with cross-tool dependencies, nothing essential is missing.

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

Parameters3/5

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

Schema description coverage is 100%, so both submissionId and withinSeconds are already documented, including the 'strongly preferred' rationale and the 30-3600 fallback range. The description reinforces using the exact ID from the submit_receipt response but adds little syntactic or format meaning beyond the schema, matching the baseline 3.

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

Purpose5/5

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

States a specific verb and resource ('Check the authoritative final outcome of a receipt image/PDF batch') and ties it to submit_receipt explicitly. It also differentiates from the sibling get_scan_status ('This is for uploaded receipts; use get_scan_status for Gmail scans'), so an agent can pick between them 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.

Usage Guidelines5/5

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

Gives an explicit trigger (call after submit_receipt returns a submissionId, using that exact ID and the polling interval/time allowance from the response) and names the alternative tool plus its condition (Gmail scans -> get_scan_status). It also warns that a pending result must not trigger a duplicate resubmission, which is a clear when-not-to-act rule.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_mileage_summaryA
Read-only
Inspect

Mileage analytics — totals, breakdowns by month / client / purpose / category, plus deduction framing (cents-per-mile or cents-per-km × distance, country-aware IRS / CRA rates). Examples: 'mileage this year', 'miles driven for Acme', 'mileage by month', 'mileage deduction estimate', 'business miles last quarter'. Supports YoY / MoM / QoQ comparison phrasing. Returns: { message, data: { totalDistance, deductionEstimate?, breakdown?, comparison?, sampleMeta? } }.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoNatural language mileage question (e.g., 'mileage this year', 'miles driven for Acme client')
groupByNoHow to group the breakdown
dateRangeNoTime period filter. Use exactly one variant — pick the shape that matches the user's phrasing.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
messageNo
successNoPresent only on the error branch (out of scope) / async path.

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already cover the read-only, non-destructive safety profile. The description adds genuine behavioral context: country-aware IRS/CRA rates, cents-per-mile/km deduction framing, and YoY/MoM/QoQ comparison handling. It does not discuss edge cases like empty data, but that is a minor gap for a read-only summary tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is compact, front-loads the core purpose, and packs example queries, grouping dimensions, comparison support, and return shape into two sentences worth reading. There is no filler, repetition, or tautology.

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

Completeness4/5

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

With a rich output schema, full parameter documentation, and read-only annotations, the description only needs to cover scoping and use conditions. It does so with practical examples, comparison semantics, and country-aware deduction behavior. It could still be more explicit about sibling-tool routing or how query, groupBy, and dateRange interact, but nothing essential is missing for correct invocation.

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

Parameters3/5

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 useful example queries and return-shape context. However, it mentions breakdowns 'by client' while the groupBy enum exposes 'tag' rather than 'client', creating a mild mismatch between prose and schema. The dateRange guidance is helpful but largely restates schema intent.

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

Purpose5/5

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

The description opens with a specific action-target pair—'Mileage analytics'—and enumerates totals, breakdown dimensions, and deduction framing, making the tool's scope unmistakable. The mileage focus clearly distinguishes it from sibling reporting tools like get_income_summary and get_spending_summary.

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

Usage Guidelines4/5

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

Concrete natural-language examples ('mileage this year', 'miles driven for Acme', 'mileage deduction estimate') give clear context for when this tool applies. It also mentions comparison phrasing support, but it does not explicitly state when to prefer an alternative analytics sibling or what conditions rule this tool out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_monthly_books_reviewA
Read-only
Inspect

Month-end summary of the user's books for one calendar month: income recorded, money spent, net, top spending categories and merchants, plus alerts for anything unusual that month. Examples: 'how did last month go', 'close out my books for June', 'monthly review', 'what did I make and spend in May'. Defaults to the last completed month. Figures come from the user's own recorded data; advisory notes are estimates, not tax advice. closeReadiness summarizes current recorded-book checks and Action Needed categories with authenticated app links. Explain unknown coverage and limitations; it does not close books or certify tax completeness.

ParametersJSON Schema
NameRequiredDescriptionDefault
periodNoCalendar month in YYYY-MM format. Defaults to the last completed month.
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
messageNo
successYes
closeReadinessNo

TDQS

A4.4/5.0
Behavior5/5

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

Beyond the readOnly/openWorld/destructive annotations, the description discloses provenance ('figures come from the user's own recorded data'), reliability caveats ('advisory notes are estimates, not tax advice'), and explicit non-capabilities ('it does not close books or certify tax completeness'). It also explains what closeReadiness contains (recorded-book checks, Action Needed categories, authenticated app links), which the annotations alone would 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.

Conciseness4/5

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

The purpose and scope are front-loaded, then contents, examples, defaults, and caveats follow in a logical order. It is dense and slightly run-on in the trailing sentences about closeReadiness and unknown coverage, but nearly every sentence carries distinct information.

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?

With an output schema present, return-value detail is not required, and the description still covers defaults, data provenance, advisory-vs-authoritative distinctions, and the closeReadiness summary field. For a two-optional-param read-only review tool, 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.

Parameters3/5

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

Schema description coverage is 100%, so both parameters (period format, clientEmail scope) are already documented in the schema, and the description only restates the default-month behavior. The accountant-restriction note on clientEmail also appears in the schema, so the description adds essentially no parameter meaning beyond structured fields — the baseline 3.

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

Purpose5/5

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

The description names a specific verb+resource ('Month-end summary of the user's books for one calendar month') and enumerates the exact contents (income recorded, money spent, net, top categories/merchants, alerts). Its scope — a single calendar month defaulting to the last completed month — clearly separates it from sibling tools like get_pnl, get_spending_summary, and get_income_summary, which are not month-scoped reviews.

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

Usage Guidelines4/5

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

Concrete user-utterance examples ('how did last month go', 'close out my books for June', 'monthly review') give clear context for when to invoke it, and the clientEmail note scopes accountant use. It never names an alternative tool or states when NOT to use this one, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_per_tag_pnlA
Read-only
Inspect

Per-tag P&L — revenue, cost, profit, and margin grouped by tag (per-client, per-property, per-event, per-realtor-deal). Requires both income AND expense rows to be tagged with matching labels. Common tag-prefix shortcuts: 'Prop –' (rentals), 'Client –' (client billings), 'Wedding –' (events), 'Realtor –' (real estate deals). Examples: 'per-tag P&L this year', 'profit by client', 'profit by property', 'profit on the Smith wedding', 'per-client P&L this year vs last' (YoY). Supports YoY / MoM / QoQ comparison phrasing. Margin renders as multiplier in loss territory. Defaults to year-to-date if no date range given.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoNatural language per-tag P&L question
dateRangeNoTime period filter. Use exactly one variant — pick the shape that matches the user's phrasing.
tagPrefixNoOptional prefix to limit which tags are bucketed (e.g., 'Prop –' for properties only, 'Client –' for clients only). When omitted, all tags are included.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNoInline branch: computed analytics, or null when no spreadsheet / no data.
typeNoAsync branch only: "analytics_pending" — poll pollEndpoint or call poll_analytics with jobId.
jobIdNoAsync branch only.
messageNoHuman-readable narrative (both branches).
successNoAsync branch only.
responseNoChat alias of message.
pollEndpointNoAsync branch only.
pollIntervalNoAsync branch only: milliseconds.
estimatedTimeNoAsync branch only: seconds.

TDQS

A4.6/5.0
Behavior5/5

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

Beyond the readOnlyHint=true and destructiveHint=false annotations, the description discloses key behaviors: the requirement for matching tags on income and expense rows, that margin renders as a multiplier in loss territory, and that the tool defaults to year-to-date when no date range is given. These are valuable behavioral details not captured in annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

The description is long but well-organized: it leads with the core purpose, then prerequisites, tag prefixes, examples, and behavioral quirks. Every sentence adds value, and the structure is front-loaded with the most important information. It is slightly verbose but not padded.

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 tool with three parameters (including a complex dateRange schema) and an output schema, the description covers the main use cases, preconditions, defaults, and output quirks. Nothing essential is missing for an agent to invoke it correctly.

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?

The schema already provides descriptions for all parameters (100% coverage), but the description adds significant contextual meaning: it explains tag-prefix shortcuts (e.g., 'Prop –' for properties), provides example natural-language queries for the query parameter, and clarifies the default behavior of the dateRange parameter. This enriches the schema beyond its raw definitions.

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

Purpose5/5

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

The description clearly states the tool produces per-tag P&L (revenue, cost, profit, margin) grouped by tag, and explicitly lists the tag categories (client, property, event, realtor-deal). It includes concrete example queries that make the purpose unmistakable and distinguishes it from broader P&L tools like get_pnl.

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

Usage Guidelines4/5

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

The description provides explicit example queries that indicate when to use this tool (e.g., 'profit by client', 'per-tag P&L this year') and states the prerequisite that both income and expense rows must be tagged with matching labels. It does not name sibling tools to avoid, but the examples and focused scope imply the appropriate usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_pnlA
Read-only
Inspect

Compute Profit & Loss (P&L / net income / margin) by combining the Income tab with expense tabs. Examples: 'am I profitable this year', 'P&L for Q1', 'net income last quarter', 'what's my margin', 'P&L this year vs last' (YoY). Supports period comparison — YoY, MoM, QoQ, same-month-prev-year. Margin renders as multiplier in loss territory ('expenses 5.4× revenue') so the user gets a readable signal instead of '-436.9% margin'. Returns: { message, data: { revenue, expenses, netIncome, margin, comparison?, sampleMeta? } }.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoNatural language P&L question (e.g., 'P&L for Q1 2025', 'am I profitable')
dateRangeNoTime period filter. Use exactly one variant — pick the shape that matches the user's phrasing.
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNoInline branch: computed analytics, or null when no spreadsheet / no data.
typeNoAsync branch only: "analytics_pending" — poll pollEndpoint or call poll_analytics with jobId.
jobIdNoAsync branch only.
messageNoHuman-readable narrative (both branches).
successNoAsync branch only.
responseNoChat alias of message.
pollEndpointNoAsync branch only.
pollIntervalNoAsync branch only: milliseconds.
estimatedTimeNoAsync branch only: seconds.

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnlyHint, destructiveHint=false), and the description adds genuinely non-obvious behavior: margin is rendered as a multiplier in loss territory ('expenses 5.4× revenue') rather than a negative percentage, and comparison output is conditional. It does not mention auth/permission nuances or data-freshness limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loads the core definition, then example phrasings, then the return shape — a sensible priority order with no filler sentences. The inline example list is somewhat long, but each example earns its place by teaching the NL query surface an agent must match.

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

Completeness4/5

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

An output schema exists, so the brief returns summary ({ message, data: {...} }) is supplementary rather than required, and the description covers the data sources, comparison support, and the margin edge case. Missing only the when-not-to-use boundary versus per-tag and analytics siblings.

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

Parameters3/5

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

Schema description coverage is 100% and the dateRange oneOf variants are fully self-documenting, so the baseline is 3. The description's restatement of example query strings and period-comparison semantics adds only marginal meaning beyond the schema and does not clarify e.g. precedence between 'query' and 'dateRange' or the clientEmail authorization rule.

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

Purpose5/5

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

States a specific verb and resource ('Compute Profit & Loss ... by combining the Income tab with expense tabs') and defines scope (revenue minus expenses plus margin), which inherently separates it from single-axis siblings like get_income_summary and get_spending_summary. The natural-language example queries ('am I profitable this year', 'what's my margin') make the intent unmistakable.

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

Usage Guidelines4/5

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

Gives strong contextual triggers via example phrasings and enumerates supported comparison modes (YoY, MoM, QoQ, same-month-prev-year). It does not, however, state when NOT to use it or name alternatives such as get_per_tag_pnl for per-tag breakdowns or get_deep_analytics, so routing against close siblings 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.

get_recent_activityA
Read-only
Inspect

Show what the user (or their AI assistants) has recently done in ExpenseBot via this MCP server: which tools were called, when, with what arguments, and whether they succeeded. This is a log of assistant TOOL CALLS, not the processing history of a document. Useful for questions like 'what did I do this week' or 'which tools has my assistant run', and to give the user transparency into AI-assisted actions. Returns the most recent N entries from the audit log (default 20, max 100).

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
actionIdNoOptional: filter to a single tool/action name
sinceDaysNoOnly show actions from the last N days (default 7)

Output Schema

ParametersJSON Schema
NameRequiredDescription
countYes
actionsYes
successYes

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already cover read-only and non-destructive nature, and the description adds valuable context about what the log contains (tool calls, arguments, timestamps, success/failure). It also states the default limit and maximum, which are not in annotations. Since annotations cover safety, the description provides additional behavioral detail without contradicting them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is three sentences with no fluff. The primary purpose is stated first, followed by a clarifying distinction and then usage context and parameters. Every sentence earns its place, and the length is proportional to the tool's complexity.

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

Completeness4/5

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

The tool has an output schema, so return format is covered. The description covers purpose, use cases, data scope, and parameter defaults. It does not explicitly mention pagination or error cases, but for a read-only audit log with a limit parameter, those are not critical. It is complete enough for correct invocation.

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

Parameters3/5

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

Schema description coverage is 67% (actionId and sinceDays have descriptions, limit does not). The description compensates for limit by stating 'default 20, max 100', which is not in the schema. However, it does not add beyond the schema for actionId or sinceDays, so overall it meets baseline without enriching beyond what is already clear. A 3 is appropriate.

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

Purpose5/5

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

The description clearly states a specific verb ('Show') and resource (recent activity log) and explicitly distinguishes it from document processing history. It names the underlying content (tool calls, arguments, success/failure) and directly differentiates from the sibling 'trace_document' by clarifying it is not processing history. This is unambiguous.

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

Usage Guidelines4/5

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

The description gives concrete use cases: 'what did I do this week' and 'which tools has my assistant run', and states it provides transparency into AI-assisted actions. It also clarifies it is not for document processing history, though it does not explicitly name an alternative tool. It could be stronger by pointing to specific siblings, but the guidance is clear and actionable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_report_detailsA
Read-only
Inspect

Get full details of a specific expense report including all expenses, totals, and compliance status. Use only when the user explicitly asks to inspect an existing report's details. Never call this as a preflight or follow-up to creating, sharing, or billing from a report; those tools already return the required result and links.

ParametersJSON Schema
NameRequiredDescriptionDefault
reportIdYesThe report/spreadsheet ID
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.
analysisRequestedYesSet true only when the user explicitly asked to inspect this report. Do not ask for or copy their full message, and do not infer consent from report creation, sharing, or billing.

Output Schema

ParametersJSON Schema
NameRequiredDescription
reportYesThe full stored report record.
successYes

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds useful context: it discloses what the call returns (all expenses, totals, compliance status) and a meaningful gating rule about consent for inspection. It doesn't mention rate limits or the accountant-specific client scoping beyond the schema, hence not a full 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

Three sentences, front-loaded with the capability, then the usage condition, then the exclusion. Every sentence carries a distinct and necessary constraint with zero filler.

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?

An output schema exists, so return values need not be spelled out, and annotations cover safety. Combined with 100% schema coverage, the description supplies the remaining needed pieces: scope, consent gating, and the preflight/follow-up prohibition.

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

Parameters3/5

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

Schema description coverage is 100%, so all three parameters are already documented, including the analysisRequested consent semantics and the clientEmail accountant restriction. The description reinforces the consent constraint but adds no syntax or format detail beyond the schema, so baseline 3 applies.

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

Purpose5/5

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

States a specific verb (Get) and resource (full details of a specific expense report) and enumerates the payload (all expenses, totals, compliance status). It is clearly distinguishable from siblings like list_reports (enumeration) and check_compliance (single check).

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?

Explicitly states when to use it ('only when the user explicitly asks to inspect an existing report's details') and when not to ('never call this as a preflight or follow-up to creating, sharing, or billing'), explaining that those tools already return the required result and links. This is explicit when/when-not guidance with the alternative mechanism named.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_scan_statusA
Read-only
Inspect

Check the authoritative status of the user's Gmail receipt scans. Returns the active scan lock, the same live phase and item progress shown by ExpenseBot's in-app status pill, queued or attention-needed years, completed calendar years, current merchant/category exclusions, and recent outcomes. Call when the user asks whether a scan is running, finished, stuck, or what it is doing. When complete, show the returned spreadsheetUrl or reviewExpensesUrl; when setup or reconnection is needed, show gmailScanUrl. Read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
lockNoScan-lock state (active, startedAt, ageSeconds, dispatchComplete, pendingProcessingCount).
statusNoScan lifecycle state.
messageNo
successYes
latestScanNoMost recent scan outcome (success, emailsFound, emailsTagged, timestamp).
gmailScanUrlNo
scanInProgressNo
spreadsheetUrlNo
reviewExpensesUrlNo

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, and the description reinforces this with 'Read-only.' It goes beyond the annotations by detailing the fields returned (spreadsheetUrl, reviewExpensesUrl, gmailScanUrl) and instructing which to display under which condition, plus noting the data mirrors the in-app status pill. No contradictions 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.

Conciseness5/5

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

The description is well-structured: it opens with the core purpose, then lists returned data, then gives usage triggers and follow-up actions, and closes with 'Read-only.' Every sentence adds value; it is compact without being terse, and the most critical info (purpose and when to call) is front-loaded.

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

Completeness4/5

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

For a read-only status tool with no parameters and an output schema (which presumably documents the return structure), the description covers the essential agent needs: what it returns, when to call, and how to act on the results. It could mention edge cases (e.g., no active scan) but these are likely implied by the output schema, so it is sufficiently complete.

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?

The tool has zero parameters, so the description carries no parameter burden. The baseline for 0 parameters is 4, and the description adds no unnecessary parameter info. Schema coverage is trivially 100% (empty schema), so this score is appropriate.

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

Purpose5/5

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: 'Check the authoritative status of the user's Gmail receipt scans.' It enumerates the exact returned data (scan lock, live phase, item progress, years, exclusions, outcomes) and explicitly ties it to a status-pill context, which clearly differentiates it from siblings like scan_gmail (which initiates scans) and get_last_receipt_result (which returns a single result).

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

Usage Guidelines4/5

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

The description gives an explicit trigger: 'Call when the user asks whether a scan is running, finished, stuck, or what it is doing.' It also provides follow-up actions (show spreadsheetUrl/reviewExpensesUrl when complete, gmailScanUrl when setup needed). However, it does not explicitly state when NOT to use it or name alternative tools, so it lacks exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_spending_summaryA
Read-only
Inspect

Summarize the user's recorded expenses with totals and breakdowns by category, merchant, month, tag, source, or payment method. Supports date ranges, period comparisons, and total, count, or average metrics. Read-only. Returns: { message, data: { total, breakdown?, comparison?, sampleMeta? } }.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoNatural language question (e.g., 'how much did I spend in March', 'top merchants this quarter')
metricNo
groupByNo
dateRangeNoTime period filter. Use exactly one variant — pick the shape that matches the user's phrasing.
categoriesNoOptional configured expense categories to include in the summary.
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNoInline branch: computed analytics, or null when no spreadsheet / no data.
typeNoAsync branch only: "analytics_pending" — poll pollEndpoint or call poll_analytics with jobId.
jobIdNoAsync branch only.
messageNoHuman-readable narrative (both branches).
successNoAsync branch only.
responseNoChat alias of message.
pollEndpointNoAsync branch only.
pollIntervalNoAsync branch only: milliseconds.
estimatedTimeNoAsync branch only: seconds.

TDQS

A3.5/5.0
Behavior3/5

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

The annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered by structured data. The description adds a compact return shape ('Returns: { message, data: { total, breakdown?, comparison?, sampleMeta? } }'), which gives some behavioral context beyond annotations. It does not describe auth requirements, rate limits, or edge cases, but the annotations cover the important safety dimension.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is three sentences long, front-loaded with the core capability, then covering options, read-only status, and return shape. There is no filler or redundant elaboration; every clause contributes to an agent's understanding.

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

Completeness4/5

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

After accounting for the existing output schema and annotations, the description fills the main gap by documenting the enum parameters and the overall purpose. It lacks context about natural-language query usage (e.g., that query can be a free-form question) and does not mention the clientEmail restriction, but these are already in the schema. The main omitted guidance about which sibling to use instead is captured in the usage_guidelines dimension.

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 description coverage is 67%, and the two undocumented parameters (metric and groupBy) are clear from the description: it lists 'category, merchant, month, tag, source, or payment method' and 'total, count, or average metrics', mapping directly to the enum values. This adds real semantics beyond the schema. Other params (query, dateRange, categories, clientEmail) are adequately described in the schema.

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 description states a specific verb and resource: 'Summarize the user's recorded expenses with totals and breakdowns...' It also enumerates grouping dimensions and metrics, making the intent concrete. It does not explicitly name sibling alternatives like get_income_summary or get_pnl, but the 'expenses' scope and read-only nature implicitly distinguish it from those.

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

Usage Guidelines2/5

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

No explicit when-to-use or when-not-to-use guidance is given. The description mentions capabilities ('Supports date ranges, period comparisons...') but does not clarify when to choose this rather than sibling tools like get_income_summary, get_pnl, or get_deep_analytics. With a large sibling list, the absence of route-to-alternative guidance is a significant gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_spreadsheet_urlA
Read-only
Inspect

Return the user's master ExpenseBot Google Sheet plus authenticated ExpenseBot workspace links, each with a label and a description of when to use it. Use this when the user asks to open, view, check, or edit their spreadsheet; review expenses or income; manually scan Gmail; reconcile; connect or manage a bank/credit card; open Automation Hub or General Settings; create or open reports; or asks where a submitted receipt went. Choose and show the one or two links relevant to the request instead of listing the entire catalog. After a receipt submission, prefer Review expenses plus the Google Sheet. After an income write, prefer Review income plus the Sheet. For a Gmail scan or connection request, use the Scan Gmail link, which opens the existing Gmail scanning interface. Bank/card requests use the Reconcile link; configuration requests use the returned Automation Hub or General Settings link. Category, G/L, and account-code requests use Category management; client, project, and trip groups use Group management; questions about what counts as Personal use Personal rules. For client billing setup, use the returned clientBillingHandoffs instead of giving a generic dashboard tour: manageClients creates or manages the client list, tagReadyExpenses opens Review Expenses filtered to blank or default-Business rows, and createClientReport opens report creation by group after expenses have been assigned. Choose the one next step that matches the user's current state. If the user names a client and an exact expense scope and asks the assistant to do the work, use search_expenses and group_expenses instead; show its grounded preview and require explicit confirmation before changing any rows. When the user asks to choose receipt images from Google Photos, use the returned Google Photos link; it opens the existing authenticated picker and handles any required Google consent in ExpenseBot. When they ask where ExpenseBot stores receipt files, return the Drive folder link only when it is available; never invent or request a folder ID. For a complete, unambiguous cash expense or mileage trip, use the direct write tool. Use the returned Manual expense link for multiple entries, credits/refunds, detailed tax/tip entry, per diem, incomplete details, or visual review. Use the Mileage and travel link for route calculation, mileage settings, repeated trips, calendar/rideshare imports, per diem, or visual review. These links open the existing reviewed app forms. When the user wants to import income from a screenshot, PDF, or CSV but no attachment is available in chat (or the review is visually complex), use the returned Import income links — they open the existing Add Income importer, which parses the file and shows every row for review before anything is saved. Team setup requests use the returned role-aware Team setup handoff: eligible solo business owners open Add teammates, while existing team owners and co-admins open Team Management. If the handoff says owner access is required, explain that instead of presenting a broken link. Complex or visually reviewed report requests use the returned Create report link. Read-only; this tool does not create or modify spreadsheet rows.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYes
messageYes
successYes
reportsUrlYes
dashboardUrlYes
gmailScanUrlYes
spreadsheetIdYes
spreadsheetUrlYes
reviewIncomeUrlYes
reviewExpensesUrlYes
gmailActionNeededUrlYes

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, destructiveHint=false, openWorldHint=false, and the description reinforces this with 'Read-only; this tool does not create or modify spreadsheet rows.' Beyond annotations it adds genuinely useful behavior: show only one or two relevant links rather than the full catalog, never invent a Drive folder ID, explain owner-access requirements instead of presenting a broken link, and require explicit confirmation before row changes.

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?

This is a single ~350-word run-on paragraph with no headings or bullets, and much of the routing prose duplicates its own output — the description states each returned link already carries 'a description of when to use it.' The purpose is front-loaded, but the volume is disproportionate and unstructured even for a dispatcher tool.

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

Completeness4/5

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

Coverage of scenarios is remarkably complete for a zero-param dispatcher, and the presence of an output schema means return contents need not be explained. The only shortfall is that the heavy per-scenario routing rules logically belong in the returned link descriptions, making the tool description an oversized complement rather than a gap.

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?

The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. Referenced fields like clientBillingHandoffs and the label/description pairs are part of the output, not inputs.

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

Purpose5/5

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: 'Return the user's master ExpenseBot Google Sheet plus authenticated ExpenseBot workspace links, each with a label and a description of when to use it.' That immediately distinguishes it from siblings like get_spending_summary or search_expenses — it is a link/handoff catalog, not a data or write tool.

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 provides an exhaustive when-to-use map: after receipt submission prefer Review expenses + Sheet, after income write prefer Review income, Gmail requests use Scan Gmail, bank/card use Reconcile, categories use Category management, etc. It also names explicit alternatives (search_expenses and group_expenses for client-scoped work, direct write tools for clean cash/mileage entries) and the conditions that select them.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_subscription_auditA
Read-only
Inspect

Subscription audit — wraps the Subscription Auditor engine to find recurring charges, duplicates, price increases, and trial-conversion suspects in the user's expenses. Examples: 'recurring subscriptions', 'duplicate subscriptions', 'price increases', 'trial conversions', 'subscriptions over $20/month'. Returns: { message, data: { recurring, duplicates, priceIncreases, trialConversions, totalMonthlyCost, sampleMeta? } }.

ParametersJSON Schema
NameRequiredDescriptionDefault
focusNoNarrow the audit to a single category (default: all)
queryNoNatural language subscription question (e.g., 'find duplicate subscriptions', 'price increases this year')
dateRangeNoTime period filter. Use exactly one variant — pick the shape that matches the user's phrasing.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
messageNo
successNoPresent only on the error branch (out of scope) / async path.

TDQS

A4.1/5.0
Behavior4/5

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

The structured annotations already say readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description meaningfully adds that the tool wraps a 'Subscription Auditor engine' and enumerates the output categories, which helps the agent predict behavior. It does not contradict any annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is compact and front-loaded: first sentence states the purpose, then a short example list, then a brief return signature. Every part earns its place; no verbose filler or circular wording.

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?

Combined with a rich input schema and an existing output schema, the description supplies the tool's engine, domain keywords, natural-language examples, and return shape. There is no material gap that would prevent an agent from choosing and calling the tool correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so baseline is 3. The description's natural-language examples give useful phrasing for the `query` parameter, but it does not clarify the relationship between `focus` and `query` (e.g., whether query overrides focus or they combine), which is a real semantic gap beyond the schema.

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 description names a specific resource ('subscriptions') and a clear verb ('audit/find'), enumerates the exact categories it returns (recurring, duplicates, price increases, trial conversions), and gives natural-language examples. It does not explicitly contrast itself with sibling tools like get_spending_summary or get_deep_analytics, so it stops short of 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 Guidelines4/5

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

The examples ('recurring subscriptions,' 'duplicate subscriptions,' 'subscriptions over $20/month') establish a clear trigger context for an agent deciding to invoke this tool. It does not name alternatives or exclusion conditions, but the type of question this tool handles is unmistakable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_trip_suggestionsA
Read-only
Inspect

Show ExpenseBot's current high-confidence Trip Intelligence proposals from Review Expenses. Read-only: never groups, tags, creates reports, or writes to the spreadsheet. Each proposal returns strict YYYY-MM-DD dates, evidence, locations, and the exact Receipt IDs with expected current tags. Do not infer a client/project from geography. When nextAction is present, use exactly its group_expenses params with confirm:false, show the canonical preview, then ask for explicit confirmation before any write. Never send confirm:true from this read result alone. In accountant clientEmail mode this tool can read accepted clients but may return no executable nextAction; hand off to Review Expenses or the owner's own assistant connection to apply.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum high-confidence proposals to return (default 3).
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.

Output Schema

ParametersJSON Schema
NameRequiredDescription
countNo
messageNo
successYes
proposalsNoRead-only trip proposals; accepting one continues through group_expenses.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, but the description goes well beyond that: it reinforces the read-only nature with concrete examples of what is never done, specifies the exact return format (strict YYYY-MM-DD dates, evidence, locations, Receipt IDs, expected tags), and discloses a non-obvious rule: 'Never send confirm:true from this read result alone.' It also exposes the accountant-mode limitation. These details add significant behavioral context that annotations alone do not provide, with 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.

Conciseness5/5

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

The description is dense but every sentence earns its place. It leads with the primary purpose, then layers read-only constraints, return format, the critical nextAction workflow, and a mode-specific caveat. There is no filler or redundancy; the structure is logical and the most safety-critical guidance ('Never send confirm:true') is emphasized. The length is justified by the complexity of the tool's behavior.

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?

Given the tool's complexity—read-only retrieval with a conditional write handoff and accountant-specific behavior—the description covers all necessary aspects: what it returns, what the agent should do with nextAction, what not to do (infer client from geography, send confirm:true), and when to hand off. The presence of an output schema reduces the need to explain return values, and the description fills the remaining gaps about usage policy and edge cases.

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

Parameters3/5

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

Schema description coverage is 100% for both parameters (limit and clientEmail), each with clear descriptions and constraints. The tool description does not add any meaning about the parameters themselves—it only contextualizes how to use the returned nextAction, which is about output handling, not parameter semantics. Per the rubric, with high schema coverage, the baseline is 3, and the description does not elevate it further.

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

Purpose5/5

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

The description opens with a precise statement of what the tool does: it shows ExpenseBot's high-confidence Trip Intelligence proposals from Review Expenses. It clearly distinguishes itself from write operations by explicitly listing what it never does (groups, tags, creates reports, writes to spreadsheet) and provides a concrete handoff path (Review Expenses or owner's assistant) that separates it from sibling tools. The purpose is unambiguous and specific.

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?

The description gives explicit when-to-use and how-to-proceed instructions: it states the tool is read-only, and when nextAction is present, exactly which parameters to use (group_expenses with confirm:false), how to present results (show canonical preview), and when to ask for confirmation before any write. It also details the accountant clientEmail mode and directs the agent to hand off when no executable nextAction is returned. This is thorough, actionable guidance that removes ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

group_expensesA
Destructive
Inspect

Group an exact set of recorded expenses for a client, project, trip, job, or other user-named purpose. Examples: 'group my Mexico meals for client Rob', 'put these Vegas expenses under the Vegas project', or 'group these for client Rob and create a report'. The user does not need to know about tags: ExpenseBot resolves the requested name against existing groups and proposes creating one only when needed. Use exact expenseId values returned by search_expenses. A grounded preview is automatic for every bulk request; the user does not need to ask for one. First call with confirm omitted/false, show the returned exact rows, count, totals, proposed group, exclusions, and conflicts, then ask for approval. Only after explicit user approval, repeat the same operationId and selection with confirm:true. A premature confirm:true is converted to preview. When the request includes a report, set createReport:true on that confirmed group_expenses call and use its report result; do not run a separate broader create_report query. For 'without personal expenses', set excludePersonal:true; ExpenseBot removes Personal-tagged and Personal-category rows before preview so their Personal marker is never overwritten. The confirmed operation returns exact report and Bill Client links. Expenses already assigned to another ordinary report are excluded; if none remain, no duplicate report is created and the result links to Reports instead. Keep the user-facing response concise and do not add unsolicited tax or substantiation advice. Do not call check_compliance, get_report_details, or tax/deductibility tools before or after this workflow unless the user explicitly asks for that separate analysis. In user-facing prose call the destination a group, not a tag. Treat preview totals as provisional; after confirmation use only the terminal result's exact count, total, and currency without reconciling it against the preview. A returned Bill Client URL opens the reviewed billing handoff and does not mean an invoice was created, sent, or confirmed. Sharing remains a separate share_report action with recipient confirmation.

ParametersJSON Schema
NameRequiredDescriptionDefault
undoNoUndo a completed grouping within its undo window.
groupNoThe user-named destination group to apply to every eligible selected expense.
statusNoRead operation status using operationId.
confirmNoOmit/false for preview; true only after explicit approval.
selectionNoExact expenses returned by search_expenses and approved for this bounded operation.
operationIdYesStable idempotency key generated once for the preview and reused unchanged.
reportTitleNoOptional title when createReport is true; otherwise ExpenseBot derives one from the group.
createReportNoCreate a report from the exact eligible grouped expenses.
excludedTagsNoExisting groups to exclude before preview and grouping.
approveNewGroupNoTrue only when the preview says a new group is required.
excludePersonalNoExclude Personal-tagged and Personal-category expenses from both grouping and the exact report.
excludedCategoriesNoExpense categories to exclude before preview and grouping.

Output Schema

ParametersJSON Schema
NameRequiredDescription
undoNo
groupNo
reportNo
statusYes
messageNo
previewNo
successYes
manifestNo
candidatesNo
exclusionsNo
reportsUrlNo
finalAnswerNo
operationIdNo
confirmationNo
suggestedTagNo
creationStatusNo
responseGuidanceNo
reviewExpensesUrlNo
requiresConfirmationNo
requiresNewGroupApprovalNo

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare destructiveHint=true, openWorldHint=true, and readOnlyHint=false, but the description adds substantial behavioral context beyond them: automatic grounded preview, premature confirm conversion, operationId idempotency reuse, personal-marker protection, conflict/exclusion behavior, duplicate-report avoidance, and the meaning of returned Bill Client links.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

The description is long and dense, but it front-loads the purpose and examples before moving into workflow constraints. Almost every sentence serves to prevent a specific failure mode in a complex destructive bulk operation, though the single-paragraph format and volume keep it from being maximally concise.

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?

Given the tool's complexity, 12 parameters, nested objects, destructive annotation, and the presence of an output schema, the description is complete enough to guide correct invocation. It covers preview/confirmation, report creation, exclusions, idempotency, conflicts, and post-confirmation reporting behavior without needing to explain return values.

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 description coverage is 100%, so the schema already carries the basic parameter definitions. The description adds meaningful workflow semantics for confirm, createReport, excludePersonal, approveNewGroup, and selection stability, though it does not add equally clear guidance for undo, status, reportTitle, excludedTags, or excludedCategories.

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

Purpose5/5

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

The description states a specific verb and resource: grouping an exact set of recorded expenses for a client, project, trip, job, or other user-named purpose. It is clearly differentiated from siblings by naming search_expenses as the source of exact IDs and explicitly steering report creation into this call rather than a separate create_report query.

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?

Usage is highly prescriptive: first call with confirm omitted/false to preview, then only after explicit approval repeat with confirm:true. It names alternatives and exclusions, including when to use createReport:true and excludePersonal:true, and explicitly says not to call check_compliance, get_report_details, or tax tools unless separately asked.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_categoriesA
Read-only
Inspect

List the user's configured expense categories and billing accounts. Use this BEFORE calling tools that need a category (add_cash_expense, add_mileage_entry, create_report) so you pass the user's actual category names, not guesses. Returns the live list from their spreadsheet.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYes
categoriesYesExpense categories for the user.
billingAccountsNoBilling/card accounts, when configured.

TDQS

A4.7/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, destructiveHint=false, and openWorldHint=true, so safety is covered structurally. The description adds useful context beyond that: the data is live from the user's spreadsheet, which tells the agent results are fresh rather than cached. It doesn't discuss failure modes (e.g., no categories configured), so it stops short of 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.

Conciseness5/5

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

Three sentences, none wasted, and the ordering instruction is front-loaded after the purpose. The rationale for the call order is compressed into a single clause rather than spelled out.

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?

An output schema exists, so return values need no explanation here, and with zero parameters and full annotation coverage the description has nothing left to carry. It supplies the one thing structured fields can't: the required call ordering relative to sibling tools.

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?

Zero parameters, so there is nothing to document and the baseline is 4. The description correctly adds no parameter noise.

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

Purpose5/5

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

States a specific verb+resource ('List the user's configured expense categories and billing accounts') and scopes it precisely enough to separate it from list_income_categories and list_tags. An agent can identify the tool's job 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.

Usage Guidelines5/5

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

Explicitly prescribes when to call it ('BEFORE calling tools that need a category') and names the exact downstream tools (add_cash_expense, add_mileage_entry, create_report). It also states the reason — passing real category names instead of guesses — which closes the loop on why this ordering matters.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_client_close_readinessA
Read-only
Inspect

Accountant-only month-end overview of accepted client workspaces. Returns page-level readiness and affected-client category counts with an authenticated Client Reports link. Use for 'how many clients need attention before month end'. No client identities, amounts, or individual items are returned; select a client in ExpenseBot to resolve items. Unavailable sources remain unknown. Each page uses the current roster; do not combine pages into a verified total.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageNoPage number; starts at 1. Use nextPage from the previous result.
limitNoClient workspaces per page; default and maximum 5.
periodNoCalendar month YYYY-MM; defaults to the last completed UTC month.

Output Schema

ParametersJSON Schema
NameRequiredDescription
asOfYes
linksYes
scopeYes
periodYes
messageYes
successYes
coverageYes
pageInfoYes
categoriesYes
limitationsYes
statusCountsYes
evaluatedClientCountYes
unavailableCandidateCountYes

TDQS

A4.3/5.0
Behavior5/5

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

Beyond the readOnly/destructive/openWorld annotations, it discloses meaningful behavioral traits: accountant-only access, that no client identities, amounts, or individual items are returned, that unavailable sources stay 'unknown', and two pagination caveats (page uses the current roster; pages must not be summed into a verified total). These are exactly the constraints an agent needs to avoid misreporting results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Four sentences, front-loaded with the scope and return shape before the usage trigger and caveats. Every sentence carries information (audience, output, trigger, data limits, pagination warning), though the density is high enough that it reads more like a spec block than a tight synopsis.

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

Completeness4/5

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

With an output schema present, the description needn't explain return fields, and it correctly focuses on limits, audience, and pagination behavior instead. It is nearly complete; only a pointer on how the page-link/pagination should be consumed is left to the schema.

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

Parameters3/5

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

All three parameters are fully documented in the schema (100% coverage), including the page/limit/period semantics and the nextPage chaining hint, so the schema does the heavy lifting. The description only reinforces the page-level nature of results, adding no syntax or format details beyond what is already structured. Baseline 3 applies.

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

Purpose5/5

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

The description states a specific verb and resource ('month-end overview of accepted client workspaces' with 'page-level readiness and affected-client category counts') and immediately scopes it as accountant-only. It is clearly distinguishable from sibling tools like list_client_invoices or get_client_advance_balances, which deal in per-client detail rather than aggregate readiness.

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

Usage Guidelines4/5

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

It gives an explicit trigger ('Use for "how many clients need attention before month end"') and redirects detail resolution to another surface ('select a client in ExpenseBot to resolve items'). It never names a sibling tool directly nor states when not to use it, but the operational context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_client_invoicesA
Read-only
Inspect

List the user's issued client invoices (accounts receivable) — who owes them money, how much, and when it is due. Examples: 'which invoices are outstanding', 'what does Acme still owe me', 'any overdue invoices', 'how much am I waiting to get paid'. status accepts 'active' (default), 'open', 'overdue', 'needs_review', 'paid', 'void', 'superseded', or 'all'. Returns invoice numbers, status, totals by currency, delivery state, and private document links. Read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax invoices to return (default 25, max 100).
statusNoFilter by invoice status (default active/outstanding).
clientNameNoOptional: exact client name (case-insensitive).
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYes
invoicesYes

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds meaningful behavioral context by listing the returned fields (invoice numbers, status, totals by currency, delivery state, private document links) and the default status behavior, which goes beyond the annotations without contradicting them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

The description is well-structured: it opens with the core purpose, then gives examples, then details status options and return fields. Each section earns its place, though the examples and enumeration could be trimmed without loss. It is appropriately sized for a tool with four optional parameters.

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

Completeness4/5

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

Given the tool's complexity (four optional parameters, an output schema, and a clear read-only role), the description covers the essential context: what it lists, how to filter, and what it returns. The limit parameter is documented in the schema, and the output schema handles return details. Nothing critical is missing 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.

Parameters3/5

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

Schema description coverage is 100%, so the baseline is 3. The description does not add extra meaning for the parameters beyond what the schema already documents; it only reiterates the status enum values and default, which are already in the schema. No additional semantic value is provided.

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

Purpose5/5

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

The description states a clear verb ('list') and resource ('the user's issued client invoices'), with a plain-language explanation of accounts receivable and concrete example queries. It clearly distinguishes from sibling tools like get_client_invoice (singular) by framing itself as a listing operation.

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

Usage Guidelines4/5

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

The description gives example phrasings that map to typical use cases and enumerates the status filter options, providing clear context for when to use this tool. It does not explicitly name alternatives or say when not to use it, but the examples imply the intended scope, so this is strong but not explicit on exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_income_categoriesA
Read-only
Inspect

List ExpenseBot's fixed income tax categories. Unlike Expense Accounts, these are not user-configured. Use this BEFORE calling add_income so you pass an exact canonical category instead of guessing.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
notesNo
legacyNoDeprecated income categories still accepted on read.
successYes
categoriesYes

TDQS

A4.3/5.0
Behavior3/5

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

Annotations already provide read-only and non-destructive hints. The description adds that the categories are fixed and not user-configured, which is a useful behavioral fact but largely reinforces the existing openWorldHint=false annotation. There is 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.

Conciseness5/5

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

Two sentences, first is front-loaded with the core purpose, second provides usage context and the rationale for the action. Every sentence earns its place with no waste.

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 zero-parameter list tool with an output schema present and annotations covering read-only/destructive behaviors, this description is complete. It tells the agent when to use it, what it is, and why it matters, so nothing essential is missing.

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?

This tool has zero parameters and 100% schema description coverage. The description does not need to add parameter detail, so the high baseline of 4 applies.

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

Purpose5/5

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

Clearly identifies the specific action (list) and resource (ExpenseBot's fixed income tax categories), distinguishing it from user-configured Expense Accounts. The mention of income tax and the unmodifiable nature makes the purpose unambiguous.

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

Usage Guidelines4/5

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

Explicitly instructs to call this tool before add_income and warns against guessing categories, indicating when to use it. It contrasts with Expense Accounts without naming the exact sibling tool, so it stops short of a fully explicit when-not/alternative tool list.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_reportsA
Read-only
Inspect

List the user's expense reports with pagination. Filter by status (All, Draft, Submitted, Shared). Each exact report includes its app, Bill Client, and accounting handoff URLs. Use the matching accounting URL only when the user explicitly asks to send or export that report; the app keeps organization, mapping, preview, and final confirmation.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageNoOne-based results page to return.
limitNoMaximum reports to return on this page.
filterNoOptional report-status filter; All returns every authorized status.All
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.

Output Schema

ParametersJSON Schema
NameRequiredDescription
pageNo
limitNo
filterNo
reportsNoPresent when the user has reports.
successYes
creationStatusYesProvenance of the listing, e.g. "existing_only".
responseGuidanceNo

TDQS

A3.9/5.0
Behavior4/5

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

Beyond the readOnlyHint, the description discloses that results embed accounting handoff URLs and warns against acting on them unless explicitly asked. The note that 'the app keeps organization, mapping, and final confirmation' clarifies the tool's limits, adding meaningful 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.

Conciseness4/5

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

The description is short and front-loaded with the core action, followed by filter options, return contents, and a safety caveat. There is minor ambiguity in 'each exact report' and the phrase 'the app keeps organization, mapping, and final confirmation,' but overall it is efficient.

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

Completeness4/5

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

For a simple read-only listing tool with an output schema, the description covers its purpose, filtering, pagination, relevant returned data, and an important usage caveat. It does not specify when to prefer sibling tools, but that is not essential for a straightforward list operation.

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

Parameters3/5

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

Schema coverage is 100%, with page, limit, filter, and clientEmail all described. The description adds only high-level context about pagination and status, and does not meaningfully extend the parameter explanations beyond what the schema already provides.

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

Purpose5/5

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: 'List the user's expense reports with pagination.' It is unambiguous against siblings like add_expense or export_report because the focus is purely read-only listing. The mention of status filtering and returned URLs further distinguishes it from detail or send operations.

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

Usage Guidelines3/5

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

It gives a clear operational caveat—use the accounting URL only when the user explicitly asks to send/export—but it does not explicitly route an agent away from or toward sibling tools such as get_report_details or export_report. The filtering and pagination context is useful, yet alternative selection guidance is missing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_tagsA
Read-only
Inspect

List the user's configured groups. ExpenseBot stores clients, projects, project codes, properties, and trips as tags (for example 'Client: Acme', 'Vegas Trip', or 'Property: 123 Main'). Use this when the user asks 'what groups/projects/clients do I have?' and before filtering, grouping, or reporting when the intended existing name is unclear.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
tagsYesClient / project / trip groups (Column K values).
successYes

TDQS

A3.8/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, so the main safety concern is covered by structured data. The description adds clarity about what data is listed (clients, projects, project codes, properties, trips), but it does not disclose any deeper behavioral traits such as pagination, response limits, or auth requirements. This is adequate though not exceptional.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Three sentences, front-loaded with the core responsibility, followed by helpful examples and usage timing. Nothing is wasted, though the example list could be slightly shorter without loss of value.

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

Completeness4/5

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

For a zero-parameter read-only listing tool, the description fully covers what it returns conceptually, how the data is modeled, and when to invoke it. The output schema exists, so return structure does not need to be spelled out in the description. Minor ambiguity about whether all tags are returned or just top-level groups is not significant.

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?

The tool takes zero parameters and schema coverage is 100%, so there are no parameter semantics to explain. The description compensates by making the meaning of the tool's resource domain clear, which is more useful here than parameter documentation would be.

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 description names a specific verb and resource ('List the user's configured groups') and explains what counts as a group by giving concrete tag examples like 'Client: Acme' and 'Property: 123 Main'. It is clearly differentiated from sibling list tools such as list_categories and list_reports because it focuses on user-configured tags representing clients/projects/trips, though it does not explicitly name an alternative.

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

Usage Guidelines4/5

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

The description gives explicit use-case guidance: use it when the user asks 'what groups/projects/clients do I have?' and before filtering/grouping/reporting when an existing name is unclear. It lacks an explicit when-not-to-use clause naming alternatives, but the context it provides is sufficient for an agent to make an appropriate selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

mark_client_invoice_paidA
Destructive
Inspect

Mark an issued client invoice as paid after explicit confirmation. Requires a strict calendar-valid paidDate in YYYY-MM-DD format. This updates the accounts-receivable invoice record only; it never creates or moves an Income row. Record the actual payment separately or link an existing Income row.

ParametersJSON Schema
NameRequiredDescriptionDefault
paidViaNoOptional payment method or source, such as bank transfer or check.
paidDateYesCalendar-valid payment date in YYYY-MM-DD format.
invoiceIdYesStable invoice ID from list_client_invoices.
paidAmountNoOptional amount received.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
paidViaYes
successYes
paidDateYes
invoiceIdYes
paidAmountYes
invoiceNumberNo
incomeRowCreatedYes

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=false and destructiveHint=true, indicating a write/destructive operation. The description adds meaningful scope: it specifies the update is limited to the invoice record and explicitly states what it does not do (create/move Income rows). It also enforces a strict date format requirement. This goes beyond the annotations and provides useful 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.

Conciseness5/5

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

The description is three concise sentences with no filler. The primary action and key constraint are front-loaded, and each sentence earns its place by adding scope or exclusion information. It is well-structured for quick parsing.

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

Completeness4/5

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

Given that an output schema exists and the tool has only four parameters, the description covers the essential context: the action, the required date format, the scope of the update, and what it does not do. It does not explain the meaning of 'explicit confirmation' in workflow terms, but that is a minor gap given the output schema and annotations provide the rest.

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

Parameters3/5

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

Schema coverage is 100%, so all four parameters are already described in the schema. The description reinforces the paidDate format ('strict calendar-valid paidDate in YYYY-MM-DD format'), but this largely duplicates the schema's format and description. It adds no new meaning to the parameters beyond what the schema provides, so a baseline 3 is appropriate.

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

Purpose5/5

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

The description states a specific action ('Mark an issued client invoice as paid') and clearly differentiates it from income-recording siblings by noting it 'updates the accounts-receivable invoice record only; it never creates or moves an Income row.' This distinguishes it from add_income and similar tools, making the purpose unambiguous.

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

Usage Guidelines4/5

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

The description gives clear context: it is for marking an invoice paid 'after explicit confirmation' and explicitly directs the user to 'Record the actual payment separately or link an existing Income row.' This implies the tool is not for recording income itself. It doesn't name a specific alternative tool, but the guidance is sufficient to route an agent appropriately.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

parse_expenseA
Read-only
Inspect

Parse a natural language expense description into structured fields. Does NOT add the expense — just returns the parsed fields for review. Example: "Lunch at Chipotle $15.50 today" → {merchant: "Chipotle", total: 15.50, ...}

ParametersJSON Schema
NameRequiredDescriptionDefault
textYesNatural language expense (e.g., "Coffee at Starbucks $6.50 yesterday")

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYes
expensesYes
categoryNamesYes
validationErrorsYes

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, and the description reinforces and adds context by stating the tool does not add anything and only returns parsed fields. It goes beyond the annotation in explaining that the result is for review, but it does not discuss failure modes or edge cases; the annotation bar is low here, making this more than strong enough.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is compact: two clear sentences and an example. It front-loads the core purpose, adds the key behavioral caveat, and illustrates the input/output transformation. There is no redundant phrase or irrelevant detail.

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?

With a single parameter, a full output schema, and annotations that signal read-only safety, the description is fully sufficient. It tells an agent what the tool does, what input it takes, what kind of output to expect, and that it has no side effects. No critical information needed for correct invocation is missing.

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

Parameters3/5

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

Schema description coverage is 100%; the schema already describes the `text` parameter with its own example. The description adds another example and shows the mapping to output, but it does not enrich parameter semantics beyond what the schema already conveys, so the baseline of 3 is maintained.

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

Purpose5/5

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 ('Parse a natural language expense description') and immediately clarifies it does NOT add the expense, distinguishing it from sibling add/search tools. The example illustrates a concrete merchant/total/date format, leaving no ambiguity about its job.

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

Usage Guidelines4/5

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

The description clearly frames the tool as a parse-and-review step, stating 'Does NOT add the expense' and 'just returns the parsed fields for review.' It gives context for when to use it but does not explicitly name an alternative add tool such as add_cash_expense, so the routing is clear but not directly explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

prepare_client_invoiceAInspect

Prepare a client invoice from a saved ExpenseBot report without creating it. Reads canonical report rows, allocates a unique invoice number, verifies the client ledger identity and advance, and returns exact totals plus a short-lived preparationId. Call this first, show the preview to the user, then call create_client_invoice after explicit confirmation. It reserves the invoice number and preview for 30 minutes but creates no invoice, document, Income row, email, or payment.

ParametersJSON Schema
NameRequiredDescriptionDefault
taxPctNoTax percentage applied after markup and custom line items; use 0 for no tax.
reportIdYesSaved ExpenseBot report ID to invoice.
taxLabelNoTax label such as HST, GST, or VAT.
issueDateYesCalendar-valid invoice date in YYYY-MM-DD format.
markupPctNoMarkup applied only to rebilled expense rows.
clientNameYesFull client or business name shown on the invoice.
receiptIdsNoOptional exact report receipt IDs. Defaults to every current report expense.
billToEmailNoOptional client email printed on the invoice. This does not send email.
paymentTermsNoInvoice due terms. Defaults to the client's billing profile or net30.
clientAddressNoOptional billing address printed in the invoice's Bill To section.
invoiceNumberNoOptional custom invoice number. ExpenseBot allocates one when omitted.
advanceAppliedNoVerified client advance to apply against this invoice.
customLineItemsNoOptional fee/service lines. They are taxed but never marked up.
mentionSupportingReceiptsNoWhen true, note on the invoice that supporting receipts are available.

Output Schema

ParametersJSON Schema
NameRequiredDescription
previewYes
successYes
expiresAtYes
preparationIdYes

TDQS

A4.9/5.0
Behavior5/5

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

The annotations already state destructiveHint=false and openWorldHint=false, but the description adds the crucial contract: it reserves the invoice number and preparationId for 30 minutes, returns no persistence, and explicitly lists what it does not create ('no invoice, document, Income row, email, or payment'). This is substantive behavioral transparency 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.

Conciseness5/5

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

The description is dense but every sentence earns its place: purpose, behavior, workflow, and side-effect limitations are all front-loaded and concise. It avoids padding while covering a multi-step, state-reserving operation.

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?

Given the lifecycle complexity (requirements parameters, short-lived reservation, and linking to create_client_invoice), the description gives the exact sequence and side-effect model needed to call it correctly. Output schema already covers return values, so missing return details are not a gap.

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?

Although the schema already documents all 14 parameters thoroughly (100% coverage), the description enriches the meaning of key parameters by explaining canonical report rows, unique invoice number allocation, client ledger verification, advance verification, and preparationId. That moves it beyond a bare schema restatement.

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

Purpose5/5

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

The description clearly states a specific action ('Prepare a client invoice'), the resource ('a saved ExpenseBot report'), and the key constraint ('without creating it'). It also distinguishes itself from the sibling create_client_invoice by defining the prepare-then-confirm workflow, so an agent can select this tool rather than its sibling.

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 gives explicit ordering and conditions: 'Call this first, show the preview to the user, then call create_client_invoice after explicit confirmation.' This directly tells the agent when to use this tool and when the alternative applies, leaving no ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

process_gmail_receiptsAInspect

Process specific Gmail emails as receipts. Pass Gmail message IDs and they'll be converted to PDF, extracted by AI, and added to the user's expense spreadsheet. Max 25 emails per request. Requires Gmail to be connected in ExpenseBot settings.

ParametersJSON Schema
NameRequiredDescriptionDefault
emailIdsYesGmail message IDs to process as receipts
accountEmailNoOptional: which Gmail account to use (for users with multiple linked accounts)

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
errorNo
messageYes
successYes
emailCountNo
processInfoNo
submissionIdNo
processedItemsNo
spreadsheetUrlNo
reviewExpensesUrlNo

TDQS

A3.9/5.0
Behavior4/5

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

Annotations indicate readOnlyHint=false and destructiveHint=false, consistent with the write operation described (adding to a spreadsheet). The description adds valuable behavioral context beyond annotations: the pipeline (converted to PDF, AI-extracted, added to sheet), the 25-email cap, and the prerequisite of a connected Gmail account. It doesn't address idempotency or duplicate handling, but given the moderate annotations, the additional detail justifies a 4.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is two sentences with zero filler. It front-loads the purpose and directly states input, outcome, limit, and prerequisite. Every clause is necessary and informative, making it highly concise and well-structured.

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

Completeness4/5

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

For a tool with a simple 2-parameter schema, an output schema present, and annotations covering safety and world effects, the description covers all essential usage aspects: input type, processing steps, limit, and prerequisite. The output format is likely deferred to the output schema. Minor gaps like error handling or idempotency are not critical given the existing structured data, so a 4 is appropriate.

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 descriptions already cover both parameters (emailIds and accountEmail) with 100% coverage, giving a baseline of 3. The description adds the key constraint 'Max 25 emails per request' for emailIds and the prerequisite of a connected Gmail, enriching parameter usage. It also clarifies accountEmail's optionality implicitly via 'Requires Gmail to be connected'. This extra guidance raises the score.

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 description clearly states the verb 'process', the resource 'specific Gmail emails', and the outcome (converted to PDF, AI-extracted, added to expense spreadsheet). It also specifies the input format (Gmail message IDs) and a hard limit (max 25). While it doesn't explicitly name sibling tools like scan_gmail or scan_gmail_years, the focus on 'specific' emails and message IDs makes the purpose distinct enough. However, an explicit contrast would push it to 5.

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

Usage Guidelines3/5

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

The description provides practical constraints: it requires Gmail to be connected and limits to 25 emails per request. It also implies usage by passing specific message IDs, which differentiates from broader scan tools. However, there is no explicit when-to-use versus scan_gmail/scan_gmail_years, and no 'when not to use' guidance. Thus, it gives context but leaves alternative selection to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

request_accounting_integrationAInspect

Request support for an accounting package only after checking ExpenseBot's reviewed direct and import-file destinations and finding no matching package. Do not use this for a listed or Beta destination, a general compatibility question, or without the exact package name. This is a confirmation-gated write: after the user approves, it creates one deduplicated request bound to the authenticated ExpenseBot account and emails ExpenseBot's internal team. It does not create an integration or make the requested package immediately available. On accepted or previously recorded requests, tell the user ExpenseBot will email them within one week with an update.

ParametersJSON Schema
NameRequiredDescriptionDefault
packageNameYesExact accounting package requested by the user.

Output Schema

ParametersJSON Schema
NameRequiredDescription
messageYes
successYes
duplicateYes
packageNameYes

TDQS

A4.6/5.0
Behavior5/5

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

Beyond the annotations (readOnlyHint=false, destructiveHint=false), the description details the behavior: it is a confirmation-gated write that creates a deduplicated request bound to the account, emails the internal team, does not create an integration, and includes post-acceptance user communication. This fully discloses side effects and limitations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

The description is dense but each sentence serves a purpose: usage condition, exclusions, behavior, and outcome. It is front-loaded with the primary condition. Slightly verbose but well-structured and free of redundancy.

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 confirmation-gated write with an output schema, the description covers the workflow, side effects, and expected user communication. It clearly states what it does not do (create integration) and what happens on accepted requests, leaving no critical gaps for an agent to invoke it correctly.

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

Parameters3/5

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

The schema already describes packageName as 'Exact accounting package requested by the user' with 100% coverage. The description reinforces exactness and the requirement for an exact name but does not add significant new meaning beyond the schema's existing description.

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

Purpose5/5

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

The description clearly states the tool's purpose: request support for an accounting package, with explicit conditions for when it applies. It distinguishes itself from siblings by specifying not to use it for listed/Beta destinations or general compatibility questions, making its scope unambiguous.

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?

The description provides explicit when-to-use and when-not-to-use guidance: only after checking reviewed destinations and finding no match, and explicitly excludes listed/Beta destinations, general compatibility questions, and missing exact package names. It also notes it is confirmation-gated, which is a critical usage constraint.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scan_gmailAInspect

Trigger a background Gmail scan to discover and process receipt emails from the last ~60 days (default). Runs asynchronously — returns immediately, user gets an email summary when done. Like clicking "Find Receipts in Gmail" in the UI. For whole PAST YEARS (e.g. 2023, or 2020-2022) use scan_gmail_years instead; to check a scan's progress use get_scan_status.

ParametersJSON Schema
NameRequiredDescriptionDefault
endDateNoOptional: explicit end date (YYYY-MM-DD)
startDateNoOptional: explicit start date (YYYY-MM-DD) instead of lookbackDays
accountEmailNoOptional: which Gmail account to scan
lookbackDaysNoHow many days back to scan (default 60, max depends on subscription)

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
statusNo
messageNo
successYes
_metadataNo
sessionIdNo
orchestratedNo
submissionIdNo
scanInProgressNo
spreadsheetUrlNo
reviewExpensesUrlNo

TDQS

A4.4/5.0
Behavior4/5

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

The description discloses key behavioral traits not visible in annotations: it runs asynchronously, returns immediately, and sends an email summary when complete. This substantially informs the agent's expectations beyond the readOnlyHint/openWorldHint/destructiveHint fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is concise, front-loaded with the core action and async behavior, and uses three focused sentences. The UI analogy and sibling routing add value without redundancy.

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

Completeness4/5

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

Given the output schema exists and there are no required parameters, the description covers what an agent needs: the action, async nature, follow-up status tool, and alternative for longer ranges. Minor gaps like interaction between startDate and lookbackDays are already addressed in the schema.

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

Parameters3/5

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 four parameters. The description adds a useful default context ('last ~60 days (default)') but does not materially expand parameter meaning beyond what the schema provides.

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

Purpose5/5

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

The description states a specific verb and resource ('Trigger a background Gmail scan to discover and process receipt emails') with a time scope ('last ~60 days'). It clearly differentiates from siblings by naming scan_gmail_years and get_scan_status, which serve different scopes and purposes.

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?

The description explicitly says when to use this tool versus alternatives: use scan_gmail_years for whole past years and get_scan_status to check progress. It also clarifies the asynchronous behavior so an agent knows not to expect results in the immediate response.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scan_gmail_yearsAInspect

Scan one or more COMPLETED PRIOR years of the user's Gmail for receipts (e.g. years:[2023] or years:[2020,2021,2022]). Long-running background job: the first eligible year starts immediately and the rest queue, running one at a time (each full year typically takes a couple of hours; the user can close the app). ALWAYS call first WITHOUT confirmStart to preview which years are eligible, then ask the user to confirm, then call again with confirmStart:true. For receipts from the last ~60 days use scan_gmail instead. To check how a scan is going, use get_scan_status. The current in-progress year cannot be year-scanned.

ParametersJSON Schema
NameRequiredDescriptionDefault
yearsYesCompleted prior years to scan, e.g. [2020, 2021]. Max 10.
accountEmailNoOptional: which connected Gmail inbox to scan (multi-inbox users). Defaults to the primary.
confirmStartNoOmit or false = preview only (no scan starts). Must be exactly true to start scanning.

Output Schema

ParametersJSON Schema
NameRequiredDescription
accountNo
messageYes
previewNo
successYes
eligibleNo
gmailUrlNo
scanningNo
notEligibleNo
queuedYearsNo
startedYearNo
alreadyQueuedNo
alreadyScannedNo
archiveThroughYearNo

TDQS

A4.9/5.0
Behavior5/5

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

The description discloses that this is a long-running background job, that years queue and run one at a time, that each year takes a couple of hours, that the user can close the app, and that the current in-progress year cannot be year-scanned. These behavioral traits go well beyond the annotations (readOnlyHint=false, openWorldHint=true, destructiveHint=false) and give the agent critical operational context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is dense but every sentence earns its place: scope, timing, queueing behavior, call sequence, sibling routing, and an exclusion constraint. It is front-loaded with the core purpose and immediately gives the critical usage pattern. No fluff.

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?

Given the tool's complexity (long-running, queued, multi-step confirmation), the description covers everything an agent needs: what it does, when to use it, how to invoke it safely, what to expect, and how to monitor it. The output schema exists, so return values need no description. This is complete.

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 three parameters. The description adds meaning by explaining the confirmStart preview-then-confirm workflow and the constraint that the current in-progress year cannot be scanned. It doesn't add much about accountEmail, but the schema covers it. Baseline 3 plus the confirmStart workflow context justifies a 4.

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

Purpose5/5

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

The description clearly states the tool scans completed prior years of Gmail for receipts, with a specific verb and resource. It distinguishes itself from scan_gmail by noting the ~60-day boundary, and from get_scan_status for monitoring. The title 'Scan past years of Gmail' reinforces the purpose.

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?

The description provides explicit when-to-use guidance: use for completed prior years, use scan_gmail for last ~60 days, use get_scan_status to check progress. It also gives a precise call sequence: first call without confirmStart to preview, then confirm with the user, then call with confirmStart:true. This is exemplary usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_expensesA
Read-only
Inspect

Search and filter the user's expenses. Returns matching expense rows from their spreadsheet. Filter by category, merchant, date range, amount, or tags. Results are paginated: when hasMore is true, call again with nextCursor and the same filters. Do not split a date range into repeated overlapping searches.

Use the optional query parameter for deterministic natural-language recall over merchant, city/location, Notes (including receipt items, delivery source, payer, and Business purpose), Tag, and category. Each matching result includes matchedFields and a short matchReason so you can explain why it was selected. When several rows plausibly match, a disambiguation list is returned; each option carries the exact expenseId. Structured filters (categories, merchants, dateRange, tags, minAmount, maxAmount) combine with the query using AND semantics. Each result includes expenseId, the exact durable Receipt ID required by update_expense.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagsNoFilter by tags
limitNoResults per page (default 20, maximum 50)
queryNoOptional natural-language recall over the user's existing expense fields (merchant, city/location, Notes including receipt items, delivery source, payer, and Business purpose, Tag, category). Deterministic case-insensitive matching — no embeddings or model classifiers. Combine with structured filters using AND semantics. Examples: 'Tribeca restaurant', 'Bodewell project hardware store', 'client dinner note'.
cursorNoOpaque nextCursor returned by the previous search_expenses page. Reuse the same filters; never construct or edit this value.
dateRangeNoTime period filter. Use exactly one variant — pick the shape that matches the user's phrasing.
maxAmountNoMaximum expense amount
merchantsNoFilter by merchant names (e.g., ['Uber', 'Starbucks'])
minAmountNoMinimum expense amount
categoriesNoFilter by expense categories (e.g., ['Travel', 'Meals'])
hasReceiptNoWhen true, return only expenses with a receipt link. When false, return only expenses without one.
clientEmailNoClient account email. Accountants may use this only for an accepted ExpenseBot client.

Output Schema

ParametersJSON Schema
NameRequiredDescription
countYesRows matched by the filters.
totalYesSum of the matched set in home currency.
hasMoreNo
messageNo
expensesYesMatching expense rows, newest first, paginated.
pageInfoNo
nextCursorNoOpaque signed continuation; reuse with identical filters.
queryAppliedNo
totalMatchedNoPresent for free-text searches.

TDQS

A4.6/5.0
Behavior5/5

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

Well beyond the annotations' read-only/open-world profile: it discloses pagination contract (hasMore/nextCursor), non-determinism is explicitly ruled out, disambiguation-list behavior, matchedFields/matchReason on each row, AND semantics between query and filters, and that expenseId is the exact durable ID required by update_expense.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loads purpose and result semantics, then moves to pagination and query behavior. Two dense paragraphs with essentially no filler, though the return-field recap (matchedFields, expenseId) is more verbose than strictly needed given an output schema exists.

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 an 11-parameter, zero-required search tool with an output schema, the description covers everything an agent needs to invoke it correctly: filter combination rules, pagination loop, query matching scope, and disambiguation handling. Nothing material is missing.

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 baseline is 3, but the description adds real value: it defines what the free-text query matcher covers (merchant, city/location, Notes, Tag, category), asserts deterministic case-insensitive matching, and specifies that query combines with structured filters via AND. The cursor semantics are also reinforced beyond the schema.

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

Purpose5/5

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

States a specific verb+resource ('Search and filter the user's expenses') and immediately scopes the result set to the user's spreadsheet rows. An agent can distinguish this from get_expense_by_id, search, and search_knowledge 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.

Usage Guidelines4/5

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

Gives concrete operational guidance: paginate via nextCursor with the same filters when hasMore is true, don't split a date range into overlapping searches, use query for natural-language recall, and combine it with structured filters via AND. It stops short of naming sibling alternatives (e.g., get_expense_by_id for a known ID, search_knowledge for docs), so no explicit when-not routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_knowledgeA
Read-only
Inspect

Search ExpenseBot's complete product and help knowledge base. Use this before guessing when the user asks how a feature works, what ExpenseBot supports, or needs setup, billing, Gmail, scan, report, workflow, or troubleshooting instructions. Set fullAnswer:true on the first call when the user asks for complete, detailed, or step-by-step instructions. Use the default excerpt mode for discovery and quick answers; if an excerpt is truncated, call again with fullAnswer:true before presenting complete instructions. Each result has a stable id.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoNumber of ranked matching articles to return (1-10).
queryYesSearch terms
fullAnswerNoSet true on the first call for complete, detailed, or step-by-step instructions. Leave false for discovery or quick-answer excerpts.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultsYes
successYes

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, so the read-only behavior is known. The description adds value beyond annotations by explaining the excerpt-vs-fullAnswer behavior, the truncation retry guidance, and that results are stable. These are meaningful behavioral details, though it doesn't touch on any edge cases or limitations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is concise, front-loaded with the primary purpose, and every sentence earns its place. It covers what, when, and how to advance detail level without repetitive or filler content. The distinction between 'discovery'' and 'complete instructions' is compact and clear.

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?

With a read-only annotation, an output schema, and 100% schema parameter descriptions, the structure already carries a lot. The description completes the remaining decisions: when to use it, how to get full answers, when to retry, and the stability property. No significant gap remains for the agent.

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% and the schema already documents query, limit, and fullAnswer. The description adds semantic value by contextualizing fullAnswer: set true on the first call when complete/detailed instructions are needed, and use excerpt mode for discovery. This gives agents a decision rule beyond the schema's bare default of false.

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

Purpose5/5

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

The description names a specific action and resource: 'Search ExpenseBot's complete product and help knowledge base.' It enumerates typical use categories (setup, billing, Gmail, scan, report, workflow, troubleshooting), which makes the tool's purpose unambiguous and clearly sets it apart from expense-search siblings like 'search_expenses' or the more generic 'search'.

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

Usage Guidelines4/5

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

The description explicitly states when to use the tool: 'Use this before guessing when the user asks how a feature works, what ExpenseBot supports, or needs setup, billing, Gmail, scan, report, workflow, or troubleshooting instructions.' It also distinguishes between excerpt mode and fullAnswer mode with a retry rule. However, it does not explicitly say when NOT to use it or name sibling alternatives, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

send_report_to_accountingAInspect

Review and then post an ExpenseBot report to the owner's accounting destination. STEP 1: call with provider, reportId, and optional mode/mappings, without confirm. The server reads the live report and destination, applies Omit/Personal/split/date/currency rules, checks Zoho bank-feed matches, and returns the complete proposal plus proposalId. NOTHING is posted in step 1. Show the complete proposal and ask for approval. STEP 2: call with only provider, proposalId, and confirm:true. The server posts only the frozen, account-bound proposal, revalidates live state, and rejects changed reports or mappings. Never add mapping fields to the confirmation call. Owner accounts only; acting for a client is not supported. Agent-driven posting is currently available only for Zoho Books (zoho_books); QuickBooks Online, Xero, Wave, and FreeAgent are intentionally not accepted by this tool yet because their previews cannot freeze/revalidate the canonical provider plan that shareReport executes. Use those providers' web flows until they satisfy the full push contract.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNoStep 1 only (Zoho Books): automatic document treatment, paid expenses, or bills.
confirmNoStep 2 only: true posts the exact staged proposal. No other options may change.
providerYesAccounting destination provider. Agent-driven posting is currently available only for Zoho Books.
reportIdNoStep 1 only: ExpenseBot report ID owned by the signed-in account.
proposalIdNoStep 2 only: proposalId returned by the read-only preview.
forceRepushNoStep 1 only: explicitly include disclosed uncertain prior outcomes in the frozen proposal.
taxMappingsNoStep 1 only: observed tax-rate string to live destination tax ID overrides.
accountMappingsNoStep 1 only: ExpenseBot category to live destination account ID overrides.
reportingTagMappingsNoStep 1 only: ExpenseBot tag to live destination reporting-tag option.

Output Schema

ParametersJSON Schema
NameRequiredDescription
phaseYes
unitsNo
countsNo
createdNo
successYes
providerYes
reportIdNo
expiresAtNo
proposalIdYes
finalAnswerNo
organizationNo
matchedExistingNo
omittedExpensesNo
idempotentReplayNo
responseGuidanceNo
confirmationContractNo
confirmationQuestionNo
confirmationRequiredNo
skippedAlreadyPushedNo
reconciliationRequiredNo

TDQS

A4.9/5.0
Behavior5/5

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

Annotations indicate readOnlyHint=false and destructiveHint=false, but the description adds critical behavioral context: step 1 is read-only (nothing posted), step 2 posts only the frozen proposal after revalidation. It also discloses the provider limitation and the fact that client-account posting is unsupported. This goes far beyond what annotations provide, making the tool's behavior fully transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is lengthy but well-organized, front-loading the purpose and then clearly delineating steps. Every sentence adds essential information—step details, provider limitations, and warnings. There is no fluff; the length is justified by the tool's complexity. The structure with step labels enhances readability.

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?

Given the tool's complexity (9 parameters, two-step process, nested objects) and the presence of an output schema, the description covers all necessary aspects: the workflow, step-specific parameters, provider restrictions, and safety checks. It tells the agent exactly what to do and what not to do, making it complete for correct invocation.

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?

The schema has 100% description coverage, so the baseline is 3. The description adds step-specific associations (e.g., mode is step 1 only, confirm is step 2 only) and explains the purpose of mappings and forceRepush. This goes beyond the schema's basic descriptions, though the schema already covers the parameters well. The added workflow context justifies a 4.

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

Purpose5/5

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

The description clearly states the tool's purpose: to review and then post an ExpenseBot report to the owner's accounting destination. It specifies the two-step process and distinguishes it from siblings like share_report by emphasizing the posting action. The verb 'post' and resource 'report' are specific, and the mention of 'review' and 'approval' clarifies the workflow.

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?

The description provides explicit step-by-step usage instructions: step 1 without confirm, step 2 with confirm and only proposalId. It explicitly states which providers are supported (Zoho Books only) and directs users to use web flows for others. It also warns against adding mapping fields to the confirmation call, leaving no ambiguity about when and how to use the tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

share_reportA
Destructive
Inspect

Share one existing expense report after confirmation. Default to reviewer: they can open and edit that report's Google Sheet, inspect receipts, comment, approve, or request changes, but cannot act for the owner or use accounting integrations. Use accountant only when the user explicitly asks for ongoing accounting access; it creates the established accountant relationship and broader report-management workflow.

ParametersJSON Schema
NameRequiredDescriptionDefault
reportIdYes
recipientsYes
recipientRoleNoUse reviewer for report-only approval. Use accountant only for explicit ongoing accountant access.reviewer

Output Schema

ParametersJSON Schema
NameRequiredDescription
messageYes
successYes
sharedWithYes
finalAnswerNo
recipientRoleYes
responseGuidanceNo

TDQS

A3.8/5.0
Behavior4/5

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

Annotations already indicate mutating and destructive behavior, but the description adds valuable context: it specifies what the reviewer can and cannot do (open/edit the Google Sheet, comment, approve, but not act for owner or use accounting integrations) and explains that the accountant role creates a broader relationship. This goes beyond the annotations without contradicting them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

The description is two sentences and front-loaded with the core action. It packs role behavior into a compact but readable form. No unnecessary filler, though it could be slightly more concise without losing nuance.

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

Completeness3/5

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

The description covers the main decision point (role selection) thoroughly, and an output schema exists so return values are not needed. However, it does not explicitly clarify the semantics of reportId and recipients beyond what the schema shows, and it leaves questions like whether sharing is reversible or what happens on re-share unanswered. Given the tool's mutation and destructive annotation, a bit more detail would be helpful.

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

Parameters3/5

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

Schema coverage is only 33% (only recipientRole has a description). The description compensates well for recipientRole by explaining when to use each value, but it does not explicitly describe reportId or recipients beyond the context of 'sharing a report' and the email format implied by the schema. This is a partial compensation.

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 description clearly states the tool shares an existing expense report, which is a specific verb and resource. It differentiates the reviewer and accountant roles, though it does not explicitly contrast with sibling tools like send_report_to_accounting. The purpose is unambiguous and useful for an agent.

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

Usage Guidelines4/5

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

The description gives explicit guidance on when to use each role: default to reviewer unless the user explicitly requests ongoing accountant access. This helps the agent decide between recipientRole values, but it does not mention when to prefer this tool over alternatives like send_report_to_accounting, which is a minor gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

submit_receiptAInspect

Submit a photo or PDF of a receipt for processing. Covers requests phrased as 'log this', 'log this receipt', 'save this receipt', 'expense this', or 'add this to my expenses', including when the user simply shares a photo of a receipt or invoice. The receipt image is validated, uploaded to cloud storage, and processed by AI to extract vendor, amount, date, tax, and category. The expense appears in the user's spreadsheet in about 1-3 minutes, and longer for PDFs or large batches. Handles images and PDFs, mixed together in one batch.

TO SEND FILES (preferred, and required for PDFs): call this tool with filesToUpload listing every file the user gave you. It returns one signed upload URL per file. Upload them ONE AT A TIME with an HTTP PUT, telling the user which file you just finished and how many remain, then call this tool ONCE with uploadRefs for all of them — that processes the whole set as a single batch, like the ExpenseBot web app. Do not call this tool once per file.

Use the photo parameter for one image or PDF attached in ChatGPT. MCP clients that cannot supply file references may use photoBase64 for one small image; use the upload flow for large files or batches.

Optional note and tag values use the same receipt metadata path as ExpenseBot's camera, file uploader, and forwarded-email intake. The note is stored in the Notes column (L); the tag is stored in the Tag column (K). Batch defaults apply to every file, and each uploadRefs item may override either value for that file.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagNoBatch-level project or client tag applied to every expense row. Individual files may override it. Stored in the Tag column (K) through the same path as the web receipt uploader.
noteNoBatch-level context note applied to every file in the upload. Individual files may override this with their own note. Stored through the existing receipt-note pipeline in the Notes column (L).
photoNoSingle attached receipt image or PDF supplied by ChatGPT. For multiple files or large files, use filesToUpload and uploadRefs instead.
filenameNoOptional filename (e.g., 'lunch_receipt.jpg')
mimeTypeNoMIME type of the file (default: image/jpeg)
uploadRefNoSingle-file shorthand for uploadRefs. Use uploadRefs when there is more than one file.
uploadRefsNoSubmit previously uploaded files as ONE batch. Include every uploadRef from the filesToUpload step. If any file fails validation the whole batch is rejected and nothing is processed.
photoBase64NoLegacy fallback for MCP clients that send one small image as complete base64 data. ChatGPT should use photo or uploadRefs instead.
filesToUploadNoRequest upload URLs for one or more files. Include EVERY file the user provided in a single call. Returns one signedUrl + uploadRef per file; upload each, then call this tool again with uploadRefs.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNo
filesNo
statusNo
messageNo
successYes
fileCountNo
statusCodeNo
submissionIdNo
spreadsheetUrlNo
reviewExpensesUrlNo

TDQS

A4.8/5.0
Behavior5/5

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

Goes well beyond the annotations (readOnlyHint=false, openWorldHint=true): it discloses validation, cloud upload, AI processing, the 1-3 minute latency (longer for PDFs/batches), the one-file-at-a-time PUT requirement with progress reporting, and the all-or-nothing batch rejection behavior. This is rich operational context an agent needs before invoking.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loaded with purpose and the routing rules, then a clearly delineated 'TO SEND FILES' section. It is long, but the complexity (9 params, multi-step upload) justifies most of it; there is mild redundancy where note/tag storage paths are restated relative to the schema.

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 multi-modal, multi-call ingestion tool, the description covers the full workflow, error semantics, latency expectations, and input-mode selection. Since an output schema exists, it correctly avoids explaining return values and instead spends its budget on the invocation contract.

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 baseline is 3, but the description adds real meaning the schema alone lacks: the two-call contract between filesToUpload and uploadRefs, the batch-default-with-per-file-override semantics for note/tag, and the destination columns (K for Tag, L for Notes). It stops short of 5 only because the schema already carries much of the field-level detail.

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

Purpose5/5

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

States a specific verb+resource ('submit a photo or PDF of a receipt for processing') and enumerates the natural-language phrasings that map to it ('log this', 'expense this'). It also clarifies downstream behavior (AI extraction of vendor, amount, date, tax, category), so an agent knows exactly what this tool accomplishes versus siblings like parse_expense or process_gmail_receipts.

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?

Explicitly routes between the available input modes: filesToUpload+uploadRefs as the preferred path and required for PDFs, photo for a single ChatGPT attachment, photoBase64 only as a legacy fallback, and the upload flow for batches. It even states the sequencing rule ('call this tool ONCE with uploadRefs... do not call once per file'), 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.

trace_documentA
Read-only
Inspect

Trace what happened to a specific receipt, invoice, forwarded email, Gmail receipt, or PDF in ExpenseBot processing. Use when the user asks where a document went, why it did not show up, whether it was skipped, routed to income, deduped, or removed as a duplicate. NOT for the status, progress, or completion of a Gmail scan job (running, queued, finished, or stuck) — a scan is not a document; use get_scan_status for that. Returns a read-only timeline from the user's lineage audit trail.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNoHow many days back to search.
limitNoMaximum lineage events to return.
queryNoMerchant, sender, subject, invoice number, or phrase to search for, e.g. 'Blue Rocket', 'Driversnote', 'Uber receipt'.
outcomeNoOptional processing outcome filter.all
threadIdNoGmail thread ID if known.
messageIdNoGmail message ID if known.
receiptIdNoExpenseBot receipt/invoice ID if known, e.g. IR-123, RO-abc, RR-456.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYes
typeYes
messageYes
successYes

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already establish readOnlyHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is pre-declared. The description adds that the result is a timeline drawn from the user's lineage audit trail, which tells the agent the shape and provenance of the data. It stops short of noting pagination/limit interaction or coverage limits of the audit trail.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Front-loaded with purpose, then usage triggers, then the exclusion — a good ordering with no filler. The outcome vocabulary partially restates the schema enum rather than adding new information, which keeps it from being maximally tight.

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?

With an output schema present, return values need not be described, and annotations carry the safety profile. Purpose, trigger conditions, the sibling exclusion, and the audit-trail provenance together give an agent everything needed to call this correctly among a large sibling set.

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 baseline is 3; the description earns an extra point by enumerating the same outcome vocabulary the 'outcome' enum exposes (skipped, income_routed, deduped, removed_as_duplicate), effectively explaining that filter's intended values. It does not clarify how query or the ID parameters interact when several are supplied, so it is not a 5.

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

Purpose5/5

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

States a specific verb ('trace') plus a concrete resource set (receipt, invoice, forwarded email, Gmail receipt, PDF) scoped to ExpenseBot processing. It also names the nearest sibling it is not, so an agent can separate this from scan-status tools without reading either schema.

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?

Explicitly enumerates the triggering user questions ('where a document went, why it did not show up, whether it was skipped, routed to income, deduped, or removed as a duplicate') and gives a hard exclusion with the correct alternative (NOT scan job status/progress — use get_scan_status). 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_expenseA
Destructive
Inspect

Update a single existing expense row's category, tag, merchant, or business purpose (e.g. 'recategorize expense X to Meals' or 'tag it Client – Acme'). Identify it only by the exact expenseId returned by search_expenses (the full Receipt ID from Column Q). Never use a date, displayed number, or sheet row number as expenseId. Two-phase operation: calls with confirm omitted or false return a preview of the current→proposed change and make no change. After user confirmation, a call with confirm:true applies the proposed change. If the expense is not found, search once for a current exact expenseId; never retry the same stale ID. If a business-purpose update exceeds the Notes capacity, stop and send the user to https://www.expensebot.ai/review-expenses?source=mcp instead of retrying. Only category/tag/merchant/businessPurpose are editable — amounts, dates, and notes are not editable via the assistant. Does not create or delete rows.

ParametersJSON Schema
NameRequiredDescriptionDefault
fieldsYesPartial update object. Include one or more of these optional keys; omit every field that should remain unchanged.
confirmNoOmit or false = preview only (no write). Must be exactly true to apply the change.
expenseIdYesExact full Receipt ID from Column Q, returned as expenseId by search_expenses.

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataYes
messageYes
successYes
finalAnswerNo
responseGuidanceNo

TDQS

A4.8/5.0
Behavior5/5

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

Annotations declare destructiveHint=true and readOnlyHint=false, and the description goes well beyond them: the preview-then-confirm write protocol, the exact set of non-editable fields (amounts, dates, notes), the Notes-capacity failure path, and the anti-retry rule. An agent knows precisely what gets mutated and when.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

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

Purpose and examples are front-loaded in the first sentence, followed by identification rules, then the confirm workflow. It is dense and long, but nearly every sentence carries an operational rule; only the inline examples ('recategorize expense X to Meals') are marginally expendable.

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?

An output schema exists, so return previews need not be described. Combined with annotations, the nested 'fields' object, and the confirm flag, the description covers identification, mutation scope, failure handling, and safety. Nothing an agent needs in order 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.

Parameters4/5

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 meaning: expenseId must be the exact full Receipt ID from Column Q and must NOT be a date, displayed number, or sheet row number. The editable key set for 'fields' is also clarified in prose. Minor gap: no format/example guidance for the string values themselves.

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

Purpose5/5

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

The description states a specific verb and resource ('Update a single existing expense row') and enumerates exactly what is editable (category, tag, merchant, business purpose). It also explicitly disclaims adjacent operations ('Does not create or delete rows'), which separates it from siblings like add_cash_expense, search_expenses, and correct_expenses.

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 names the alternative for finding the row ('the exact expenseId returned by search_expenses'), gives a fallback when the ID is stale ('search once ... never retry the same stale ID'), and routes overflow cases to a specific URL. The two-phase preview/confirm workflow defines the conditions for each call.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

whatif_affordA
Read-only
Inspect

Can I afford $X/month? Recomputes the user's Safe Monthly Draw (how much they can safely pay themselves) with an added recurring monthly cost, and returns a yes/tight/no verdict plus the before/after numbers. Use for questions like 'can I afford a $500/mo hire' or 'what if I add a $200/mo software subscription'. Requires at least 3 months of income history — otherwise returns insufficient_data rather than a guess.

ParametersJSON Schema
NameRequiredDescriptionDefault
deltaMonthlyYesThe new recurring monthly cost being considered, in the user's home currency

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNoThe full what-if scenario: assumptions, evidence, and computed outcome.
messageNoScenario narrative; may be absent if the scenario carried no template or evidence.
successYes

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already mark readOnlyHint=true and destructiveHint=false, so the read-only nature is covered. The description adds noteworthy behavioral detail beyond annotations: the requirement of at least 3 months of income history and the insufficient_data non-guess response, which helps an agent anticipate edge conditions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

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

The description is three sentences: a functional summary with output types, a practical use-case framing, and a key prerequisite/edge behavior. Every sentence has value, the most important facts are front-loaded, and nothing is redundant or verbose.

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?

Given the single param, full schema coverage, non-destructive readOnly annotations, and the presence of an output schema, this description covers the tool's purpose, prerequisites, behavior, and result shape. No significant information an agent needs to select or invoke it is missing.

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

Parameters3/5

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

The schema covers the single parameter completely with a clear description, including minimum value and currency context. The tool description adds no new parameter-specific information since 100% of the schema is already described, so the baseline 3 is appropriate.

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?

Description clearly states it recomputes the user's Safe Monthly Draw with an added monthly cost and returns a yes/tight/no verdict plus before/after numbers. It does not explicitly name or differentiate the whatif_client and whatif_tax_setaside siblings, but the affordability context is specific enough.

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

Usage Guidelines4/5

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

The description provides concrete usage examples like 'can I afford a $500/mo hire' and mentions a precondition (3 months of income history) including the failure mode (returning insufficient_data). It does not give an explicit when-not or alternative tool recommendation among the siblings, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

whatif_clientA
Read-only
Inspect

What if a client pays late or leaves? mode='late30' shifts that client's OPEN invoice amounts out of the near-term expectation (they still owe it, it's just not landing this month). mode='gone' removes that client's trailing monthly income contribution and recomputes Safe Draw against the reduced baseline. Use for questions like 'what if Acme Corp pays 30 days late' or 'what happens if I lose my biggest client'. Client identity is matched against the Income tab's tag/source/description fields — best effort, not a guaranteed match.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeYes'late30' = shift open invoices 30 days late; 'gone' = client stops paying entirely
clientNameYesThe client's name as it appears on invoices or income rows

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNoThe full what-if scenario: assumptions, evidence, and computed outcome.
messageNoScenario narrative; may be absent if the scenario carried no template or evidence.
successYes

TDQS

A4.3/5.0
Behavior4/5

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

Despite readOnlyHint=true and destructiveHint=false, the description adds valuable behavioral context: 'late30' shifts open invoices without removing the debt, while 'gone' removes trailing monthly income and recomputes Safe Draw. The best-effort matching caveat against Income tab fields is an important disclosure 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.

Conciseness4/5

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

Three sentences deliver the entire picture: purpose, mode semantics, usage examples, and caveat. It is front-loaded with the 'what if' framing and has little wasted prose—just slightly redundant with the opening question and the example phrasing.

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

Completeness4/5

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

For a two-parameter, read-only what-if tool with an output schema, the description covers the core effect, usage scenarios, and matching caveat. It doesn't explicitly describe the return shape, but with an output schema present that is not required. Minor omission: it doesn't explain what 'Safe Draw' means, but it's a domain term utilized for context.

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 baseline is 3, but the description enriches both parameters. It clarifies the real-world meaning of 'late30' and 'gone' and adds that clientName matching is best-effort, which is not captured in the schema's field description.

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

Purpose5/5

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

The description states a specific scenario ('client pays late or leaves') and explains the effect of each mode, making the tool's purpose unambiguous. The two example questions ('what if Acme Corp pays 30 days late', 'what happens if I lose my biggest client') clearly distinguish this tool from the sibling whatif_afford and whatif_tax_setaside tools.

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

Usage Guidelines4/5

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

The description explicitly says to use it for questions like 'what if a client pays 30 days late' or 'what if I lose my biggest client', giving the agent a clear when-to-use signal. It doesn't name alternatives or state when not to use it, but the scenario-based guidance is strong enough to route correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

whatif_tax_setasideA
Read-only
Inspect

What should I set aside for taxes? Surfaces the same monthly tax set-aside estimate already computed for Safe Monthly Draw — wiring, not new math. Flat-rate estimate (default 30%) against trailing income minus recurring + variable spend. Use for 'how much should I set aside for taxes this month'.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
dataNoThe full what-if scenario: assumptions, evidence, and computed outcome.
messageNoScenario narrative; may be absent if the scenario carried no template or evidence.
successYes

TDQS

A3.9/5.0
Behavior4/5

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

Annotations already declare the tool safe and read-only. The description adds meaningful behavior beyond that: it mentions the estimate is not newly computed but reuses the Safe Monthly Draw value ('wiring, not new math') and reveals the flat 30% default rate applied to income minus spend. These details help the agent understand derivation and variability.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

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

The description is compact and front-loaded, but it repeats the key idea: the opening 'What should I set aside for taxes?' and the closing 'Use for...' convey the same usage scenario. This redundancy makes it slightly less concise than it could be.

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

Completeness4/5

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

For a zero-parameter estimation tool with a schema and existing output schema, the description is largely complete. It gives the calculation basis and the invocation scenario. The only slight omission is that the default rate's adjustability is implied but not stated; for the estimation accuracy for context this is still sufficient.

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?

With zero parameters and 100% schema coverage, the baseline is 4. The description still adds value by mentioning the conceptual formula inputs (trailing income minus recurring + variable spend), which helps the agent understand what data the estimate is based on.

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 description clearly says the tool surfaces the monthly tax set-aside estimate and frames it as answering 'What should I set aside for taxes?'. It doesn't explicitly name sibling what-if tools, but the tax-specific focus and the reference to Safe Monthly Draw make it distinguishable.

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

Usage Guidelines4/5

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

The description explicitly states when to use it: for the question 'how much should I set aside for taxes this month'. It doesn't mention exclusions or alternatives, but the when-to-use is clear and direct.

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. 2 tool updates
    • Changedadd_income_from_csv15 fields changed
      • changedInput schema / properties / bulkNote / description
        Previous value: -"Step 2 only: one note (max 120 chars) prepended to every confirmed row's Notes, same as the app's 'Add note to all entries'."New value: +"Step 2 note prepended to confirmed rows (max 120 characters)."
      • changedInput schema / properties / confirm / description
        Previous value: -"Step 2 only: true writes the staged rows. Omit it (with previewId absent) to parse and preview without writing."New value: +"Step 2: true writes the staged selection only after user approval."
      • changedInput schema / properties / csvContent / description
        Previous value: -"Attached CSV/TSV/text file supplied by ChatGPT for step 1. The server downloads and decodes the attachment securely."New value: +"Step 1 ChatGPT attachment; securely downloaded and decoded."
      • changedInput schema / properties / csvText / description
        Previous value: -"Legacy fallback for MCP clients that send raw CSV/TSV/text content inline. ChatGPT should use csvContent."New value: +"Step 1 inline CSV/TSV/text fallback; ChatGPT should use csvContent."
      • changedInput schema / properties / keepBothIndexes / description
        Previous value: -"Step 2 only: preview indexes of duplicate-flagged rows the user explicitly wants to keep anyway (the app's 'Keep Both')."New value: +"Step 2 duplicate indexes the user explicitly approved keeping."
      • changedInput schema / properties / paymentMethod / description
        Previous value: -"Fallback payment rail for rows where the parser found none (Cash, Check, Bank transfer, Wallet app, Credit/Debit card, Payment processor, or Other)."New value: +"Fallback payment rail only when absent from the parsed row."
      • changedInput schema / properties / previewId / description
        Previous value: -"Step 2 only: the previewId returned by the step-1 call."New value: +"Step 2 ID returned by the unexpired preview."
      • changedInput schema / properties / rowEdits / description
        Previous value: -"Step 2 only: user-approved corrections, at most 50 rows. Fields: date (ISO YYYY-MM-DD), source, amount, currency, category, paymentMethod, description, notes, reference, fees, taxCollected, tag."New value: +"Step 2 approved edits; at most 50 selected preview rows."
      • changedInput schema / properties / rowEdits / items / properties / fields / additionalProperties
        Previous value: -trueNew value: +false
      • changedInput schema / properties / rowEdits / items / properties / fields / description
        Previous value: -"Only the user-approved income fields to replace on this staged row: date, source, amount, currency, category, paymentMethod, description, notes, reference, fees, taxCollected, or tag."New value: +"Only approved replacements on this staged row."
      • addedInput schema / properties / rowEdits / items / properties / fields / properties
        Added value: +{
        +  "amount": {
        +    "description": "Non-zero income amount.",
        +    "type": [
        +      "number",
        +      "string",
        +      "boolean"
        +    ]
        +  },
        +  "category": {
        +    "description": "Income category.",
        +    "type": [
        +      "string",
        +      "number",
        +      "boolean",
        +      "null"
        +    ]
        +  },
        +  "currency": {
        +    "description": "Currency code.",
        +    "type": [
        +      "string",
        +      "number",
        +      "boolean",
        +      "null"
        +    ]
        +  },
        +  "date": {
        +    "description": "ISO YYYY-MM-DD calendar date.",
        +    "type": "string"
        +  },
        +  "description": {
        +    "description": "Income description.",
        +    "type": [
        +      "string",
        +      "number",
        +      "boolean",
        +      "null"
        +    ]
        +  },
        +  "fees": {
        +    "description": "Non-negative platform fees.",
        +    "type": [
        +      "number",
        +      "string",
        +      "boolean",
        +      "null"
        +    ]
        +  },
        +  "notes": {
        +    "description": "Income notes.",
        +    "type": [
        +      "string",
        +      "number",
        +      "boolean",
        +      "null"
        +    ]
        +  },
        +  "paymentMethod": {
        +    "description": "Payment rail.",
        +    "type": [
        +      "string",
        +      "number",
        +      "boolean",
        +      "null"
        +    ]
        +  },
        +  "reference": {
        +    "description": "Income reference.",
        +    "type": [
        +      "string",
        +      "number",
        +      "boolean",
        +      "null"
        +    ]
        +  },
        +  "source": {
        +    "description": "Income source label.",
        +    "type": [
        +      "string",
        +      "number",
        +      "boolean"
        +    ]
        +  },
        +  "tag": {
        +    "description": "Client/project tag.",
        +    "type": [
        +      "string",
        +      "number",
        +      "boolean",
        +      "null"
        +    ]
        +  },
        +  "taxCollected": {
        +    "description": "Non-negative collected tax.",
        +    "type": [
        +      "number",
        +      "string",
        +      "boolean",
        +      "null"
        +    ]
        +  }
        +}
      • addedInput schema / properties / rowEdits / items / properties / index / minimum
        Added value: +0
      • addedInput schema / properties / rowEdits / maxItems
        Added value: +50
      • changedInput schema / properties / selectedIndexes / description
        Previous value: -"Step 2 only: preview row indexes to write. Omit to write all staged rows."New value: +"Step 2 preview row indexes to write; omit for all staged rows."
      • changedInput schema / properties / tag / description
        Previous value: -"Optional client/project tag. In step 1 it pre-fills the staged rows; in step 2 it applies to the confirmed selection."New value: +"Optional client/project tag for staged or confirmed rows."
    • Changedcheck_tax_deductibility3 fields changed
      • addedInput schema / properties / category / description
        Added value: +"Optional expense category to match the reference rule."
      • addedInput schema / properties / merchant / description
        Added value: +"Optional merchant name only when relevant to the expense type."
      • changedInput schema / properties / query / description
        Previous value: -"Deductibility question (e.g., 'is Uber tax deductible', 'home office write-off rules')"New value: +"Brief expense-type deduction question, e.g. 'are business meals deductible?'; no account numbers or unrelated personal context."
  2. 2 tool updates
    • Changedcheck_tax_deductibility1 field changed
      • addedOutput schema / properties / data / properties
        Added value: +{
        +  "availability": {
        +    "enum": [
        +      "unknown"
        +    ],
        +    "type": "string"
        +  },
        +  "category": {
        +    "type": "string"
        +  },
        +  "country": {
        +    "type": "string"
        +  },
        +  "deductiblePercent": {
        +    "maximum": 100,
        +    "minimum": 0,
        +    "type": "number"
        +  },
        +  "reviewRequired": {
        +    "type": "boolean"
        +  }
        +}
    • Changedget_expense_by_id5 fields changed
      • removedOutput schema / properties / expense
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "The expense record.",
        -  "type": "object"
        -}
      • addedOutput schema / properties / headers
        Added value: +{
        +  "items": {},
        +  "type": "array"
        +}
      • addedOutput schema / properties / labeled
        Added value: +{
        +  "additionalProperties": true,
        +  "type": "object"
        +}
      • addedOutput schema / properties / sheetName
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / values
        Added value: +{
        +  "items": {},
        +  "type": "array"
        +}
  3. 2 tool updates
    • Changedcheck_compliance3 fields changed
      • addedInput schema / properties / analysisRequested
        Added value: +{
        +  "description": "Set true only when the user explicitly asked for this report's compliance analysis. Do not ask for or copy their full message, and do not infer consent from report creation, sharing, or billing.",
        +  "enum": [
        +    true
        +  ],
        +  "type": "boolean"
        +}
      • removedInput schema / properties / userRequest
        Removed value: -{
        -  "description": "The user's exact request explicitly asking for compliance, policy, audit, substantiation, or business-purpose analysis. Do not paraphrase or invent intent.",
        -  "type": "string"
        -}
      • changedInput schema / required
        Previous value: -[
        -  "reportId",
        -  "userRequest"
        -]New value: +[
        +  "reportId",
        +  "analysisRequested"
        +]
    • Changedget_report_details3 fields changed
      • addedInput schema / properties / analysisRequested
        Added value: +{
        +  "description": "Set true only when the user explicitly asked to inspect this report. Do not ask for or copy their full message, and do not infer consent from report creation, sharing, or billing.",
        +  "enum": [
        +    true
        +  ],
        +  "type": "boolean"
        +}
      • removedInput schema / properties / userRequest
        Removed value: -{
        -  "description": "The user's exact request asking to inspect this report. Do not paraphrase or invent diagnostic intent.",
        -  "type": "string"
        -}
      • changedInput schema / required
        Previous value: -[
        -  "reportId",
        -  "userRequest"
        -]New value: +[
        +  "reportId",
        +  "analysisRequested"
        +]
  4. 1 tool update
    • Addedlist_client_close_readiness
  5. 1 tool update
    • Changedget_monthly_books_review1 field changed
      • addedOutput schema / properties / closeReadiness
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "actionNeeded": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "checks": {
        +      "additionalProperties": {
        +        "enum": [
        +          "ready",
        +          "needs_action",
        +          "blocked",
        +          "unknown",
        +          "not_applicable"
        +        ],
        +        "type": "string"
        +      },
        +      "type": "object"
        +    },
        +    "status": {
        +      "enum": [
        +        "ready",
        +        "needs_action",
        +        "blocked",
        +        "unknown"
        +      ],
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
  6. 4 tool updates
    • Changedadd_income2 fields changed
      • changedInput schema / properties / date / description
        Previous value: -"Income date (YYYY-MM-DD). Defaults to today."New value: +"Income date (YYYY-MM-DD). Required. Use the date the user gives; for \"today\" send today's calendar date in their timezone. If no date was given, ask."
      • changedInput schema / required
        Previous value: -[
        -  "amount",
        -  "source",
        -  "paymentMethod"
        -]New value: +[
        +  "amount",
        +  "source",
        +  "paymentMethod",
        +  "date"
        +]
    • Changedadd_mileage_entry2 fields changed
      • changedInput schema / properties / date / description
        Previous value: -"Trip date (YYYY-MM-DD). Defaults to today."New value: +"Trip date (YYYY-MM-DD). Required. Use the date the user gives; for \"today\" send today's calendar date in their timezone. If no date was given, ask."
      • changedInput schema / required
        Previous value: -[
        -  "distance",
        -  "purpose"
        -]New value: +[
        +  "distance",
        +  "purpose",
        +  "date"
        +]
    • Changedfetch2 fields changed
      • changedInput schema / properties / id / description
        Previous value: -"Item ID from a previous `search` or `list_reports` call. Format: 'expense:<row>' (integer row number) or 'report:<reportId>' (alphanumeric Firestore doc id, ~20 chars)."New value: +"Item ID from a previous `search` or `list_reports` call. Format: 'expense:<URL-encoded Column-Q expenseId>', 'report:<reportId>' (alphanumeric Firestore doc id, ~20 chars), or 'kb:<entryId>' (knowledge-base entry id)."
      • changedInput schema / properties / id / pattern
        Previous value: -"^(expense:\\d+|report:[A-Za-z0-9_-]+)$"New value: +"^(expense:.+|report:[A-Za-z0-9_-]+|kb:.+)$"
    • Changedget_last_receipt_result4 fields changed
      • addedOutput schema / properties / counts / properties / heldForReview
        Added value: +{
        +  "description": "Files held for review and not added. Present only when at least one.",
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / receipts / description
        Added value: +"Processed receipt summaries. Held files are not listed here; see data.outcomes."
      • changedOutput schema / properties / verdict / description
        Previous value: -"Terminal branch only."New value: +"Terminal branch only. held_for_review: every file was held for review, so none was added. mixed also covers any batch that includes held files."
      • changedOutput schema / properties / verdict / enum
        Previous value: -[
        -  "added",
        -  "duplicate",
        -  "skipped",
        -  "errored",
        -  "mixed",
        -  "unknown"
        -]New value: +[
        +  "added",
        +  "duplicate",
        +  "skipped",
        +  "errored",
        +  "mixed",
        +  "unknown",
        +  "held_for_review"
        +]
  7. 23 tool updates
    • Changedadd_cash_expense1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "expense": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "category": {
        +          "type": "string"
        +        },
        +        "currency": {
        +          "type": "string"
        +        },
        +        "date": {
        +          "format": "date",
        +          "type": "string"
        +        },
        +        "isClientAdvance": {
        +          "type": "boolean"
        +        },
        +        "merchant": {
        +          "type": "string"
        +        },
        +        "receivedAmount": {
        +          "type": "number"
        +        },
        +        "recordId": {
        +          "type": "string"
        +        },
        +        "rowNumber": {
        +          "minimum": 1,
        +          "type": "integer"
        +        },
        +        "tag": {
        +          "type": "string"
        +        },
        +        "total": {
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "recordId": {
        +      "type": "string"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "reviewExpensesUrl": {
        +      "type": "string"
        +    },
        +    "rowNumber": {
        +      "minimum": 1,
        +      "type": "integer"
        +    },
        +    "spreadsheetUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedadd_income1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "deduped": {
        +      "type": "boolean"
        +    },
        +    "destinations": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "matchedRow": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "amount": {
        +          "type": "number"
        +        },
        +        "currency": {
        +          "type": "string"
        +        },
        +        "date": {
        +          "format": "date",
        +          "type": "string"
        +        },
        +        "rowNumber": {
        +          "minimum": 1,
        +          "type": "integer"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "recordId": {
        +      "type": "string"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "rowNumber": {
        +      "minimum": 1,
        +      "type": "integer"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "tabCreated": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedadd_income_from_csv1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "allOf": [
        +    {
        +      "if": {
        +        "properties": {
        +          "preview": {
        +            "const": true
        +          }
        +        },
        +        "required": [
        +          "preview"
        +        ]
        +      },
        +      "then": {
        +        "required": [
        +          "previewId",
        +          "expiresAt",
        +          "entryCount",
        +          "entries",
        +          "confirmationContract"
        +        ]
        +      }
        +    },
        +    {
        +      "if": {
        +        "properties": {
        +          "confirmed": {
        +            "const": true
        +          }
        +        },
        +        "required": [
        +          "confirmed"
        +        ]
        +      },
        +      "then": {
        +        "required": [
        +          "previewId",
        +          "written",
        +          "deduped",
        +          "failed",
        +          "unattempted",
        +          "total"
        +        ]
        +      }
        +    }
        +  ],
        +  "properties": {
        +    "confirmationContract": {
        +      "type": "string"
        +    },
        +    "confirmed": {
        +      "type": "boolean"
        +    },
        +    "deduped": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "duplicateCount": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "duplicates": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "entries": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "amount": {
        +            "type": "number"
        +          },
        +          "currency": {
        +            "type": "string"
        +          },
        +          "date": {
        +            "format": "date",
        +            "type": "string"
        +          },
        +          "duplicate": {
        +            "type": "boolean"
        +          },
        +          "index": {
        +            "minimum": 0,
        +            "type": "integer"
        +          },
        +          "payer": {
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "entryCount": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "expiresAt": {
        +      "format": "date-time",
        +      "type": "string"
        +    },
        +    "expiresInMinutes": {
        +      "minimum": 1,
        +      "type": "integer"
        +    },
        +    "failed": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "idempotentReplay": {
        +      "type": "boolean"
        +    },
        +    "maxChatEntries": {
        +      "minimum": 1,
        +      "type": "integer"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "preview": {
        +      "type": "boolean"
        +    },
        +    "previewId": {
        +      "type": "string"
        +    },
        +    "projectionTruncated": {
        +      "type": "boolean"
        +    },
        +    "refundRowCount": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "rejectedRows": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "reviewImportUrl": {
        +      "type": "string"
        +    },
        +    "reviewIncomeUrl": {
        +      "type": "string"
        +    },
        +    "spreadsheetUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "tooLargeForChat": {
        +      "type": "boolean"
        +    },
        +    "total": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "totalsByCurrency": {
        +      "additionalProperties": {
        +        "type": "number"
        +      },
        +      "type": "object"
        +    },
        +    "unattempted": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "warnings": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "written": {
        +      "minimum": 0,
        +      "type": "integer"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedadd_income_from_file1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "allOf": [
        +    {
        +      "if": {
        +        "properties": {
        +          "preview": {
        +            "const": true
        +          }
        +        },
        +        "required": [
        +          "preview"
        +        ]
        +      },
        +      "then": {
        +        "required": [
        +          "previewId",
        +          "expiresAt",
        +          "entryCount",
        +          "entries",
        +          "confirmationContract"
        +        ]
        +      }
        +    },
        +    {
        +      "if": {
        +        "properties": {
        +          "confirmed": {
        +            "const": true
        +          }
        +        },
        +        "required": [
        +          "confirmed"
        +        ]
        +      },
        +      "then": {
        +        "required": [
        +          "previewId",
        +          "written",
        +          "deduped",
        +          "failed",
        +          "unattempted",
        +          "total"
        +        ]
        +      }
        +    }
        +  ],
        +  "properties": {
        +    "confirmationContract": {
        +      "type": "string"
        +    },
        +    "confirmed": {
        +      "type": "boolean"
        +    },
        +    "deduped": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "duplicateCount": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "duplicates": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "entries": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "amount": {
        +            "type": "number"
        +          },
        +          "currency": {
        +            "type": "string"
        +          },
        +          "date": {
        +            "format": "date",
        +            "type": "string"
        +          },
        +          "duplicate": {
        +            "type": "boolean"
        +          },
        +          "index": {
        +            "minimum": 0,
        +            "type": "integer"
        +          },
        +          "payer": {
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "entryCount": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "expiresAt": {
        +      "format": "date-time",
        +      "type": "string"
        +    },
        +    "expiresInMinutes": {
        +      "minimum": 1,
        +      "type": "integer"
        +    },
        +    "failed": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "idempotentReplay": {
        +      "type": "boolean"
        +    },
        +    "maxChatEntries": {
        +      "minimum": 1,
        +      "type": "integer"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "preview": {
        +      "type": "boolean"
        +    },
        +    "previewId": {
        +      "type": "string"
        +    },
        +    "projectionTruncated": {
        +      "type": "boolean"
        +    },
        +    "refundRowCount": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "rejectedRows": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "reviewImportUrl": {
        +      "type": "string"
        +    },
        +    "reviewIncomeUrl": {
        +      "type": "string"
        +    },
        +    "spreadsheetUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "tooLargeForChat": {
        +      "type": "boolean"
        +    },
        +    "total": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "totalsByCurrency": {
        +      "additionalProperties": {
        +        "type": "number"
        +      },
        +      "type": "object"
        +    },
        +    "unattempted": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "warnings": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "written": {
        +      "minimum": 0,
        +      "type": "integer"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedadd_mileage_entry1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "reviewExpensesUrl": {
        +      "type": "string"
        +    },
        +    "rowNumber": {
        +      "minimum": 1,
        +      "type": "integer"
        +    },
        +    "settings": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "allowancePerUnit": {
        +          "type": "number"
        +        },
        +        "unit": {
        +          "type": "string"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "spreadsheetUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedcorrect_expenses2 fields changed
      • addedInput schema / properties / change / oneOf
        Added value: +[
        +  {
        +    "properties": {
        +      "type": {
        +        "const": "category"
        +      }
        +    },
        +    "required": [
        +      "category"
        +    ]
        +  },
        +  {
        +    "properties": {
        +      "type": {
        +        "const": "businessPurpose"
        +      }
        +    },
        +    "required": [
        +      "businessPurpose"
        +    ]
        +  },
        +  {
        +    "properties": {
        +      "type": {
        +        "const": "attendees"
        +      }
        +    },
        +    "required": [
        +      "attendees"
        +    ]
        +  }
        +]
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "change": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "confirmation": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "manifest": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "operationId": {
        +      "type": "string"
        +    },
        +    "preview": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "requiresConfirmation": {
        +      "type": "boolean"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "reviewExpensesUrl": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "undo": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "status"
        +  ],
        +  "type": "object"
        +}
    • Changedcreate_client_invoice1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "advanceApplied": {
        +      "type": "number"
        +    },
        +    "artifactWarning": {
        +      "type": "string"
        +    },
        +    "clientName": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "currency": {
        +      "type": "string"
        +    },
        +    "documentUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "docxUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "dueDate": {
        +      "format": "date",
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "grandTotal": {
        +      "type": "number"
        +    },
        +    "invoiceId": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "invoiceNumber": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "issueDate": {
        +      "format": "date",
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "pdfUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "recovered": {
        +      "type": "boolean"
        +    },
        +    "sourceReportId": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "status": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "total": {
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedcreate_report1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "accountingHandoffUrls": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "accountingSetupUrl": {
        +      "type": "string"
        +    },
        +    "billClientUrl": {
        +      "type": "string"
        +    },
        +    "creationStatus": {
        +      "type": "string"
        +    },
        +    "data": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "appReportUrl": {
        +          "type": "string"
        +        },
        +        "appliedFilters": {
        +          "additionalProperties": true,
        +          "type": "object"
        +        },
        +        "billClientUrl": {
        +          "type": "string"
        +        },
        +        "creationStatus": {
        +          "type": "string"
        +        },
        +        "functionalTotal": {
        +          "type": "number"
        +        },
        +        "homeCurrency": {
        +          "type": "string"
        +        },
        +        "link": {
        +          "type": "string"
        +        },
        +        "personalExcludedCount": {
        +          "minimum": 0,
        +          "type": "integer"
        +        },
        +        "receiptCount": {
        +          "minimum": 0,
        +          "type": "integer"
        +        },
        +        "reportId": {
        +          "type": "string"
        +        },
        +        "title": {
        +          "type": "string"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "nextActions": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "reportId": {
        +      "type": "string"
        +    },
        +    "reportUrl": {
        +      "type": "string"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedexport_report1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "availableInReport": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "expiresAt": {
        +      "format": "date-time",
        +      "type": "string"
        +    },
        +    "notes": {
        +      "type": "string"
        +    },
        +    "report": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "id": {
        +          "type": "string"
        +        },
        +        "spreadsheetId": {
        +          "type": "string"
        +        },
        +        "status": {
        +          "type": "string"
        +        },
        +        "title": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "title",
        +        "spreadsheetId"
        +      ],
        +      "type": "object"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "urls": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "pdf": {
        +          "type": "string"
        +        },
        +        "report": {
        +          "type": "string"
        +        },
        +        "view": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "view",
        +        "pdf",
        +        "report"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "report",
        +    "urls",
        +    "expiresAt",
        +    "availableInReport",
        +    "notes"
        +  ],
        +  "type": "object"
        +}
    • Changedfix_compliance12 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • removedInput schema / properties / action
        Removed value: -{
        -  "enum": [
        -    "add_purpose",
        -    "set_category",
        -    "add_tag"
        -  ],
        -  "type": "string"
        -}
      • addedInput schema / properties / change
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Exactly one correction. The selected type determines which matching value field is required.",
        +  "oneOf": [
        +    {
        +      "properties": {
        +        "type": {
        +          "const": "category"
        +        }
        +      },
        +      "required": [
        +        "category"
        +      ]
        +    },
        +    {
        +      "properties": {
        +        "type": {
        +          "const": "businessPurpose"
        +        }
        +      },
        +      "required": [
        +        "businessPurpose"
        +      ]
        +    },
        +    {
        +      "properties": {
        +        "type": {
        +          "const": "attendees"
        +        }
        +      },
        +      "required": [
        +        "attendees"
        +      ]
        +    }
        +  ],
        +  "properties": {
        +    "attendeeMode": {
        +      "description": "Use add unless the user explicitly asks to replace existing attendees.",
        +      "enum": [
        +        "add",
        +        "replace"
        +      ],
        +      "type": "string"
        +    },
        +    "attendees": {
        +      "description": "Attendee names explicitly supplied by the user when type is attendees; never infer names.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "maxItems": 25,
        +      "minItems": 1,
        +      "type": "array"
        +    },
        +    "businessPurpose": {
        +      "description": "User-supplied business purpose to apply when type is businessPurpose.",
        +      "type": "string"
        +    },
        +    "category": {
        +      "description": "Configured expense category to apply when type is category.",
        +      "type": "string"
        +    },
        +    "type": {
        +      "description": "The one correction kind to apply to every exact selected expense.",
        +      "enum": [
        +        "category",
        +        "businessPurpose",
        +        "attendees"
        +      ],
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "type"
        +  ],
        +  "type": "object"
        +}
      • addedInput schema / properties / confirm
        Added value: +{
        +  "description": "Omit/false for preview; true only after explicit approval.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / operationId
        Added value: +{
        +  "description": "Stable idempotency key generated once for preview and reused unchanged.",
        +  "pattern": "^expense-bulk:[a-zA-Z0-9_-]{8,100}$",
        +  "type": "string"
        +}
      • removedInput schema / properties / reportId
        Removed value: -{
        -  "type": "string"
        -}
      • addedInput schema / properties / selection
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Exact expenses returned by search_expenses and approved for this bounded correction.",
        +  "properties": {
        +    "items": {
        +      "description": "One exact receipt identity per selected expense; never broaden this list after preview.",
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "expected": {
        +            "additionalProperties": false,
        +            "description": "Optional optimistic-concurrency values copied from the search result.",
        +            "properties": {
        +              "category": {
        +                "description": "Current category observed before preview for conflict detection.",
        +                "type": "string"
        +              },
        +              "notes": {
        +                "description": "Current structured Notes value observed before preview for conflict detection.",
        +                "type": "string"
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "receiptId": {
        +            "description": "Exact full expenseId/Column-Q receipt identity from search_expenses.",
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "receiptId"
        +        ],
        +        "type": "object"
        +      },
        +      "maxItems": 100,
        +      "minItems": 1,
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "items"
        +  ],
        +  "type": "object"
        +}
      • addedInput schema / properties / status
        Added value: +{
        +  "description": "Read operation status using operationId.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / undo
        Added value: +{
        +  "description": "Undo a completed correction within its Undo window.",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / value
        Removed value: -{
        -  "description": "The value to apply",
        -  "type": "string"
        -}
      • changedInput schema / required
        Previous value: -[
        -  "reportId",
        -  "action",
        -  "value"
        -]New value: +[
        +  "operationId"
        +]
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "change": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "confirmation": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "manifest": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "operationId": {
        +      "type": "string"
        +    },
        +    "preview": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "requiresConfirmation": {
        +      "type": "boolean"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "reviewExpensesUrl": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "undo": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "status"
        +  ],
        +  "type": "object"
        +}
    • Changedgroup_expenses1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "candidates": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "confirmation": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "creationStatus": {
        +      "type": "string"
        +    },
        +    "exclusions": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "group": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "manifest": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "operationId": {
        +      "type": "string"
        +    },
        +    "preview": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "report": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "reportsUrl": {
        +      "type": "string"
        +    },
        +    "requiresConfirmation": {
        +      "type": "boolean"
        +    },
        +    "requiresNewGroupApproval": {
        +      "type": "boolean"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "reviewExpensesUrl": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "suggestedTag": {
        +      "type": "string"
        +    },
        +    "undo": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "status"
        +  ],
        +  "type": "object"
        +}
    • Changedmark_client_invoice_paid1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "incomeRowCreated": {
        +      "const": false,
        +      "type": "boolean"
        +    },
        +    "invoiceId": {
        +      "type": "string"
        +    },
        +    "invoiceNumber": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "paidAmount": {
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "paidDate": {
        +      "format": "date",
        +      "type": "string"
        +    },
        +    "paidVia": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "const": "paid",
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "invoiceId",
        +    "status",
        +    "paidDate",
        +    "paidAmount",
        +    "paidVia",
        +    "incomeRowCreated",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedparse_expense1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "categoryNames": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "expenses": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "amount": {
        +            "type": "number"
        +          },
        +          "category": {
        +            "type": "string"
        +          },
        +          "currency": {
        +            "type": "string"
        +          },
        +          "date": {
        +            "format": "date",
        +            "type": "string"
        +          },
        +          "merchant": {
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "validationErrors": {
        +      "items": {},
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "expenses",
        +    "validationErrors",
        +    "categoryNames"
        +  ],
        +  "type": "object"
        +}
    • Changedprepare_client_invoice1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "expiresAt": {
        +      "format": "date-time",
        +      "type": "string"
        +    },
        +    "preparationId": {
        +      "type": "string"
        +    },
        +    "preview": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "advanceApplied": {
        +          "type": "number"
        +        },
        +        "balanceDue": {
        +          "type": "number"
        +        },
        +        "clientName": {
        +          "type": "string"
        +        },
        +        "clientTag": {
        +          "type": "string"
        +        },
        +        "currency": {
        +          "type": "string"
        +        },
        +        "customLineCount": {
        +          "minimum": 0,
        +          "type": "integer"
        +        },
        +        "dueDate": {
        +          "format": "date",
        +          "type": "string"
        +        },
        +        "expenseCount": {
        +          "minimum": 0,
        +          "type": "integer"
        +        },
        +        "grandTotal": {
        +          "type": "number"
        +        },
        +        "hasTaxRegistration": {
        +          "type": "boolean"
        +        },
        +        "invoiceNumber": {
        +          "type": "string"
        +        },
        +        "issueDate": {
        +          "format": "date",
        +          "type": "string"
        +        },
        +        "markupAmount": {
        +          "type": "number"
        +        },
        +        "markupPct": {
        +          "type": "number"
        +        },
        +        "paymentTerms": {
        +          "type": "string"
        +        },
        +        "reportId": {
        +          "type": "string"
        +        },
        +        "reportTitle": {
        +          "type": "string"
        +        },
        +        "subtotal": {
        +          "type": "number"
        +        },
        +        "taxAmount": {
        +          "type": "number"
        +        },
        +        "taxConfirmationRequired": {
        +          "type": "boolean"
        +        },
        +        "taxLabel": {
        +          "type": "string"
        +        },
        +        "taxPct": {
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "preparationId",
        +    "expiresAt",
        +    "preview"
        +  ],
        +  "type": "object"
        +}
    • Changedprocess_gmail_receipts1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "emailCount": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "error": {
        +      "type": "string"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "processInfo": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "processingCompleted": {
        +          "type": "boolean"
        +        },
        +        "sessionId": {
        +          "type": "string"
        +        },
        +        "status": {
        +          "type": "string"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "processedItems": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "reviewExpensesUrl": {
        +      "type": "string"
        +    },
        +    "spreadsheetUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "submissionId": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedrequest_accounting_integration1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "duplicate": {
        +      "type": "boolean"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "packageName": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "duplicate",
        +    "packageName",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedscan_gmail1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "_metadata": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "orchestrated": {
        +      "type": "boolean"
        +    },
        +    "reviewExpensesUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "scanInProgress": {
        +      "type": "boolean"
        +    },
        +    "sessionId": {
        +      "type": "string"
        +    },
        +    "spreadsheetUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "status": {
        +      "type": "string"
        +    },
        +    "submissionId": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedscan_gmail_years1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "allOf": [
        +    {
        +      "if": {
        +        "properties": {
        +          "preview": {
        +            "const": true
        +          }
        +        },
        +        "required": [
        +          "preview"
        +        ]
        +      },
        +      "then": {
        +        "required": [
        +          "account",
        +          "eligible",
        +          "alreadyScanned",
        +          "notEligible"
        +        ]
        +      }
        +    }
        +  ],
        +  "properties": {
        +    "account": {
        +      "type": "string"
        +    },
        +    "alreadyQueued": {
        +      "items": {
        +        "type": "integer"
        +      },
        +      "type": "array"
        +    },
        +    "alreadyScanned": {
        +      "items": {
        +        "type": "integer"
        +      },
        +      "type": "array"
        +    },
        +    "archiveThroughYear": {
        +      "type": "integer"
        +    },
        +    "eligible": {
        +      "items": {
        +        "type": "integer"
        +      },
        +      "type": "array"
        +    },
        +    "gmailUrl": {
        +      "type": "string"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "notEligible": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "code": {
        +            "type": "string"
        +          },
        +          "reason": {
        +            "type": "string"
        +          },
        +          "year": {
        +            "type": "integer"
        +          }
        +        },
        +        "required": [
        +          "year",
        +          "reason"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "preview": {
        +      "type": "boolean"
        +    },
        +    "queuedYears": {
        +      "items": {
        +        "type": "integer"
        +      },
        +      "type": "array"
        +    },
        +    "scanning": {
        +      "items": {
        +        "type": "integer"
        +      },
        +      "type": "array"
        +    },
        +    "startedYear": {
        +      "type": [
        +        "integer",
        +        "null"
        +      ]
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedsend_report_to_accounting1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "allOf": [
        +    {
        +      "if": {
        +        "properties": {
        +          "phase": {
        +            "const": "preview"
        +          }
        +        },
        +        "required": [
        +          "phase"
        +        ]
        +      },
        +      "then": {
        +        "required": [
        +          "expiresAt",
        +          "confirmationRequired",
        +          "confirmationQuestion",
        +          "confirmationContract"
        +        ]
        +      }
        +    },
        +    {
        +      "if": {
        +        "properties": {
        +          "phase": {
        +            "const": "completed"
        +          }
        +        },
        +        "required": [
        +          "phase"
        +        ]
        +      },
        +      "then": {
        +        "required": [
        +          "reportId",
        +          "finalAnswer",
        +          "responseGuidance"
        +        ]
        +      }
        +    }
        +  ],
        +  "properties": {
        +    "confirmationContract": {
        +      "type": "string"
        +    },
        +    "confirmationQuestion": {
        +      "type": "string"
        +    },
        +    "confirmationRequired": {
        +      "type": "boolean"
        +    },
        +    "counts": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "created": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "expiresAt": {
        +      "format": "date-time",
        +      "type": "string"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "idempotentReplay": {
        +      "type": "boolean"
        +    },
        +    "matchedExisting": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "omittedExpenses": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "organization": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "phase": {
        +      "enum": [
        +        "preview",
        +        "completed"
        +      ],
        +      "type": "string"
        +    },
        +    "proposalId": {
        +      "type": "string"
        +    },
        +    "provider": {
        +      "type": "string"
        +    },
        +    "reconciliationRequired": {
        +      "type": "boolean"
        +    },
        +    "reportId": {
        +      "type": "string"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "skippedAlreadyPushed": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "units": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "phase",
        +    "provider",
        +    "proposalId"
        +  ],
        +  "type": "object"
        +}
    • Changedshare_report1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "recipientRole": {
        +      "type": "string"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "sharedWith": {
        +      "items": {
        +        "format": "email",
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "sharedWith",
        +    "recipientRole"
        +  ],
        +  "type": "object"
        +}
    • Changedsubmit_receipt1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "allOf": [
        +    {
        +      "if": {
        +        "properties": {
        +          "status": {
        +            "const": "upload_required"
        +          }
        +        },
        +        "required": [
        +          "status"
        +        ]
        +      },
        +      "then": {
        +        "required": [
        +          "data"
        +        ]
        +      }
        +    }
        +  ],
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "expiresInSeconds": {
        +          "minimum": 1,
        +          "type": "integer"
        +        },
        +        "files": {
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "filename": {
        +                "type": "string"
        +              },
        +              "id": {
        +                "type": "string"
        +              },
        +              "mimeType": {
        +                "type": "string"
        +              },
        +              "requiredHeaders": {
        +                "additionalProperties": {
        +                  "type": "string"
        +                },
        +                "type": "object"
        +              },
        +              "signedUrl": {
        +                "type": "string"
        +              },
        +              "uploadRef": {
        +                "type": "string"
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "requiredHeaders": {
        +          "additionalProperties": {
        +            "type": "string"
        +          },
        +          "type": "object"
        +        },
        +        "reviewExpensesUrl": {
        +          "type": "string"
        +        },
        +        "signedUrl": {
        +          "type": "string"
        +        },
        +        "spreadsheetUrl": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "submissionId": {
        +          "type": "string"
        +        },
        +        "uploadRef": {
        +          "type": "string"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "fileCount": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "files": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "reviewExpensesUrl": {
        +      "type": "string"
        +    },
        +    "spreadsheetUrl": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "status": {
        +      "type": "string"
        +    },
        +    "statusCode": {
        +      "type": "integer"
        +    },
        +    "submissionId": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedtrace_document1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "events": {
        +          "items": {
        +            "additionalProperties": true,
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "filters": {
        +          "additionalProperties": true,
        +          "type": "object"
        +        },
        +        "searchLabel": {
        +          "type": "string"
        +        },
        +        "summary": {
        +          "additionalProperties": true,
        +          "type": "object"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "type": {
        +      "const": "lineage_trace",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "type",
        +    "message",
        +    "data"
        +  ],
        +  "type": "object"
        +}
    • Changedupdate_expense1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "applied": {
        +          "type": "boolean"
        +        },
        +        "diff": {
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "column": {
        +                "type": "string"
        +              },
        +              "columnLetter": {
        +                "type": "string"
        +              },
        +              "field": {
        +                "type": "string"
        +              },
        +              "from": {},
        +              "to": {}
        +            },
        +            "required": [
        +              "field",
        +              "from",
        +              "to"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "dryRun": {
        +          "type": "boolean"
        +        },
        +        "expenseId": {
        +          "type": "string"
        +        },
        +        "preview": {
        +          "type": "boolean"
        +        },
        +        "reviewExpenseUrl": {
        +          "type": "string"
        +        },
        +        "unmapped": {
        +          "items": {
        +            "type": "string"
        +          },
        +          "type": "array"
        +        }
        +      },
        +      "required": [
        +        "applied",
        +        "expenseId",
        +        "diff",
        +        "unmapped"
        +      ],
        +      "type": "object"
        +    },
        +    "finalAnswer": {
        +      "type": "string"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "data"
        +  ],
        +  "type": "object"
        +}
  8. 1 tool update
    • Changedget_scan_status5 fields changed
      • changedOutput schema / properties / gmailScanUrl / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / latestScan / type
        Previous value: -"object"New value: +[
        +  "object",
        +  "null"
        +]
      • changedOutput schema / properties / reviewExpensesUrl / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / spreadsheetUrl / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / status / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
  9. 30 tool updates
    • Changedcheck_compliance1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedcheck_feature1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedcheck_tax_deductibility1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedget_accounting_integration_status1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "description": "Per-provider connection and rollout state.",
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedget_accounting_push_status1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedget_client_advance_balances1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "balances": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "homeCurrency": {
        +      "type": "string"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedget_client_invoice1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "invoice": {
        +      "additionalProperties": true,
        +      "description": "The invoice record. Absent only on the error branch (not-found), which never carries structuredContent.",
        +      "properties": {
        +        "advanceApplied": {
        +          "type": "number"
        +        },
        +        "balanceDue": {
        +          "type": "number"
        +        },
        +        "billClientUrl": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "clientName": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "clientTag": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "currency": {
        +          "type": "string"
        +        },
        +        "deliveryStatus": {
        +          "type": "string"
        +        },
        +        "displayStatus": {
        +          "description": "Derived: overdue is computed from status + dueDate, not stored.",
        +          "enum": [
        +            "open",
        +            "overdue",
        +            "paid",
        +            "void",
        +            "superseded",
        +            "needs_review"
        +          ],
        +          "type": "string"
        +        },
        +        "documentUrl": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "dueDate": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "grandTotal": {
        +          "type": "number"
        +        },
        +        "invoiceId": {
        +          "description": "Durable invoice identity.",
        +          "type": "string"
        +        },
        +        "invoiceNumber": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "issueDate": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "paidAmount": {
        +          "description": "null until paid.",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "paidDate": {
        +          "description": "null until paid.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "pdfUrl": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "reportUrl": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "status": {
        +          "description": "Stored status, e.g. \"open\", \"paid\", \"void\".",
        +          "type": "string"
        +        },
        +        "subtotal": {
        +          "type": "number"
        +        },
        +        "taxAmount": {
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedget_credits_refunds1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "credits": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "refunds": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedget_deep_analytics1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "description": "Two branches. Async: type='analytics_pending' with jobId — poll poll_analytics until complete. Inline: message + data with the computed analytics (data may be null when there is no spreadsheet or no data yet). success is present only on the async branch.",
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "description": "Inline branch: computed analytics, or null when no spreadsheet / no data.",
        +      "type": [
        +        "object",
        +        "null"
        +      ]
        +    },
        +    "estimatedTime": {
        +      "description": "Async branch only: seconds.",
        +      "type": "number"
        +    },
        +    "jobId": {
        +      "description": "Async branch only.",
        +      "type": "string"
        +    },
        +    "message": {
        +      "description": "Human-readable narrative (both branches).",
        +      "type": "string"
        +    },
        +    "pollEndpoint": {
        +      "description": "Async branch only.",
        +      "type": "string"
        +    },
        +    "pollInterval": {
        +      "description": "Async branch only: milliseconds.",
        +      "type": "number"
        +    },
        +    "response": {
        +      "description": "Chat alias of message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Async branch only.",
        +      "type": "boolean"
        +    },
        +    "type": {
        +      "description": "Async branch only: \"analytics_pending\" — poll pollEndpoint or call poll_analytics with jobId.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [],
        +  "type": "object"
        +}
    • Changedget_expense_by_id1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "expense": {
        +      "additionalProperties": true,
        +      "description": "The expense record.",
        +      "type": "object"
        +    },
        +    "reviewExpenseUrl": {
        +      "type": "string"
        +    },
        +    "rowNumber": {
        +      "description": "Resolved sheet row for the expense.",
        +      "type": "integer"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedget_expense_splits1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "splits": {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedget_income_summary1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "description": "Two branches. Async: type='analytics_pending' with jobId — poll poll_analytics until complete. Inline: message + data with the computed analytics (data may be null when there is no spreadsheet or no data yet). success is present only on the async branch.",
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "description": "Inline branch: computed analytics, or null when no spreadsheet / no data.",
        +      "type": [
        +        "object",
        +        "null"
        +      ]
        +    },
        +    "estimatedTime": {
        +      "description": "Async branch only: seconds.",
        +      "type": "number"
        +    },
        +    "jobId": {
        +      "description": "Async branch only.",
        +      "type": "string"
        +    },
        +    "message": {
        +      "description": "Human-readable narrative (both branches).",
        +      "type": "string"
        +    },
        +    "pollEndpoint": {
        +      "description": "Async branch only.",
        +      "type": "string"
        +    },
        +    "pollInterval": {
        +      "description": "Async branch only: milliseconds.",
        +      "type": "number"
        +    },
        +    "response": {
        +      "description": "Chat alias of message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Async branch only.",
        +      "type": "boolean"
        +    },
        +    "type": {
        +      "description": "Async branch only: \"analytics_pending\" — poll pollEndpoint or call poll_analytics with jobId.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [],
        +  "type": "object"
        +}
    • Changedget_mileage_summary1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "description": "Success returns message + data with the computed summary; success is not emitted on the inline analytics path.",
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": [
        +        "object",
        +        "null"
        +      ]
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Present only on the error branch (out of scope) / async path.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [],
        +  "type": "object"
        +}
    • Changedget_monthly_books_review1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedget_per_tag_pnl1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "description": "Two branches. Async: type='analytics_pending' with jobId — poll poll_analytics until complete. Inline: message + data with the computed analytics (data may be null when there is no spreadsheet or no data yet). success is present only on the async branch.",
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "description": "Inline branch: computed analytics, or null when no spreadsheet / no data.",
        +      "type": [
        +        "object",
        +        "null"
        +      ]
        +    },
        +    "estimatedTime": {
        +      "description": "Async branch only: seconds.",
        +      "type": "number"
        +    },
        +    "jobId": {
        +      "description": "Async branch only.",
        +      "type": "string"
        +    },
        +    "message": {
        +      "description": "Human-readable narrative (both branches).",
        +      "type": "string"
        +    },
        +    "pollEndpoint": {
        +      "description": "Async branch only.",
        +      "type": "string"
        +    },
        +    "pollInterval": {
        +      "description": "Async branch only: milliseconds.",
        +      "type": "number"
        +    },
        +    "response": {
        +      "description": "Chat alias of message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Async branch only.",
        +      "type": "boolean"
        +    },
        +    "type": {
        +      "description": "Async branch only: \"analytics_pending\" — poll pollEndpoint or call poll_analytics with jobId.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [],
        +  "type": "object"
        +}
    • Changedget_pnl1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "description": "Two branches. Async: type='analytics_pending' with jobId — poll poll_analytics until complete. Inline: message + data with the computed analytics (data may be null when there is no spreadsheet or no data yet). success is present only on the async branch.",
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "description": "Inline branch: computed analytics, or null when no spreadsheet / no data.",
        +      "type": [
        +        "object",
        +        "null"
        +      ]
        +    },
        +    "estimatedTime": {
        +      "description": "Async branch only: seconds.",
        +      "type": "number"
        +    },
        +    "jobId": {
        +      "description": "Async branch only.",
        +      "type": "string"
        +    },
        +    "message": {
        +      "description": "Human-readable narrative (both branches).",
        +      "type": "string"
        +    },
        +    "pollEndpoint": {
        +      "description": "Async branch only.",
        +      "type": "string"
        +    },
        +    "pollInterval": {
        +      "description": "Async branch only: milliseconds.",
        +      "type": "number"
        +    },
        +    "response": {
        +      "description": "Chat alias of message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Async branch only.",
        +      "type": "boolean"
        +    },
        +    "type": {
        +      "description": "Async branch only: \"analytics_pending\" — poll pollEndpoint or call poll_analytics with jobId.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [],
        +  "type": "object"
        +}
    • Changedget_recent_activity1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "actions": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "actionId": {
        +            "type": "string"
        +          },
        +          "durationMs": {
        +            "type": "number"
        +          },
        +          "success": {
        +            "type": "boolean"
        +          },
        +          "timestamp": {
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "count": {
        +      "type": "integer"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "count",
        +    "actions"
        +  ],
        +  "type": "object"
        +}
    • Changedget_report_details1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "report": {
        +      "additionalProperties": true,
        +      "description": "The full stored report record.",
        +      "type": "object"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "report"
        +  ],
        +  "type": "object"
        +}
    • Changedget_scan_status1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "description": "Authoritative live scan state. Fields may sit at the top level or under data depending on the branch; nothing beyond success is guaranteed, so treat each as optional.",
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    "gmailScanUrl": {
        +      "type": "string"
        +    },
        +    "latestScan": {
        +      "additionalProperties": true,
        +      "description": "Most recent scan outcome (success, emailsFound, emailsTagged, timestamp).",
        +      "type": "object"
        +    },
        +    "lock": {
        +      "additionalProperties": true,
        +      "description": "Scan-lock state (active, startedAt, ageSeconds, dispatchComplete, pendingProcessingCount).",
        +      "type": "object"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "reviewExpensesUrl": {
        +      "type": "string"
        +    },
        +    "scanInProgress": {
        +      "type": "boolean"
        +    },
        +    "spreadsheetUrl": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "description": "Scan lifecycle state.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedget_spending_summary1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "description": "Two branches. Async: type='analytics_pending' with jobId — poll poll_analytics until complete. Inline: message + data with the computed analytics (data may be null when there is no spreadsheet or no data yet). success is present only on the async branch.",
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "description": "Inline branch: computed analytics, or null when no spreadsheet / no data.",
        +      "type": [
        +        "object",
        +        "null"
        +      ]
        +    },
        +    "estimatedTime": {
        +      "description": "Async branch only: seconds.",
        +      "type": "number"
        +    },
        +    "jobId": {
        +      "description": "Async branch only.",
        +      "type": "string"
        +    },
        +    "message": {
        +      "description": "Human-readable narrative (both branches).",
        +      "type": "string"
        +    },
        +    "pollEndpoint": {
        +      "description": "Async branch only.",
        +      "type": "string"
        +    },
        +    "pollInterval": {
        +      "description": "Async branch only: milliseconds.",
        +      "type": "number"
        +    },
        +    "response": {
        +      "description": "Chat alias of message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Async branch only.",
        +      "type": "boolean"
        +    },
        +    "type": {
        +      "description": "Async branch only: \"analytics_pending\" — poll pollEndpoint or call poll_analytics with jobId.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [],
        +  "type": "object"
        +}
    • Changedget_subscription_audit1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "description": "Success returns message + data with the computed summary; success is not emitted on the inline analytics path.",
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "type": [
        +        "object",
        +        "null"
        +      ]
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Present only on the error branch (out of scope) / async path.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [],
        +  "type": "object"
        +}
    • Changedget_trip_suggestions1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "count": {
        +      "type": "integer"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "proposals": {
        +      "description": "Read-only trip proposals; accepting one continues through group_expenses.",
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_categories1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "billingAccounts": {
        +      "description": "Billing/card accounts, when configured.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "categories": {
        +      "description": "Expense categories for the user.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "categories"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_client_invoices1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "invoices": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "advanceApplied": {
        +            "type": "number"
        +          },
        +          "clientName": {
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "currency": {
        +            "type": "string"
        +          },
        +          "displayStatus": {
        +            "enum": [
        +              "open",
        +              "overdue",
        +              "paid",
        +              "void",
        +              "superseded",
        +              "needs_review"
        +            ],
        +            "type": "string"
        +          },
        +          "grandTotal": {
        +            "type": "number"
        +          },
        +          "invoiceId": {
        +            "type": "string"
        +          },
        +          "invoiceNumber": {
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "issueDate": {
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "status": {
        +            "type": "string"
        +          },
        +          "total": {
        +            "type": "number"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "invoices"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_income_categories1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "categories": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "legacy": {
        +      "description": "Deprecated income categories still accepted on read.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "notes": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "categories"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_reports1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "creationStatus": {
        +      "description": "Provenance of the listing, e.g. \"existing_only\".",
        +      "type": "string"
        +    },
        +    "filter": {
        +      "type": "string"
        +    },
        +    "limit": {
        +      "type": "integer"
        +    },
        +    "page": {
        +      "type": "integer"
        +    },
        +    "reports": {
        +      "description": "Present when the user has reports.",
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "responseGuidance": {
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "creationStatus"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_tags1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "success": {
        +      "type": "boolean"
        +    },
        +    "tags": {
        +      "description": "Client / project / trip groups (Column K values).",
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "tags"
        +  ],
        +  "type": "object"
        +}
    • Changedwhatif_afford1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "description": "The full what-if scenario: assumptions, evidence, and computed outcome.",
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Scenario narrative; may be absent if the scenario carried no template or evidence.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedwhatif_client1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "description": "The full what-if scenario: assumptions, evidence, and computed outcome.",
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Scenario narrative; may be absent if the scenario carried no template or evidence.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
    • Changedwhatif_tax_setaside1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "data": {
        +      "additionalProperties": true,
        +      "description": "The full what-if scenario: assumptions, evidence, and computed outcome.",
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Scenario narrative; may be absent if the scenario carried no template or evidence.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success"
        +  ],
        +  "type": "object"
        +}
  10. 2 tool updates
    • Changedcreate_report1 field changed
      • addedInput schema / properties / recipientRole
        Added value: +{
        +  "default": "reviewer",
        +  "description": "Access for every shareWith recipient. Use reviewer for one-report review, comments, approval, or change requests. Use accountant only when the user explicitly wants ongoing report management and accounting-software access.",
        +  "enum": [
        +    "reviewer",
        +    "accountant"
        +  ],
        +  "type": "string"
        +}
    • Changedshare_report1 field changed
      • addedInput schema / properties / recipientRole
        Added value: +{
        +  "default": "reviewer",
        +  "description": "Use reviewer for report-only approval. Use accountant only for explicit ongoing accountant access.",
        +  "enum": [
        +    "reviewer",
        +    "accountant"
        +  ],
        +  "type": "string"
        +}
  11. 1 tool update
    • Changedsearch_knowledge1 field changed
      • changedInput schema / properties / fullAnswer / description
        Previous value: -"Set true to return complete article answers instead of short excerpts."New value: +"Set true on the first call for complete, detailed, or step-by-step instructions. Leave false for discovery or quick-answer excerpts."
  12. 57 tool updates
    • Changedadd_cash_expense1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedadd_income1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedadd_income_from_csv1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedadd_income_from_file1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedadd_mileage_entry1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedcheck_compliance1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedcheck_feature1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedcheck_tax_deductibility1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedcorrect_expenses1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedcreate_client_invoice1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedcreate_report1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedexport_report1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedfix_compliance1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_accounting_integration_status1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_accounting_push_status1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_client_advance_balances1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_client_invoice1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_credits_refunds1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_deep_analytics1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_expense_by_id1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_expense_splits1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_income_summary1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_last_receipt_result18 fields changed
      • addedOutput schema / allOf
        Added value: +[
        +  {
        +    "if": {
        +      "properties": {
        +        "found": {
        +          "const": true
        +        }
        +      },
        +      "required": [
        +        "found"
        +      ]
        +    },
        +    "then": {
        +      "required": [
        +        "verdict",
        +        "counts",
        +        "receipts",
        +        "submissionId",
        +        "spreadsheetUrl",
        +        "reviewExpensesUrl",
        +        "data",
        +        "timestamp"
        +      ]
        +    }
        +  }
        +]
      • changedOutput schema / description
        Previous value: -"Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple."New value: +"found:false means processing is still pending; found:true is a terminal result."
      • addedOutput schema / properties / counts
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "added": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "duplicates": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "errors": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "skipped": {
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    "submitted": {
        +      "minimum": 0,
        +      "type": "integer"
        +    }
        +  },
        +  "required": [
        +    "submitted",
        +    "added",
        +    "duplicates",
        +    "skipped",
        +    "errors"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / properties / data / description
        Removed value: -"Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results."
      • removedOutput schema / properties / error
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Present only when success === false.",
        -  "properties": {
        -    "code": {
        -      "type": "string"
        -    },
        -    "hint": {
        -      "type": "string"
        -    },
        -    "message": {
        -      "type": "string"
        -    }
        -  },
        -  "type": "object"
        -}
      • addedOutput schema / properties / found
        Added value: +{
        +  "type": "boolean"
        +}
      • removedOutput schema / properties / message / description
        Removed value: -"Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning."
      • addedOutput schema / properties / receipts
        Added value: +{
        +  "items": {
        +    "additionalProperties": true,
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / reviewExpensesUrl
        Added value: +{
        +  "type": "string"
        +}
      • removedOutput schema / properties / sampleMeta
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -  "properties": {
        -    "isTruncated": {
        -      "type": "boolean"
        -    },
        -    "sampleCount": {
        -      "type": "integer"
        -    },
        -    "totalCount": {
        -      "type": "integer"
        -    }
        -  },
        -  "type": "object"
        -}
      • addedOutput schema / properties / spreadsheetUrl
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / state
        Added value: +{
        +  "const": "processing",
        +  "description": "Present on pending polls for a specific submission.",
        +  "type": "string"
        +}
      • addedOutput schema / properties / submissionId
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / success / const
        Added value: +true
      • removedOutput schema / properties / success / description
        Removed value: -"False on tool errors; check before reading `data`."
      • addedOutput schema / properties / timestamp
        Added value: +{
        +  "format": "date-time",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / verdict
        Added value: +{
        +  "description": "Terminal branch only.",
        +  "enum": [
        +    "added",
        +    "duplicate",
        +    "skipped",
        +    "errored",
        +    "mixed",
        +    "unknown"
        +  ],
        +  "type": "string"
        +}
      • addedOutput schema / required
        Added value: +[
        +  "success",
        +  "found",
        +  "message"
        +]
    • Changedget_mileage_summary1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_monthly_books_review1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_per_tag_pnl1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_pnl1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_recent_activity1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_report_details1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_scan_status1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_spending_summary1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_spreadsheet_url16 fields changed
      • removedOutput schema / description
        Removed value: -"Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple."
      • addedOutput schema / properties / dashboardUrl
        Added value: +{
        +  "type": "string"
        +}
      • removedOutput schema / properties / data / description
        Removed value: -"Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results."
      • removedOutput schema / properties / error
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Present only when success === false.",
        -  "properties": {
        -    "code": {
        -      "type": "string"
        -    },
        -    "hint": {
        -      "type": "string"
        -    },
        -    "message": {
        -      "type": "string"
        -    }
        -  },
        -  "type": "object"
        -}
      • addedOutput schema / properties / gmailActionNeededUrl
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / gmailScanUrl
        Added value: +{
        +  "type": "string"
        +}
      • removedOutput schema / properties / message / description
        Removed value: -"Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning."
      • addedOutput schema / properties / reportsUrl
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / reviewExpensesUrl
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / reviewIncomeUrl
        Added value: +{
        +  "type": "string"
        +}
      • removedOutput schema / properties / sampleMeta
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -  "properties": {
        -    "isTruncated": {
        -      "type": "boolean"
        -    },
        -    "sampleCount": {
        -      "type": "integer"
        -    },
        -    "totalCount": {
        -      "type": "integer"
        -    }
        -  },
        -  "type": "object"
        -}
      • addedOutput schema / properties / spreadsheetId
        Added value: +{
        +  "minLength": 1,
        +  "type": "string"
        +}
      • addedOutput schema / properties / spreadsheetUrl
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / success / const
        Added value: +true
      • removedOutput schema / properties / success / description
        Removed value: -"False on tool errors; check before reading `data`."
      • addedOutput schema / required
        Added value: +[
        +  "success",
        +  "spreadsheetId",
        +  "spreadsheetUrl",
        +  "reviewExpensesUrl",
        +  "reviewIncomeUrl",
        +  "gmailScanUrl",
        +  "gmailActionNeededUrl",
        +  "reportsUrl",
        +  "dashboardUrl",
        +  "data",
        +  "message"
        +]
    • Changedget_subscription_audit1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedget_trip_suggestions1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedgroup_expenses1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedlist_categories1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedlist_client_invoices1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedlist_income_categories1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedlist_reports1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedlist_tags1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedmark_client_invoice_paid1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedparse_expense1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedprepare_client_invoice1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedprocess_gmail_receipts1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedrequest_accounting_integration1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedscan_gmail1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedscan_gmail_years1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedsearch_expenses15 fields changed
      • removedOutput schema / description
        Removed value: -"Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple."
      • addedOutput schema / properties / count
        Added value: +{
        +  "description": "Rows matched by the filters.",
        +  "type": "integer"
        +}
      • removedOutput schema / properties / data
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -  "type": "object"
        -}
      • removedOutput schema / properties / error
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Present only when success === false.",
        -  "properties": {
        -    "code": {
        -      "type": "string"
        -    },
        -    "hint": {
        -      "type": "string"
        -    },
        -    "message": {
        -      "type": "string"
        -    }
        -  },
        -  "type": "object"
        -}
      • addedOutput schema / properties / expenses
        Added value: +{
        +  "description": "Matching expense rows, newest first, paginated.",
        +  "items": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "amount": {
        +        "description": "Functional/home-currency amount.",
        +        "type": "number"
        +      },
        +      "category": {
        +        "type": "string"
        +      },
        +      "currency": {
        +        "description": "Home currency code when available.",
        +        "type": "string"
        +      },
        +      "date": {
        +        "type": "string"
        +      },
        +      "dateIso": {
        +        "description": "Canonical YYYY-MM-DD when available.",
        +        "type": "string"
        +      },
        +      "expenseId": {
        +        "description": "Durable Receipt ID (Column Q). The only identity accepted by update_expense.",
        +        "type": "string"
        +      },
        +      "location": {
        +        "type": "string"
        +      },
        +      "matchReason": {
        +        "type": "string"
        +      },
        +      "matchedFields": {
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "merchant": {
        +        "type": "string"
        +      },
        +      "originalAmount": {
        +        "type": "number"
        +      },
        +      "originalCurrency": {
        +        "type": "string"
        +      },
        +      "tag": {
        +        "description": "Column K group (client/project/trip).",
        +        "type": "string"
        +      },
        +      "updateEligible": {
        +        "type": "boolean"
        +      }
        +    },
        +    "required": [
        +      "expenseId",
        +      "date",
        +      "amount",
        +      "tag",
        +      "updateEligible"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / hasMore
        Added value: +{
        +  "type": "boolean"
        +}
      • removedOutput schema / properties / message / description
        Removed value: -"Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning."
      • addedOutput schema / properties / nextCursor
        Added value: +{
        +  "description": "Opaque signed continuation; reuse with identical filters.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / pageInfo
        Added value: +{
        +  "additionalProperties": true,
        +  "type": "object"
        +}
      • addedOutput schema / properties / queryApplied
        Added value: +{
        +  "type": "string"
        +}
      • removedOutput schema / properties / sampleMeta
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -  "properties": {
        -    "isTruncated": {
        -      "type": "boolean"
        -    },
        -    "sampleCount": {
        -      "type": "integer"
        -    },
        -    "totalCount": {
        -      "type": "integer"
        -    }
        -  },
        -  "type": "object"
        -}
      • removedOutput schema / properties / success
        Removed value: -{
        -  "description": "False on tool errors; check before reading `data`.",
        -  "type": "boolean"
        -}
      • addedOutput schema / properties / total
        Added value: +{
        +  "description": "Sum of the matched set in home currency.",
        +  "type": "number"
        +}
      • addedOutput schema / properties / totalMatched
        Added value: +{
        +  "description": "Present for free-text searches.",
        +  "type": "integer"
        +}
      • addedOutput schema / required
        Added value: +[
        +  "expenses",
        +  "count",
        +  "total"
        +]
    • Changedsearch_knowledge8 fields changed
      • removedOutput schema / description
        Removed value: -"Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple."
      • removedOutput schema / properties / data
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -  "type": "object"
        -}
      • removedOutput schema / properties / error
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Present only when success === false.",
        -  "properties": {
        -    "code": {
        -      "type": "string"
        -    },
        -    "hint": {
        -      "type": "string"
        -    },
        -    "message": {
        -      "type": "string"
        -    }
        -  },
        -  "type": "object"
        -}
      • removedOutput schema / properties / message
        Removed value: -{
        -  "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -  "type": "string"
        -}
      • addedOutput schema / properties / results
        Added value: +{
        +  "items": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "answer": {
        +        "type": "string"
        +      },
        +      "id": {
        +        "type": "string"
        +      },
        +      "question": {
        +        "type": "string"
        +      },
        +      "relevance": {
        +        "type": "number"
        +      },
        +      "truncated": {
        +        "type": "boolean"
        +      },
        +      "type": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "id",
        +      "question",
        +      "answer"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • removedOutput schema / properties / sampleMeta
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -  "properties": {
        -    "isTruncated": {
        -      "type": "boolean"
        -    },
        -    "sampleCount": {
        -      "type": "integer"
        -    },
        -    "totalCount": {
        -      "type": "integer"
        -    }
        -  },
        -  "type": "object"
        -}
      • removedOutput schema / properties / success / description
        Removed value: -"False on tool errors; check before reading `data`."
      • addedOutput schema / required
        Added value: +[
        +  "success",
        +  "results"
        +]
    • Changedsend_report_to_accounting1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedshare_report1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedsubmit_receipt1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedtrace_document1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedupdate_expense1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedwhatif_afford1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedwhatif_client1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
    • Changedwhatif_tax_setaside1 field changed
      • changedOutput schema / (root)
        Previous value: -{
        -  "additionalProperties": true,
        -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
        -  "properties": {
        -    "data": {
        -      "additionalProperties": true,
        -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
        -      "type": "object"
        -    },
        -    "error": {
        -      "additionalProperties": true,
        -      "description": "Present only when success === false.",
        -      "properties": {
        -        "code": {
        -          "type": "string"
        -        },
        -        "hint": {
        -          "type": "string"
        -        },
        -        "message": {
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "message": {
        -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
        -      "type": "string"
        -    },
        -    "sampleMeta": {
        -      "additionalProperties": true,
        -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
        -      "properties": {
        -        "isTruncated": {
        -          "type": "boolean"
        -        },
        -        "sampleCount": {
        -          "type": "integer"
        -        },
        -        "totalCount": {
        -          "type": "integer"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "success": {
        -      "description": "False on tool errors; check before reading `data`.",
        -      "type": "boolean"
        -    }
        -  },
        -  "type": "object"
        -}New value: +null
  13. 3 tool updates
    • Changedget_accounting_integration_status2 fields changed
      • changedInput schema / properties / provider / description
        Previous value: -"Accounting destination provider."New value: +"Accounting destination provider. QuickBooks Online, Xero, Wave, FreeAgent (beta), or Zoho Books."
      • changedInput schema / properties / provider / enum
        Previous value: -[
        -  "zoho_books"
        -]New value: +[
        +  "zoho_books",
        +  "quickbooks",
        +  "xero",
        +  "wave",
        +  "freeagent"
        +]
    • Changedget_accounting_push_status1 field changed
      • changedInput schema / properties / provider / enum
        Previous value: -[
        -  "zoho_books"
        -]New value: +[
        +  "zoho_books",
        +  "quickbooks",
        +  "xero",
        +  "wave",
        +  "freeagent"
        +]
    • Changedsend_report_to_accounting2 fields changed
      • changedInput schema / properties / mode / description
        Previous value: -"Step 1 only: automatic document treatment, paid expenses, or bills."New value: +"Step 1 only (Zoho Books): automatic document treatment, paid expenses, or bills."
      • changedInput schema / properties / provider / description
        Previous value: -"Accounting destination provider."New value: +"Accounting destination provider. Agent-driven posting is currently available only for Zoho Books."
  14. 13 tool updates
    • Changedadd_income_from_csv6 fields changed
      • changedInput schema / properties / csvContent / description
        Previous value: -"Raw CSV/TSV/text content (step 1). On ChatGPT you may pass the attached file reference object directly; the server fetches and decodes the bytes."New value: +"Attached CSV/TSV/text file supplied by ChatGPT for step 1. The server downloads and decodes the attachment securely."
      • addedInput schema / properties / csvContent / properties
        Added value: +{
        +  "download_url": {
        +    "description": "Temporary HTTPS URL supplied by ChatGPT for downloading the attachment.",
        +    "format": "uri",
        +    "type": "string"
        +  },
        +  "file_id": {
        +    "description": "Attachment identifier supplied by ChatGPT.",
        +    "type": "string"
        +  },
        +  "file_name": {
        +    "description": "Original attachment filename supplied by ChatGPT.",
        +    "type": "string"
        +  },
        +  "mime_type": {
        +    "description": "Attachment MIME type supplied by ChatGPT.",
        +    "type": "string"
        +  }
        +}
      • addedInput schema / properties / csvContent / required
        Added value: +[
        +  "download_url",
        +  "file_id"
        +]
      • changedInput schema / properties / csvContent / type
        Previous value: -"string"New value: +"object"
      • addedInput schema / properties / csvText
        Added value: +{
        +  "description": "Legacy fallback for MCP clients that send raw CSV/TSV/text content inline. ChatGPT should use csvContent.",
        +  "type": "string"
        +}
      • addedInput schema / properties / rowEdits / items / properties / fields / description
        Added value: +"Only the user-approved income fields to replace on this staged row: date, source, amount, currency, category, paymentMethod, description, notes, reference, fees, taxCollected, or tag."
    • Changedadd_income_from_file6 fields changed
      • changedInput schema / properties / photo / description
        Previous value: -"Base64-encoded image/PDF bytes (step 1). On ChatGPT you may pass the attached file reference object directly; the server fetches the bytes."New value: +"Attached image or PDF supplied by ChatGPT for step 1. The server downloads and parses the attachment securely."
      • addedInput schema / properties / photo / properties
        Added value: +{
        +  "download_url": {
        +    "description": "Temporary HTTPS URL supplied by ChatGPT for downloading the attachment.",
        +    "format": "uri",
        +    "type": "string"
        +  },
        +  "file_id": {
        +    "description": "Attachment identifier supplied by ChatGPT.",
        +    "type": "string"
        +  },
        +  "file_name": {
        +    "description": "Original attachment filename supplied by ChatGPT.",
        +    "type": "string"
        +  },
        +  "mime_type": {
        +    "description": "Attachment MIME type supplied by ChatGPT.",
        +    "type": "string"
        +  }
        +}
      • addedInput schema / properties / photo / required
        Added value: +[
        +  "download_url",
        +  "file_id"
        +]
      • changedInput schema / properties / photo / type
        Previous value: -"string"New value: +"object"
      • addedInput schema / properties / photoBase64
        Added value: +{
        +  "description": "Legacy fallback for MCP clients that send complete image/PDF bytes as base64. ChatGPT should use photo.",
        +  "type": "string"
        +}
      • addedInput schema / properties / rowEdits / items / properties / fields / description
        Added value: +"Only the user-approved income fields to replace on this staged row: date, source, amount, currency, category, paymentMethod, description, notes, reference, fees, taxCollected, or tag."
    • Changedadd_mileage_entry1 field changed
      • addedInput schema / properties / notes / description
        Added value: +"Optional user-supplied context stored with the mileage entry."
    • Changedcorrect_expenses8 fields changed
      • addedInput schema / properties / change / properties / attendees / description
        Added value: +"Attendee names explicitly supplied by the user when type is attendees; never infer names."
      • addedInput schema / properties / change / properties / businessPurpose / description
        Added value: +"User-supplied business purpose to apply when type is businessPurpose."
      • addedInput schema / properties / change / properties / category / description
        Added value: +"Configured expense category to apply when type is category."
      • addedInput schema / properties / change / properties / type / description
        Added value: +"The one correction kind to apply to every exact selected expense."
      • addedInput schema / properties / selection / description
        Added value: +"Exact expenses returned by search_expenses and approved for this bounded correction."
      • addedInput schema / properties / selection / properties / items / description
        Added value: +"One exact receipt identity per selected expense; never broaden this list after preview."
      • addedInput schema / properties / selection / properties / items / items / properties / expected / properties / category / description
        Added value: +"Current category observed before preview for conflict detection."
      • addedInput schema / properties / selection / properties / items / items / properties / expected / properties / notes / description
        Added value: +"Current structured Notes value observed before preview for conflict detection."
    • Changedcreate_report1 field changed
      • addedInput schema / properties / categories / description
        Added value: +"Optional configured expense categories to include in the report."
    • Changedget_accounting_push_status2 fields changed
      • addedInput schema / properties / provider / description
        Added value: +"Accounting destination provider used for the prior posting attempt."
      • addedInput schema / properties / reportId / description
        Added value: +"Exact ExpenseBot report ID from the prior proposal or posting attempt."
    • Changedget_client_invoice2 fields changed
      • changedInput schema / properties / invoiceId / description
        Previous value: -"Stable invoice ID from list_client_invoices."New value: +"Stable invoice ID from list_client_invoices. Required unless invoiceNumber is supplied."
      • changedInput schema / properties / invoiceNumber / description
        Previous value: -"Human invoice number when invoiceId is unavailable."New value: +"Human invoice number when invoiceId is unavailable; it must identify one unique invoice."
    • Changedget_spending_summary1 field changed
      • addedInput schema / properties / categories / description
        Added value: +"Optional configured expense categories to include in the summary."
    • Changedgroup_expenses7 fields changed
      • addedInput schema / properties / group / description
        Added value: +"The user-named destination group to apply to every eligible selected expense."
      • addedInput schema / properties / group / properties / kind / description
        Added value: +"How to label the destination in user-facing confirmation text."
      • addedInput schema / properties / reportTitle / description
        Added value: +"Optional title when createReport is true; otherwise ExpenseBot derives one from the group."
      • addedInput schema / properties / selection / description
        Added value: +"Exact expenses returned by search_expenses and approved for this bounded operation."
      • addedInput schema / properties / selection / properties / items / description
        Added value: +"One exact receipt identity per selected expense; never broaden this list after preview."
      • addedInput schema / properties / selection / properties / items / items / properties / expected / description
        Added value: +"Optional current state copied from search results for conflict detection."
      • addedInput schema / properties / selection / properties / items / items / properties / expected / properties / tag / description
        Added value: +"Current group/tag value observed before preview; an empty string means ungrouped."
    • Changedlist_reports3 fields changed
      • addedInput schema / properties / filter / description
        Added value: +"Optional report-status filter; All returns every authorized status."
      • addedInput schema / properties / limit / description
        Added value: +"Maximum reports to return on this page."
      • addedInput schema / properties / page / description
        Added value: +"One-based results page to return."
    • Changedprepare_client_invoice6 fields changed
      • addedInput schema / properties / clientAddress / description
        Added value: +"Optional billing address printed in the invoice's Bill To section."
      • addedInput schema / properties / customLineItems / items / properties / amount / description
        Added value: +"Positive amount for this fee or service line in the invoice currency."
      • addedInput schema / properties / customLineItems / items / properties / description / description
        Added value: +"User-approved fee or service description shown on the invoice."
      • addedInput schema / properties / mentionSupportingReceipts / description
        Added value: +"When true, note on the invoice that supporting receipts are available."
      • addedInput schema / properties / paymentTerms / description
        Added value: +"Invoice due terms. Defaults to the client's billing profile or net30."
      • addedInput schema / properties / taxPct / description
        Added value: +"Tax percentage applied after markup and custom line items; use 0 for no tax."
    • Changedsend_report_to_accounting2 fields changed
      • addedInput schema / properties / reportingTagMappings / additionalProperties / properties / optionId / description
        Added value: +"Live option ID belonging to tagId, returned in the step-1 proposal."
      • addedInput schema / properties / reportingTagMappings / additionalProperties / properties / tagId / description
        Added value: +"Live accounting-provider reporting-tag ID returned in the step-1 proposal."
    • Changedsubmit_receipt5 fields changed
      • changedInput schema / properties / photo / description
        Previous value: -"Base64-encoded image or PDF data (JPEG, PNG, HEIC, WebP, or PDF). Max 15MB. Only for small images — large payloads get truncated or dropped in transit; use uploadRef instead."New value: +"Single attached receipt image or PDF supplied by ChatGPT. For multiple files or large files, use filesToUpload and uploadRefs instead."
      • addedInput schema / properties / photo / properties
        Added value: +{
        +  "download_url": {
        +    "description": "Temporary HTTPS URL supplied by ChatGPT for downloading the attachment.",
        +    "format": "uri",
        +    "type": "string"
        +  },
        +  "file_id": {
        +    "description": "Attachment identifier supplied by ChatGPT.",
        +    "type": "string"
        +  },
        +  "file_name": {
        +    "description": "Original attachment filename supplied by ChatGPT.",
        +    "type": "string"
        +  },
        +  "mime_type": {
        +    "description": "Attachment MIME type supplied by ChatGPT.",
        +    "type": "string"
        +  }
        +}
      • addedInput schema / properties / photo / required
        Added value: +[
        +  "download_url",
        +  "file_id"
        +]
      • changedInput schema / properties / photo / type
        Previous value: -"string"New value: +"object"
      • addedInput schema / properties / photoBase64
        Added value: +{
        +  "description": "Legacy fallback for MCP clients that send one small image as complete base64 data. ChatGPT should use photo or uploadRefs instead.",
        +  "type": "string"
        +}
  15. 5 tool updates
    • Changedcreate_client_invoice16 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • removedInput schema / properties / advanceApplied
        Removed value: -{
        -  "minimum": 0,
        -  "type": "number"
        -}
      • removedInput schema / properties / billToEmail
        Removed value: -{
        -  "type": "string"
        -}
      • removedInput schema / properties / clientAddress
        Removed value: -{
        -  "type": "string"
        -}
      • removedInput schema / properties / clientName
        Removed value: -{
        -  "description": "In in-app chat, the full client name shown on the invoice.",
        -  "type": "string"
        -}
      • removedInput schema / properties / customLineItems
        Removed value: -{
        -  "items": {
        -    "properties": {
        -      "amount": {
        -        "exclusiveMinimum": 0,
        -        "type": "number"
        -      },
        -      "description": {
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "description",
        -      "amount"
        -    ],
        -    "type": "object"
        -  },
        -  "maxItems": 25,
        -  "type": "array"
        -}
      • removedInput schema / properties / invoiceNumber
        Removed value: -{
        -  "type": "string"
        -}
      • removedInput schema / properties / issueDate
        Removed value: -{
        -  "description": "In in-app chat, a calendar-valid YYYY-MM-DD invoice date.",
        -  "type": "string"
        -}
      • removedInput schema / properties / markupPct
        Removed value: -{
        -  "maximum": 100,
        -  "minimum": 0,
        -  "type": "number"
        -}
      • removedInput schema / properties / mentionSupportingReceipts
        Removed value: -{
        -  "type": "boolean"
        -}
      • removedInput schema / properties / paymentTerms
        Removed value: -{
        -  "enum": [
        -    "due_on_receipt",
        -    "net7",
        -    "net14",
        -    "net15",
        -    "net30",
        -    "net45",
        -    "net60"
        -  ],
        -  "type": "string"
        -}
      • removedInput schema / properties / receiptIds
        Removed value: -{
        -  "items": {
        -    "type": "string"
        -  },
        -  "type": "array"
        -}
      • removedInput schema / properties / reportId
        Removed value: -{
        -  "description": "In in-app chat, the saved report ID to prepare and confirm.",
        -  "type": "string"
        -}
      • removedInput schema / properties / taxLabel
        Removed value: -{
        -  "type": "string"
        -}
      • removedInput schema / properties / taxPct
        Removed value: -{
        -  "maximum": 100,
        -  "minimum": 0,
        -  "type": "number"
        -}
      • addedInput schema / required
        Added value: +[
        +  "preparationId"
        +]
    • Changedget_credits_refunds4 fields changed
      • addedInput schema / properties / dateRange / additionalProperties
        Added value: +false
      • changedInput schema / properties / dateRange / description
        Previous value: -"Optional window to narrow results. Both bounds inclusive, YYYY-MM-DD."New value: +"Optional open-ended or closed window to narrow results. Supply startDate, endDate, or both; supplied bounds are inclusive YYYY-MM-DD dates."
      • addedInput schema / properties / dateRange / properties / endDate / format
        Added value: +"date"
      • addedInput schema / properties / dateRange / properties / startDate / format
        Added value: +"date"
    • Changedget_expense_splits4 fields changed
      • addedInput schema / properties / dateRange / additionalProperties
        Added value: +false
      • changedInput schema / properties / dateRange / description
        Previous value: -"Optional window to search for split expenses. Both bounds inclusive, YYYY-MM-DD."New value: +"Optional open-ended or closed window to search for split expenses. Supply startDate, endDate, or both; supplied bounds are inclusive YYYY-MM-DD dates."
      • addedInput schema / properties / dateRange / properties / endDate / format
        Added value: +"date"
      • addedInput schema / properties / dateRange / properties / startDate / format
        Added value: +"date"
    • Changedmark_client_invoice_paid1 field changed
      • addedInput schema / properties / paidDate / format
        Added value: +"date"
    • Changedprepare_client_invoice1 field changed
      • addedInput schema / properties / issueDate / format
        Added value: +"date"
  16. 5 tool updates
    • Addedcreate_client_invoice
    • Addedget_client_invoice
    • Changedlist_client_invoices2 fields changed
      • changedInput schema / properties / status / description
        Previous value: -"Filter by invoice status (default open)."New value: +"Filter by invoice status (default active/outstanding)."
      • changedInput schema / properties / status / enum
        Previous value: -[
        -  "open",
        -  "paid",
        -  "all"
        -]New value: +[
        +  "active",
        +  "open",
        +  "overdue",
        +  "needs_review",
        +  "paid",
        +  "void",
        +  "superseded",
        +  "all"
        +]
    • Addedmark_client_invoice_paid
    • Addedprepare_client_invoice

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to manage personal finances through natural language, including adding, editing, listing, and deleting expenses, organizing them into categories, and setting monthly budgets with overspend warnings. It also provides spending summaries with month-over-month comparisons, budget status checks, and exportable Markdown or CSV reports, all backed by PostgreSQL storage with Google OAuth-authenticated per-user accounts.
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI assistants to perform financial analysis, budget forecasting, compliance checks, expense categorization, and risk assessment, returning structured JSON with audit-ready governance receipts.
    5
    63 npm
    1
    Business Source 1.1
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.