Parse Excel. Verify every formula.
Open a workbook or drop in one MCP config. Either way: typed cells, a formula dependency graph, and a verification certificate. Deterministic — no OCR, no model guessing at your numbers.
Free, no account needed. Reads .xlsx and .xlsm. Get an API key →
// .mcp.json — Claude Code, Cursor, or any MCP client
{
"mcpServers": {
"basis": {
"url": "https://api.basis1.tech/mcp",
"headers": {
"Authorization": "Bearer $BASIS_KEY"
}
}
}
}
// "Parse quarterly-model.xlsx"
// → 3 sheets, 12,847 cells, 4,291 formulas
// → audit: 4,289 match · 2 diverge · 99.95%Three ways to parse Excel
Cache readers
openpyxl · SheetJS
Read the values Excel wrote to the cache when the file was last saved. If inputs changed since then, the numbers are wrong — and nothing tells you. Formulas come back as text strings, not computed values.
Document AI
LlamaParse · Textract
Run your spreadsheet through a vision model. Non-deterministic — the same file can produce different output on different days. A number that looks like 1,840,000 could come back as 1,840.000 or be silently dropped.
Basis
formula evaluation + verification
Evaluates every formula from source, compares the result against Excel's own cached values, and reports where it disagrees — by coordinate. Same bytes in, same JSON out, every time.
Benchmarks
Measured across the Enron spreadsheets and FUSE — two public corpora, 4,207 formula-bearing workbooks. Each .xlsx stores the values Excel computed when it was saved, making each file its own test case. Method and limitations →
97.31%
Accuracy
of workbooks recompute cell-for-cell with zero divergence
4M+
Formulas tested
across 3,344 Excel-written workbooks
26M/s
Eval speed
single-threaded formula throughput
736 KB
Engine
entire engine compiled to WebAssembly
3,254 of 3,344 workbooks recompute cell-for-cell against Excel's own cached values. 4,033,846 formulas compared.
What that turns up in real workbooks
Measured across 4,207 formula-bearing workbooks from two public corpora. Private working models (Enron) consistently show more findings than publicly shared spreadsheets (FUSE). Method and limitations →
Rate of any high-precision finding
By finding type (all formula-bearing workbooks)
Two products, one engine
One Rust core — its own decompressor, zip reader, XML tokenizer, formula compiler and calculation engine. Zero third-party libraries. Compiles to WebAssembly for the browser and to a native library for the server, so a number computed in the app and a number computed by the API come from the same code.
The API
A workbook as a computation graph
One call turns an .xlsx into typed cells, a formula dependency edge list, cell-anchored images and a verification certificate. Deterministic — no OCR, no bounding boxes, no model guessing at your numbers.
Read the API docs →The spreadsheet
Every cell keeps its history
Open a real .xlsx and edit it. Every cell answers who set it, when, and what it was before. Branch a scenario instead of copying the file, diff it cell by cell, merge it back. Save, and Excel never knows it left.
Open the app →21 MCP tools
One config line gives any MCP client — Claude Code, Cursor, Windsurf, or your own agent — a full spreadsheet engine. Deterministic tooling, not a model guessing at your numbers.
Parse
Read any workbook
Parse .xlsx into typed cells, formulas, edges, and images. Sync or async — large files run as background jobs.
parse_workbook · submit_job · stream_job
Compute
Live sessions
Open a workbook as a live computation session. Set inputs by name or cell ref, recalculate, read outputs — the spreadsheet becomes a function.
create_session · set_inputs · get_outputs
Diagnose
Trace and audit
Read any cell's formula, walk its dependency graph forward or backward, and run a full workbook audit — broken refs, cycles, hardcoded plugs.
get_cell_formula · trace_dependencies · audit_workbook
Edit
Formula-level writes
Set formulas, define named ranges, add sheets. Every edit recalculates the full graph. Read ranges back in bulk to verify.
set_inputs · define_variable · get_formula_range
Version
Checkpoint and rollback
Commit a session's state before risky edits. Restore to any checkpoint if the change breaks something. Diff two workbooks cell-by-cell.
commit_session · restore_session · diff_workbooks
Export
Save back to xlsx
Snapshot the session as xlsx bytes. The output opens in Excel — calcChain stripped, formulas intact, no repair prompt.
snapshot_session · evaluate_formula
- Add the config to .mcp.json or your client's MCP settings
- Ask your agent to parse, audit, or edit any .xlsx
- Every tool call is deterministic — same bytes in, same result out
Replace $BASIS_KEY with your token. Get an API key →
// .mcp.json
{
"mcpServers": {
"basis": {
"url": "https://api.basis1.tech/mcp",
"headers": {
"Authorization": "Bearer $BASIS_KEY"
}
}
}
}FAQ
Is my workbook stored?
No. Your file is processed in memory and discarded after the response. Nothing is written to disk or retained.
What formats are supported?
.xlsx and .xlsm are parsed natively. Legacy .xls and CSV are accepted via server-side conversion. .xlsb and .ods are not yet supported.
How is accuracy measured?
Every .xlsx stores the values Excel computed when it was saved. Basis recomputes every formula independently and compares — each file is its own test case. 97.31% of 3,344 workbooks match cell-for-cell across 4,033,846 formulas.
Can I run it locally?
The engine is 736 KB of WebAssembly — 276 functions in under a megabyte. The spreadsheet at app.basis1.tech runs it entirely in your browser — your workbook never leaves your machine unless you choose to save it.
Verified Excel semantics
Matching Excel means matching its edge cases, not its documentation. Each of these cost us a bug, a regression test and a corpus scan — so we wrote them down.
- Why a formula that worked yesterday shows #NAME? today
#NAME? after a round-trip
- Why Excel says it found unreadable content in your workbook
“We found a problem with some content”
- Why your dates are off by exactly 4 years, or exactly 1 day
Dates shifted by 1,462 days
- Why SUM breaks when a cell errors, but COUNT keeps working
SUM returns an error, COUNT doesn't
- Why COUNTIF doesn't find the row you know is there
COUNTIF misses a row that is clearly there