{"ok":true,"site":{"host":"pineapplesour.github.io","url":"https://pineapplesour.github.io/tallyroom/","type":"live","desc":"Drop a bank or card statement into the tab. Nothing uploads. A WebMCP agent analyses it in place, and you watch every tool call it makes.","tools":[{"name":"get_overview","kind":"answer","impl":"imperative","description":"Start here. Returns what is loaded (files, row count, date range, accounts, currencies), the money totals, how much of the data is still uncategorised, any budgets, what the person is currently looking at on screen, and the page's egress meter showing how many bytes have left the browser.","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"title":"Overview of the loaded statement"},"inputSchema":{"type":"object","properties":{}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"query_transactions","kind":"answer","impl":"imperative","description":"Return individual transactions matching a filter, newest first by default, together with the totals for the whole matching set. Use this when you need to look at specific rows. The row list is capped (default 50, maximum 500) but the totals always cover every match, so prefer summarize_spending when you only need a number.","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"title":"List individual transactions"},"inputSchema":{"type":"object","properties":{"from":{"type":"string","description":"Only include transactions on or after this date (YYYY-MM-DD)."},"to":{"type":"string","description":"Only include transactions on or before this date (YYYY-MM-DD)."},"category":{"type":"string","description":"Restrict to one category. Use 'Uncategorised' for rows with no category yet."},"account":{"type":"string","description":"Restrict to accounts whose name contains this text."},"merchant":{"type":"string","description":"Restrict to a merchant. Matching is fuzzy, so 'starbucks' catches every branch and spelling."},"search":{"type":"string","description":"Free-text match against the transaction description."},"flow":{"type":"string","enum":["all","spend","income"],"description":"Which side of the ledger to include. Defaults to all."},"minAmount":{"type":"number","description":"Minimum absolute amount."},"maxAmount":{"type":"number","description":"Maximum absolute amount."},"sort":{"type":"string","enum":["date-desc","date-asc","amount-desc","amount-asc"],"description":"Row ordering. Defaults to date-desc."},"limit":{"type":"number","description":"How many rows to return, 1-500. Defaults to 50."}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"summarize_spending","kind":"answer","impl":"imperative","description":"Group the transactions and total each group - by category, merchant, month, account or day. This is the cheap way to answer almost any 'how much' question; it reads every matching row but returns only the groups.","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"title":"Group and total spending"},"inputSchema":{"type":"object","properties":{"from":{"type":"string","description":"Only include transactions on or after this date (YYYY-MM-DD)."},"to":{"type":"string","description":"Only include transactions on or before this date (YYYY-MM-DD)."},"category":{"type":"string","description":"Restrict to one category. Use 'Uncategorised' for rows with no category yet."},"account":{"type":"string","description":"Restrict to accounts whose name contains this text."},"merchant":{"type":"string","description":"Restrict to a merchant. Matching is fuzzy, so 'starbucks' catches every branch and spelling."},"search":{"type":"string","description":"Free-text match against the transaction description."},"flow":{"type":"string","enum":["all","spend","income"],"description":"Which side of the ledger to include. Defaults to all."},"minAmount":{"type":"number","description":"Minimum absolute amount."},"maxAmount":{"type":"number","description":"Maximum absolute amount."},"groupBy":{"type":"string","enum":["category","merchant","month","account","day"],"description":"What to group by. Defaults to category."},"top":{"type":"number","description":"Keep only the largest N groups and fold the rest into 'Other'."}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"get_trend","kind":"answer","impl":"imperative","description":"Return a month-by-month series, optionally for one category or merchant, so you can talk about whether something is going up or down rather than just its total.","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"title":"Month-by-month trend"},"inputSchema":{"type":"object","properties":{"from":{"type":"string","description":"Only include transactions on or after this date (YYYY-MM-DD)."},"to":{"type":"string","description":"Only include transactions on or before this date (YYYY-MM-DD)."},"category":{"type":"string","description":"Restrict to one category. Use 'Uncategorised' for rows with no category yet."},"account":{"type":"string","description":"Restrict to accounts whose name contains this text."},"merchant":{"type":"string","description":"Restrict to a merchant. Matching is fuzzy, so 'starbucks' catches every branch and spelling."},"search":{"type":"string","description":"Free-text match against the transaction description."},"flow":{"type":"string","enum":["all","spend","income"],"description":"Which side of the ledger to include. Defaults to all."},"minAmount":{"type":"number","description":"Minimum absolute amount."},"maxAmount":{"type":"number","description":"Maximum absolute amount."},"groupBy":{"type":"string","enum":["category","merchant","account"],"description":"Which dimension `value` refers to. Defaults to category."},"value":{"type":"string","description":"The single category, merchant or account to chart. Omit for everything."}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"list_merchants","kind":"answer","impl":"imperative","description":"Group the raw description strings into merchants, collapsing branch names, card reference numbers and spelling variants. Ask for onlyUncategorised to get exactly the work that still needs deciding: a few dozen clusters instead of a thousand rows. This is the right input to propose_categories.","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"title":"Merchant clusters"},"inputSchema":{"type":"object","properties":{"from":{"type":"string","description":"Only include transactions on or after this date (YYYY-MM-DD)."},"to":{"type":"string","description":"Only include transactions on or before this date (YYYY-MM-DD)."},"category":{"type":"string","description":"Restrict to one category. Use 'Uncategorised' for rows with no category yet."},"account":{"type":"string","description":"Restrict to accounts whose name contains this text."},"merchant":{"type":"string","description":"Restrict to a merchant. Matching is fuzzy, so 'starbucks' catches every branch and spelling."},"search":{"type":"string","description":"Free-text match against the transaction description."},"flow":{"type":"string","enum":["all","spend","income"],"description":"Which side of the ledger to include. Defaults to all."},"minAmount":{"type":"number","description":"Minimum absolute amount."},"maxAmount":{"type":"number","description":"Maximum absolute amount."},"onlyUncategorised":{"type":"boolean","description":"Only clusters that have no category yet. Defaults to false."},"limit":{"type":"number","description":"How many clusters to return, largest spend first. Defaults to 60."}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"find_recurring","kind":"answer","impl":"imperative","description":"Detect charges that repeat on a schedule - subscriptions, rent, memberships - by clustering merchants and testing the gaps between charges for weekly, monthly, quarterly or yearly regularity. Reports the typical amount, the annualised cost, and any price change between the early and recent charges, which is how a quiet price rise gets caught.","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"title":"Recurring charges and subscriptions"},"inputSchema":{"type":"object","properties":{"minOccurrences":{"type":"number","description":"How many charges before something counts as recurring. Defaults to 3."}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"find_anomalies","kind":"answer","impl":"imperative","description":"Flag transactions worth a second look: charges far outside the normal range for that same merchant (scored against the median and MAD, so one big charge cannot hide another), the same amount billed twice within two days, and unusually large one-off payments at merchants with no history.","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"title":"Outliers, duplicates and unusual charges"},"inputSchema":{"type":"object","properties":{"from":{"type":"string","description":"Only include transactions on or after this date (YYYY-MM-DD)."},"to":{"type":"string","description":"Only include transactions on or before this date (YYYY-MM-DD)."},"category":{"type":"string","description":"Restrict to one category. Use 'Uncategorised' for rows with no category yet."},"account":{"type":"string","description":"Restrict to accounts whose name contains this text."},"merchant":{"type":"string","description":"Restrict to a merchant. Matching is fuzzy, so 'starbucks' catches every branch and spelling."},"search":{"type":"string","description":"Free-text match against the transaction description."},"flow":{"type":"string","enum":["all","spend","income"],"description":"Which side of the ledger to include. Defaults to all."},"minAmount":{"type":"number","description":"Minimum absolute amount."},"maxAmount":{"type":"number","description":"Maximum absolute amount."},"sensitivity":{"type":"number","description":"Robust z-score threshold for outliers. Lower finds more. Defaults to 3.5."}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"check_budgets","kind":"answer","impl":"imperative","description":"Compare each category's monthly budget against what was actually spent, month by month, and report which months went over. Returns nothing useful until budgets exist - use set_budget first.","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"title":"Budgets versus actual spending"},"inputSchema":{"type":"object","properties":{"month":{"type":"string","description":"Restrict to one month (YYYY-MM). Omit for every month."}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"load_sample_data","kind":"act","impl":"imperative","description":"Load the synthetic twelve-month statement that ships with this app, so the tools have something to work on. Entirely invented data - no real person's finances. Use this when nothing is loaded and you want to demonstrate the app.","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"title":"Load the synthetic sample statement"},"inputSchema":{"type":"object","properties":{}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"focus_view","kind":"act","impl":"imperative","description":"Drive the page the person is looking at: filter the table, change the sort or the chart grouping, highlight specific transactions, and leave a short message above the table explaining what you did. Use this constantly - it is what makes the conversation and the screen agree with each other. It only changes the view, never the data.","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"title":"Change what the person sees"},"inputSchema":{"type":"object","properties":{"from":{"type":"string","description":"Only include transactions on or after this date (YYYY-MM-DD)."},"to":{"type":"string","description":"Only include transactions on or before this date (YYYY-MM-DD)."},"category":{"type":"string","description":"Restrict to one category. Use 'Uncategorised' for rows with no category yet."},"account":{"type":"string","description":"Restrict to accounts whose name contains this text."},"merchant":{"type":"string","description":"Restrict to a merchant. Matching is fuzzy, so 'starbucks' catches every branch and spelling."},"search":{"type":"string","description":"Free-text match against the transaction description."},"flow":{"type":"string","enum":["all","spend","income"],"description":"Which side of the ledger to include. Defaults to all."},"minAmount":{"type":"number","description":"Minimum absolute amount."},"maxAmount":{"type":"number","description":"Maximum absolute amount."},"sort":{"type":"string","enum":["date-desc","date-asc","amount-desc","amount-asc"],"description":"How to order the table."},"groupBy":{"type":"string","enum":["category","merchant","month","account"],"description":"What the chart groups by."},"highlightIds":{"type":"array","items":{"type":"string"},"description":"Transaction ids to mark in the table."},"message":{"type":"string","description":"A short line shown above the table, e.g. 'These are the three charges I think are duplicates.'"},"reset":{"type":"boolean","description":"Clear every filter first."}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"propose_categories","kind":"act","impl":"imperative","description":"Propose rules that assign a category to every transaction matching a merchant. This does NOT change anything on its own: the rules are staged, the page shows the person exactly how many rows each rule would touch, and they approve or discard. Two extra tools - apply_staged_changes and discard_staged_changes - appear while something is staged. Build the rule list from list_merchants, and prefer one rule per merchant cluster using its `key`.","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"title":"Propose categorisation rules (needs approval)"},"inputSchema":{"type":"object","properties":{"rules":{"type":"array","description":"The rules to stage.","items":{"type":"object","properties":{"merchantKey":{"type":"string","description":"A merchant `key` from list_merchants. Matches that cluster exactly. Preferred."},"contains":{"type":"string","description":"Alternative to merchantKey: match any description containing this text."},"category":{"type":"string","description":"The category to assign, e.g. Groceries, Dining, Transport, Subscriptions."},"note":{"type":"string","description":"Optional one-line reason, shown to the person in the review panel."}},"required":["category"]}},"summary":{"type":"string","description":"One line describing the batch, shown at the top of the review panel."}},"required":["rules"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"set_transaction_category","kind":"act","impl":"imperative","description":"Set the category on a small, explicit list of transaction ids. Use this for one-off corrections; for anything that should apply to a whole merchant, use propose_categories so the person can see the blast radius first. A manual category always wins over a rule.","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"title":"Categorise specific transactions"},"inputSchema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"},"description":"Transaction ids, from query_transactions. At most 50."},"category":{"type":"string","description":"The category to assign."}},"required":["ids","category"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"set_budget","kind":"act","impl":"imperative","description":"Set a monthly budget for one category, or clear it by omitting the amount. Budgets show up as a line on the chart and feed check_budgets.","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"title":"Set or clear a monthly budget"},"inputSchema":{"type":"object","properties":{"category":{"type":"string","description":"The category to budget."},"monthlyAmount":{"type":"number","description":"Monthly limit. Omit to clear the budget."}},"required":["category"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"annotate_transaction","kind":"act","impl":"imperative","description":"Attach a short note to one transaction, and optionally flag it so it stands out in the table. Useful for marking something the person should follow up on, like a duplicate charge to dispute.","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"title":"Add a note or flag to a transaction"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"The transaction id."},"note":{"type":"string","description":"The note to attach."},"flagged":{"type":"boolean","description":"Whether to flag the row."}},"required":["id"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"},{"name":"pin_insight","kind":"act","impl":"imperative","description":"Put a finding on the board at the top of the page so it survives the conversation: a title, one line of detail, an optional number, and optionally the transaction ids it refers to so the person can click straight through to them. Pin the things worth acting on, not everything you noticed.","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"title":"Pin a finding to the board"},"inputSchema":{"type":"object","properties":{"title":{"type":"string","description":"Short headline, a few words."},"detail":{"type":"string","description":"One sentence of explanation."},"amount":{"type":"number","description":"The number that matters, if there is one."},"tone":{"type":"string","enum":["neutral","good","warning","critical"],"description":"How the card is coloured. Defaults to neutral."},"transactionIds":{"type":"array","items":{"type":"string"},"description":"Transactions this refers to, so the card can link to them."}},"required":["title"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/tallyroom/"}],"category":"Finance & Crypto","favicon":"/favicons/pineapplesour.github.io.svg","apiSurface":"spec","toolCount":15}}