Skip to main content
Glama

Sum invoices over a window

well_sum_invoices
Read-only

Sum a workspace's billed amounts over a window of whole months, grouped by month, currency and billing context. Arithmetic only — this tool holds no definition of MRR or recurrence, and returns no figure the app renders.

Use it when you are computing a figure whose RULES you are stating yourself: recurring revenue over a window you chose, a total restricted to the billing contexts a reader confirmed, a per-month series behind a trend you are about to describe. The server derives no MRR of its own, so an MRR figure starts here: state the rules, sum exactly those rows, then put the result on a card with well_render_mrr.

The window is whole months. from and to are both the first day of a month, as YYYY-MM-01; from is inclusive and to is EXCLUSIVE, so June to August is 2026-06-01 to 2026-09-01. A bound inside a month is refused rather than widened, and so is a window longer than 36 months.

Which rows are billed amounts is decided here, and stated so you can say it. A canceled invoice is left out. Only billing documents count: invoices, debit notes, credit notes and subscription billing statements, so a proforma and the invoice it precedes are not summed twice, and an order, a quote or a payment advice never is. A row with no document type is read as an invoice. Every amount is NET of tax (items_total), because tax collected is owed onward rather than earned.

party_scope is required, and it decides whose invoice this is. sales is what the workspace ISSUED — its receivables, and the only side revenue can come from. purchase is what it received. The two are the same rows read from opposite ends, so no default is offered: a server choosing a side would answer a different question from the one asked. intra_self is an invoice between two companies the workspace owns, and unattributed is one Well could place on neither side.

Those four scopes partition every invoice exactly once, which is what makes an incomplete picture visible rather than silent. unattributed_count comes back on every call, whatever scope you asked for: it counts the invoices in the window that landed in that fourth bucket. State it beside any total, because an unattributed invoice may still belong in the figure and nothing here can tell you whether it does.

Every row carries ONE month, ONE currency and ONE billing context. Currency is always a grouping key, named or not: adding EUR to USD gives a number denominated in nothing, and no field on the result would tell you it happened. Convert the per-currency subtotals yourself, at a rate you can state, before you add them.

sum is already net of credit notes. A credit note subtracts its magnitude from its own month-currency-context bucket, whichever sign it was stored with; credit_note_sum and credit_note_count report what that removed, so you can say what the figure netted. Do not subtract them a second time. A bucket whose credit notes outweigh its invoices nets negative, and that is a real state rather than an error.

billing_context is null on rows that name no billing arrangement — none stored, unknown, or a value Well has no label for — and that is a third answer rather than a kind of one-off. The field is filled by extraction, not by a billing system, so a workspace can carry real recurring revenue on rows that say nothing about it. unclassified_count totals those rows. The recurring-contexts card offers them as one choice, keyed "unclassified", so apply that key to these rows and only these. Counted or not, report the count rather than letting a reader read the remainder as "everything else".

corrected_or_consolidated_count counts the corrected and consolidated invoices among the rows. Each replaces invoices Well holds no link to, so when those originals fall in the same window the sum counts that billing twice. The rows keep them, because dropping them would lose the revenue whenever the originals fall outside the window. State the count beside any total whenever it is not zero.

excluded_malformed counts billing documents in the window with no readable net amount or no currency. They are in none of the rows and none of the sums, so state the count beside any total. It comes back null when the count could not be read, which is NOT 0: zero says every row was readable, null says nobody counted.

partial: true means the aggregate was cut short and every figure is a FLOOR rather than a measurement.

When the token authorizes one workspace, call this directly — no other tool call is needed first. When it authorizes several, this read will not guess which one you mean: pass workspace_id on the call.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
toYesEXCLUSIVE end of the window: the first day of the month after the last one you want, YYYY-MM-01.
fromYesInclusive start of the window: the first day of a month, YYYY-MM-01.
party_scopeYesWhich side of the invoice the workspace occupies: `sales` for what it issued, `purchase` for what it received. Required; see the description.
workspace_idNoTarget workspace. Omit when the token authorizes one workspace. Required when it authorizes several: this read reports one workspace's own figures and will not choose which.
conversation_idNoThe conversation id returned by the previous Well result, in its meta under well/conversation_id, in its structuredContent, or in its JSON text block. Pass it back on every call in the same conversation, including a call a card makes, so the chosen workspace and the earlier answers still apply. It decides the conversation on its own: nothing the host states about the session replaces it. Omit it only on the first call of a conversation.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
rowsYes
errorNo
windowYes
partialYes
successYes
conversation_idNoThe conversation this result belongs to. Pass it back as the conversation_id argument on every later Well call in the same conversation.
excluded_malformedYes
resolved_workspaceNoThe workspace that answered, when the caller named none and the token authorizes several.
unattributed_countYes
unclassified_countYes
conversation_id_noteNoPresent only when the server opened a fresh lane, stating that no choice recorded earlier was read.
conversation_id_sourceNoWhere the conversation id came from: the host's own request meta, the caller's argument, or a fresh lane the server opened.
corrected_or_consolidated_countYes

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed4 schema fields changed
    • addedInput schema / properties / conversation_id
      Added value: +{
      +  "description": "The conversation id returned by the previous Well result, in its meta under well/conversation_id, in its structuredContent, or in its JSON text block. Pass it back on every call in the same conversation, including a call a card makes, so the chosen workspace and the earlier answers still apply. It decides the conversation on its own: nothing the host states about the session replaces it. Omit it only on the first call of a conversation.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / conversation_id
      Added value: +{
      +  "description": "The conversation this result belongs to. Pass it back as the conversation_id argument on every later Well call in the same conversation.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / conversation_id_note
      Added value: +{
      +  "description": "Present only when the server opened a fresh lane, stating that no choice recorded earlier was read.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / conversation_id_source
      Added value: +{
      +  "description": "Where the conversation id came from: the host's own request meta, the caller's argument, or a fresh lane the server opened.",
      +  "enum": [
      +    "host_meta",
      +    "argument",
      +    "minted"
      +  ],
      +  "type": "string"
      +}
  2. Added

TDQS

A5/5.0
Behavior5/5

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

The annotations only declare readOnlyHint, destructiveHint, and openWorldHint. The description adds a large amount of behavioral context: whole-month exclusivity, refusal of in-month bounds, cancellation/document-type filtering, net-of-tax semantics, party_scope partitioning, credit-note netting, null billing contexts, corrected/consolidated double counting, malformed exclusions, and partial-floor behavior. No contradiction with readOnlyHint exists since the tool is explicitly arithmetic-only.

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?

Though long, the description is front-loaded with purpose and usage before caveats, and each paragraph is bold-labeled with a distinct operational constraint. Every paragraph carries information needed to call the tool correctly; there is no filler or 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 five parameters, an output schema, and high complexity, the description covers all necessary calling details: window format, row selection, scoping, currency handling, credit notes, unclassified rows, corrected/consolidated rows, malformed rows, partial results, and authorization behavior. 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.

Parameters5/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 goes well beyond the schema: it explains the EXCLUSIVE window bound, that a bound inside a month is refused, that party_scope has no default because the scopes partition invoices, how workspace_id behaves under multi-workspace authorization, and the meaning of derived counts. This significantly improves parameter understanding.

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 operation and resource: 'Sum a workspace's billed amounts over a window of whole months, grouped by month, currency and billing context.' It also explicitly disclaims MRR/recurrence definition, which separates it from render/MRR siblings and prevents semantic overlap.

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 guidance: 'Use it when you are computing a figure whose RULES you are stating yourself,' and directs MRR flows to start here then render with well_render_mrr. It also states when workspace_id is required versus omitted. This clearly differentiates its role from alternatives.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources