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 75 days
Last Tested
Transport
Streamable HTTP · MCP 2025-11-25
URL
Repository
TotesMagotes/expensebot-mcp
GitHub Stars
0
Server Listing
ExpenseBot MCP

TDQS

A3.6/5.0

Scored across 62 tools

Disambiguation2/5

The set contains many overlapping or adjacent tools: search vs. search_expenses vs. fetch, get_spending_summary vs. get_deep_analytics vs. get_pnl vs. get_monthly_books_review, and update_expense vs. correct_expenses vs. fix_compliance vs. group_expenses. Long descriptions try to disambiguate, but the boundaries are easy to miss and misselection is likely.

Naming Consistency4/5

Most tools follow a predictable snake_case verb_noun convention such as add_income_from_csv, get_report_details, list_client_invoices, and send_report_to_accounting. A few names deviate, notably fetch, search, trace_document, and the whatif_* prefix, but overall consistency is good.

Tool Count1/5

With 62 tools, the surface is far beyond a well-scoped MCP server and contains many highly specialized analytics, compliance, accounting, and import variants. Even for a broad expense-management product, this is an extreme count that burdens tool selection and maintainability.

Completeness4/5

The surface covers expenses, income, reports, invoices, tags, mileage, Gmail scanning, accounting integrations, analytics, and what-if planning quite thoroughly. Some gaps remain, such as no explicit expense deletion or income update/delete tools, but the core workflows are broadly covered.

Available Tools

62 tools
add_cash_expenseAInspect

Add an expense without a receipt, or record money a client provided in advance. 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 that only for money a client supplied before related spending, never for vendor refunds, reimbursements, earned fees, retainers or generic deposits. For multiple entries, credits/refunds, tax or tip breakdowns, per diem or incomplete details, use the Manual expense link from get_spreadsheet_url instead.

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

NameRequiredDescription
dataNo
expenseNo
messageYes
successYes
recordIdNo
rowNumberNo
finalAnswerNo
spreadsheetUrlNo
responseGuidanceNo
reviewExpensesUrlNo

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare readOnly=false, destructive=false, openWorld=true; the description adds substantial context beyond them: the write goes 'directly to the user's expense spreadsheet after confirmation', the tool reuses or creates the canonical client group, applies the 'Cash advance received' account, and stores the amount as a negative expense. That is exactly the side-effect and state-mutation disclosure an agent needs for a write tool.

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 core action and the two modes before the exclusions. It is fairly long (six-plus sentences) but nearly every clause carries routing or behavioral information; only the repeated advance guidance in the closing sentence is mildly redundant.

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 mutation tool with an output schema (so return values need not be explained) and non-destructive annotations, the description supplies prerequisites, mode branching, side effects, and fallbacks. An agent can call it correctly without opening any sibling.

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 only implies: category is required for ordinary expenses but not for a client advance, merchant is derived from clientName in the advance path, and total is stored negated. It doesn't cover tax/tip/subtotal handling, but those are adequately self-documented.

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 ('Add an expense without a receipt'), then immediately distinguishes two distinct modes: ordinary expense vs. client advance. It also names the exact sibling (list_categories) needed for the ordinary path, making it clearly separable from add_income and 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 Guidelines5/5

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

Explicitly routes the agent: ordinary expense → list_categories first; client advance → isClientAdvance=true plus clientName. It also lists hard exclusions ('never for vendor refunds, reimbursements, earned fees, retainers or generic deposits') and names the fallback path (Manual expense link from get_spreadsheet_url) for multi-entry, refunds, tax/tip breakdowns, and per diem.

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.

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

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.

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

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). Two steps. STEP 1: call with the file and WITHOUT confirm. ExpenseBot parses it with the same importer as the app's Add Income screen, checks every row against the Income tab for duplicates, and returns a preview with previewId, exact row count, totals by currency, per-row details, duplicate flags and rejected rows. NOTHING is saved in step 1; the attachment is consent to parse, not to write. Show the parsed rows and duplicates, then STEP 2: call again with confirm: true and the previewId to write exactly those rows, including only user-approved changes (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 visually complex reviews (mixed income/expense rows, many edits), send the user to the Add Income app link from get_spreadsheet_url.

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

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 declare non-read-only but non-destructive; the description supplies the missing operational detail: nothing is written in step 1, the attachment is consent to parse only, previews expire after 15 minutes, and an expired or unknown previewId never writes. These are non-obvious safety behaviors that go well beyond the annotation flags.

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 resource and format list, then cleanly chunked into STEP 1 and STEP 2 with a closing fallback route. It is long for a single tool description, but nearly every clause carries actionable information; only the format enumeration in the first sentence borders on excess.

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, the description covers the workflow, the write-safety boundary, expiry, duplicate handling, and post-confirm next steps (spreadsheetUrl, reviewIncomeUrl). With annotations and an output schema present, 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 sequencing semantics the schema only implies: which parameters belong to step 2, that selectedIndexes/keepBothIndexes/rowEdits/bulkNote compose into 'only user-approved changes,' and that previewId ties step 2 back to step 1. Remaining duplication of per-field descriptions keeps it from 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 and resource (import income from an attached screenshot/image/PDF), enumerates supported formats and size limit, and implicitly separates itself from add_income and add_income_from_csv by naming the file-based, two-step parse-then-confirm model. An agent can identify the tool and its scope 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?

Gives an explicit two-step protocol (call without confirm, then call with confirm:true and previewId), states which step accepts which arguments, and routes visually complex reviews to the Add Income app link from get_spreadsheet_url. It also specifies the duplicate policy (skipped unless keepBothIndexes) so the agent knows the default behavior.

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.

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

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.

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

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.

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

Output 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.

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

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, e.g. 'categorize these as Travel', 'add Client kickoff dinner as the business purpose for these meals', '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 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; replace substitutes only the attendee segment. Business purpose and attendees preserve the structured Notes field (card, description, inbox and other typed segments). Formula Notes and changes exceeding 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 over-100-row cleanup, send the user to https://www.expensebot.ai/review-expenses?source=mcp.

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

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; the description goes well beyond that by disclosing the mandatory preview gate, that a premature confirm:true is downgraded to preview, attendeeMode add-vs-replace semantics, that Notes structured segments are preserved while Formula Notes and over-limit changes are skipped, and that the confirmed result reports applied/conflicted/failed counts with Undo support. These are exactly the operational facts an agent needs before a destructive write.

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 examples, followed by the preview/confirm protocol and then constraints, so scanning order matches decision order. It is long and dense for a tool description, but nearly every sentence carries a rule an agent would otherwise get wrong; only the Notes/Formula detail is somewhat compressed.

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, 6-parameter tool with an output schema, the description supplies the full operating protocol (search → select → preview → approve → confirm → undo) plus the guardrails that annotations alone leave ambiguous. Return-value detail is correctly left to the output schema, with only the applied/conflicted/failed counts summarized.

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 3 is the floor; the description still adds workflow meaning the schema cannot express, namely that operationId must be generated once and reused unchanged between preview and confirm, and that the selection list must not be broadened after preview. It also explains the conflict detection purpose of expected values indirectly through 'before-to-after changes'.

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 concrete verb (correct) plus the exact resources it can touch (category, business purpose, attendees) on a bounded set of recorded expenses, and makes the scope explicit with 'exact bounded set' and an example set that maps directly to the three change types. It is clearly distinguishable from search_expenses (upstream selector) and update_expense (singular edit).

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 the sequence: call search_expenses first, then pass full expenseId values, preview with confirm omitted/false, ask once, then confirm:true with the same operationId. It also states what the tool will not do (no duplicate omission, no amount/date edits, no inferred business context, no broad Calendar matching) and routes unsupported or over-100-row work to an external URL, giving an unambiguous alternative path.

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.

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

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 intended group is unclear. For 'all expenses in August except personal' set excludePersonal=true (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 needing visual review. Reports scoped to an existing client/project/trip group include matching expenses not already on another ordinary report. Can optionally share with recipients. If no unreported matches remain, no duplicate report is created; explain the expenses are already in Reports. Terminal results also include role-appropriate accountingHandoffUrls; use only the exact destination the user requested, since 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 analysis.

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 matching 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

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

TDQS

A4.6/5.0
Behavior4/5

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

Annotations declare readOnly=false and destructiveHint=true; the description adds real behavioral context beyond them: no duplicate report is created when no unreported matches remain, group-scoped reports only pull expenses not already on another ordinary report, and accountingHandoffUrls do not mean an export occurred. It stops short of clarifying the destructive/irreversible aspect implied by destructiveHint (what a created report locks or changes), so not a perfect score.

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 key routing advice are front-loaded, and nearly every sentence carries non-obvious operational detail (duplicate suppression, group scoping, handoff URLs, tool-call exclusions). It is dense and long, but the density is mostly justified rather than padding.

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, and the description still names the key returns (report summary, report link, Bill Client link, applied filters, Wizard fallback). Combined with the duplicate-suppression rule, sharing behavior, and the terminal handoff URL caveat, an agent has everything needed to call this 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 100%, so baseline is 3; the description still adds meaning by explaining that tags are ExpenseBot's grouping layer for clients/projects/trips and that excludePersonal covers both the Personal tag and the Personal expense category to match Report Wizard behavior. It adds little on shareWith/recipientRole beyond what the schema already documents.

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?

Opens with a specific verb+resource (create an expense report) and immediately scopes the inputs it accepts (date, client, project, trip, category, merchant). It is clearly distinguishable from siblings such as list_reports, get_report_details, and export_report, which it implicitly rules out via the 'do not call check_compliance, get_report_details...' instruction.

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 routing: call list_tags when the intended group is unclear, use excludePersonal=true for the 'all expenses except personal' case, use recipientRole only when ongoing accounting access is wanted. It also states negative conditions – do not pre- or post-call compliance, report-detail, or tax tools unless the user asks.

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

create_tagAInspect

Create a standalone group/tag without selecting or changing any expenses. The first call prepares a preview; after the user explicitly chooses to create it, send confirm:true with the returned operationId (fresh duplicate checks run). A first call with confirm:true still returns a preview. An exact canonical duplicate is a no-op; archived tags are not reactivated. Similar names are returned as choices; set allowSimilar:true only when the user explicitly chooses a distinct new group despite them. Use list_tags to inspect all groups.

NameRequiredDescriptionDefault
nameYesName of the new group/tag.
statusNoWhen true, read the verified outcome for operationId without creating or changing a tag.
confirmNoConfirm only after the user approves the preview; a first confirm call remains preview-only.
operationIdNoServer-issued preview operation ID to confirm a staged create.
allowSimilarNoAllow creation alongside similar names only when the user explicitly chooses a new distinct group.

Output Schema

NameRequiredDescription
nameNo
errorNo
statusNo
choicesNo
createdNo
messageNo
successYes
retryableNo
existingTagNo
operationIdNo
similarTagsNo
confirmationNo
existingTagsNo
previewStaleNo
canonicalNameNo
requestedNameNo
existingMatchesNo
outcomeVerifiedNo
existingTagCountNo
groupManagementUrlNo
confirmationRequiredNo
requiresConfirmationNo
existingTagsTruncatedNo

TDQS

A4.9/5.0
Behavior5/5

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

Goes well beyond the annotations: discloses a staged preview/confirm transaction, that fresh duplicate checks re-run on confirm, that exact canonical duplicates are a no-op, that archived tags are not reactivated, and that similar names surface as choices. These are non-obvious semantics an agent would otherwise get wrong on a write 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?

Six tight sentences, front-loaded with the core action then the confirmation protocol, then the edge cases. No restatement of the tool name and no filler; each sentence carries a distinct constraint.

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 two-step confirm flow is the main complexity risk for this tool and it is fully spelled out, including the fresh-duplicate-check behavior on the second call. An output schema exists, so omitting return-value detail is correct, and nothing needed 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, but the description adds meaning beyond the field docs: it explains the operationId round-trip between calls, that a first confirm:true call still only previews, and the risk condition under which allowSimilar is legitimate. It does not mention the status parameter's polling use, which the schema covers instead.

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 the specific verb and resource ('Create a standalone group/tag') and immediately scopes it against the obvious sibling: it does not select or change expenses, unlike group_expenses. An agent can distinguish this from group_expenses and list_tags 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?

Gives explicit when-to-use choreography (preview first, confirm only after user approval), an exclusion (don't set allowSimilar unless the user explicitly wants a distinct group), and routes inspection to list_tags. The confirm-without-preview edge case is called out rather than left to inference.

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

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.

NameRequiredDescriptionDefault
reportIdYesReport ID from list_reports

Output 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.

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

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 get each full expenseId and pass those exact identities; never select every report row or infer a business purpose, category or attendee. Same reviewed workflow as correct_expenses: the first call always prepares a grounded preview and changes nothing, 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 over-100-row cleanup open https://www.expensebot.ai/review-expenses?source=mcp.

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

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

TDQS

A4.7/5.0
Behavior5/5

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

While annotations confirm destructive intent, the description adds critical operational behavior: it mandates a two-stage workflow (preview without changing anything, followed by explicit approval to execute), guarantees idempotency via 'operationId', and discloses outcome reporting (applied/conflicted/failed counts and Undo support).

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 definition is extremely verbose, detailing a complex multi-stage operational workflow (preview, approval, execution, reporting). While every sentence serves a functional purpose, the overall length sacrifices conciseness for maximum behavioral 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?

Given the complexity (multi-stage state change, idempotency, and failure modes), the description is highly complete. It successfully marries the low-level parameters provided by the schema with the high-level business logic and behavioral rules required for safe execution.

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 100% schema description coverage, the baseline is 3. The description adds valuable constraints and context, such as specifying that selections must use 'exact full expenseId/Column-Q receipt identity' and that the `confirm:true` status is only valid *after* explicit user approval, adding necessary intent-based guidance beyond the structure.

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 identifies the function ('correct compliance issues'), the target resource ('recorded expenses'), and the strict prerequisite workflow (must use issues found by 'check_compliance'). This makes it immediately distinct from 'check_compliance' and 'correct_expenses' siblings.

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 explicit preconditions ('Use only after the user explicitly asks to fix issues found by check_compliance'). Furthermore, it mandates strict usage constraints (bounded set of expenses, exact IDs from search_expenses) and outlines a complex, staged workflow.

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.

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

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.

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

Output 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.

NameRequiredDescriptionDefault
clientNameNoOptional client name or canonical Client tag. Omit to list every open balance backed by a recorded client advance.
clientEmailNoAccepted client's account email (accountants only).

Output Schema

NameRequiredDescription
dataNo
messageNo
successYes
balancesNo
homeCurrencyNo

TDQS

A4.4/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 them: it defines the sign convention (negative = money remains to refund, positive = client owes the user) and explicitly states it never records a refund, creates an invoice, or recomputes the ledger in prose. That is exactly the kind of domain behavior an agent cannot infer from annotations or schema.

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 verb+resource, then usage examples, then the crucial sign-convention and read-only caveats — logical ordering with little waste. The middle list of three example questions is slightly verbose but serves as genuine usage scaffolding.

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 needn't explain return shapes; it covers the semantic interpretation of the returned balances (sign convention) and the handoff behavior (Refund leftover / Bill Client), which are the pieces a schema cannot convey. 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.

Parameters3/5

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

Schema description coverage is 100%, so both clientName and clientEmail are already documented in the schema. The description reinforces the 'omit to list all open balances' default but adds no syntax or format detail beyond what the schema provides, so the baseline of 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') and resource ('per-client advance balances'), plus ties it to the visible Client advances section of the app so an agent knows exactly which data surface it touches. The example questions ('how much of Acme's advance remains?', 'which client floats are still open?') further pin the domain apart from siblings like get_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 example questions give clear usage context, and the closing sentence explicitly excludes writing behavior. It does not explicitly compare against the nearest sibling (get_credits_refunds) or say when a different tool should be chosen, so it stops short of full when/when-not routing.

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.

NameRequiredDescriptionDefault
invoiceIdNoStable invoice ID from list_client_invoices. Required unless invoiceNumber is supplied.
clientEmailNoAccepted client's account email (accountants only).
invoiceNumberNoHuman invoice number when invoiceId is unavailable; it must identify one unique invoice.

Output Schema

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

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, and the description reinforces rather than contradicts that. It adds context annotations do not carry: links are 'private' Google Doc/PDF/DOCX and only present 'when available', and invoiceNumber lookups fail unless unique. No mention of failure behavior when the number is ambiguous, which is the one real gap.

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?

Two sentences, front-loaded with purpose before constraints, and no filler. The return-field list is dense but each item maps to real output the agent may need, though it pushes the sentence toward a catalogue rather than a summary.

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 field enumeration is arguably redundant, but it is harmless and the identifier rules cover the only decision an agent must make before calling. The gap is guidance on recovering from a non-unique invoiceNumber or resolving the ID via list_client_invoices.

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 schema already documents all three parameters, including the accountants-only constraint on clientEmail. The description adds only the 'exactly one identifier required' and 'must be unique' semantics, and it never mentions clientEmail at all, so it is at the baseline for a fully-covered 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 ('Get') and a precisely scoped resource ('full financial and delivery summary for one issued client invoice'), and the singular scope cleanly separates it from the sibling list_client_invoices. The enumeration of what the summary contains leaves no ambiguity about the payload.

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 explicit identifier rules: 'Provide invoiceId or invoiceNumber — one is required; invoiceNumber is accepted only when it is unique.' That tells the agent how to satisfy the call, but it never names list_client_invoices as the way to obtain an invoiceId, nor states when to prefer this over the list tool.

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.

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.
clientEmailNoAccepted client's account email (accountants only).

Output Schema

NameRequiredDescription
dataNo
creditsNo
messageNo
refundsNo
successYes

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, and the description reinforces this with concrete negative guarantees — no card scanning, no review-decision changes, no row insertion — which are more actionable than the hints alone. It also discloses result ordering and the default window (25, newest first), which annotations cannot convey.

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

Conciseness4/5

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

Front-loads what the tool lists, then examples, then result behavior and safety guarantees — a sensible order. Slightly dense in one paragraph, but every clause (examples, ordering, read-only guarantees) carries information.

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 and full parameter documentation, the description need not explain return shapes; it still covers scope, ordering, and safety. It omits mention of the clientEmail accountant path, which the schema covers, so nothing critical 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 limit, dateRange bounds, and clientEmail are already fully documented. The description only adds the 'newest first' ordering and restates the default of 25, adding modest value 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 (list) and a precise resource (card refunds, cashback/rewards, statement credits already recorded), plus how they are recorded (negative expenses or matched to original charge). The quoted user-intent examples ('did my refund come through', 'was that return recorded') make the scope unmistakable against generic search tools like 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?

Provides concrete example queries that trigger it and tells the agent to narrow with dateRange. The closing clause implicitly routes scanning/mutating cases elsewhere ('never scans cards, changes review decisions, or adds rows'), though it does not name the alternative tools (scan_gmail, correct_expenses) explicitly.

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.

NameRequiredDescriptionDefault
queryYesThe analytics question to answer
dateRangeNoTime period filter; use exactly one variant matching the user's phrasing.

Output 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?

Beyond the read-only, non-destructive annotations, the description discloses important behavior: it takes 10-30 seconds, runs as a background job that is polled automatically, and returns a truncation flag indicating whether the full dataset was visible. These details materially affect how an agent should invoke and interpret the 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 and front-loaded: purpose first, then usage examples, then latency/async behavior, then return-shape caveat. Every sentence provides useful information without repetition.

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 annotations, full schema coverage, and existing output schema, the description is complete enough to call the tool correctly. It adds the important async latency and truncation semantics that an agent needs when interpreting results.

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 the query and dateRange parameters are already well documented in the schema. The description reinforces usage with examples like Q1 vs Q2, but it does not add parameter-level 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 ('Run deeper, multi-step analytics on the user's expenses') and distinguishes the tool from simpler summary or lookup siblings through the phrase 'deeper, multi-step'. The explanatory-question examples further clarify its distinct analytical role.

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 clear when-to-use guidance with concrete examples such as 'why did my spending increase' and 'compare Q1 vs Q2'. It does not explicitly name alternatives like get_spending_summary or get_pnl, so the agent must infer that simpler summaries belong elsewhere.

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.

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

Output 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.

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.
clientEmailNoAccepted client's account email (accountants only).

Output Schema

NameRequiredDescription
dataNo
splitsNo
messageNo
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 openWorldHint=false, so the description earns credit for the substantive additions: results are one parent payment with nested lines, split lines are never counted as separate expenses, and totals remain correct as a consequence. That aggregation semantics is exactly the kind of non-obvious behavior an agent needs, and it is not derivable from the annotations.

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

Conciseness4/5

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

Four sentences, front-loaded with the core behavior and aggregation caveat before the example queries. The examples are functional rather than filler, though the read-only sentence partially duplicates the readOnlyHint annotation.

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 details are unnecessary; the description covers scope, aggregation semantics, and the read/write boundary. Combined with fully documented parameters, 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%, with limit, dateRange bounds, receiptId, clientEmail and the onlySplit flag all documented in-schema. The description adds no parameter-level detail beyond what the schema provides (it never mentions onlySplit=false for inspecting an unsplit receipt, nor the accountants-only constraint on clientEmail), 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 and resource (show how an expense was split) and enumerates the split dimensions (categories, clients, properties, business vs personal). The parent-payment-with-nested-allocation framing cleanly distinguishes it from flat listing tools like search_expenses or get_expense_by_id.

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 three concrete example queries that map directly to invocation, plus an explicit boundary: reads happen here, edits happen in the Review workspace. It stops short of naming sibling tools (e.g., use search_expenses to locate the receipt first, then this to inspect its allocation), so routing is clear but not exhaustively specified.

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.

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 matching 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

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?

Beyond the readOnly and non-destructive annotations, the description discloses that it returns message/data with total plus optional breakdown, comparison and sampleMeta. It also states what the tool does not do: scanning, imports, mutations, tax returns and liability determination, which is 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 front-loaded with the usage trigger and then packs purpose, parameters, return shape and exclusions into a short paragraph. Each clause carries information and there is little repetition.

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 annotations and an output schema present, the description supplies enough context about when to call it, what it returns and what it explicitly does not do. An agent can invoke it correctly without additional documentation.

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, including the dateRange variants and groupBy enum. The description reinforces the groupBy dimensions and filters but adds no syntax or interaction detail beyond the schema, 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?

The description states a specific verb and resource: it reads income totals from the ExpenseBot Income tab. It clearly separates this from scanning, importing, mutating, tax-return and liability work, so an agent can distinguish it from siblings such as add_income or scan_gmail.

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 this when the user asks about income already recorded...') and exclusions ('No scanning, imports, mutations...'). It does not name alternative tools for adjacent tasks such as expense summaries or recording income, so it falls just short of full 5-level guidance.

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 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 receipt summaries. A held_for_review file (withheld for missing details or a possible duplicate) was not added; relay the next step in its message. found:false is pending with no terminal verdict; found:true is terminal. On completion report the authoritative per-file outcomes. Show spreadsheetUrl and reviewExpensesUrl for rows actually added, not 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 send held uploads to Gmail Action Needed. Unknown outcomes stay unknown; do not blindly resubmit. For uploaded receipts only; use get_scan_status for Gmail scans. Read-only.

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

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.7/5.0
Behavior5/5

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

Annotations only declare readOnly/non-destructive/closed-world; the description goes far beyond by disclosing the pending-vs-terminal semantics via found:false/found:true, the full verdict taxonomy (added, duplicate, skipped, held_for_review, errored), what held_for_review means operationally, and the hard rule against blind resubmission. This is exactly the async/authority context annotations cannot carry.

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

Conciseness3/5

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

Front-loaded and mostly well-organized, but the 'do not resubmit' warning is stated twice ('must not trigger a duplicate resubmission' and 'Unknown outcomes stay unknown; do not blindly resubmit'), and the spreadsheetUrl/reviewExpensesUrl presentation rules overlap with what the output schema already encodes. A few sentences do not earn their 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 an async polling tool with held/review states and an output schema, the description covers the lifecycle, polling budget, terminal-state interpretation, and downstream reporting obligations. Nothing an agent needs to call it or act on the result 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 meaning the schema lacks: it binds submissionId to the polling interval and time allowance returned by submit_receipt, and frames withinSeconds as a fallback path rather than a primary input. Marginal but real added value over the field 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 ('Check the authoritative final outcome of a receipt image/PDF batch submitted with submit_receipt') and explicitly names the sibling it is not for ('use get_scan_status for Gmail scans'). An agent can distinguish it from submit_receipt and get_scan_status 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?

Gives the exact trigger ('After submit_receipt returns submissionId, call this tool with that exact ID'), the polling cadence source and time budget, an explicit negative ('A pending result is normal and must not trigger a duplicate resubmission'), and the alternative for out-of-scope uploads. When, when-not, and alternatives are all covered.

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? } }.

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 matching the user's phrasing.

Output Schema

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

TDQS

A3.9/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 safety is covered. The description adds genuinely useful behavioral context the annotations lack: deduction framing with country-aware IRS/CRA rates and cents-per-mile computation.

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 purpose ('Mileage analytics — totals, breakdowns...') before listing dimensions, deduction framing, examples and return shape. Dense but each clause carries information; the parenthetical rate explanation is slightly heavy but 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?

With an output schema, full schema coverage, and safe annotations, the description covers what an agent needs to call it and interpret results. The gap is minor: it doesn't clarify how free-text filtering (e.g. by client) interacts with groupBy.

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 parameters are well documented and the baseline is 3. The description's examples loosely map to the query parameter, but it also says breakdown 'by client' while the groupBy enum offers 'tag' rather than 'client', a minor mismatch that doesn't clarify over 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?

States a specific verb and resource ('Mileage analytics — totals, breakdowns') and enumerates the breakdown dimensions and deduction framing, so an agent knows exactly what it returns. It doesn't explicitly name competing siblings like get_spending_summary or get_income_summary to route away from them, keeping it just 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?

Provides concrete usage examples ('mileage this year', 'miles driven for Acme', 'business miles last quarter') and notes comparison phrasing support, giving clear context for when to reach for it. It never states when-not-to-use or the write alternative (add_mileage_entry), so no exclusions.

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.

NameRequiredDescriptionDefault
periodNoCalendar month in YYYY-MM format. Defaults to the last completed month.
clientEmailNoAccepted client's account email (accountants only).

Output Schema

NameRequiredDescription
dataNo
messageNo
successYes
closeReadinessNo

TDQS

A4.3/5.0
Behavior5/5

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

This is highly transparent. The description explicitly warns that advisory notes are 'estimates, not tax advice' and states that the tool 'does not close books or certify tax completeness.' This crucial context far exceeds what the readOnlyHint annotation provides.

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 long because it attempts to cover definition, usage examples, and legal disclaimers in one block. While comprehensive, it sacrifices conciseness for exhaustive explanation.

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 complexity of financial summaries, the description is remarkably complete. It covers scope, limitations, output intent (summary and alerts), and even provides specific example prompts, ensuring an agent can handle various user queries.

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%, meaning the schema itself is robust. The description merely reinforces the schema's descriptions and the implied default behavior of the 'period' parameter, thus meeting the baseline score.

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 provides a 'Month-end summary' encompassing specific financial metrics like income, spending, net, categories, and alerts. This comprehensive scope distinguishes it from simpler sibling tools like 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?

The description provides numerous clear examples of user intent ('how did last month go', 'close out my books for June'), which serves as strong guidance for when to use the tool. It also clarifies the default behavior, using the last completed month.

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.

NameRequiredDescriptionDefault
queryNoNatural language per-tag P&L question
dateRangeNoTime period filter; use exactly one variant matching 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

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.0
Behavior4/5

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

Annotations already cover read-only safety and closed-world scope, but the description adds real behavior: the tool silently returns nothing unless both sides of a tag are posted, YTD is the default window, and 'margin renders as multiplier in loss territory' is a genuine output-format quirk. That is meaningful context beyond the annotation set.

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 lead sentence front-loads what is computed and how it is grouped, and the constraint sentences follow in priority order. The five example phrases and four prefix shortcuts are somewhat list-heavy for one description, but they are the natural language this tool parses, so they earn their 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?

With an output schema present, the description need not explain return values, and it does cover the data precondition, the default date window, and the loss-margin rendering quirk. What remains unstated is what an agent should do when the tag-matching precondition fails (empty bucket vs. error), which is the one operational 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?

Schema coverage is 100%, so the baseline is 3, but the description adds two things the schema does not: the year-to-date default when dateRange is omitted, and the semantic framing of tagPrefix as a bucketing limiter tied to the same prefix shortcuts it lists. That is additive rather than redundant.

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

Purpose4/5

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

The opening clause names the specific output (revenue, cost, profit, margin) and the grouping dimension (tag), which distinguishes it from the untagged sibling get_pnl. The tag-type parenthetical (per-client, per-property, per-event, per-realtor-deal) sharpens the resource further, though it never names get_pnl or get_income_summary explicitly as the 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?

It gives concrete query phrasings ('profit by client', 'per-client P&L this year vs last') and states the precondition that both income and expense rows must carry matching tags, plus the default of year-to-date. It stops short of saying when to prefer this over get_pnl or get_income_summary, so the routing decision is only implied.

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? } }.

NameRequiredDescriptionDefault
queryNoNatural language P&L question (e.g., 'P&L for Q1 2025', 'am I profitable')
dateRangeNoTime period filter; use exactly one variant matching the user's phrasing.
clientEmailNoAccepted client's account email (accountants only).

Output 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=true and destructive=false, so safety is covered. The description adds genuine behavioral detail beyond that: period-comparison modes supported (YoY/MoM/QoQ/same-month-prev-year) and the non-obvious loss-margin rendering as a multiplier rather than a negative percentage, which is exactly the kind of surprise an agent should be warned about.

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 purpose, trigger examples, comparison modes, and return shape appear in that order with no filler; the margin-multiplier note is a single justified sentence preventing misread output. It is dense but every clause carries signal.

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 reporting tool with full schema coverage and an output schema, the description supplies what's needed: what it combines, what periods/comparisons are legal, and what the payload contains. Minor gap: no guidance on default period when no dateRange is supplied (0 required params).

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 earns above baseline by explaining that comparison types are driven from the natural-language query and by mapping example phrasings to dateRange styles. It does not add much on clientEmail, but the schema already documents it as accountant-only.

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 enumerates the metrics produced (net income, margin). The examples cement scope so an agent can distinguish it from income-only siblings like get_income_summary or get_per_tag_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?

Provides concrete trigger phrasings ('am I profitable this year', 'P&L for Q1', 'what's my margin', YoY comparison), which is clear usage context. It stops short of naming alternatives or stating when NOT to use it (e.g., use get_per_tag_pnl for per-tag breakdowns, get_income_summary for revenue-only).

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).

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

Output 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. Without checkFreshness:true, returns the stored report only. When explicitly requested together with analysisRequested:true, freshness reads canonical Google Sheets sources with a 5-second bound; if full tax-year replay is unavailable, status is unknown, not current. Never call this as a preflight or follow-up to creating, sharing, or billing from a report; those tools already return the requested result and links.

NameRequiredDescriptionDefault
reportIdYesThe report/spreadsheet ID
clientEmailNoAccepted client's account email (accountants only).
checkFreshnessNoSet true only when the user explicitly asks whether this Year End report is current. Requires analysisRequested:true; runs a bounded Google Sheets source check and may return unknown. Never use after report creation, sharing, or billing.
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

NameRequiredDescription
reportYesThe full stored report record.
successYes
freshnessNo

TDQS

A4.8/5.0
Behavior5/5

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

Goes well beyond the readOnly/openWorld annotations: it discloses that the default path reads only the stored report, that the freshness path hits canonical Google Sheets sources under a 5-second bound, and that an incomplete tax-year replay yields 'unknown' rather than 'current'. That degraded-result semantics is exactly the kind of behavior an agent cannot infer from structured fields.

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

Conciseness4/5

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

Front-loaded with the resource and its payload, then the gating rules, then the exception path — a sound ordering with no filler. The freshness sentence restates some of what the schema already says about analysisRequested, which is the only 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?

Complete for a four-parameter read tool with an output schema: return shape needn't be described, and the description instead spends its budget on the non-obvious parts — consent requirements, the freshness escalation, and the unknown-vs-current failure mode. Nothing needed 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, but the description adds genuine meaning: it ties checkFreshness to a bounded source check, explains the unknown-status outcome, and reinforces that analysisRequested must reflect explicit user intent. clientEmail (accountants-only) is not mentioned in the description, leaving one parameter to the schema alone.

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 ('Get full details of a specific expense report') plus the payload it returns: all expenses, totals, and compliance status. It also carves itself out from the write-side siblings (create, share, bill) by declaring it is never a preflight or follow-up to those, so an agent can place 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 Guidelines5/5

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

Gives an explicit positive condition ('only when the user explicitly asks to inspect an existing report's details') and an explicit negative one ('Never call this as a preflight or follow-up to creating, sharing, or billing'). The checkFreshness escalation has its own trigger condition, so both the default and the elevated path are routable.

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 authoritative Gmail receipt scan status. Returns the active lock and live phase/progress shown in-app, queued or attention-needed periods, completed calendar years, and tax-period statuses with their returned labels and inclusive ISO bounds, plus exclusions and recent outcomes. Use when asked if a scan is running, finished, stuck, or what it is doing. On completion show spreadsheetUrl or reviewExpensesUrl; for setup/reconnection show gmailScanUrl. Read-only.

NameRequiredDescriptionDefault

No parameters

Output 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, openWorldHint=false, and destructiveHint=false. The description adds useful behavioral detail beyond that, including live phase/progress, queued/attention-needed periods, exclusions, recent outcomes, and which URL to surface after completion versus setup/reconnection.

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 front-loaded with purpose and then usage triggers. It is somewhat dense and lists many return fields even though an output schema exists, but the routing guidance is valuable and most sentences earn their 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?

With zero parameters, an output schema present, and annotations covering safety, the description is complete enough for correct invocation. It explains what kind of status is returned and how to route the user to the appropriate URL afterward.

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?

There are zero parameters, so no parameter semantics are needed. The baseline for a parameterless tool is 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 states a specific verb and resource ('Check authoritative Gmail receipt scan status') and clearly separates this status tool from scan-triggering or receipt-processing siblings. It also names the domain object ('Gmail receipt scan') precisely.

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 explicit usage triggers: asked whether a scan is running, finished, stuck, or what it is doing. It does not explicitly name alternatives such as scan_gmail or get_last_receipt_result, but the intended 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_spending_summaryB
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? } }.

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 matching the user's phrasing.
categoriesNoOptional configured expense categories to include in the summary.
clientEmailNoAccepted client's account email (accountants only).

Output 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

B3.4/5.0
Behavior3/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 description's 'Read-only.' adds nothing. The 'Returns:' line duplicates the existing output schema. The only genuine behavioral signal is the mention of period comparisons, which is modest added context over structured fields.

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

Conciseness4/5

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

Purpose and scope are front-loaded in the first sentence, capabilities follow, and the read-only note is a four-word line. The trailing 'Returns:' block partly repeats the output schema and could be dropped, but overall there is little waste.

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?

With annotations and an output schema present, the safety and return-shape burden is covered. What is missing is guidance for a tool with zero required parameters: no statement of default scope (all-time? last month?), and no mention of the optional 'clientEmail' scoping for accountants, which is a meaningful behavioral difference.

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%, so the schema carries most of the weight and baseline 3 applies. The description echoes the groupBy and metric enum values and the date-range concept, but says nothing about 'categories' or 'clientEmail' (accountant-only scoping), nor about behavior when no date range is supplied.

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 ('summarize the user's recorded expenses') and enumerates the exact grouping dimensions and metrics, so an agent can tell it apart from get_income_summary and search_expenses. It does not explicitly differentiate itself from analytical siblings like get_pnl or get_deep_analytics, which leaves some overlap to inference.

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?

Usage is implied by the capability list ('supports date ranges, period comparisons, and total, count, or average metrics'), so an agent can infer that this is the tool for spending roll-ups. However, there are no explicit when-to-use/when-not rules and no named alternative such as get_pnl or get_deep_analytics for profit or deeper analysis.

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 workspace links, each with a label and when-to-use description. Use when the user asks to open, view or edit their spreadsheet; review expenses or income; scan Gmail; reconcile; connect or manage a bank/card; open Automation Hub or General Settings; create or open reports; or asks where a submitted receipt went. Show only the one or two relevant links, not the catalog. After a receipt submission prefer Review expenses plus the Sheet; after an income write, Review income plus the Sheet. Gmail scan or connection: Scan Gmail. Bank/card: Reconcile. Configuration: Automation Hub or General Settings. Category, G/L and account codes: Category management. Client, project and trip groups: Group management. What counts as Personal: Personal rules. Client billing setup: use the returned clientBillingHandoffs, not a generic tour: manageClients manages the client list, tagReadyExpenses opens Review Expenses filtered to blank or default-Business rows, createClientReport opens report creation by group once expenses are assigned; pick the one step matching the user's state. If the user names a client and an exact expense scope and wants the assistant to do the work, use search_expenses and group_expenses instead (grounded preview, explicit confirmation before changing rows). Google Photos receipt picking: Google Photos link (existing authenticated picker, handles Google consent). Receipt file storage: the Drive folder link only when available; never invent or request a folder ID. A complete, unambiguous cash expense or mileage trip: use the direct write tool. Manual expense link: multiple entries, credits/refunds, detailed tax/tip, per diem, incomplete details, visual review. Mileage and travel link: route calculation, mileage settings, repeated trips, calendar/rideshare imports, per diem, visual review. Income import from a screenshot, PDF or CSV with no attachment in chat (or a visually complex review): Import income links; the importer shows every row for review before saving. Team setup: the role-aware Team setup handoff (solo business owners open Add teammates; team owners and co-admins open Team Management); if it says owner access is required, explain that instead of presenting a broken link. Complex or visually reviewed report requests: Create report link. Read-only; modifies no spreadsheet rows.

NameRequiredDescriptionDefault

No parameters

Output Schema

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

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, destructiveHint=false, and openWorldHint=false, so the safety profile is covered by structured data; 'Read-only; modifies no spreadsheet rows' largely restates it. What the description does add beyond annotations is operational: show only one or two relevant links rather than the catalog, never invent or request a folder ID, surface clientBillingHandoffs, and explain an owner-access-required message instead of presenting a broken link.

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

Conciseness2/5

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

The opening sentence front-loads the purpose correctly, but the body sprawls into a several-hundred-word catalog of sibling routing rules, which sits awkwardly against its own instruction to show only one or two links rather than a catalog. Much of the content is relevant, yet it is dense, semicolon-chained, and hard to scan for the single link an agent needs.

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, return-value detail is not required, and annotations carry the safety profile, so the remaining need is knowing which links apply in which state. The description covers that thoroughly, including fallback behavior for owner-access and the clientBillingHandoffs path, leaving little an agent would need to guess.

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 applies. The schema is empty and consistent with the description's no-argument link lookup.

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 first sentence states a specific verb and resource: it returns the master ExpenseBot Google Sheet plus labeled workspace links, each with a when-to-use description. That is concrete enough to distinguish it from data-mutation siblings, though the core function is quickly buried under a long downstream routing catalog.

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 enumerates explicit triggers (open/view/edit spreadsheet, review expenses, scan Gmail, reconcile, manage bank/card, open Automation Hub, create reports, trace a receipt) and then maps each scenario to a specific sibling or link. It also gives explicit exclusions: use the direct write tool for a complete unambiguous cash expense, use search_expenses plus group_expenses when the user names a client and exact scope.

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

get_statement_import_statusA
Read-only
Inspect

Check 'Did my statement finish importing?' for at most five jobs. Returns status and authenticated review link. Extraction isn't saved; read/save jobs may overlap, so counts aren't statement totals. No job found doesn't prove no import; older non-job imports are excluded. Read-only: never uploads, parses, saves, retries, or recovers.

NameRequiredDescriptionDefault
clientEmailNoAccepted client's account email (accountants only).

Output Schema

NameRequiredDescription
linksYes
statusYes
importsYes
successYes
limitationsYes
availabilityYes

TDQS

A4.1/5.0
Behavior5/5

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

Goes well beyond the readOnlyHint/destructiveHint annotations by disclosing that extraction isn't saved, that read and save jobs may overlap so counts aren't statement totals, that absence of a job is not proof of no import, and that the tool never uploads, parses, saves, retries or recovers. These are precisely the side-effect and data-validity caveats an agent needs before acting on the result.

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 tight sentences, front-loaded with the core question and answer shape, followed by the caveats in descending order of importance. No filler and no repetition of the name or title.

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 return values need no explanation, and the description covers the scoping and validity caveats thoroughly. The one gap is scope selection: with no job identifier parameter, it is not stated which five jobs are examined (presumably the caller's most recent), leaving the agent to infer the lookup window.

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 single parameter (clientEmail, accountants-only) is fully documented in the schema at 100% coverage, so the baseline is 3. The description adds nothing about the parameter — in particular it doesn't clarify what happens when clientEmail is omitted, which matters since it is not required.

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?

Opens with the exact user question it answers ('Did my statement finish importing?') and states the resource (import jobs) plus the return payload (status and authenticated review link). It is clearly distinct from neighbors like get_scan_status, get_last_receipt_result and trace_document by virtue of being about statement import jobs, though it never names a sibling explicitly.

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 a clear invocation context (checking whether an import finished) and a hard bound of 'at most five jobs'. It also supplies a key interpretation rule — 'No job found doesn't prove no import; older non-job imports are excluded' — which tells the agent when the answer is inconclusive. It stops short of naming which alternative tool to use for older/non-job imports.

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? } }.

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 matching the user's phrasing.

Output Schema

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

TDQS

A4.2/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true, destructiveHint=false and closed-world scope, so the safety profile is covered. The description adds that results are heuristic ('suspects') and that analysis is scoped to the user's expenses, but it does not note data prerequisites (e.g. need for imported expenses) or that sampleMeta indicates partial samples. Adequate but thin beyond structured fields.

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

Conciseness5/5

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

One sentence of purpose plus two compact labeled fragments (examples, returns), all front-loaded with zero filler. The structure lets an agent extract routing guidance and return shape at a glance.

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 and all three parameters documented, the description needs only to convey intent and detection categories, which it does. The only residual gap is data prerequisites/empty-result behavior, which is minor for a read-only audit tool.

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 adds real value by mapping natural-language examples to the `query` parameter and listing categories that mirror the `focus` enum (recurring, duplicates, price increases, trial conversions). It does not discuss how `focus` interacts with `query` or `dateRange`, leaving minor ambiguity.

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 ('Subscription audit') and enumerates exactly what it detects: recurring charges, duplicates, price increases, trial conversions. This clearly separates it from generic siblings like get_spending_summary or get_deep_analytics without the agent needing to open 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 Guidelines4/5

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

The sample phrasings ('recurring subscriptions', 'price increases', 'trial conversions', 'over $20/month') make it clear what kinds of user requests should route here, giving strong positive guidance. It stops short of explicit exclusions, e.g. that non-subscription spend analysis belongs to get_spending_summary.

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

Review trips from the engine behind Review Expenses Find. Default is high-confidence only; for the trip list set includeCandidates:true: real trips first (AI-reviewed order when available), second homes and regular spots grouped; page with offset while remaining > 0. Name a trip with destination (e.g. "Copenhagen"); mode:area with area lists every expense in a country or city. Show dates, recorded locations, count and uncertainty; locations are not travel or client proof. Copy exact selections to group_expenses with a user-chosen trip/client name and confirm:false, then request approval. Possible additions stay out unless the user includes them (includePossibleAdditions:true). Merge/deduplicate selections for one destination; separate destinations need separate confirmations. Preserve existing assignments, Personal and read-only rows. Never infer clients or countries from currency. clientEmail mode is read-only.

NameRequiredDescriptionDefault
areaNoCountry or city for mode area, e.g. "Denmark" or "Copenhagen, Denmark". Ambiguous places return candidates to ask about.
modeNoepisode (default): trips. area: every expense located in the area.
limitNoResult cap (default 3, max 10), not an exhaustive list; see remaining.
offsetNoSkip this many results to page through remaining trips.
refreshNoRecheck current recorded expenses for suggested trips; never change their tags.
dateRangeNoInclusive period to find overlapping trips; returns full trip membership, including boundary dates outside the period.
clientEmailNoAccepted client's account email (accountants only).
destinationNoTrip the user named, e.g. "Copenhagen". Several matches return them for clarification; never guess.
includeCandidatesNoDefault false: high-confidence only. True: also show uncertain candidates without saving recommendations or changing expenses.
includePossibleAdditionsNoDefault false. True only when the user explicitly asks to include the separate possible additions in the selection.

Output Schema

NameRequiredDescription
countNo
messageNo
successYes
proposalsNoRead-only trip proposals; accepting one continues through group_expenses.
remainingNoMore matching trips available with a larger offset.

TDQS

A4.1/5.0
Behavior5/5

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

Beyond the read-only annotations, the description discloses important behavioral constraints: locations are not travel or client proof, clientEmail mode is read-only, refresh never changes tags, candidates are not saved, and existing assignments/Personal/read-only rows must be preserved. These cautions materially reduce misuse risk.

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 dense and runs many workflow, mode, caveat, and downstream-tool instructions together in one block. Most content is relevant, but the lack of separation and the packed semicolon clauses make it harder to scan than a well-structured description.

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 10-parameter tool with an output schema, the description covers the key modes, defaults, paging behavior, downstream grouping workflow, and important safety caveats. It is broadly complete, though the dense presentation leaves some operational details harder to extract than necessary.

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 defines all parameters. The description still adds useful semantics for mode:area, includeCandidates, destination disambiguation, offset paging, includePossibleAdditions, and the read-only clientEmail mode, though it does not add much for limit, refresh, or dateRange 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 clearly identifies a retrieval/review tool for trip suggestions, including default high-confidence behavior and an area mode. It is more specific than the name alone, but the phrase 'engine behind Review Expenses Find' is awkward and the description does not sharply distinguish this from sibling search/list 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?

It gives substantial conditional guidance: use includeCandidates:true for uncertain candidates, use destination when the user names a trip, use mode:area to list expenses in a country or city, and keep possible additions out unless explicitly included. It also names group_expenses as the downstream tool, but it does not directly state when to prefer this over sibling expense-search tools.

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, e.g. 'group my Mexico meals for client Rob' or 'group these for client Rob and create a report'. The user need not know about tags: ExpenseBot resolves the name against existing groups and proposes creating one only when needed. Use exact expenseId values from search_expenses. Existing assignments are preserved by default; set replaceExistingTags:true only for an explicit replacement request. A grounded preview is automatic for every bulk request. First call with confirm omitted/false, show the 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). If the request includes a report, set createReport:true on that confirmed call and use its report result; do not run a separate broader create_report query. For 'without personal expenses' set excludePersonal:true; Personal-tagged and Personal-category rows are removed before preview so their marker is never overwritten. The confirmed operation returns exact report and Bill Client links. Expenses already on another ordinary report are excluded; if none remain, no duplicate report is created and the result links to Reports. Keep the response concise, with no unsolicited tax or substantiation advice; do not call check_compliance, get_report_details or tax/deductibility tools around this workflow unless the user asks. 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 to the preview. A Bill Client URL opens the reviewed billing handoff; it does not mean an invoice was created, sent, or confirmed. Sharing remains a separate share_report action with recipient confirmation.

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.
replaceExistingTagsNoDefault false preserves existing assignments. True only after the user explicitly requests replacement; preview shows existing and proposed tags.

Output 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 mark it destructive and open-world; the description goes well beyond them by disclosing the mandatory preview, the premature-confirm downgrade, operationId idempotency reuse, conflict/exclusion handling, preservation of existing assignments, and that a Bill Client URL is not an invoice. That is unusually rich mutation 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?

Purpose and the preview/confirm rule are front-loaded and there are no filler sentences, but the block runs long and packs many rules into dense prose. Almost every sentence earns its place; the size is justified by the tool's complexity but hurts 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?

For a 13-parameter, nested-object, destructive bulk tool with an output schema, the description covers the workflow, safety gates, exclusion behavior, report creation and what the returned links mean. 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 schema already documents each parameter; the description adds cross-parameter workflow meaning instead of repeating field text — confirm only after approval, reuse the same operationId, set createReport on the confirmed call, excludePersonal semantics. It stops short of explaining undo/status/approveNewGroup interplay, so 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 (group) and resource (an exact set of recorded expenses) plus the user-named destination kinds, with concrete examples. It also names the sibling it depends on (search_expenses) and the one it replaces (create_report), so an agent can distinguish it 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?

Explicitly says when to use it, when not to call adjacent tools ('do not call check_compliance, get_report_details or tax/deductibility tools'), and that a separate create_report must not be run. The preview-then-confirm workflow and the replaceExistingTags condition are stated outright.

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.

NameRequiredDescriptionDefault

No parameters

Output 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.

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

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.

NameRequiredDescriptionDefault
limitNoMax invoices to return (default 25, max 100).
statusNoFilter by invoice status (default active/outstanding).
clientNameNoOptional: exact client name (case-insensitive).
clientEmailNoAccepted client's account email (accountants only).

Output Schema

NameRequiredDescription
successYes
invoicesYes

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, so the safety profile is covered; the description reinforces scope ('the user's issued client invoices') and enumerates return contents (invoice numbers, status, totals by currency, delivery state, document links). Some of this overlaps the output schema, keeping it from 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-loads the core purpose and AR framing before the examples and status enum, and every sentence carries information. The example list is slightly long but each maps to a distinct status filter, so little 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?

With annotations covering safety, a full output schema handling return values, and 100% parameter coverage, the description supplies sufficient scope, status semantics, and usage examples. 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 coverage is 100%, so the schema already documents all four parameters and the enum. The description adds minor value by clarifying that 'active' is the default/outstanding bucket and restating the enum, but adds no syntax or behavioral nuance 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?

States a specific verb and resource ('List the user's issued client invoices') plus the domain framing ('accounts receivable — who owes them money'). This clearly distinguishes it from get_client_invoice (single), create_client_invoice, and mark_client_invoice_paid without needing 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?

The example queries ('which invoices are outstanding', 'what does Acme still owe me', 'any overdue invoices') give concrete situations for use, and the default 'active' status is called out. It does not explicitly name sibling alternatives it should be preferred over (e.g., get_client_invoice for a single invoice), so it falls 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.

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.

NameRequiredDescriptionDefault

No parameters

Output 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.

NameRequiredDescriptionDefault
pageNoOne-based results page to return.
limitNoMaximum reports to return on this page.
filterNoOptional report-status filter; All returns every authorized status.All
clientEmailNoAccepted client's account email (accountants only).

Output Schema

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

TDQS

A4/5.0
Behavior4/5

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

Annotations already establish this as a read-only, non-destructive, closed-world operation. The description adds valuable context about pagination, returned report contents, and a caution around when to use the accounting handoff URL, though it does not describe pagination limits or authorization behavior in detail.

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 definition is front-loaded with purpose and filter behavior, then moves to return contents and a usage constraint. It is efficient overall, though the 'Each exact report...' sentence could be tighter.

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, full annotation coverage, and 100% schema description coverage, the description provides enough context for correct invocation. It covers purpose, pagination, filtering, return content, and the key caution around accounting URLs.

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 page, limit, filter, and clientEmail are already documented in the schema. The description mentions pagination and status filtering but adds no syntax or semantics beyond the schema descriptions and enum values.

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 clearly identifies the resource as expense reports, distinguishing it from sibling tools like list_client_invoices or list_tags.

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 provides useful guidance on the returned accounting URL ('use only when the user explicitly asks to send or export'), but it does not explain when to choose list_reports over alternatives such as search_expenses or get_report_details. The filtering behavior is stated, but tool-selection guidance is only implied.

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.

NameRequiredDescriptionDefault

No parameters

Output 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.

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

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, ...}

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

Output 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.

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

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.

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

Output 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.

NameRequiredDescriptionDefault
packageNameYesExact accounting package requested by the user.

Output 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.

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

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 completed Gmail periods for receipts. In eligible split-tax-year accounts, a number is the period's ending year (2025 = 2024–25 tax year); otherwise it is a calendar year. Use returned tax-period labels and inclusive ISO bounds. Long-running: the first eligible period starts immediately; the rest queue one at a time (each full period typically takes hours; the app may close). Preview without confirmStart (no scan starts), ask the user to confirm, then start with confirmStart:true. For recent receipts (~60 days), use scan_gmail. The in-progress period cannot be year-scanned; use get_scan_status for progress.

NameRequiredDescriptionDefault
yearsYesCompleted periods; ending year for eligible tax-year accounts (2025 = 2024–25), calendar year otherwise. 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

NameRequiredDescription
accountNo
messageYes
previewNo
successYes
eligibleNo
gmailUrlNo
scanningNo
notEligibleNo
queuedYearsNo
startedYearNo
alreadyQueuedNo
alreadyScannedNo
archiveThroughYearNo

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=false, openWorldHint=true, destructiveHint=false. The description goes well beyond them, disclosing that the operation is long-running, that the first eligible period starts immediately while the rest queue sequentially, that each period can take hours, and that the app may close mid-run — exactly the kind of risk context an agent needs before triggering a write.

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, then year semantics, then the long-running/queueing behavior, then the confirmation workflow, then alternatives. Dense but every sentence carries distinct information; slightly long, which is justified by the genuinely complex async behavior but not 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?

An output schema exists, so return values need no explanation. Given the multi-step confirmation flow, hours-long async execution, and queue semantics, the description covers everything an agent needs to call this correctly and warn the user.

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 workflow meaning the schema cannot: the preview-vs-start distinction for confirmStart and the tax-year vs calendar-year interpretation of years values, tied to returned tax-period labels. It largely echoes the schema's own wording for these fields, keeping it short of 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 and resource ('Scan completed Gmail periods for receipts') and immediately scopes it against the sibling: recent receipts (~60 days) belong to scan_gmail, and in-progress periods cannot be year-scanned. An agent can route between the Gmail scanning siblings without opening any schema.

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

Usage Guidelines5/5

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

Gives explicit when-to-use (completed periods), when-not (in-progress period, recent ~60 days), named alternatives (scan_gmail, get_scan_status), and a concrete precondition workflow: preview without confirmStart, ask the user, then start with confirmStart:true.

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 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 overlapping searches.

The optional query parameter does deterministic natural-language recall over merchant, city/location, Notes (receipt items, delivery source, payer, Business purpose), Tag and category. Results include matchedFields and a short matchReason to explain the match; when several rows plausibly match, a disambiguation list is returned and each option carries the exact expenseId. Structured filters (categories, merchants, dateRange, tags, minAmount, maxAmount) combine with the query using AND. Each result includes expenseId, the exact durable Receipt ID required by update_expense. For country-based grouping or client-assignment review use groupBy:country with explicit YYYY-MM-DD start/end; this read-only mode reviews at most 100 matching expenses, so narrow the range if too large. Use exact configured categories for travel; a foreign expense is not necessarily travel. Recorded locations can resolve to derived countries, which is not proof of travel. Unknowns stay separate even with a country filter; ask about ambiguous places, never infer from currency. Country review supports categories, merchants, tags, countries and tagAssignment, not free query or pagination. Show counts and currency-separated totals, then copy the returned selectionItems (with tag/date/location expectations) into group_expenses after the user chooses destinations.

NameRequiredDescriptionDefault
tagsNoFilter by tags
limitNoResults per page (default 20, maximum 50)
queryNoOptional natural-language recall over merchant, city/location, Notes (receipt items, delivery source, payer, Business purpose), Tag and category. Deterministic case-insensitive matching, no embeddings. Combined with structured filters using AND. Examples: 'Tribeca restaurant', 'client dinner note'.
cursorNoOpaque nextCursor returned by the previous search_expenses page. Reuse the same filters; never construct or edit this value.
groupByNoRead-only country review; requires explicit dateRange.start/end and at most 100 matches.
countriesNoCountry-review filter: supported country names/codes, or unknown. Never infer a country from currency or an ambiguous city.
dateRangeNoTime period filter; use exactly one variant matching 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.
clientEmailNoAccepted client's account email (accountants only).
tagAssignmentNoCountry review: unassigned means blank/Business; no_client means no canonical client tag, including project/trip-tagged rows. Neither implies business use.

Output Schema

NameRequiredDescription
countYesRows matched by the filters.
totalYesHome-currency sum in normal search; null in country review. Use countryGroups totalsByCurrency, never sum currencies.
hasMoreNo
messageNo
expensesYesMatching expense rows, newest first, paginated.
pageInfoNo
nextCursorNoOpaque signed continuation; reuse with identical filters.
queryAppliedNo
totalMatchedNoPresent for free-text searches.
countryGroupsNoComplete bounded country review; unknown locations remain separate.

TDQS

A4.6/5.0
Behavior5/5

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

The description adds substantial behavior beyond readOnly/openWorld/non-destructive annotations: deterministic non-embedding recall, AND combination of query and filters, matchedFields/matchReason/disambiguation output, 100-row cap for country review, unknown-country handling, and currency-separated totals. These details materially help an agent understand what happens and how to interpret 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?

The description is dense and front-loaded, with no filler, but it arrives as a single long paragraph that mixes search mechanics, country-review rules, and workflow instructions. It earns most of its length for a 14-parameter tool, though better structure would improve scanability.

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 14 optional parameters, pagination, country-review mode, travel caveats, and an existing output schema, the description covers what an agent needs to call it correctly. Return-value details are 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 coverage is 100%, so the baseline is 3, but the description adds cross-parameter meaning: query is deterministic natural-language recall, structured filters combine with it using AND, travel must use exact configured categories, and country filters must not infer from currency. Some individual parameters such as hasReceipt and clientEmail are left to the schema, so it is not a full 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?

The description opens with a specific verb and resource: 'Search and filter the user's expenses; returns matching rows from their spreadsheet.' It clearly distinguishes this from sibling tools like get_expense_by_id or get_spending_summary by describing search/filter behavior over spreadsheet rows.

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 strong operational guidance: paginate with nextCursor when hasMore is true, avoid overlapping date-range searches, use groupBy:country for country grouping or client-assignment review, and do not use free query or pagination in that mode. It also names a follow-up tool, group_expenses, but does not explicitly contrast search_expenses with sibling search or summary tools.

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.

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

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 not accepted yet because their previews cannot freeze/revalidate the plan shareReport executes. Use those providers' web flows.

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

NameRequiredDescription
phaseYes
unitsNo
countsNo
createdNo
successYes
providerYes
reportIdNo
expiresAtNo
proposalIdYes
finalAnswerNo
organizationNo
matchedExistingNo
omittedExpensesNo
idempotentReplayNo
responseGuidanceNo
confirmationContractNo
confirmationQuestionNo
confirmationRequiredNo
skippedAlreadyPushedNo
reconciliationRequiredNo

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=false / destructiveHint=false / openWorldHint=true; the description adds the substantive behavior: step 1 posts nothing, the proposal is frozen and account-bound, step 2 revalidates live state and rejects changed reports or mappings, and mapping fields must not be resent. It also discloses the authorization constraint (owner-only) and the hard provider limitation. This is context the structured fields cannot supply.

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 verb and resource, then cleanly segmented into STEP 1 and STEP 2 with an explicit warning sentence. Every sentence carries operational weight, though the repeated emphasis that nothing is posted and the mapping-field prohibition restate adjacent sentences, adding some density without new 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 9-parameter, two-phase mutation tool with nested mapping objects and an output schema, the description covers the full decision surface: gating conditions, mutation semantics, revalidation behavior, authorization scope, and provider limits. Return-value shapes are correctly 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 already 100%, so the baseline is 3, but the description adds cross-parameter semantics the schema cannot express: which parameters are step-1-only versus step-2-only, and the prohibition on adding mapping fields to the confirmation call. It does not explain forceRepush's 'disclosed uncertain prior outcomes' meaning beyond the schema's own wording, so it stops short of 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 and resource ('post an ExpenseBot report to the owner's accounting destination') and frames the operation as a staged review-then-post workflow. It explicitly distinguishes itself from shareReport by naming that sibling when explaining why other providers are unsupported. 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?

Gives an explicit two-step protocol with which parameters belong to which step, and the condition that gates step 2 (human approval of the complete proposal). It also states exclusions: owner accounts only, no acting for a client, and Zoho Books only with named providers relegated to their web flows. Nothing about when to call 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.

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.

NameRequiredDescriptionDefault
reportIdYes
recipientsYes
recipientRoleNoUse reviewer for report-only approval. Use accountant only for explicit ongoing accountant access.reviewer

Output 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 'log this', 'save this receipt', 'expense this', 'add this to my expenses', including when the user simply shares a photo of a receipt or invoice. The file is validated, uploaded and processed by AI to extract vendor, amount, date, tax and category. The expense appears in the spreadsheet in about 1-3 minutes, longer for PDFs or large batches. Handles images and PDFs mixed 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 to process the whole set as a single batch. Do not call this tool once per file.

Use the photo parameter for one image or PDF attached in ChatGPT. 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 use the same metadata path as ExpenseBot's camera, uploader and forwarded-email intake: note goes to Notes (column L), tag to Tag (column K). Batch defaults apply to every file; each uploadRefs item may override either value.

NameRequiredDescriptionDefault
tagNoBatch-level project or client tag for every row; a file may override it. Stored in the Tag column (K).
noteNoBatch-level note for every file; a file may override it. Stored 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: 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 every file the user provided, in one call. Returns a signedUrl + uploadRef per file; upload each, then call again with uploadRefs.

Output 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?

Annotations only declare readOnlyHint=false, destructiveHint=false, openWorldHint=true. The description adds real context beyond that: AI extraction fields, a 1-3 minute latency expectation that is longer for PDFs/batches, atomic batch rejection, column destinations (K/L), and shared metadata path with camera/uploader/email intake.

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 trigger phrases, then the file-transfer mechanics, then parameter nuances. It is longer than most definitions but nearly every sentence carries operational information; the parenthetical column letters and 'same metadata path as ExpenseBot's camera' line are slightly incidental.

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, nested-object, multi-step mutation with an output schema, the description covers the full workflow, timing expectations, failure atomicity, and mode selection. An agent has everything needed to invoke it correctly on the first try.

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 genuinely adds semantics the schema cannot express: the two-phase upload workflow, that tag/note are batch defaults overridable per uploadRef item, and which physical spreadsheet columns they land in.

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 immediately enumerates the user intents it covers ('log this', 'save this receipt', 'expense this'). An agent can distinguish this from siblings like add_cash_expense or parse_expense 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?

Explicitly routes between modes: filesToUpload/uploadRefs is 'preferred, and required for PDFs'; photo for a single ChatGPT attachment; photoBase64 only as legacy fallback. It also gives an exclusion ('Do not call this tool once per file') and the required two-step order, 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.

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

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', 'tag it Client – Acme'). Identify it only by the exact expenseId from search_expenses (the full Receipt ID from Column Q); never a date, displayed number or sheet row number. Two-phase: calls with confirm omitted/false return a current→proposed preview and change nothing; after user confirmation, confirm:true applies it. 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. Only category/tag/merchant/businessPurpose are editable; amounts, dates and notes are not. Does not create or delete rows.

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

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?

Beyond the destructiveHint/openWorldHint annotations, the description discloses the two-phase preview→confirm contract ('return a current→proposed preview and change nothing'), the not-found recovery rule, the Notes-capacity abort, and the non-editable field set. That is substantial behavioral context an agent could not infer from annotations alone.

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 action and editable fields, then layered rules in a logical order with zero filler. It is dense and long for a tool description, but nearly every clause carries an operational constraint rather than restating 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 destructive mutation with nested parameters and an output schema, the description covers identification, preview semantics, confirmation, failure handling, and scope limits — everything needed to invoke it correctly without guessing.

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 adds mapping between natural-language intents and the fields object ('recategorize expense X to Meals', 'tag it Client – Acme') and reinforces that expenseId must be the exact Receipt ID, not a row number. It does not add format or validation detail beyond that.

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 ('Update a single existing expense row') and enumerates exactly which fields are editable (category, tag, merchant, business purpose) with example phrasings. It also draws explicit boundaries against siblings: 'Does not create or delete rows' and routes the agent to search_expenses for the identifier.

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 strong when/when-not guidance: identify only via exact expenseId from search_expenses, never a date/displayed number/sheet row, and 'If the expense is not found, search once ... never retry the same stale ID.' It also names the fallback path (review-expenses URL) when business-purpose text exceeds Notes capacity.

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.

NameRequiredDescriptionDefault
deltaMonthlyYesThe new recurring monthly cost being considered, in the user's home currency

Output 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.

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

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'.

NameRequiredDescriptionDefault

No parameters

Output 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. 22 tool updates
    • Changedadd_income_from_file2 fields changed
      • 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 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)."
      • 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 the user-approved fields to replace on this staged row (same field names as rowEdits)."
    • Changedcreate_report2 fields changed
      • changedInput schema / properties / dateRange / description
        Previous value: -"Time period filter. Use exactly one variant — pick the shape that matches the user's phrasing."New value: +"Time period filter; use exactly one variant matching the user's phrasing."
      • changedInput schema / properties / dateRange / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "month": {
        -        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "month",
        -        "type": "string"
        -      },
        -      "year": {
        -        "description": "4-digit year (e.g., 2026)",
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "month",
        -      "year"
        -    ],
        -    "title": "Specific month",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "quarter": {
        -        "maximum": 4,
        -        "minimum": 1,
        -        "type": "integer"
        -      },
        -      "type": {
        -        "const": "quarter",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "quarter",
        -      "year"
        -    ],
        -    "title": "Quarter of a year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "year",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "year"
        -    ],
        -    "title": "Full year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "end": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "start": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "specific",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "start",
        -      "end"
        -    ],
        -    "title": "Specific date range",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "relative",
        -        "type": "string"
        -      },
        -      "value": {
        -        "description": "Relative range phrase, e.g., 'last month', 'YTD', 'this quarter', 'last 7 days'",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "value"
        -    ],
        -    "title": "Relative natural-language range",
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "month": {
        +        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "month",
        +        "type": "string"
        +      },
        +      "year": {
        +        "description": "4-digit year (e.g., 2026)",
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "month",
        +      "year"
        +    ],
        +    "title": "Specific month",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "quarter": {
        +        "maximum": 4,
        +        "minimum": 1,
        +        "type": "integer"
        +      },
        +      "type": {
        +        "const": "quarter",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "quarter",
        +      "year"
        +    ],
        +    "title": "Quarter of a year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "year",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "year"
        +    ],
        +    "title": "Full year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "end": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "start": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "specific",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "start",
        +      "end"
        +    ],
        +    "title": "Specific date range",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "relative",
        +        "type": "string"
        +      },
        +      "value": {
        +        "description": "Relative phrase, e.g. 'last month', 'YTD', 'last 7 days'",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "value"
        +    ],
        +    "title": "Relative natural-language range",
        +    "type": "object"
        +  }
        +]
    • Changedget_client_advance_balances1 field changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
    • Changedget_client_invoice1 field changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
    • Changedget_credits_refunds1 field changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
    • Changedget_deep_analytics3 fields changed
      • changedInput schema / properties / dateRange / description
        Previous value: -"Time period filter. Use exactly one variant — pick the shape that matches the user's phrasing."New value: +"Time period filter; use exactly one variant matching the user's phrasing."
      • changedInput schema / properties / dateRange / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "month": {
        -        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "month",
        -        "type": "string"
        -      },
        -      "year": {
        -        "description": "4-digit year (e.g., 2026)",
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "month",
        -      "year"
        -    ],
        -    "title": "Specific month",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "quarter": {
        -        "maximum": 4,
        -        "minimum": 1,
        -        "type": "integer"
        -      },
        -      "type": {
        -        "const": "quarter",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "quarter",
        -      "year"
        -    ],
        -    "title": "Quarter of a year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "year",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "year"
        -    ],
        -    "title": "Full year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "end": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "start": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "specific",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "start",
        -      "end"
        -    ],
        -    "title": "Specific date range",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "relative",
        -        "type": "string"
        -      },
        -      "value": {
        -        "description": "Relative range phrase, e.g., 'last month', 'YTD', 'this quarter', 'last 7 days'",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "value"
        -    ],
        -    "title": "Relative natural-language range",
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "month": {
        +        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "month",
        +        "type": "string"
        +      },
        +      "year": {
        +        "description": "4-digit year (e.g., 2026)",
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "month",
        +      "year"
        +    ],
        +    "title": "Specific month",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "quarter": {
        +        "maximum": 4,
        +        "minimum": 1,
        +        "type": "integer"
        +      },
        +      "type": {
        +        "const": "quarter",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "quarter",
        +      "year"
        +    ],
        +    "title": "Quarter of a year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "year",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "year"
        +    ],
        +    "title": "Full year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "end": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "start": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "specific",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "start",
        +      "end"
        +    ],
        +    "title": "Specific date range",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "relative",
        +        "type": "string"
        +      },
        +      "value": {
        +        "description": "Relative phrase, e.g. 'last month', 'YTD', 'last 7 days'",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "value"
        +    ],
        +    "title": "Relative natural-language range",
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / description
        Previous value: -"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."New value: +"Async: type='analytics_pending' with jobId (poll poll_analytics). Inline: message + data (data may be null). success only on the async branch."
    • Changedget_expense_splits1 field changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
    • Changedget_income_summary3 fields changed
      • changedInput schema / properties / dateRange / description
        Previous value: -"Time period filter. Use exactly one variant — pick the shape that matches the user's phrasing."New value: +"Time period filter; use exactly one variant matching the user's phrasing."
      • changedInput schema / properties / dateRange / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "month": {
        -        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "month",
        -        "type": "string"
        -      },
        -      "year": {
        -        "description": "4-digit year (e.g., 2026)",
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "month",
        -      "year"
        -    ],
        -    "title": "Specific month",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "quarter": {
        -        "maximum": 4,
        -        "minimum": 1,
        -        "type": "integer"
        -      },
        -      "type": {
        -        "const": "quarter",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "quarter",
        -      "year"
        -    ],
        -    "title": "Quarter of a year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "year",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "year"
        -    ],
        -    "title": "Full year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "end": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "start": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "specific",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "start",
        -      "end"
        -    ],
        -    "title": "Specific date range",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "relative",
        -        "type": "string"
        -      },
        -      "value": {
        -        "description": "Relative range phrase, e.g., 'last month', 'YTD', 'this quarter', 'last 7 days'",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "value"
        -    ],
        -    "title": "Relative natural-language range",
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "month": {
        +        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "month",
        +        "type": "string"
        +      },
        +      "year": {
        +        "description": "4-digit year (e.g., 2026)",
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "month",
        +      "year"
        +    ],
        +    "title": "Specific month",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "quarter": {
        +        "maximum": 4,
        +        "minimum": 1,
        +        "type": "integer"
        +      },
        +      "type": {
        +        "const": "quarter",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "quarter",
        +      "year"
        +    ],
        +    "title": "Quarter of a year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "year",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "year"
        +    ],
        +    "title": "Full year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "end": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "start": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "specific",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "start",
        +      "end"
        +    ],
        +    "title": "Specific date range",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "relative",
        +        "type": "string"
        +      },
        +      "value": {
        +        "description": "Relative phrase, e.g. 'last month', 'YTD', 'last 7 days'",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "value"
        +    ],
        +    "title": "Relative natural-language range",
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / description
        Previous value: -"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."New value: +"Async: type='analytics_pending' with jobId (poll poll_analytics). Inline: message + data (data may be null). success only on the async branch."
    • Changedget_mileage_summary3 fields changed
      • changedInput schema / properties / dateRange / description
        Previous value: -"Time period filter. Use exactly one variant — pick the shape that matches the user's phrasing."New value: +"Time period filter; use exactly one variant matching the user's phrasing."
      • changedInput schema / properties / dateRange / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "month": {
        -        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "month",
        -        "type": "string"
        -      },
        -      "year": {
        -        "description": "4-digit year (e.g., 2026)",
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "month",
        -      "year"
        -    ],
        -    "title": "Specific month",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "quarter": {
        -        "maximum": 4,
        -        "minimum": 1,
        -        "type": "integer"
        -      },
        -      "type": {
        -        "const": "quarter",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "quarter",
        -      "year"
        -    ],
        -    "title": "Quarter of a year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "year",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "year"
        -    ],
        -    "title": "Full year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "end": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "start": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "specific",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "start",
        -      "end"
        -    ],
        -    "title": "Specific date range",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "relative",
        -        "type": "string"
        -      },
        -      "value": {
        -        "description": "Relative range phrase, e.g., 'last month', 'YTD', 'this quarter', 'last 7 days'",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "value"
        -    ],
        -    "title": "Relative natural-language range",
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "month": {
        +        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "month",
        +        "type": "string"
        +      },
        +      "year": {
        +        "description": "4-digit year (e.g., 2026)",
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "month",
        +      "year"
        +    ],
        +    "title": "Specific month",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "quarter": {
        +        "maximum": 4,
        +        "minimum": 1,
        +        "type": "integer"
        +      },
        +      "type": {
        +        "const": "quarter",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "quarter",
        +      "year"
        +    ],
        +    "title": "Quarter of a year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "year",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "year"
        +    ],
        +    "title": "Full year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "end": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "start": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "specific",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "start",
        +      "end"
        +    ],
        +    "title": "Specific date range",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "relative",
        +        "type": "string"
        +      },
        +      "value": {
        +        "description": "Relative phrase, e.g. 'last month', 'YTD', 'last 7 days'",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "value"
        +    ],
        +    "title": "Relative natural-language range",
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / description
        Previous value: -"Success returns message + data with the computed summary; success is not emitted on the inline analytics path."New value: +"Returns message + data; no success flag on the inline path."
    • Changedget_monthly_books_review1 field changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
    • Changedget_per_tag_pnl3 fields changed
      • changedInput schema / properties / dateRange / description
        Previous value: -"Time period filter. Use exactly one variant — pick the shape that matches the user's phrasing."New value: +"Time period filter; use exactly one variant matching the user's phrasing."
      • changedInput schema / properties / dateRange / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "month": {
        -        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "month",
        -        "type": "string"
        -      },
        -      "year": {
        -        "description": "4-digit year (e.g., 2026)",
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "month",
        -      "year"
        -    ],
        -    "title": "Specific month",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "quarter": {
        -        "maximum": 4,
        -        "minimum": 1,
        -        "type": "integer"
        -      },
        -      "type": {
        -        "const": "quarter",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "quarter",
        -      "year"
        -    ],
        -    "title": "Quarter of a year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "year",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "year"
        -    ],
        -    "title": "Full year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "end": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "start": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "specific",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "start",
        -      "end"
        -    ],
        -    "title": "Specific date range",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "relative",
        -        "type": "string"
        -      },
        -      "value": {
        -        "description": "Relative range phrase, e.g., 'last month', 'YTD', 'this quarter', 'last 7 days'",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "value"
        -    ],
        -    "title": "Relative natural-language range",
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "month": {
        +        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "month",
        +        "type": "string"
        +      },
        +      "year": {
        +        "description": "4-digit year (e.g., 2026)",
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "month",
        +      "year"
        +    ],
        +    "title": "Specific month",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "quarter": {
        +        "maximum": 4,
        +        "minimum": 1,
        +        "type": "integer"
        +      },
        +      "type": {
        +        "const": "quarter",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "quarter",
        +      "year"
        +    ],
        +    "title": "Quarter of a year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "year",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "year"
        +    ],
        +    "title": "Full year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "end": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "start": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "specific",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "start",
        +      "end"
        +    ],
        +    "title": "Specific date range",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "relative",
        +        "type": "string"
        +      },
        +      "value": {
        +        "description": "Relative phrase, e.g. 'last month', 'YTD', 'last 7 days'",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "value"
        +    ],
        +    "title": "Relative natural-language range",
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / description
        Previous value: -"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."New value: +"Async: type='analytics_pending' with jobId (poll poll_analytics). Inline: message + data (data may be null). success only on the async branch."
    • Changedget_pnl4 fields changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
      • changedInput schema / properties / dateRange / description
        Previous value: -"Time period filter. Use exactly one variant — pick the shape that matches the user's phrasing."New value: +"Time period filter; use exactly one variant matching the user's phrasing."
      • changedInput schema / properties / dateRange / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "month": {
        -        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "month",
        -        "type": "string"
        -      },
        -      "year": {
        -        "description": "4-digit year (e.g., 2026)",
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "month",
        -      "year"
        -    ],
        -    "title": "Specific month",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "quarter": {
        -        "maximum": 4,
        -        "minimum": 1,
        -        "type": "integer"
        -      },
        -      "type": {
        -        "const": "quarter",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "quarter",
        -      "year"
        -    ],
        -    "title": "Quarter of a year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "year",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "year"
        -    ],
        -    "title": "Full year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "end": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "start": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "specific",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "start",
        -      "end"
        -    ],
        -    "title": "Specific date range",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "relative",
        -        "type": "string"
        -      },
        -      "value": {
        -        "description": "Relative range phrase, e.g., 'last month', 'YTD', 'this quarter', 'last 7 days'",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "value"
        -    ],
        -    "title": "Relative natural-language range",
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "month": {
        +        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "month",
        +        "type": "string"
        +      },
        +      "year": {
        +        "description": "4-digit year (e.g., 2026)",
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "month",
        +      "year"
        +    ],
        +    "title": "Specific month",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "quarter": {
        +        "maximum": 4,
        +        "minimum": 1,
        +        "type": "integer"
        +      },
        +      "type": {
        +        "const": "quarter",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "quarter",
        +      "year"
        +    ],
        +    "title": "Quarter of a year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "year",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "year"
        +    ],
        +    "title": "Full year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "end": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "start": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "specific",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "start",
        +      "end"
        +    ],
        +    "title": "Specific date range",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "relative",
        +        "type": "string"
        +      },
        +      "value": {
        +        "description": "Relative phrase, e.g. 'last month', 'YTD', 'last 7 days'",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "value"
        +    ],
        +    "title": "Relative natural-language range",
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / description
        Previous value: -"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."New value: +"Async: type='analytics_pending' with jobId (poll poll_analytics). Inline: message + data (data may be null). success only on the async branch."
    • Changedget_report_details2 fields changed
      • changedInput schema / properties / checkFreshness / description
        Previous value: -"Set true only when the user explicitly asks whether this existing Year End report is current. Requires analysisRequested:true and performs a bounded Google Sheets source check; may return unknown if full tax-year replay is unavailable. Never use after report creation, sharing, or billing."New value: +"Set true only when the user explicitly asks whether this Year End report is current. Requires analysisRequested:true; runs a bounded Google Sheets source check and may return unknown. Never use after report creation, sharing, or billing."
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
    • Changedget_scan_status1 field changed
      • changedOutput schema / description
        Previous value: -"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."New value: +"Authoritative live scan state. Fields may be top-level or under data; only success is guaranteed, so treat the rest as optional."
    • Changedget_spending_summary4 fields changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
      • changedInput schema / properties / dateRange / description
        Previous value: -"Time period filter. Use exactly one variant — pick the shape that matches the user's phrasing."New value: +"Time period filter; use exactly one variant matching the user's phrasing."
      • changedInput schema / properties / dateRange / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "month": {
        -        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "month",
        -        "type": "string"
        -      },
        -      "year": {
        -        "description": "4-digit year (e.g., 2026)",
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "month",
        -      "year"
        -    ],
        -    "title": "Specific month",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "quarter": {
        -        "maximum": 4,
        -        "minimum": 1,
        -        "type": "integer"
        -      },
        -      "type": {
        -        "const": "quarter",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "quarter",
        -      "year"
        -    ],
        -    "title": "Quarter of a year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "year",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "year"
        -    ],
        -    "title": "Full year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "end": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "start": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "specific",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "start",
        -      "end"
        -    ],
        -    "title": "Specific date range",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "relative",
        -        "type": "string"
        -      },
        -      "value": {
        -        "description": "Relative range phrase, e.g., 'last month', 'YTD', 'this quarter', 'last 7 days'",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "value"
        -    ],
        -    "title": "Relative natural-language range",
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "month": {
        +        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "month",
        +        "type": "string"
        +      },
        +      "year": {
        +        "description": "4-digit year (e.g., 2026)",
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "month",
        +      "year"
        +    ],
        +    "title": "Specific month",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "quarter": {
        +        "maximum": 4,
        +        "minimum": 1,
        +        "type": "integer"
        +      },
        +      "type": {
        +        "const": "quarter",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "quarter",
        +      "year"
        +    ],
        +    "title": "Quarter of a year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "year",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "year"
        +    ],
        +    "title": "Full year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "end": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "start": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "specific",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "start",
        +      "end"
        +    ],
        +    "title": "Specific date range",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "relative",
        +        "type": "string"
        +      },
        +      "value": {
        +        "description": "Relative phrase, e.g. 'last month', 'YTD', 'last 7 days'",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "value"
        +    ],
        +    "title": "Relative natural-language range",
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / description
        Previous value: -"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."New value: +"Async: type='analytics_pending' with jobId (poll poll_analytics). Inline: message + data (data may be null). success only on the async branch."
    • Changedget_statement_import_status1 field changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
    • Changedget_subscription_audit3 fields changed
      • changedInput schema / properties / dateRange / description
        Previous value: -"Time period filter. Use exactly one variant — pick the shape that matches the user's phrasing."New value: +"Time period filter; use exactly one variant matching the user's phrasing."
      • changedInput schema / properties / dateRange / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "month": {
        -        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "month",
        -        "type": "string"
        -      },
        -      "year": {
        -        "description": "4-digit year (e.g., 2026)",
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "month",
        -      "year"
        -    ],
        -    "title": "Specific month",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "quarter": {
        -        "maximum": 4,
        -        "minimum": 1,
        -        "type": "integer"
        -      },
        -      "type": {
        -        "const": "quarter",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "quarter",
        -      "year"
        -    ],
        -    "title": "Quarter of a year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "year",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "year"
        -    ],
        -    "title": "Full year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "end": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "start": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "specific",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "start",
        -      "end"
        -    ],
        -    "title": "Specific date range",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "relative",
        -        "type": "string"
        -      },
        -      "value": {
        -        "description": "Relative range phrase, e.g., 'last month', 'YTD', 'this quarter', 'last 7 days'",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "value"
        -    ],
        -    "title": "Relative natural-language range",
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "month": {
        +        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "month",
        +        "type": "string"
        +      },
        +      "year": {
        +        "description": "4-digit year (e.g., 2026)",
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "month",
        +      "year"
        +    ],
        +    "title": "Specific month",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "quarter": {
        +        "maximum": 4,
        +        "minimum": 1,
        +        "type": "integer"
        +      },
        +      "type": {
        +        "const": "quarter",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "quarter",
        +      "year"
        +    ],
        +    "title": "Quarter of a year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "year",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "year"
        +    ],
        +    "title": "Full year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "end": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "start": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "specific",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "start",
        +      "end"
        +    ],
        +    "title": "Specific date range",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "relative",
        +        "type": "string"
        +      },
        +      "value": {
        +        "description": "Relative phrase, e.g. 'last month', 'YTD', 'last 7 days'",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "value"
        +    ],
        +    "title": "Relative natural-language range",
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / description
        Previous value: -"Success returns message + data with the computed summary; success is not emitted on the inline analytics path."New value: +"Returns message + data; no success flag on the inline path."
    • Changedget_trip_suggestions1 field changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
    • Changedlist_client_invoices1 field changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
    • Changedlist_reports1 field changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
    • Changedsearch_expenses4 fields changed
      • changedInput schema / properties / clientEmail / description
        Previous value: -"Client account email. Accountants may use this only for an accepted ExpenseBot client."New value: +"Accepted client's account email (accountants only)."
      • changedInput schema / properties / dateRange / description
        Previous value: -"Time period filter. Use exactly one variant — pick the shape that matches the user's phrasing."New value: +"Time period filter; use exactly one variant matching the user's phrasing."
      • changedInput schema / properties / dateRange / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "month": {
        -        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "month",
        -        "type": "string"
        -      },
        -      "year": {
        -        "description": "4-digit year (e.g., 2026)",
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "month",
        -      "year"
        -    ],
        -    "title": "Specific month",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "quarter": {
        -        "maximum": 4,
        -        "minimum": 1,
        -        "type": "integer"
        -      },
        -      "type": {
        -        "const": "quarter",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "quarter",
        -      "year"
        -    ],
        -    "title": "Quarter of a year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "year",
        -        "type": "string"
        -      },
        -      "year": {
        -        "type": "integer"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "year"
        -    ],
        -    "title": "Full year",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "end": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "start": {
        -        "description": "ISO date YYYY-MM-DD (inclusive)",
        -        "format": "date",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "specific",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "start",
        -      "end"
        -    ],
        -    "title": "Specific date range",
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "type": {
        -        "const": "relative",
        -        "type": "string"
        -      },
        -      "value": {
        -        "description": "Relative range phrase, e.g., 'last month', 'YTD', 'this quarter', 'last 7 days'",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "value"
        -    ],
        -    "title": "Relative natural-language range",
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "month": {
        +        "description": "Month name or 1-12 (e.g., 'March' or '3')",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "month",
        +        "type": "string"
        +      },
        +      "year": {
        +        "description": "4-digit year (e.g., 2026)",
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "month",
        +      "year"
        +    ],
        +    "title": "Specific month",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "quarter": {
        +        "maximum": 4,
        +        "minimum": 1,
        +        "type": "integer"
        +      },
        +      "type": {
        +        "const": "quarter",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "quarter",
        +      "year"
        +    ],
        +    "title": "Quarter of a year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "year",
        +        "type": "string"
        +      },
        +      "year": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "year"
        +    ],
        +    "title": "Full year",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "end": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "start": {
        +        "description": "ISO date YYYY-MM-DD (inclusive)",
        +        "format": "date",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "specific",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "start",
        +      "end"
        +    ],
        +    "title": "Specific date range",
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "type": {
        +        "const": "relative",
        +        "type": "string"
        +      },
        +      "value": {
        +        "description": "Relative phrase, e.g. 'last month', 'YTD', 'last 7 days'",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "value"
        +    ],
        +    "title": "Relative natural-language range",
        +    "type": "object"
        +  }
        +]
      • changedInput schema / properties / query / description
        Previous value: -"Optional 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'."New value: +"Optional natural-language recall over merchant, city/location, Notes (receipt items, delivery source, payer, Business purpose), Tag and category. Deterministic case-insensitive matching, no embeddings. Combined with structured filters using AND. Examples: 'Tribeca restaurant', 'client dinner note'."
    • Changedsubmit_receipt6 fields changed
      • changedInput schema / properties / filesToUpload / description
        Previous value: -"Request 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."New value: +"Request upload URLs for every file the user provided, in one call. Returns a signedUrl + uploadRef per file; upload each, then call again with uploadRefs."
      • changedInput schema / properties / filesToUpload / items / properties / mimeType / description
        Previous value: -"MIME type. Must match the Content-Type header used in the PUT exactly. The real format is re-detected from the file's contents on submit, so a close guess still works."New value: +"MIME type; must match the PUT Content-Type exactly. The real format is re-detected from the contents."
      • changedInput schema / properties / note / description
        Previous value: -"Batch-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)."New value: +"Batch-level note for every file; a file may override it. Stored in the Notes column (L)."
      • changedInput schema / properties / tag / description
        Previous value: -"Batch-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."New value: +"Batch-level project or client tag for every row; a file may override it. Stored in the Tag column (K)."
      • changedInput schema / properties / uploadRefs / description
        Previous value: -"Submit 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."New value: +"Submit previously uploaded files as ONE batch: every uploadRef from the filesToUpload step. If any file fails validation the whole batch is rejected and nothing is processed."
      • changedInput schema / properties / uploadRefs / items / properties / note / description
        Previous value: -"Context note for this specific file. Overrides the batch-level note if both are provided. Uses the existing receipt-note pipeline and is stored in the Notes column (L)."New value: +"Note for this file; overrides the batch note. Stored in the Notes column (L)."
  2. 2 tool updates
    • Changedget_trip_suggestions11 fields changed
      • addedInput schema / properties / area
        Added value: +{
        +  "description": "Country or city for mode area, e.g. \"Denmark\" or \"Copenhagen, Denmark\". Ambiguous places return candidates to ask about.",
        +  "type": "string"
        +}
      • addedInput schema / properties / dateRange
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Inclusive period to find overlapping trips; returns full trip membership, including boundary dates outside the period.",
        +  "properties": {
        +    "end": {
        +      "description": "Inclusive YYYY-MM-DD.",
        +      "type": "string"
        +    },
        +    "start": {
        +      "description": "Inclusive YYYY-MM-DD.",
        +      "type": "string"
        +    },
        +    "type": {
        +      "description": "Explicit calendar-date bounds.",
        +      "enum": [
        +        "specific"
        +      ],
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "start",
        +    "end"
        +  ],
        +  "type": "object"
        +}
      • addedInput schema / properties / destination
        Added value: +{
        +  "description": "Trip the user named, e.g. \"Copenhagen\". Several matches return them for clarification; never guess.",
        +  "type": "string"
        +}
      • addedInput schema / properties / includeCandidates
        Added value: +{
        +  "default": false,
        +  "description": "Default false: high-confidence only. True: also show uncertain candidates without saving recommendations or changing expenses.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / includePossibleAdditions
        Added value: +{
        +  "description": "Default false. True only when the user explicitly asks to include the separate possible additions in the selection.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum high-confidence proposals to return (default 3)."New value: +"Result cap (default 3, max 10), not an exhaustive list; see remaining."
      • changedInput schema / properties / limit / maximum
        Previous value: -5New value: +10
      • addedInput schema / properties / mode
        Added value: +{
        +  "description": "episode (default): trips. area: every expense located in the area.",
        +  "enum": [
        +    "episode",
        +    "area"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / offset
        Added value: +{
        +  "description": "Skip this many results to page through remaining trips.",
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • addedInput schema / properties / refresh
        Added value: +{
        +  "description": "Recheck current recorded expenses for suggested trips; never change their tags.",
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / remaining
        Added value: +{
        +  "description": "More matching trips available with a larger offset.",
        +  "type": "integer"
        +}
    • Changedgroup_expenses2 fields changed
      • addedInput schema / properties / selection / properties / items / items / properties / expected / properties / city
        Added value: +{
        +  "description": "Recorded location from the reviewed selection.",
        +  "type": "string"
        +}
      • addedInput schema / properties / selection / properties / items / items / properties / expected / properties / date
        Added value: +{
        +  "description": "Recorded YYYY-MM-DD from the reviewed selection.",
        +  "type": "string"
        +}
  3. 2 tool updates
    • Changedgroup_expenses1 field changed
      • addedInput schema / properties / replaceExistingTags
        Added value: +{
        +  "description": "Default false preserves existing assignments. True only after the user explicitly requests replacement; preview shows existing and proposed tags.",
        +  "type": "boolean"
        +}
    • Changedsearch_expenses8 fields changed
      • addedInput schema / properties / countries
        Added value: +{
        +  "description": "Country-review filter: supported country names/codes, or unknown. Never infer a country from currency or an ambiguous city.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / groupBy
        Added value: +{
        +  "description": "Read-only country review; requires explicit dateRange.start/end and at most 100 matches.",
        +  "enum": [
        +    "country"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / tagAssignment
        Added value: +{
        +  "description": "Country review: unassigned means blank/Business; no_client means no canonical client tag, including project/trip-tagged rows. Neither implies business use.",
        +  "enum": [
        +    "all",
        +    "unassigned",
        +    "no_client"
        +  ],
        +  "type": "string"
        +}
      • addedOutput schema / properties / countryGroups
        Added value: +{
        +  "description": "Complete bounded country review; unknown locations remain separate.",
        +  "items": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "count": {
        +        "type": "integer"
        +      },
        +      "country": {
        +        "type": "string"
        +      },
        +      "countryCode": {
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "selectionItems": {
        +        "items": {
        +          "additionalProperties": true,
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "totalsByCurrency": {
        +        "additionalProperties": {
        +          "type": "number"
        +        },
        +        "type": "object"
        +      }
        +    },
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / properties / expenses / items / properties / amount / description
        Previous value: -"Functional/home-currency amount."New value: +"Amount in currency; country review uses original receipt currency."
      • changedOutput schema / properties / expenses / items / properties / currency / description
        Previous value: -"Home currency code when available."New value: +"Currency of amount."
      • changedOutput schema / properties / total / description
        Previous value: -"Sum of the matched set in home currency."New value: +"Home-currency sum in normal search; null in country review. Use countryGroups totalsByCurrency, never sum currencies."
      • changedOutput schema / properties / total / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
  4. 1 tool update
    • Addedcreate_tag
  5. 2 tool updates
    • Changedget_report_details2 fields changed
      • addedInput schema / properties / checkFreshness
        Added value: +{
        +  "description": "Set true only when the user explicitly asks whether this existing Year End report is current. Requires analysisRequested:true and performs a bounded Google Sheets source check; may return unknown if full tax-year replay is unavailable. Never use after report creation, sharing, or billing.",
        +  "enum": [
        +    true
        +  ],
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / freshness
        Added value: +{
        +  "additionalProperties": false,
        +  "properties": {
        +    "checkedAt": {
        +      "format": "date-time",
        +      "type": "string"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "reportUrl": {
        +      "format": "uri",
        +      "type": "string"
        +    },
        +    "status": {
        +      "enum": [
        +        "current",
        +        "outdated",
        +        "unknown",
        +        "not_applicable"
        +      ],
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "checkedAt",
        +    "message",
        +    "reportUrl"
        +  ],
        +  "type": "object"
        +}
    • Addedget_statement_import_status
  6. 1 tool update
    • Changedscan_gmail_years1 field changed
      • changedInput schema / properties / years / description
        Previous value: -"Completed prior years to scan, e.g. [2020, 2021]. Max 10."New value: +"Completed periods; ending year for eligible tax-year accounts (2025 = 2024–25), calendar year otherwise. Max 10."
  7. 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."
  8. 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"
        +}
  9. 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"
        +]
  10. 1 tool update
    • Addedlist_client_close_readiness
  11. 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"
        +}
  12. 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"
        +]
  13. 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"
        +}

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
    83 npm
    1
    Business Source 1.1
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.