whooing_monthly_summary
Retrieve monthly income, expenses, net amount, and transaction counts for a specified month range to analyze financial trends.
Instructions
Get month-by-month income, expense, net amount, and transaction count for a month range.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| start_month | No | Start month in YYYYMM format. Defaults to current month. | |
| end_month | No | End month in YYYYMM format. Defaults to current month. | |
| section_id | No | Section ID. Defaults to WHOOING_SECTION_ID env var. |
Implementation Reference
- src/server.ts:1447-1492 (registration)Registration of the whooing_monthly_summary tool on the MCP server, including its input schema (start_month, end_month, section_id) and the handler that calls report_summary.json API and formats results via formatReportMonthlySummary.
// whooing_monthly_summary — Multi-month income/expense summary server.registerTool( "whooing_monthly_summary", { description: "Get month-by-month income, expense, net amount, and transaction count for a month range.", inputSchema: { start_month: z .string() .regex(/^\d{6}$/) .optional() .describe("Start month in YYYYMM format. Defaults to current month."), end_month: z .string() .regex(/^\d{6}$/) .optional() .describe("End month in YYYYMM format. Defaults to current month."), section_id: z .string() .optional() .describe("Section ID. Defaults to WHOOING_SECTION_ID env var."), }, annotations: { readOnlyHint: true }, }, async (args) => { const sectionId = args.section_id ?? client.defaultSectionId; const now = new Date(); const currentMonth = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}`; const startMonth = args.start_month ?? currentMonth; const endMonth = args.end_month ?? currentMonth; const results = await client.apiGet("report_summary.json", { section_id: sectionId, start_date: startMonth, end_date: endMonth, rows_type: "month", account: "expenses,income", }); const text = formatReportMonthlySummary( results as Parameters<typeof formatReportMonthlySummary>[0] ); return { content: [{ type: "text", text }] }; } ); - src/server.ts:1471-1491 (handler)The handler function for whooing_monthly_summary. Resolves section ID and month range, then calls client.apiGet('report_summary.json') with rows_type:'month' and account:'expenses,income', and formats the response using formatReportMonthlySummary.
async (args) => { const sectionId = args.section_id ?? client.defaultSectionId; const now = new Date(); const currentMonth = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}`; const startMonth = args.start_month ?? currentMonth; const endMonth = args.end_month ?? currentMonth; const results = await client.apiGet("report_summary.json", { section_id: sectionId, start_date: startMonth, end_date: endMonth, rows_type: "month", account: "expenses,income", }); const text = formatReportMonthlySummary( results as Parameters<typeof formatReportMonthlySummary>[0] ); return { content: [{ type: "text", text }] }; } - src/server.ts:1453-1468 (schema)Input schema for whooing_monthly_summary: optional start_month (YYYYMM), end_month (YYYYMM), and section_id, all validated with zod.
inputSchema: { start_month: z .string() .regex(/^\d{6}$/) .optional() .describe("Start month in YYYYMM format. Defaults to current month."), end_month: z .string() .regex(/^\d{6}$/) .optional() .describe("End month in YYYYMM format. Defaults to current month."), section_id: z .string() .optional() .describe("Section ID. Defaults to WHOOING_SECTION_ID env var."), }, - src/formatters.ts:288-326 (helper)Helper types (ReportSummaryRow, ReportSummaryResults) and the formatReportMonthlySummary function that formats the API response into a human-readable month-by-month summary of income, expenses, and net profit in Korean.
interface ReportSummaryRow { date?: string; income?: number; expenses?: number; net_income?: number; } interface ReportSummaryResults { rows?: Record<string, ReportSummaryRow>; } export function formatReportMonthlySummary(results: ReportSummaryResults): string { const rows = results.rows ?? {}; const months = Object.keys(rows).sort(); if (months.length === 0) { return "해당 기간에 월별 요약 데이터가 없습니다."; } const lines: string[] = []; lines.push("## 월별 요약"); lines.push(""); for (const month of months) { const row = rows[month] ?? {}; const income = Number(row.income ?? 0); const expenses = Number(row.expenses ?? 0); const netIncome = Number(row.net_income ?? income - expenses); const label = `${month.slice(0, 4)}-${month.slice(4, 6)}`; lines.push( `- ${label}: ` + `수입 ${formatAmount(income)}, ` + `지출 ${formatAmount(expenses)}, ` + `순이익 ${formatAmount(netIncome)}` ); } return lines.join("\n"); } - src/formatters.ts:3-9 (helper)The formatAmount helper used by formatReportMonthlySummary to format monetary values with Korean locale and '원' suffix.
function formatAmount(amount: number | string | null | undefined): string { const numeric = Number(amount ?? 0); if (!Number.isFinite(numeric)) { return `${amount ?? 0}원`; } return numeric.toLocaleString("ko-KR") + "원"; }