get_category_spending_summary
Analyze category spending patterns over time to track expenses, view monthly breakdowns, and understand budget performance with visual charts.
Instructions
Get spending summary for a category over a date range.
Args:
budget_id: The ID of the budget (use 'last-used' for default budget)
category_id: The category ID to analyze
since_date: Start date (YYYY-MM-DD format)
until_date: End date (YYYY-MM-DD format)
include_graph: Include terminal graph visualization (default: True)
Returns:
JSON string with summary including total spent, average per month, transaction count, monthly breakdown, and optional graph
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| budget_id | Yes | ||
| category_id | Yes | ||
| include_graph | No | ||
| since_date | Yes | ||
| until_date | Yes |
Implementation Reference
- src/ynab_mcp/server.py:289-313 (handler)MCP tool handler and registration via @mcp.tool() decorator. Defines input schema via type hints and docstring. Executes tool logic by delegating to YNABClient.get_category_spending_summary and returns JSON.@mcp.tool() async def get_category_spending_summary( budget_id: str, category_id: str, since_date: str, until_date: str, include_graph: bool = True, ) -> str: """Get spending summary for a category over a date range. Args: budget_id: The ID of the budget (use 'last-used' for default budget) category_id: The category ID to analyze since_date: Start date (YYYY-MM-DD format) until_date: End date (YYYY-MM-DD format) include_graph: Include terminal graph visualization (default: True) Returns: JSON string with summary including total spent, average per month, transaction count, monthly breakdown, and optional graph """ client = get_ynab_client() result = await client.get_category_spending_summary( budget_id, category_id, since_date, until_date, include_graph ) return json.dumps(result, indent=2)
- src/ynab_mcp/ynab_client.py:905-984 (helper)Core helper method in YNABClient that implements the spending summary logic: fetches transactions via YNAB API, filters by category and date range, aggregates total spent, transaction count, monthly breakdown, average per month, and generates optional terminal graph.async def get_category_spending_summary( self, budget_id: str, category_id: str, since_date: str, until_date: str, include_graph: bool = True, ) -> dict[str, Any]: """Get spending summary for a category over a date range. Args: budget_id: The budget ID or 'last-used' category_id: The category ID to analyze since_date: Start date (YYYY-MM-DD) until_date: End date (YYYY-MM-DD) include_graph: Include terminal graph visualization (default: True) Returns: Summary with total spent, average, transaction count, and monthly breakdown """ try: # Get transactions for the category url = f"{self.api_base_url}/budgets/{budget_id}/transactions" params = {"since_date": since_date} result = await self._make_request_with_retry("get", url, params=params) txn_data = result["data"]["transactions"] # Filter and aggregate total_spent = 0 transaction_count = 0 monthly_totals = {} for txn in txn_data: # Filter by category and date range if txn.get("category_id") != category_id: continue if txn["date"] > until_date: continue amount = txn["amount"] / 1000 if txn.get("amount") else 0 total_spent += amount transaction_count += 1 # Track monthly totals month_key = txn["date"][:7] # YYYY-MM if month_key not in monthly_totals: monthly_totals[month_key] = 0 monthly_totals[month_key] += amount # Calculate average per month num_months = len(monthly_totals) if monthly_totals else 1 average_per_month = total_spent / num_months if num_months > 0 else 0 # Convert monthly totals to sorted list monthly_breakdown = [ {"month": month, "spent": amount} for month, amount in sorted(monthly_totals.items()) ] result = { "category_id": category_id, "date_range": {"start": since_date, "end": until_date}, "total_spent": total_spent, "transaction_count": transaction_count, "average_per_month": average_per_month, "num_months": num_months, "monthly_breakdown": monthly_breakdown, } # Add graph if requested if include_graph and monthly_breakdown: graph_data = [(item["month"], item["spent"]) for item in monthly_breakdown] result["graph"] = self._generate_graph( graph_data, f"Monthly Spending: {since_date} to {until_date}" ) return result except Exception as e:
- src/ynab_mcp/ynab_client.py:848-904 (helper)Supporting utility method used by get_category_spending_summary to generate terminal-based graph visualization of monthly spending data using termgraph library.def _generate_graph(self, data: list[tuple], title: str = "") -> str: """Generate a terminal graph using termgraph. Args: data: List of (label, value) tuples title: Graph title Returns: String containing the terminal graph """ if not data: return "" # Capture termgraph output old_stdout = sys.stdout sys.stdout = StringIO() try: # Prepare data for termgraph labels = [label for label, _ in data] values = [[abs(value)] for _, value in data] # Configure termgraph args = { "stacked": False, "width": 50, "format": "{:.2f}", "suffix": "", "no_labels": False, "color": None, "vertical": False, "different_scale": False, "calendar": False, "start_dt": None, "custom_tick": "", "delim": "", "verbose": False, "label_before": False, "histogram": False, "no_values": False, } # Print title if title: print(f"\n{title}") print("=" * len(title)) # Generate graph tg.chart(colors=[], data=values, args=args, labels=labels) # Get the output output = sys.stdout.getvalue() return output finally: sys.stdout = old_stdout