Why this exists
Clients keep asking for "AI in my app". Most of what gets shipped under that name is a chat box wired straight to a language model — which is exactly the setup that confidently reports last quarter's revenue as a number nobody can find anywhere in the business. For anything involving money, that's worse than useless.
I built this to work the other way round. The model is good at understanding a messy human question and deciding what needs computing. It is not the thing that computes. So it doesn't.
How it actually works
The app defines a small set of tools the model is allowed to call, then runs an agent loop: ask Claude, and if it responds by requesting a tool, run that tool in real JavaScript over the parsed rows, feed the result back, and repeat until it has enough to answer in plain English.
you ─ question ─▶ Claude ─ "call aggregate(revenue, sum, by=region)" ─┐
▲ │
│ your code runs it
└────────── real numbers fed back ◀─────────────────┘
(repeats until Claude answers without asking for a tool)
The model chooses which tool and what arguments. My code decides what the tool actually does — and that boundary is the whole design. There are three tools:
get_columns— what's in this dataset? Lets the model orient itself before guessing at column names.aggregate— sum, average, count and so on, with optional grouping.filter_rows— narrow to the rows that match a condition.
The build
- Next.js 16 and React 19, with the agent loop living in a route handler on the server so the API key never reaches the browser.
- Streaming — the answer arrives token by token instead of the user watching a spinner for eight seconds.
- CSV parsed once with Papa Parse and held against a dataset ID, so follow-up questions don't re-upload or re-parse.
- Follow-ups keep context — "and which was worst?" resolves against the previous question.
- TypeScript throughout, including the tool schemas the model is handed.
Where it goes next
It's a focused build, and it's honest about its edges: the dataset lives in memory rather than a database, and answers come back as text and figures rather than rendered charts. Both are deliberate next steps rather than things I've quietly skipped — the agent architecture is the part that had to be right first, because charts drawn from hallucinated numbers are just prettier lies.
For a client build, the same pattern points at a database instead of a CSV, with auth and per-user access on top — the shape doesn't change, only what sits behind the tools.
What this one proves
That I build AI features the way they have to be built when money is involved: tool definitions, the agent loop, streaming, and — most importantly — knowing which parts of a system an LLM should never be trusted to do.