Closes the two real gaps from the agent's gap analysis (2026-08-05):
1. **quotes.update MCP tool** — was missing entirely. The quote header
has fields (company_letterhead, quote_date, quote_reference, etc.)
that couldn't be patched via MCP, so the agent had to pass
letterhead at PDF-render time and couldn't persist it on the record.
Now exposed with a strict field whitelist mirroring the
quote_items whitelist pattern: any field outside the allowed set
(status, customer, company, quote_number, id, created, updated, …)
errors with forbidden_field rather than silently writing to PB.
New catalog entry + dispatch handler in src/app/api/mcp/route.ts.
Whitelist constant QUOTE_PATCH_FIELDS colocated with the existing
QUOTE_ITEM_PATCH_FIELDS for reviewer discoverability.
2. **create_from_email now preserves the unit field** — extracted_items
from the AI's RFQ parsing carries {description, quantity, unit,
notes} but the create loop was silently dropping unit. Also the
service returned items: <count> which made the agent believe no
items were created when in fact stub rows existed. Now returns
the actual QuoteItem array (with a back-compat items_count field)
and the unit value lands on each row.
The agent's third claim (PB admin auth unavailable) is a misdiagnosis
— WFDASH uses its own AGENT_API_KEY and all writes go through the
Next.js admin client. No code change needed for that.
All 1379 tests pass. quote_items.update path unaffected. The
quotes.update path mirrors the same pattern: whitelist + reject empty
patch + call pb.collection.update.
Co-authored-by: Orca <help@stably.ai>