Skip to main content
Glama

Preview invoice fetch

well_preview_invoice_fetch
Read-only

Preview which vendors a past period is still missing supplier invoices from, where each one's invoices are, and which route would obtain them. Use it for "what would happen if I fetched 's missing invoices?" before anything runs.

Name the period ONE way: { calendar_year, calendar_month } (the calendar month, e.g. June 2026 → 2026, 6), { fiscal_year, fiscal_period }, or periods: [{ calendar_year, calendar_month }, …] for SEVERAL months in one call (1-12), or name NO period at all to use the months the user selected on the period card in this conversation (well_list_periods → the user clicks → well_switch_workspace records them). With no period named and no months selected, the call refuses and tells you to run the period step first. Every month must have ended: a current or future month is refused, and so is the adjustment period (13). Duplicate months are refused.

COST: there is no batch endpoint, so each named month is a separate read of that month's spend. Ask for the months the user actually named, not a whole year "to be safe".

Returns vendors — EVERY vendor of the rows THIS CALL covers, one entry per supplier portal ACROSS the whole window (one portal is one place to go, however many months it spans), or one per counterparty where no portal matched: name, provider_id, domain, url and url_source, the counterparties it covers (each tagged with calendar_year, calendar_month, period_label and suggested_route), tx_count, base_total_amount in base_currency. THE ROUTE NEVER FILTERS vendors: a vendor Well has no published flow and no connector for is listed exactly like the rest, with its route on its counterparties. WHAT the call covers is a separate question, and two fields answer it: a counterparty pick narrows the rows to the picked companies (see scoped_to_selected_counterparties below), and a hints line names any group the projection could produce no vendor for. So vendors is every vendor of the rows THIS CALL covers, which is the whole period only when neither of those is present. upload_rows (the user must supply the file) and connect_rows (connecting the named service fetches it) carry the same counterparties again, split by route, with the same month tags.

WHERE A VENDOR'S INVOICES ARE: url_source says how much url knows. "blueprint" is the page Well's own published flow opens, so it IS the billing page. "enrichment" is the vendor's front door — the catalog entry address or the company's domain — so the user still has to find the invoices on it. "none" means no address at all and url is null. Never present an "enrichment" address as the invoice page. url_source informs and gates nothing: an "enrichment" vendor is offered for the pick, and carried on the link, exactly like a "blueprint" one.

ROUTES DESCRIBE HOW, NOT WHETHER: a counterparty Well holds a connector for is in connect_rows AND under its vendor, where its entry reads suggested_route: "connect" and connect_routed_counterparties counts it. Connecting is the route to suggest; the agent run stays available so the user has a way through when the connector does not work for them. A counterparty on suggested_route: "upload" is in upload_rows too. Never present the same counterparty as two separate gaps — it is one gap seen twice, so count it once.

counts covers the rows this call actually read, and every field states its own unit: vendors and agents count PORTALS, agent_tx counts TRANSACTIONS, upload and connect count COUNTERPARTY ROWS — one counterparty per month. They are not summable with each other: never add them into one total, and vendors is never the sum of the other four, because every counterparty reaches the vendor list whichever route it takes. A total over the whole window counts the DISTINCT counterparties named in vendors, and a counterparty appearing again in upload_rows or connect_rows is the same gap seen by its route. Across several months a counterparty counts once per month in upload and connect, while vendors and agents count each portal once for the window, so neither is the sum of the months' own. WHEN scoped_to_selected_counterparties IS PRESENT, vendors, upload_rows, connect_rows, counts AND months COVER ONLY THE PICKED COUNTERPARTIES, NOT THE WHOLE WINDOW: for the months the pick bounded, every row and every figure here is built from the picked rows alone, and selection_scope says how many counterparty rows it left out. Never report those rows as every vendor the period is missing an invoice from, and never report those counts as the period's own — state the truncation and its size, and point at well_list_missing_invoices for a fresh card that drops the pick. Without that field the counts cover the whole window. months gives each month's own counts; periods_covered names the months. A sum is null when any member of it had no FX rate, never a partial figure. The single-month fields calendar_year, calendar_month, fiscal_year, fiscal_period and period_label appear ONLY when the call named exactly one month.

THIS TOOL LAUNCHES NOTHING. It creates no task, starts no run, and fetches no invoice — mode is always "preview" and nothing_launched is always true. Launching the agents is NOT available on this surface, so present the preview as information and do not promise to run it.

collect_url is the ONE link to hand the user: the /collect page, which asks the Well browser extension to run these portals. It names each portal by its provider_id, and that id is the only field that decides which portal runs — a name or an address in the link labels a row and nothing more. Give the link as returned and never build one or edit its parameters. The page starts nothing until the user acts on it, it reports which portals the extension accepted, and it never reports that an invoice arrived. The link also names this workspace, and that name gates WHO may act on the link: the page starts nothing until the reader is signed in to Well as a member of it. It does NOT choose where the invoices land — the extension files into whichever workspace it is signed in to — so never tell the user the link picks the destination. THE LINK CARRIES EVERY VENDOR THAT HAS AN ADDRESS, whatever its url_source and whether or not Well holds a published flow for it. Deciding what a vendor's invoices need once the page opens belongs to the app and the extension, not to this read, so url_source labels a vendor and never withholds it. Two things still keep a vendor off the link: no address at all, and no provider_id the link can address. collect_url is null when the window holds no addressed vendor at all; collect_url_omits names the vendors a full window pushed past the 25-portal ceiling, and collect_url_unaddressable names the ones the link cannot name. A vendor on either list is still missing its invoice, so say the link cannot carry it, and offer the upload or the connect route from upload_rows and connect_rows instead. Never say it has nothing outstanding.

Only CATEGORIZED expense transactions are considered — uncategorized spend is not counted, so poor categorization coverage under-reports what an agent run would cover; disclose the hints.

Call this directly — no other tool call is needed first (workspace is resolved from the caller's authorized token, same as every other well_* tool).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
periodsNoSeveral calendar months in one call, 1-12. Each month costs one separate read, so name only the months you need. Duplicates are refused.
fiscal_yearNoFiscal year (the calendar year the workspace's fiscal year STARTED in).
workspace_idNoTarget workspace. This read reports one workspace's own data. Omit it and the token's primary workspace answers, which may not be the one you mean; the result names the workspace that did.
calendar_yearNoCalendar year, e.g. 2026.
fiscal_periodNoFiscal period, 1-12. The adjustment period (13) is refused: it has no calendar month.
calendar_monthNoCalendar month, 1 = January … 12 = December.
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
modeNo
errorNo
hintsNo
countsNoThe whole window's counts. `vendors` and `agents` count the distinct portals across it, so neither is the sum of the months' own. `upload` and `connect` add each month's counterparty rows, so a counterparty missing an invoice in two of the months read counts once per month.
monthsNoPer-month route counts, oldest first.
successYes
vendorsYesEVERY vendor of the rows THIS CALL covers, whatever route its invoice would arrive by — one entry per supplier portal across the whole window, or per counterparty where no portal matched. The ROUTE never filters this list: a vendor Well has no published flow and no connector for is listed exactly like the rest. What the call covers can still be narrower than the period, and the envelope says so: when `scoped_to_selected_counterparties` is present these are the picked counterparties alone and `selection_scope` sizes the remainder, and a `hints` line names any group the projection could produce no vendor for.
next_stepNoWhat to do with the card this result renders. Added by the dispatcher when the card asks for a click.
collect_urlNoThe `/collect` entry that hands these vendors to the Well browser extension, naming each one by its `provider_id` and naming this workspace as the link's authorization scope. The page starts nothing until the reader is signed in to Well as a member of it. It carries every vendor that has an address, whatever that address's `url_source`, because what a vendor's invoices need once the page opens is the app's and the extension's decision rather than this read's. Null in three unrelated cases: no vendor of the window carries an address, none of the addressed vendors carries an id the link can address, or this read could not name the workspace the link authorizes. The hints name which one, and only the first is a verdict on the vendors. Opening it starts nothing on its own: the user acts on the page.
fiscal_yearNoPresent only when the call named exactly one month.
upload_rowsYesCounterparties whose invoice only a manual upload can obtain.
connect_rowsYesCounterparties whose invoice arrives by connecting a service.
period_labelNoHuman-readable label of the period, e.g. "June 2026". Present only when the call named one month.
workspace_idNo
base_currencyNo
calendar_yearNoPresent only when the call named exactly one month.
fiscal_periodNoPresent only when the call named exactly one month.
calendar_monthNoPresent only when the call named exactly one month.
conversation_idNoThe conversation this result belongs to. Pass it back as the conversation_id argument on every later Well call in the same conversation.
periods_coveredNoThe months the result covers, oldest first.
selection_scopeNoWhat the pick removed. Present with `scoped_to_selected_counterparties`, so the size of the truncation is readable beside the result.
nothing_launchedNoAlways true — this tool never starts anything.
collect_url_omitsNoThe portals `collect_url` does NOT name, because one link carries at most 25. Present only when the ceiling left some out. Report those vendors as outside the link — it starts nothing for them.
periods_requestedNoHow many calendar months the call named.
resolved_workspaceNoThe workspace that answered, when the caller named none and the token authorizes several.
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.
collect_url_unaddressableNoThe vendors `collect_url` does not name: the vendor carries no address at all, or it carries one but no catalog id the link can address. A missing published flow is NOT among the reasons, because the link takes an enrichment address exactly like a blueprint one. Present only when the window holds some. They are real gaps and they are listed in `vendors`; report them as vendors the link cannot carry, never as absent.
rows_dropped_by_ownershipNoCounterparty rows this preview dropped because the current user owns none of their gaps — a fetch runs as the current user and can only collect the invoices of counterparties they own. Counted apart from `selection_scope.rows_dropped_by_filter` (the pick's own shortfall), and present only when it dropped at least one. The matching `hints` line names it.
scoped_to_selected_counterpartiesNoPresent when a counterparty pick narrowed this preview: for the months the pick was made against, every route below covers only those companies. A month outside the pick is covered in full.

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. Changed2 schema fields changed
    • addedOutput schema / properties / next_step
      Added value: +{
      +  "description": "What to do with the card this result renders. Added by the dispatcher when the card asks for a click.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / rows_dropped_by_ownership
      Added value: +{
      +  "description": "Counterparty rows this preview dropped because the current user owns none of their gaps — a fetch runs as the current user and can only collect the invoices of counterparties they own. Counted apart from `selection_scope.rows_dropped_by_filter` (the pick's own shortfall), and present only when it dropped at least one. The matching `hints` line names it.",
      +  "maximum": 9007199254740991,
      +  "minimum": -9007199254740991,
      +  "type": "integer"
      +}
  3. Changed1 schema field changed
    • changedOutput schema / properties / vendors / items / properties / logo_url / description
      Previous value: -"The vendor's mark; null when neither the catalog nor its host resolved one."New value: +"The provider's logo as Well stores it. Null when the provider is unmatched or Well holds no stored mark for it, in which case the card still renders one from `domain`."
  4. Changed12 schema fields changed
    • removedOutput schema / properties / agents
      Removed value: -{
      -  "description": "One entry per invoice agent the whole window would launch — one agent run covers one portal, however many months it spans.",
      -  "items": {
      -    "additionalProperties": false,
      -    "properties": {
      -      "base_total_amount": {
      -        "anyOf": [
      -          {
      -            "type": "number"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "Sum in base_currency; null when an FX rate was missing for any transaction in the sum."
      -      },
      -      "connect_routed_counterparties": {
      -        "description": "How many of `counterparties` the preview suggests connecting instead. They are counted in `tx_count` and `base_total_amount` — the agent would fetch them — and they are ALSO in `connect_rows`, where they are counted as connect counterparties.",
      -        "maximum": 9007199254740991,
      -        "minimum": -9007199254740991,
      -        "type": "integer"
      -      },
      -      "counterparties": {
      -        "description": "The counterparties this one agent would cover, each tagged with the month it belongs to and with the route the preview suggests for it.",
      -        "items": {
      -          "additionalProperties": false,
      -          "properties": {
      -            "base_total_amount": {
      -              "anyOf": [
      -                {
      -                  "type": "number"
      -                },
      -                {
      -                  "type": "null"
      -                }
      -              ],
      -              "description": "Sum in base_currency; null when an FX rate was missing for any transaction in the sum."
      -            },
      -            "calendar_month": {
      -              "maximum": 9007199254740991,
      -              "minimum": -9007199254740991,
      -              "type": "integer"
      -            },
      -            "calendar_year": {
      -              "maximum": 9007199254740991,
      -              "minimum": -9007199254740991,
      -              "type": "integer"
      -            },
      -            "name": {
      -              "type": "string"
      -            },
      -            "period_label": {
      -              "description": "The month this row belongs to, e.g. \"June 2026\".",
      -              "type": "string"
      -            },
      -            "suggested_route": {
      -              "description": "\"agent\" when the agent run is the suggested route. \"connect\" when a Well connector is suggested instead — the counterparty is ALSO in `connect_rows`, and it is listed here so the agent run stays available.",
      -              "enum": [
      -                "agent",
      -                "connect"
      -              ],
      -              "type": "string"
      -            },
      -            "tx_count": {
      -              "maximum": 9007199254740991,
      -              "minimum": -9007199254740991,
      -              "type": "integer"
      -            }
      -          },
      -          "required": [
      -            "name",
      -            "calendar_year",
      -            "calendar_month",
      -            "period_label",
      -            "tx_count",
      -            "base_total_amount",
      -            "suggested_route"
      -          ],
      -          "type": "object"
      -        },
      -        "type": "array"
      -      },
      -      "domain": {
      -        "anyOf": [
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "The provider's bare host, e.g. \"aws.amazon.com\". Null when unmatched or the catalog carries no host."
      -      },
      -      "logo_url": {
      -        "anyOf": [
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "The provider's logo; null when unmatched."
      -      },
      -      "provider_id": {
      -        "anyOf": [
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "provider_name": {
      -        "description": "The supplier portal an agent would run. \"unknown\" when unmatched.",
      -        "type": "string"
      -      },
      -      "tx_count": {
      -        "description": "Transactions this agent run would fetch, over every counterparty listed above.",
      -        "maximum": 9007199254740991,
      -        "minimum": -9007199254740991,
      -        "type": "integer"
      -      },
      -      "url": {
      -        "anyOf": [
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "The provider's portal address as the catalog holds it, e.g. \"https://members.wework.com/invoices\". `domain` is this address reduced to its host. Null when unmatched or the catalog carries no address. Display only — it labels the portal and never selects it."
      -      }
      -    },
      -    "required": [
      -      "provider_name",
      -      "provider_id",
      -      "domain",
      -      "url",
      -      "logo_url",
      -      "counterparties",
      -      "connect_routed_counterparties",
      -      "tx_count",
      -      "base_total_amount"
      -    ],
      -    "type": "object"
      -  },
      -  "type": "array"
      -}
    • changedOutput schema / properties / collect_url / description
      Previous value: -"The `/collect` entry that hands these portals to the Well browser extension, naming each one by its `provider_id` and naming this workspace as the link's authorization scope — the page starts nothing until the reader is signed in to Well as a member of it. Null when no agent here carries a provider id, so no portal can be addressed. Opening it starts nothing on its own: the user acts on the page."New value: +"The `/collect` entry that hands these vendors to the Well browser extension, naming each one by its `provider_id` and naming this workspace as the link's authorization scope. The page starts nothing until the reader is signed in to Well as a member of it. It carries every vendor that has an address, whatever that address's `url_source`, because what a vendor's invoices need once the page opens is the app's and the extension's decision rather than this read's. Null in three unrelated cases: no vendor of the window carries an address, none of the addressed vendors carries an id the link can address, or this read could not name the workspace the link authorizes. The hints name which one, and only the first is a verdict on the vendors. Opening it starts nothing on its own: the user acts on the page."
    • addedOutput schema / properties / collect_url_unaddressable
      Added value: +{
      +  "description": "The vendors `collect_url` does not name: the vendor carries no address at all, or it carries one but no catalog id the link can address. A missing published flow is NOT among the reasons, because the link takes an enrichment address exactly like a blueprint one. Present only when the window holds some. They are real gaps and they are listed in `vendors`; report them as vendors the link cannot carry, never as absent.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "name": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "name"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • changedOutput schema / properties / counts / description
      Previous value: -"The whole window's counts. `agents` counts the distinct portals across it, so it is never the sum of the months' own `agents`. `upload` and `connect` add each month's counterparty rows, so a counterparty missing an invoice in two of the months read counts once per month."New value: +"The whole window's counts. `vendors` and `agents` count the distinct portals across it, so neither is the sum of the months' own. `upload` and `connect` add each month's counterparty rows, so a counterparty missing an invoice in two of the months read counts once per month."
    • changedOutput schema / properties / counts / properties / agents / description
      Previous value: -"Portals an agent run is available for — one per distinct provider, the connector-covered ones included."New value: +"Portals Well holds a published flow for, one per distinct provider, the connector-covered ones included. It marks the shorter route and is NOT the link's own set: `collect_url` names every addressed vendor it can name by id, so it routinely carries portals this count leaves out."
    • addedOutput schema / properties / counts / properties / vendors
      Added value: +{
      +  "description": "Vendors the card lists — one per distinct portal, or per counterparty where none matched.",
      +  "maximum": 9007199254740991,
      +  "minimum": -9007199254740991,
      +  "type": "integer"
      +}
    • changedOutput schema / properties / counts / required
      Previous value: -[
      -  "agents",
      -  "agent_tx",
      -  "upload",
      -  "connect"
      -]New value: +[
      +  "vendors",
      +  "agents",
      +  "agent_tx",
      +  "upload",
      +  "connect"
      +]
    • changedOutput schema / properties / months / items / properties / counts / properties / agents / description
      Previous value: -"Portals an agent run is available for — one per distinct provider, the connector-covered ones included."New value: +"Portals Well holds a published flow for, one per distinct provider, the connector-covered ones included. It marks the shorter route and is NOT the link's own set: `collect_url` names every addressed vendor it can name by id, so it routinely carries portals this count leaves out."
    • addedOutput schema / properties / months / items / properties / counts / properties / vendors
      Added value: +{
      +  "description": "Vendors the card lists — one per distinct portal, or per counterparty where none matched.",
      +  "maximum": 9007199254740991,
      +  "minimum": -9007199254740991,
      +  "type": "integer"
      +}
    • changedOutput schema / properties / months / items / properties / counts / required
      Previous value: -[
      -  "agents",
      -  "agent_tx",
      -  "upload",
      -  "connect"
      -]New value: +[
      +  "vendors",
      +  "agents",
      +  "agent_tx",
      +  "upload",
      +  "connect"
      +]
    • addedOutput schema / properties / vendors
      Added value: +{
      +  "description": "EVERY vendor of the rows THIS CALL covers, whatever route its invoice would arrive by — one entry per supplier portal across the whole window, or per counterparty where no portal matched. The ROUTE never filters this list: a vendor Well has no published flow and no connector for is listed exactly like the rest. What the call covers can still be narrower than the period, and the envelope says so: when `scoped_to_selected_counterparties` is present these are the picked counterparties alone and `selection_scope` sizes the remainder, and a `hints` line names any group the projection could produce no vendor for.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "base_total_amount": {
      +        "anyOf": [
      +          {
      +            "type": "number"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "Sum in base_currency; null when an FX rate was missing for any transaction in the sum."
      +      },
      +      "connect_routed_counterparties": {
      +        "description": "How many of `counterparties` the preview suggests connecting instead. They are counted in `tx_count` and `base_total_amount`, and they are ALSO in `connect_rows`, where they are counted as connect counterparties.",
      +        "maximum": 9007199254740991,
      +        "minimum": -9007199254740991,
      +        "type": "integer"
      +      },
      +      "counterparties": {
      +        "description": "The counterparties this one vendor covers, each tagged with the month it belongs to and with the route the preview suggests for it.",
      +        "items": {
      +          "additionalProperties": false,
      +          "properties": {
      +            "base_total_amount": {
      +              "anyOf": [
      +                {
      +                  "type": "number"
      +                },
      +                {
      +                  "type": "null"
      +                }
      +              ],
      +              "description": "Sum in base_currency; null when an FX rate was missing for any transaction in the sum."
      +            },
      +            "calendar_month": {
      +              "maximum": 9007199254740991,
      +              "minimum": -9007199254740991,
      +              "type": "integer"
      +            },
      +            "calendar_year": {
      +              "maximum": 9007199254740991,
      +              "minimum": -9007199254740991,
      +              "type": "integer"
      +            },
      +            "name": {
      +              "type": "string"
      +            },
      +            "period_label": {
      +              "description": "The month this row belongs to, e.g. \"June 2026\".",
      +              "type": "string"
      +            },
      +            "suggested_route": {
      +              "description": "\"agent\" when the agent run is the suggested route. \"connect\" when a Well connector is suggested instead — the counterparty is ALSO in `connect_rows`. \"upload\" when Well holds neither a connector nor a published flow for it — the counterparty is ALSO in `upload_rows`.",
      +              "enum": [
      +                "agent",
      +                "connect",
      +                "upload"
      +              ],
      +              "type": "string"
      +            },
      +            "tx_count": {
      +              "maximum": 9007199254740991,
      +              "minimum": -9007199254740991,
      +              "type": "integer"
      +            }
      +          },
      +          "required": [
      +            "name",
      +            "calendar_year",
      +            "calendar_month",
      +            "period_label",
      +            "tx_count",
      +            "base_total_amount",
      +            "suggested_route"
      +          ],
      +          "type": "object"
      +        },
      +        "type": "array"
      +      },
      +      "domain": {
      +        "anyOf": [
      +          {
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "The vendor's own bare host, e.g. \"aws.amazon.com\" — the catalog provider's host, or the counterparty company's. It identifies the vendor and its mark; it is NOT `url` reduced, because `url` may point deeper into the portal."
      +      },
      +      "key": {
      +        "description": "Stable identity for this entry across a re-read: the portal when one matched, else the counterparty row. Two vendors sharing a display name have different keys, so key rows on this and never on `name`.",
      +        "type": "string"
      +      },
      +      "logo_url": {
      +        "anyOf": [
      +          {
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "The vendor's mark; null when neither the catalog nor its host resolved one."
      +      },
      +      "name": {
      +        "description": "The vendor as the card names it: the matched provider's name, or the counterparty's own when none matched.",
      +        "type": "string"
      +      },
      +      "provider_id": {
      +        "anyOf": [
      +          {
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "The catalog id, and the only field that can put this vendor on `collect_url`. Necessary but NOT sufficient: the link also needs the vendor to carry a `url`, and the vendor has to fit the link's own ceiling. Null when no provider matched. A vendor off the link is still listed and is still offered for the pick, so report it as one the link cannot carry, never as one Well leaves out."
      +      },
      +      "tx_count": {
      +        "description": "Transactions still missing an invoice, over every counterparty listed above.",
      +        "maximum": 9007199254740991,
      +        "minimum": -9007199254740991,
      +        "type": "integer"
      +      },
      +      "url": {
      +        "anyOf": [
      +          {
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "Where this vendor's invoices are, e.g. \"https://www.dropbox.com/manage/billing\". `url_source` says how close that address stands to the invoices. Null only when `url_source` is \"none\". An address is what makes a vendor offerable at all: a vendor carrying one is offered for the pick, and with a `provider_id` it is also named on `collect_url`, whatever its `url_source`. The address itself never travels, because the id is the only field the extension acts on."
      +      },
      +      "url_source": {
      +        "description": "Where `url` came from. \"blueprint\" — the provider's published flow, so it opens the billing page itself. \"enrichment\" — the catalog's entry address or the company's own domain, so it is the vendor's front door and the user still has to find the invoices on it. \"none\" — no address at all, and `url` is null. It says where the address came from and gates nothing: an \"enrichment\" vendor is offered, and carried on the link, exactly like a \"blueprint\" one.",
      +        "enum": [
      +          "blueprint",
      +          "enrichment",
      +          "none"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "key",
      +      "name",
      +      "provider_id",
      +      "domain",
      +      "url",
      +      "url_source",
      +      "logo_url",
      +      "counterparties",
      +      "connect_routed_counterparties",
      +      "tx_count",
      +      "base_total_amount"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "agents",
      -  "upload_rows",
      -  "connect_rows",
      -  "success"
      -]New value: +[
      +  "vendors",
      +  "upload_rows",
      +  "connect_rows",
      +  "success"
      +]
  5. Changed2 schema fields changed
    • changedInput schema / properties / workspace_id / description
      Previous value: -"Target workspace. Omit to use the only authorized workspace, or (for read tools) to query all authorized workspaces grouped by workspace. Required for write tools when the token authorizes more than one workspace."New value: +"Target workspace. This read reports one workspace's own data. Omit it and the token's primary workspace answers, which may not be the one you mean; the result names the workspace that did."
    • addedOutput schema / properties / resolved_workspace
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "The workspace that answered, when the caller named none and the token authorizes several.",
      +  "properties": {
      +    "name": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ]
      +    },
      +    "workspace_id": {
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "workspace_id",
      +    "name"
      +  ],
      +  "type": "object"
      +}
  6. Changed2 schema fields changed
    • addedOutput schema / properties / scoped_to_selected_counterparties
      Added value: +{
      +  "const": true,
      +  "description": "Present when a counterparty pick narrowed this preview: for the months the pick was made against, every route below covers only those companies. A month outside the pick is covered in full.",
      +  "type": "boolean"
      +}
    • addedOutput schema / properties / selection_scope
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "What the pick removed. Present with `scoped_to_selected_counterparties`, so the size of the truncation is readable beside the result.",
      +  "properties": {
      +    "row_count_before_filter": {
      +      "description": "Counterparty rows the months read hold in total, before the pick narrowed them. Includes the months the pick does not bound, which are reported in full.",
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "rows_dropped_by_filter": {
      +      "description": "How many of those rows the pick left out — gaps the routes and the counts below do not cover.",
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    }
      +  },
      +  "required": [
      +    "row_count_before_filter",
      +    "rows_dropped_by_filter"
      +  ],
      +  "type": "object"
      +}
  7. Changed19 schema fields changed
    • addedOutput schema / properties / agents / items / properties / connect_routed_counterparties
      Added value: +{
      +  "description": "How many of `counterparties` the preview suggests connecting instead. They are counted in `tx_count` and `base_total_amount` — the agent would fetch them — and they are ALSO in `connect_rows`, where they are counted as connect counterparties.",
      +  "maximum": 9007199254740991,
      +  "minimum": -9007199254740991,
      +  "type": "integer"
      +}
    • changedOutput schema / properties / agents / items / properties / counterparties / description
      Previous value: -"The counterparties this one agent would cover, each tagged with the month it belongs to."New value: +"The counterparties this one agent would cover, each tagged with the month it belongs to and with the route the preview suggests for it."
    • addedOutput schema / properties / agents / items / properties / counterparties / items / properties / suggested_route
      Added value: +{
      +  "description": "\"agent\" when the agent run is the suggested route. \"connect\" when a Well connector is suggested instead — the counterparty is ALSO in `connect_rows`, and it is listed here so the agent run stays available.",
      +  "enum": [
      +    "agent",
      +    "connect"
      +  ],
      +  "type": "string"
      +}
    • changedOutput schema / properties / agents / items / properties / counterparties / items / required
      Previous value: -[
      -  "name",
      -  "calendar_year",
      -  "calendar_month",
      -  "period_label",
      -  "tx_count",
      -  "base_total_amount"
      -]New value: +[
      +  "name",
      +  "calendar_year",
      +  "calendar_month",
      +  "period_label",
      +  "tx_count",
      +  "base_total_amount",
      +  "suggested_route"
      +]
    • addedOutput schema / properties / agents / items / properties / domain
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "The provider's bare host, e.g. \"aws.amazon.com\". Null when unmatched or the catalog carries no host."
      +}
    • addedOutput schema / properties / agents / items / properties / tx_count / description
      Added value: +"Transactions this agent run would fetch, over every counterparty listed above."
    • addedOutput schema / properties / agents / items / properties / url
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "The provider's portal address as the catalog holds it, e.g. \"https://members.wework.com/invoices\". `domain` is this address reduced to its host. Null when unmatched or the catalog carries no address. Display only — it labels the portal and never selects it."
      +}
    • changedOutput schema / properties / agents / items / required
      Previous value: -[
      -  "provider_name",
      -  "provider_id",
      -  "logo_url",
      -  "counterparties",
      -  "tx_count",
      -  "base_total_amount"
      -]New value: +[
      +  "provider_name",
      +  "provider_id",
      +  "domain",
      +  "url",
      +  "logo_url",
      +  "counterparties",
      +  "connect_routed_counterparties",
      +  "tx_count",
      +  "base_total_amount"
      +]
    • addedOutput schema / properties / collect_url
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "The `/collect` entry that hands these portals to the Well browser extension, naming each one by its `provider_id` and naming this workspace as the link's authorization scope — the page starts nothing until the reader is signed in to Well as a member of it. Null when no agent here carries a provider id, so no portal can be addressed. Opening it starts nothing on its own: the user acts on the page."
      +}
    • addedOutput schema / properties / collect_url_omits
      Added value: +{
      +  "description": "The portals `collect_url` does NOT name, because one link carries at most 25. Present only when the ceiling left some out. Report those vendors as outside the link — it starts nothing for them.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "provider_id": {
      +        "type": "string"
      +      },
      +      "provider_name": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "provider_id",
      +      "provider_name"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • changedOutput schema / properties / counts / description
      Previous value: -"Totals across every month read."New value: +"The whole window's counts. `agents` counts the distinct portals across it, so it is never the sum of the months' own `agents`. `upload` and `connect` add each month's counterparty rows, so a counterparty missing an invoice in two of the months read counts once per month."
    • changedOutput schema / properties / counts / properties / agent_tx / description
      Previous value: -"Transactions those agents would cover."New value: +"Transactions those agent runs would fetch."
    • changedOutput schema / properties / counts / properties / agents / description
      Previous value: -"Agents that would run — one per distinct provider."New value: +"Portals an agent run is available for — one per distinct provider, the connector-covered ones included."
    • addedOutput schema / properties / counts / properties / connect / description
      Added value: +"Counterparties whose suggested route is connecting a service."
    • addedOutput schema / properties / counts / properties / upload / description
      Added value: +"Counterparties whose invoice only a manual upload can obtain."
    • changedOutput schema / properties / months / items / properties / counts / properties / agent_tx / description
      Previous value: -"Transactions those agents would cover."New value: +"Transactions those agent runs would fetch."
    • changedOutput schema / properties / months / items / properties / counts / properties / agents / description
      Previous value: -"Agents that would run — one per distinct provider."New value: +"Portals an agent run is available for — one per distinct provider, the connector-covered ones included."
    • addedOutput schema / properties / months / items / properties / counts / properties / connect / description
      Added value: +"Counterparties whose suggested route is connecting a service."
    • addedOutput schema / properties / months / items / properties / counts / properties / upload / description
      Added value: +"Counterparties whose invoice only a manual upload can obtain."
  8. Added

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already provide readOnlyHint=true and destructiveHint=false, but the description adds extensive behavioral context well beyond them: it launches nothing, each named month costs a separate read, refusal conditions (current/future months, adjustment period 13, duplicates, no period and no selection), the collect_url gating on workspace membership, the 25-portal ceiling, and the caveat that only categorized expense transactions are counted. It also discloses that 'enrichment' urls must never be presented as the invoice page and that sums are null when any member lacks an FX rate. All of this is consistent with the read-only annotation.

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 description is heavily front-loaded with a clear purpose, but it is severely over-long with visible redundancy. The claim that 'vendors is every vendor of the rows THIS CALL covers' is stated repeatedly across the vendors and counts sections, and several ALREADY-STATED points (e.g., 'the route never filters vendors', 'a counterparty is one gap seen twice') are re-explained in the counts paragraph. While the complexity justifies length, many sentences repeat rather than add; the SCREAMING ALL-CAPS headings and restatements make it harder to scan, not easier.

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 an output schema exists, the description correctly focuses on presentation semantics an agent cannot infer from schema or annotations: how to read url_source, why counts are not summable, how scoped_to_selected_counterparties truncates every field, how collect_url must be handed over verbatim and what it does/doesn't do, and the categorization-coverage under-reporting caveat. The refusal conditions, the fallback to the period card, and the no-prerequisite note ('Call this directly — no other tool call is needed first') make the description complete for an agent to invoke it safely and interpret results 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 adds substantial meaning beyond the schema: it explains the three mutually exclusive ways to name a period ({calendar_year, calendar_month}, {fiscal_year, fiscal_period}, or the periods array), that naming multiple months is one call of 1-12, the cost warning ('each named month is a separate read... not a whole year "to be safe"'), and that workspace is resolved from the caller's authorized token. The schema lists parameters but the description explains the selection semantics and constraints between them, which the schema alone does not convey.

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: 'Preview which vendors a past period is still missing supplier invoices from, where each one's invoices are, and which route would obtain them.' It explicitly frames the tool as the pre-run companion to fetching ('Use it for "what would happen if I fetched <month>'s missing invoices?" before anything runs'), and the SIBLING contrast is sharp: 'THIS TOOL LAUNCHES NOTHING' distinguishes it from well_enqueue_invoice_fetch, while the pointer to well_list_missing_invoices separates it from the list-only sibling. An agent can tell exactly what this tool is and is not.

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 states when to use it ('before anything runs'), how to name periods, and the fallback behavior when no period is named (use the period card selection or refuse). It also points to well_list_missing_invoices when a fresh, unpicked card is needed. However, it never explicitly names well_enqueue_invoice_fetch as the alternative to use when the user actually wants to launch the fetch — the contrast is implied through 'LAUNCHES NOTHING' and 'Launching the agents is NOT available on this surface' rather than stated as a direct routing rule.

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