Every chart, every agent tool response, and every REST API endpoint in this project draws from the same source of SQL truth:Documentation Index
Fetch the complete documentation index at: https://mintlify.com/astrxnomo/financial-analytics-agent/llms.txt
Use this file to discover all available pages before exploring further.
agent/lib/finance.ts. Rather than embedding query logic inside individual route handlers or tool definitions, the library centralizes all analytics SQL in one file. The agent tools in agent/tools/* are thin defineTool + Zod wrappers that call into these functions and return their output verbatim. The REST routes under app/api/finance/* do the same via parseQuery. This means fixing a query, adding a filter, or changing how a value is computed happens in exactly one place, and both consumers benefit immediately.
Type Definitions
All input shapes and return types are defined inagent/lib/finance.types.ts:
Function Reference
getSummary
type over the given date range, returning total income, total expense, and net (income − expense). This is the simplest aggregate query and is the source for the summary-tiles component at the top of a financial overview.
getTrend
metric ("income" or "expense"). When groupBy is "department", each returned TrendPoint includes a department string, producing one series per department. When groupBy is "month", all departments are aggregated into a single total series. The optional departments array filters the result to a subset of departments. The trend chart component renders lines for "month" grouping and grouped bars for "department" grouping.
getCategoryBreakdown
"expense"). All three filter parameters are optional — omitting department includes all departments, omitting category includes all categories. The chart component folds categories beyond CATEGORY_BREAKDOWN_TOP_N (8) into an “Other” band, using the same constant exported from finance.types.ts so server and client agree on the cutoff without duplicating it.
getCashflow
CashflowPoint per calendar month in the range, each with income, expense, net (income − expense for that month), and cumulativeNet (running sum of all net values up to and including that month). The cumulative net is computed in JavaScript after the query, not in SQL — it accumulates across the returned rows in order, so the result array must not be reordered after calling this function.
getBudgetStatus
BudgetRow per department for the specified month, joining the budgets table against actual expense totals from transactions. Each row includes budget (the planned amount), actual (the real spend), variance (actual − budget, positive means over-budget), and pctUsed (actual ÷ budget, where a value above 1.0 means over-budget). Departments with no budget row return budget: 0. The seed deliberately sets Engineering’s budget tight so it shows a chronic over-budget pattern.
getProfitability
net. The margin field is net / income when income is greater than zero, or null for departments that booked no revenue in the range (cost centers). A null margin is treated as “n/a” in charts and answers — returning 0 would wrongly imply break-even. The query uses a LEFT JOIN with date bounds in the JOIN clause rather than the WHERE clause, ensuring departments with no transactions in the range still appear as rows with zeroed-out values rather than disappearing from the result.
getAnomalies
mean + threshold × stdDev. The default threshold is 2.5. Each returned Anomaly includes the raw transaction fields plus categoryMean and categoryStdDev so callers can contextualize how far out the outlier sits.
A critical design detail: the statistical baseline (mean and stdDev per category) is always computed from the full unfiltered dataset first. The departments and categories filter parameters only narrow which already-identified outliers are returned — they do not shrink the sample used for baseline computation. Filtering the source rows first would change what counts as “unusual” depending on the departments asked about, rather than just showing a consistent subset of a dataset-wide anomaly list.
getHighlights
getHighlights is consumed by two callers: the /api/finance/highlights REST route (which feeds the suggested-question chips in the chat UI via useFinanceHighlights) and the agent/instructions/dates.ts dynamic instructions resolver (which grounds the agent’s session context in real data boundaries every time a session starts).
getDataOverview
/api/finance/data-overview REST route as well as the get_data_overview agent tool.
Helper Functions
Three small utilities live at the top offinance.ts and are used throughout the file:
num(v)— Converts a Postgresnumericstring (or any unknown value) to a JavaScriptnumberviaNumber(v). Every field that comes back from the database asnumericis passed throughnum()before being included in a return object.day(v)— Normalizes a value to an ISO date string (YYYY-MM-DD). When the value is aDateobject (as returned by thepostgresdriver fordatecolumns), it calls.toISOString().slice(0, 10). Otherwise it coerces to a string directly.addMonths(iso, months)— Adds a number of months to an ISO date string using UTC arithmetic, returning a new ISO date string. Used internally bygetHighlightsto compute the growth comparison windows.
Money is stored as Postgres
numeric. The num() helper converts to JS number at the lib boundary. Never pass raw numeric strings up to tools or charts.DB Connection
agent/lib/db.ts exports a single db() function that returns the shared postgres client:
db() and reused for every subsequent call in the same process. The DATABASE_URL environment variable is read at call time, not at module load time, which means the module can be imported safely in environments where the variable isn’t yet set (e.g. during TypeScript compilation). In practice, DATABASE_URL must be present in .env.local for any code path that actually executes a query.