@baruchiro/actual-mcp
Version:
Actual Budget MCP server exposing API functionality
54 lines (53 loc) • 2.77 kB
JavaScript
// Orchestrator for spending-by-category tool
import { SpendingByCategoryInputParser } from "./input-parser.js";
import { SpendingByCategoryDataFetcher } from "./data-fetcher.js";
import { SpendingByCategoryCategoryMapper } from "./category-mapper.js";
import { SpendingByCategoryTransactionGrouper } from "./transaction-grouper.js";
import { SpendingByCategoryGroupAggregator } from "./group-aggregator.js";
import { SpendingByCategoryReportGenerator } from "./report-generator.js";
import { successWithContent, errorFromCatch } from "../../utils/response.js";
export const schema = {
name: "spending-by-category",
description: "Get spending breakdown by category for a specified date range",
inputSchema: {
type: "object",
properties: {
startDate: {
type: "string",
description: "Start date in YYYY-MM-DD format",
},
endDate: {
type: "string",
description: "End date in YYYY-MM-DD format",
},
accountId: {
type: "string",
description: "Optional ID of a specific account to analyze. If not provided, all on-budget accounts will be used.",
},
includeIncome: {
type: "boolean",
description: "Whether to include income categories in the report (default: false)",
},
},
},
};
export async function handler(args) {
try {
const input = new SpendingByCategoryInputParser().parse(args);
const { startDate, endDate, accountId, includeIncome } = input;
const { accounts, categories, categoryGroups, transactions } = await new SpendingByCategoryDataFetcher().fetchAll(accountId, startDate, endDate);
const categoryMapper = new SpendingByCategoryCategoryMapper(categories, categoryGroups);
const spendingByCategory = new SpendingByCategoryTransactionGrouper().groupByCategory(transactions, (categoryId) => categoryMapper.getCategoryName(categoryId), (categoryId) => categoryMapper.getGroupInfo(categoryId), includeIncome);
const sortedGroups = new SpendingByCategoryGroupAggregator().aggregateAndSort(spendingByCategory);
let accountLabel = "Accounts: All on-budget accounts";
if (accountId) {
const account = accounts.find((a) => a.id === accountId);
accountLabel = `Account: ${account ? account.name : accountId}`;
}
const markdown = new SpendingByCategoryReportGenerator().generate(sortedGroups, { start: startDate, end: endDate }, accountLabel, includeIncome);
return successWithContent([{ type: "text", text: markdown }]);
}
catch (err) {
return errorFromCatch(err);
}
}