whooing_calendar
Retrieve a daily overview of income and expenses for a month, including transaction counts. Quickly check per-day financial activity.
Instructions
Get daily income/expense overview for a month. Shows per-day transaction counts, income, and expenses.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| start_month | No | Start month in YYYYMM format (e.g., 202604). Defaults to current month. | |
| end_month | No | End month in YYYYMM format (e.g., 202604). Defaults to current month. | |
| section_id | No | Section ID. Defaults to WHOOING_SECTION_ID env var. |
Implementation Reference
- src/server.ts:1403-1445 (registration)Registration of the whooing_calendar tool with server.registerTool, including inputSchema with start_month, end_month, and section_id optional parameters.
// whooing_calendar — Daily income/expense overview server.registerTool( "whooing_calendar", { description: "Get daily income/expense overview for a month. " + "Shows per-day transaction counts, income, and expenses.", inputSchema: { start_month: z .string() .optional() .describe("Start month in YYYYMM format (e.g., 202604). Defaults to current month."), end_month: z .string() .optional() .describe("End month in YYYYMM format (e.g., 202604). 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("calendar.json", { section_id: sectionId, start_date: startMonth, end_date: endMonth, }); const text = formatCalendar( results as Parameters<typeof formatCalendar>[0] ); return { content: [{ type: "text", text }] }; } ); - src/server.ts:1426-1444 (handler)Handler function for whooing_calendar. Calls Whooing API 'calendar.json' with section_id, start_date, end_date, then formats results using formatCalendar.
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("calendar.json", { section_id: sectionId, start_date: startMonth, end_date: endMonth, }); const text = formatCalendar( results as Parameters<typeof formatCalendar>[0] ); return { content: [{ type: "text", text }] }; } - src/formatters.ts:807-856 (helper)Formatting helper function formatCalendar that takes CalendarResults and produces a human-readable daily income/expense overview in Korean with monthly summary and per-day breakdown.
export function formatCalendar(results: CalendarResults): string { const lines: string[] = []; let hasDailyRows = false; const agg = results.aggregate; if (agg) { lines.push("## 월간 요약"); lines.push(`- 수입: ${formatAmount(agg.income)}`); lines.push(`- 지출: ${formatAmount(agg.expenses)}`); if (agg.etc) lines.push(`- 기타: ${formatAmount(agg.etc)}`); lines.push(""); } const rows = results.rows ?? {}; const months = Object.keys(rows).sort(); if (months.length === 0) { lines.push("해당 기간에 데이터가 없습니다."); return lines.join("\n"); } const dayNames = ["일", "월", "화", "수", "목", "금", "토"]; for (const month of months) { const days = normalizeCalendarDays(rows[month]).filter( (d) => Number(d.count ?? 0) > 0 ); if (days.length === 0) continue; const label = `${month.slice(0, 4)}-${month.slice(4, 6)}`; lines.push(`### ${label}`); for (const d of days) { const dateStr = `${String(d.date).slice(0, 4)}-${String(d.date).slice(4, 6)}-${String(d.date).slice(6, 8)}`; const dayName = dayNames[Number(d.day)] ?? ""; const parts: string[] = []; if (Number(d.income) > 0) parts.push(`수입 ${formatAmount(Number(d.income))}`); if (Number(d.expenses) > 0) parts.push(`지출 ${formatAmount(Number(d.expenses))}`); if (Number(d.etc) > 0) parts.push(`기타 ${formatAmount(Number(d.etc))}`); lines.push(`- ${dateStr}(${dayName}) ${d.count}건: ${parts.join(", ")}`); hasDailyRows = true; } lines.push(""); } if (!hasDailyRows && !agg) { lines.push("해당 기간에 데이터가 없습니다."); } return lines.join("\n"); } - src/formatters.ts:778-792 (schema)Type definitions CalendarDay, CalendarDayRows, and CalendarResults used by the calendar tool's response formatting.
interface CalendarDay { date: string; day: number; count: number; income: number; expenses: number; etc: number; } type CalendarDayRows = CalendarDay[] | Record<string, CalendarDay>; interface CalendarResults { aggregate?: { income: number; expenses: number; etc: number }; rows?: Record<string, CalendarDayRows>; } - src/formatters.ts:794-805 (helper)Helper function normalizeCalendarDays that normalizes CalendarDayRows (either array or record) into a consistent CalendarDay array.
function normalizeCalendarDays(days: CalendarDayRows | undefined): CalendarDay[] { if (Array.isArray(days)) { return days; } if (days && typeof days === "object") { return Object.entries(days).map(([date, value]) => ({ ...value, date: value.date ?? date, })); } return []; }