You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We have UPDATE_MERCHANT_MUTATION in queries.py but it's only used indirectly through update_recurring, which requires an existing stream_id. There's no way to:
List merchants (with optional name filter)
Get a single merchant by ID
Update a merchant's name or recurring settings directly
This blocks the bill-check workflow when we need to find a merchant, create a recurring stream, or rename a merchant in place.
Proposed Tools
list_merchants(name_contains=None)
List all merchants. Optional name_contains filter narrows results (case-insensitive substring match). Returns canonical merchant representation:
Example 2: Make a merchant non-recurring (remove from recurring entirely)
The merchant currently has a recurring stream. This should remove it from list_recurring permanently. The merchant still exists, it just won't appear as a recurring obligation anymore.
update_merchant("merchant_id", recurring=False)
Example 3: Cancel/deactivate a recurring stream (keep but mark inactive)
The merchant stays in the recurring system but is marked canceled. Useful for obligations that ended but you want to keep the history.
Explicitly state that setting recurring on a merchant creates or updates the recurring stream visible in list_recurring
Document each example above (first-time recurring, remove recurring, cancel, update amount, rename)
Clarify the difference between removing recurring (recurring=False) vs canceling (recurring={"status": "canceled"})
Note PATCH semantics: only provided keys in the recurring object are changed; omitted keys are preserved
list_recurring tool description must:
Reference update_merchant as the way to add, modify, or remove recurring streams
State: "To add a new recurring obligation, find the merchant via list_merchants, then use update_merchant to flag it as recurring"
State: "To deactivate or remove a recurring stream, use update_merchant with the merchant ID (available in each stream's data)"
This cross-referencing is critical for AI agents to discover the right tool for the job.
Testing Required — Verify Before Documenting
IMPORTANT: The crossover behavior between UPDATE_MERCHANT_MUTATION and the recurring streams list has NOT been fully verified via live API testing. Before committing changes — especially tool documentation that makes claims about behavior — the following must be tested against the live Monarch API:
What we know (from existing docs and code)
From RECURRING.md:
"Items are transaction-seeded — cannot create from thin air without a transaction" (line 102)
"Each merchant can only have one recurring stream" (line 103)
Do NOT document crossover behavior (e.g., "this creates a recurring stream") as fact until verified by test.
GraphQL Queries Available
Both queries are already defined in queries.py:
MERCHANT_SEARCH_QUERY# Search merchants by name, returns recurring stream infoGET_MERCHANT_QUERY# Get single merchant by ID with transaction count, rule count, recurringUPDATE_MERCHANT_MUTATION# Update name and/or recurrence settings
Implementation Notes
The recurrence input maps: isRecurring → recurring presence, frequency → frequency, amount → amount, baseDate → starting, isActive → status (active/canceled)
update_recurring in recurring.py already builds the mutation variables — refactor to share logic
Consider deprecating update_recurring in favor of update_merchant (or make it a thin wrapper)
Expose merchant_id in list_recurring output if not already present
Summary
We have
UPDATE_MERCHANT_MUTATIONinqueries.pybut it's only used indirectly throughupdate_recurring, which requires an existingstream_id. There's no way to:This blocks the bill-check workflow when we need to find a merchant, create a recurring stream, or rename a merchant in place.
Proposed Tools
list_merchants(name_contains=None)List all merchants. Optional
name_containsfilter narrows results (case-insensitive substring match). Returns canonical merchant representation:{ "id": "...", "name": "Acme Corp", "recurring": { "frequency": "monthly", "amount": -661.10, "starting": "2026-01-09", "status": "active" } }Where
recurringisnullif the merchant has no recurring stream.get_merchant(merchant_id)Get a single merchant by ID. Same output shape as
list_merchantsitems.update_merchant(merchant_id, name=None, recurring=None)PATCH semantics — only provided fields are updated.
Example 1: Make a merchant recurring for the first time
The merchant exists but has never been flagged as recurring. This should create a new recurring stream that appears in
list_recurring.Example 2: Make a merchant non-recurring (remove from recurring entirely)
The merchant currently has a recurring stream. This should remove it from
list_recurringpermanently. The merchant still exists, it just won't appear as a recurring obligation anymore.Example 3: Cancel/deactivate a recurring stream (keep but mark inactive)
The merchant stays in the recurring system but is marked canceled. Useful for obligations that ended but you want to keep the history.
Example 4: Update just the amount for existing recurring
The merchant is already recurring. Only the amount changes — frequency, start date, and status are preserved.
Example 5: Rename a merchant
Example 6: Rename and set up recurring in one call
Tool Documentation Requirements
update_merchanttool description must:recurringon a merchant creates or updates the recurring stream visible inlist_recurringrecurring=False) vs canceling (recurring={"status": "canceled"})recurringobject are changed; omitted keys are preservedlist_recurringtool description must:update_merchantas the way to add, modify, or remove recurring streamslist_merchants, then useupdate_merchantto flag it as recurring"update_merchantwith the merchant ID (available in each stream's data)"This cross-referencing is critical for AI agents to discover the right tool for the job.
Testing Required — Verify Before Documenting
IMPORTANT: The crossover behavior between
UPDATE_MERCHANT_MUTATIONand the recurring streams list has NOT been fully verified via live API testing. Before committing changes — especially tool documentation that makes claims about behavior — the following must be tested against the live Monarch API:What we know (from existing docs and code)
From
RECURRING.md:From the memory reference:
From the codebase:
UPDATE_MERCHANT_MUTATIONexists and accepts a recurrence object (isRecurring,frequency,amount,baseDate,isActive)update_recurringalready uses this mutation successfully to modify existing streamsWhat must be verified
UPDATE_MERCHANT_MUTATIONwithisRecurring: Trueon a merchant with NO existing stream actually create a new stream inlist_recurring?isRecurring: Falseremove the stream fromlist_recurring? Or do we needCommon_MarkAsNotRecurring?isActive: Falsekeep the stream visible but inactive, vsisRecurring: Falseremoving it entirely?Recommended approach
RECURRING.mdwith answers to open question Add types and fields filters to list_accounts #10 and the verified behaviorDo NOT document crossover behavior (e.g., "this creates a recurring stream") as fact until verified by test.
GraphQL Queries Available
Both queries are already defined in
queries.py:Implementation Notes
isRecurring→ recurring presence,frequency→ frequency,amount→ amount,baseDate→ starting,isActive→ status (active/canceled)update_recurringinrecurring.pyalready builds the mutation variables — refactor to share logicupdate_recurringin favor ofupdate_merchant(or make it a thin wrapper)merchant_idinlist_recurringoutput if not already presentRelated
list_recurringimprovements (cross-reference in tool docs)