Back to Posts
Research

Sheetkit: Spreadsheets for AI, Not AI for Spreadsheets

Jerome Leclanche
2026-07-20
Sheetkit: Spreadsheets for AI, Not AI for Spreadsheets
ingram-technologies/sheetkitAn LLM-native spreadsheet toolkit, built on the IronCalc engine.RustMIT / Apache-2.0

sheetkit is an open-source toolkit (Rust, MIT/Apache-2.0) that gives a language model a live spreadsheet as a tool. It runs as an MCP server you can add to Claude Code in one line, as a network daemon with a REST API and a WebSocket channel, and as a wasm module inside a browser tab. It's built on IronCalc, an open-source spreadsheet engine, and it exists because we think the current wave of spreadsheet AI has the relationship backwards: products keep adding AI to spreadsheet UIs, when the more interesting problem is shaping the spreadsheet into something an AI can operate.

Spreadsheets are hard for language models

Arithmetic is the easy part. A spreadsheet is a two-dimensional, sparse, spatially addressed artifact; a language model consumes a one-dimensional token stream. Serialize naively (address, value, format, row by row) and the encoding drowns the content. Boilerplate dominates, homogeneous regions repeat themselves thousands of times, and a mostly empty sheet can still swallow a context window.

The reference point here is Microsoft's SpreadsheetLLM paper from mid-2024. Its encoding framework, SheetCompressor, keeps the rows and columns near structural boundaries and prunes the homogeneous interior, dedupes repeated values into an inverted index, and merges uniformly formatted cells into regions. That cut token usage by roughly 96% (a 25× average compression ratio) and pushed fine-tuned table detection to 78.9% F1, about twelve points over the previous state of the art. But those benchmarks are reading tasks: table detection, QA over sheets. The paper is about understanding spreadsheets, not editing them.

The lesson we took from it is that the encoding matters more than almost anything else you do. But the framing the paper works within treats the spreadsheet as a document: compressed, then handed to a model that answers questions about it. The model looks at a snapshot.

The sidebar wave inherits that framing

The products shipping today mostly do too. Copilot sits in a pane in Excel. Claude for Excel runs in a sidebar, reads the open workbook, explains formulas, traces dependencies, and modifies cells with citations back to what it touched. That citation trail is the strongest transparency story in the category.

These products share a contract shape: the assistant sits on the far side of an integration surface, reads a range, writes a range, and issues further reads to learn what a write did to dependent cells. Add-in APIs do expose dependency queries and batched reads, so a well-built sidebar can pull back the ripple of a write; the point is that it has to know to ask. Nothing pushes the recalculated consequences to the model. That follows from working against real Excel: an assistant that doesn't own the engine cannot return the recalc alongside the write. It matters because the recalculation loop (change a cell and every dependent formula moves) is what makes a spreadsheet more than a table.

The integration-surface contract2+ round trips
write F2 =D2*E2, fill to F10001
ok
read F2:F10001 // what changed?
9,999 values

The consequence of the write is a separate read the model must know to issue — and pay for.

The delta echo (sheetkit)1 round trip
write F2 =D2*E2, fill to F10001
recalc: 9999 cells changed
F3 ⇒ 1137.72 · F4 ⇒ 1304.34 · …

The recalculated ripple comes back in the write's own result. Act, then observe — one call.

Same edit, two tool contracts. The recalculation loop is either behind the surface or in the result.

What sheetkit does instead

sheetkit rebuilds random-access vision, instant recalc feedback, and spatial addressing as text, in a tool contract designed for a model:

  • Structure-aware views under a token budget. Opening a workbook returns a sketch: every sheet, detected table regions, per-column types, value ranges, fill formulas, deviations. A 10,000-row sheet describes itself in a few hundred tokens, and it announces every truncation. This is SpreadsheetLLM's insight applied as an interface: the compression isn't a preprocessing pass, it's the return type of the read.
  • A delta echo after every mutation. Change one cell and the tool result tells you exactly what recalculated, old value ⇒ new value, formula ripple included. The model sees what its edit did without issuing another read.
  • Range-level verbs. fill, sort, expect: one command each, not thousands of cell writes.

A session against a 10k-row file looks like this:

$ sheetd repl orders.xlsx
Sheet1: used A1:E10001
  table1 (Sheet1!A1:E10001, 10000 rows + header)
    A "Order"      text · sorted asc
    B "City"       text, 6 distinct · values: "Vienna"×1754, "Rome"×1675, …
    C "Date"       date 2024-01-01..2024-12-28
    D "Qty"        number 1..50
    E "UnitPrice"  number 1.09..499.98
> set F2 =D2*E2
> fill F2 -> F2:F10001
recalc: 9999 cells changed
  F3 ⇒ 1137.72 · F4 ⇒ 1304.34 · F5 ⇒ 7662.24 · …
> sort table1 by Total desc
⚠ 10000 formula cells moved and re-anchored to their new rows
> expect F2 > 24000
expect F2 > 24000: OK (actual 24495)

sheetd mcp exposes this over the Model Context Protocol as five tools (sheet_open, sheet_exec, sheet_view, sheet_save, sheet_close). The surface is deliberately small, because the command language carries the verbs. With Claude Code:

cargo build --release
claude mcp add sheets -- /path/to/sheetkit/target/release/sheetd mcp

A realistic task ("clean this export, add a margin column, reconcile the totals") lands in a handful of tool calls in our sessions, with every intermediate state verifiable from the transcript alone. sheetd serve runs the same engine as a network service: one authoritative session per workbook, the same tools over HTTP MCP, a REST API, and a WebSocket channel that broadcasts every applied script with its recalc delta, the acting principal, presence, and highlights.

The formula language is the sandbox

The standard answer to "the agent needs to compute something" is a code interpreter: the model writes Python, a sandboxed runtime executes it, and the result is pasted back. This works, and it costs infrastructure: a sandbox per session, resource limits, network policy. It is also opaque. The computation lives in a script the user will never open, and the sheet ends up holding pasted static values.

The formula language already is a programming language. Since LAMBDA, Microsoft Research's own conclusion is that the Excel formula language is Turing-complete: with LAMBDA, LET, and the dynamic-array functions, the formula layer is a pure functional language with reactive evaluation. sheetkit pins IronCalc at a git revision (9ee7e066) that implements this modern set; the published npm release predates roughly 150 of these functions. At the pinned revision, =LET(f, LAMBDA(self, n, IF(n<=1, 1, n*self(self, n-1))), f(f, 5)) returns 120, in the native build and in wasm.

An agent with these tools rarely needs to leave the sheet to compute:

  • No exfiltration surface. In this engine, formulas have no filesystem, no network, no processes; Excel's WEBSERVICE class of functions has no counterpart here. To be precise about what Turing-completeness costs: a formula can fail to terminate or exhaust memory, so recalculation bounds are still needed; that much of the sandbox survives. What it cannot do is reach outside the engine.
  • Every intermediate value is inspectable. A Python script returns an answer; a sheet computation is its own trace, and each step occupies a cell someone can click on.
  • The result stays alive. When the agent answers "which plans beat the average?" with a formula instead of a pasted list, the human can change an assumption and watch the answer update.
  • The delta echo closes the loop. Write a formula, and the recalculated consequences come back in the same tool result: a read-eval-print loop for a reactive language.

We're not claiming you'd want to write a ray tracer in formulas (you can; please don't). The claim is narrower. Aggregation, filtering, projection, and what-if cover most real questions asked of business data. That class is native to this substrate, and here it runs where the person who asked can audit and edit it.

The same code, native and in the browser

The core crate (sheetkit) holds the tool semantics (sketch, command language, delta computation, sessions) and sheetd is a thin daemon around it. sheetkit-wasm is the same crate compiled to WebAssembly; the only change was feature-gating xlsx I/O, whose compression libraries pull in C code. The identical session/command/delta code runs in a browser tab with no server at all.

crates/sheetkit

sketch · command language · delta echo · sessions

sheetd mcp

MCP over stdio — one line to add to Claude Code or any local agent.

sheetd serve

Network daemon: REST API, HTTP MCP, and a WebSocket channel broadcasting every applied script with its recalc delta.

sheetkit-wasm

The same crate compiled to WebAssembly — runs in a browser tab with no server at all.

One implementation of the tool semantics; the deployment mode is a build target.

That's what Ingram Sheets is: a product UI on top of the same pinned engine, where the agent's tools execute in the browser against the document you're editing and you watch its cursor work: blue for you, violet for it. Its landing page runs a replay of a real agent session in your browser, no account needed. A replay proves nothing, but it's the fastest way to see what "watching the agent work" means.

The delta echo turned out to work in the other direction too. Your own edits get the same treatment the agent's do: each burst shows in the chat transcript as a passive note, and the accumulated changes reach the agent at the earliest seam — appended to its next tool result if you edit while it's mid-task (user edited meanwhile: C3 29 ⇒ 35), or attached beside the workbook state on your next message. The prompt frames these as awareness rather than requests, so the agent adapts to your hand without replying to it, reverting it, or re-applying it. Both principals on the document now see each other move, which is the shape the collaboration protocol wants anyway — every edit an attributed event — arriving one seam early.

What it is and isn't

sheetkit is a research project, and we use it daily. The engine is a subset of Excel: it evaluates the modern formula core, and a workbook that leans on pivot tables, Power Query, or VBA is outside it. The engine is pinned to an IronCalc git revision, and stored engine bytes are version-locked to it; workbooks migrate across engine bumps through xlsx, which is lossy at the margins. The delta is computed by snapshot-diffing (IronCalc exposes no dependents API; if upstream adds one, we delete that mechanism). And we owe the token-economics claim a number: we've watched the delta echo eliminate re-reads in practice, but we haven't benchmarked it against a read-write-read contract on a task suite. Until we do, that claim is an observation, not a measurement. Measuring it is next.

We're also looking into offering this natively to agents on Ingram Cloud, our agent platform: a spreadsheet tool any agent can enable, backed by a hosted sheetkit session. The reasoning is the sandbox argument above — for the aggregation-and-what-if class of computation, a live workbook gives an agent an effect-free substrate with per-cell auditability and a deliverable that stays alive, where a code interpreter needs sandbox infrastructure and returns dead values. An agent answering questions in Slack or over email could compute in a workbook and hand back the workbook itself.

The hard part is the engine, and that work is IronCalc's. What sheetkit adds is the tool surface a model can drive: github.com/ingram-technologies/sheetkit.

References