spreadsheet-qa-kit MCP server

spreadsheet-qa-kit MCP server

Enables querying your spreadsheet using natural language questions; provides read-only tools for schema, sample data, and structured query execution with auditable computation traces.

Category
Visit Server

README

spreadsheet-qa-kit

Ask your spreadsheet a question in plain words. Get the answer - and the exact steps used to compute it.

Point it at a CSV (or an exported Google Sheet) and ask the kind of thing you'd otherwise build a pivot table for:

"What was total revenue in Q3 for the West region?"

…and the kit answers it, shows its work (which rows it filtered, which column it summed, how), and draws a simple chart when the answer breaks down by category. It reads your data - it does not edit it.

The trick: Claude never computes the number. Claude only translates your question into a concrete plan (filter region = West and quarter = Q3, then sum revenue). A small, auditable Node engine runs that plan over your real rows. So the math is plain code you can read, not model output you have to trust.

This is a real, runnable, MIT-licensed kit. No paywall, no neutered features. Run it on your own machine.


Quickstart

git clone https://github.com/0xJRP/spreadsheet-qa-kit.git
cd spreadsheet-qa-kit
npm install
npm run setup     # friendly wizard: Claude API key + path to your CSV
npm run dryrun    # answers a sample question from the bundled data and SENDS NOTHING

npm run dryrun loads the bundled sample spreadsheet, calls Claude to plan a sample question, runs that plan over the data, prints the answer with its work shown, renders a printable HTML report into out/, and shows exactly what an emailed report would contain - then sends nothing. It's the safest way to see the whole pipeline end to end. (It needs ANTHROPIC_API_KEY set, since it actually calls Claude.)

When you're ready to ask your own questions:

npm run ask -- "What was total revenue in Q3 for the West region?"
npm run ask -- "How many units did each rep sell? Show me a chart."
echo "total revenue by quarter" | xargs -0 -I{} npm run ask -- "{}"

Open the rendered report in out/ and print to PDF (Cmd/Ctrl-P → Save as PDF) to get a PDF.


What it does

   Question                Claude brain              Your output
  ┌──────────┐   ───▶   ┌────────────────┐   ───▶   ┌──────────────────┐
  │ "Q3 sales│          │ a QUERY PLAN:  │          │ the answer +     │
  │  in the  │          │ filter + group │          │ the steps used   │
  │  West?"  │          │ + aggregate    │          │ + a simple chart │
  └──────────┘          └────────────────┘          └──────────────────┘
   Telegram / CLI / MCP   (NOT the answer)            engine runs the plan
                          over YOUR real rows         locally, shows its work
  1. A question. Type it on the CLI, or text it to a Telegram bot (npm start). Plain English, the way you'd ask a coworker.
  2. A brain. Claude reads the question and the spreadsheet's schema (column names, which are numeric, a few sample values) and returns a structured query plan - which rows to filter, whether to group, what to aggregate. Claude is told the exact column names and may only use those.
  3. A deterministic engine. src/sheet.js validates that plan against the real columns and runs it over your rows in plain Node code: filter, group, sum/average/min/max/count. It builds a step-by-step trace as it goes. This is where the number comes from - not the model.
  4. The answer, with its work. You get the headline answer, the steps ("Filtered 30 rows → 4 where region eq West AND quarter eq Q3. Computed sum of revenue over 4 rows = 18753."), an optional ASCII bar chart for grouped answers, and any assumptions Claude made (e.g. how it read "Q3"). All rendered to a clean HTML report you can print to PDF.

Your data is yours, and read-only

data/sample-sales.csv ships so the dry run works out of the box. Point DATA_PATH at your own CSV (or set GOOGLE_SHEET_CSV_URL to a published Google Sheets CSV link and pass --remote). The kit only reads that file. There is no write-back path anywhere in the code: it can answer questions about your data, never change it.

The spreadsheet as an MCP tool

The sheet is also exposed as an MCP server (npm run mcp, built on the official @modelcontextprotocol/sdk). It offers three read-only tools - sheet_schema, sheet_sample, sheet_query - so any MCP-aware client (Claude Desktop, an agent) can inspect and query your spreadsheet as a tool. sheet_query takes the same structured plan and returns the value plus the computation trace, so even an external agent gets the math from the engine, not from a guess.


The safety model - read-only / draft-first

This kit is read-only by design, with a draft-first email path. That's the whole point, and it's Rabbithole's trust differentiator.

  • Your spreadsheet is never edited. There is no write-back code. The kit loads the file, computes from it, and leaves it byte-for-byte unchanged.
  • The answer is auditable. Claude proposes the plan; the kit runs it and prints every step. You can check the filter and the arithmetic yourself - the engine is a few hundred lines of plain code.
  • The model can't invent a column or a number. Every column the plan references is validated against your real header row before anything runs. A bad column name is rejected with a clear error, not silently guessed.
  • Two locks guard the one optional send. Emailing a report happens only when (1) DRY_RUN=false in your .env and (2) you type yes to the confirmation. DRY_RUN defaults to true on a fresh checkout - a hard kill switch that makes every send path a no-op until you deliberately turn it off.
  • Money is never moved. There is no payment code of any kind.
Path What guards it
Answer a question (Claude call + engine) Local; reads your data, writes nothing to it
Render an HTML report Local file write to out/
Draw a chart Local, in-memory text
Email a report DRY_RUN=false and typed yes confirmation
Edit / write back to the spreadsheet Not implemented - by design
Charge a card / take payment Not implemented - by design

CI / demos stay safe automatically: with DRY_RUN=true (the default), npm run dryrun runs the whole pipeline and provably sends nothing.


Configuration

Run npm run setup, or copy .env.example to .env and fill it in. Every value can also be supplied as a real environment variable, so npm run dryrun works non-interactively in CI:

ANTHROPIC_API_KEY=sk-ant-... npm run dryrun

Key settings (see .env.example for all of them, with comments):

Variable What it's for
ANTHROPIC_API_KEY Your Claude API key (from console.anthropic.com)
ANTHROPIC_MODEL Defaults to claude-opus-4-8. For high-volume use, claude-haiku-4-5 is cheaper and faster
DATA_PATH Path to your spreadsheet (CSV). Read-only
GOOGLE_SHEET_CSV_URL Optional public Google Sheets CSV-export URL (use with --remote)
DRAW_CHART Draw a simple text chart for grouped answers (true default)
SMTP_* / REPORT_TO Email creds - only needed to actually email a finished report
TELEGRAM_BOT_TOKEN Optional phone channel - ask questions by text
DRY_RUN Global kill switch. true (default) = nothing can send

Which model?

The default is claude-opus-4-8 - the most capable Opus-tier model, great while you're dialing in the kinds of questions you ask. Once it's reliable and you're running high volume, set ANTHROPIC_MODEL=claude-haiku-4-5 for a cheaper, faster option. Turning a question into a filter/aggregate plan is a clean structured task, so Haiku handles it well.

Want an actual PDF (not just print-to-PDF)?

The kit renders self-contained HTML so there's no headless-browser binary to install. If you want a real .pdf generated automatically, add Puppeteer (npm i puppeteer) and render the HTML report to PDF - about ten lines. Print-to-PDF from any browser works today with zero extra setup.


How it's built

  • Runtime: Node.js, ES modules.
  • Brain: the official Anthropic SDK (@anthropic-ai/sdk) with structured outputs (messages.parse + zodOutputFormat) so the query plan comes back as a typed object. Model defaults to claude-opus-4-8.
  • Engine: plain Node - filter, group, aggregate, with a step-by-step trace. The model never produces the final number.
  • Tools: the official MCP SDK (@modelcontextprotocol/sdk) exposes the spreadsheet as read-only tools.
  • Email: nodemailer (only used when you turn sending on).
  • Inbound: Telegram long-polling over fetch - no extra dependency.
src/
  config.js        # loads .env (and real env vars) into typed config
  sheet.js         # load a CSV (or fetched Google Sheet) + run a query plan (the engine)
  query-engine.js  # the Claude brain - turns a question into a structured query plan
  render.js        # answer → printable HTML report + ASCII chart + a "show your work" text
  mailer.js        # the only send path; gated behind DRY_RUN + confirmation
  pipeline.js      # question → plan → computed answer → rendered report (sends nothing)
  cli.js           # dryrun / ask commands (confirm-before-send on --email)
  setup.js         # the first-run wizard (writes .env, points at your data)
  mcp-server.js    # MCP server exposing the spreadsheet as read-only tools
  server.js        # Telegram inbound - replies to YOU with the answer + its work
data/
  sample-sales.csv       # sample spreadsheet the dry run uses (point DATA_PATH at yours)
  sample-questions.txt    # example questions

DIY, or have us run it

You just got a complete, working spreadsheet-question-answering tool for free. Run it yourself - that's the whole idea.

When you're ready for the production version, that's where we come in. Rabbithole wires this to your real tools and runs it for your team: your live Google Sheets and databases (not just CSVs), richer charts, scheduled reports, multi-sheet joins, role-based access, and guardrails tuned to how your team actually asks questions of its data.

You can run this yourself - or we'll build the production version for your whole team. → rabbithole.consulting


License

MIT © 2026 Rabbithole Consulting. See LICENSE. Use it, fork it, ship it.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured