using System.ComponentModel; using System.Text.Json; using ModelContextProtocol.Server; using MoneyMap.Models; using MoneyMap.Services; namespace MoneyMap.Mcp.Tools; [McpServerToolType] public static class BudgetTools { private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; [McpServerTool(Name = "get_budget_status"), Description("Get all active budgets with current period spending vs. limit.")] public static async Task GetBudgetStatus( [Description("Date to calculate status for (defaults to today)")] string? asOfDate = null, IBudgetService budgetService = default!) { DateTime? date = null; if (!string.IsNullOrWhiteSpace(asOfDate) && DateTime.TryParse(asOfDate, out var parsed)) date = parsed; var statuses = await budgetService.GetAllBudgetStatusesAsync(date); var result = statuses.Select(s => new { s.Budget.Id, Category = s.Budget.DisplayName, s.Budget.Amount, Period = s.Budget.Period.ToString(), s.PeriodStart, s.PeriodEnd, s.Spent, s.Remaining, PercentUsed = Math.Round(s.PercentUsed, 1), s.IsOverBudget }).ToList(); return JsonSerializer.Serialize(result, JsonOptions); } [McpServerTool(Name = "create_budget"), Description("Create a new budget for a category or total spending.")] public static async Task CreateBudget( [Description("Budget amount limit")] decimal amount, [Description("Period: Weekly, Monthly, or Yearly")] string period, [Description("Start date for period calculation, e.g. 2026-01-01")] string startDate, [Description("Category name (omit for total spending budget)")] string? category = null, IBudgetService budgetService = default!) { if (!Enum.TryParse(period, true, out var budgetPeriod)) return $"Invalid period '{period}'. Must be Weekly, Monthly, or Yearly."; var budget = new Budget { Category = category, Amount = amount, Period = budgetPeriod, StartDate = DateTime.Parse(startDate), IsActive = true }; var result = await budgetService.CreateBudgetAsync(budget); return JsonSerializer.Serialize(new { result.Success, result.Message, BudgetId = budget.Id }, JsonOptions); } [McpServerTool(Name = "update_budget"), Description("Update an existing budget's amount, period, or active status.")] public static async Task UpdateBudget( [Description("Budget ID to update")] int budgetId, [Description("New budget amount")] decimal? amount = null, [Description("New period: Weekly, Monthly, or Yearly")] string? period = null, [Description("Set active/inactive")] bool? isActive = null, IBudgetService budgetService = default!) { var budget = await budgetService.GetBudgetByIdAsync(budgetId); if (budget == null) return "Budget not found"; if (amount.HasValue) budget.Amount = amount.Value; if (!string.IsNullOrWhiteSpace(period)) { if (!Enum.TryParse(period, true, out var budgetPeriod)) return $"Invalid period '{period}'. Must be Weekly, Monthly, or Yearly."; budget.Period = budgetPeriod; } if (isActive.HasValue) budget.IsActive = isActive.Value; var result = await budgetService.UpdateBudgetAsync(budget); return JsonSerializer.Serialize(new { result.Success, result.Message }, JsonOptions); } }