* feat(mcp): supplier and date filters on list_supplier_invoices
Add supplier_id, supplier_name (case-insensitive substring, resolved
server-side against the suppliers table), date_from and date_to
(inclusive, on invoice_date) to gnubok_list_supplier_invoices, mirroring
the v1 REST supplier-invoices filter shapes (eq on supplier_id, gte/lte
on invoice_date). Unknown supplier_name returns an empty result without
touching the invoice table; malformed dates and non-uuid supplier_id
fail loudly with pointer messages.
Schema text is kept deliberately terse: the tools/list payload guard
sits at near-zero headroom, so the date format hint lives once in the
tool description and the redundant status enum prose was trimmed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NtvffGr6uVk2J2Skuz6L98
* fix(mcp): bound the supplier_name match set fed to the IN clause
PR Agent review flagged the unbounded id list: a broad substring on a
large supplier register could build an IN clause past PostgREST URL
limits. Cap name matches at 200 (cap + 1 fetched so overflow is
detected) and fail loudly with a refine hint instead of degrading.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NtvffGr6uVk2J2Skuz6L98
* fix(mcp): loud validation, literal name matching, truncation signal on list_supplier_invoices
Skeptic review refuted three behaviors; all three fixed:
1. Silent truncation: date filters invite period reconciliation but the
50-row cap was unsignalled. The invoice query now uses count exact and
returns total_count and has_more (same contract as list_invoices),
with a unique-id order tiebreaker so identical calls return identical
subsets.
2. Silent filter drop: non-string values for the new params (and blank
supplier_name) fell through typeof guards and returned the UNFILTERED
ledger presented as filtered, the same class arg-guard exists for.
They now throw clear errors. Dates are also calendar-validated, so
2026-02-30 fails loudly instead of as a raw Postgres cast error.
3. Wildcard broadening: PostgREST rewrites * in ilike values to %, so
Star*Mart matched Starke Martinsson AB. supplier_name is now matched
as a literal case-insensitive substring in JS over the company's
suppliers (fetchAllRows, parity with list_suppliers), cap unchanged.
Two redundant property descriptions dropped to fund the outputSchema
additions under the tools/list payload ceiling.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NtvffGr6uVk2J2Skuz6L98
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>