Back to PostsResearch

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. Products keep adding AI to spreadsheet UIs; sheetkit does the reverse, and shapes 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, and a mostly empty sheet can still exceed 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.

We took the encoding seriously and the framing less so. The paper treats the spreadsheet as a document: compressed once, 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.

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, but 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, and the recalculation loop (change a cell and every dependent formula moves) is the part that distinguishes a spreadsheet from 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 on-demand reads, 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 is the return type of the read, not a preprocessing pass.
  • 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, rather than a write per cell.

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 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. The computation also lives in a script separate from the document, 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. Turing-completeness does cost something: a formula can fail to terminate or exhaust memory, so recalculation bounds are still needed. What it cannot do is reach outside the engine.
  • Every intermediate value is inspectable. Each step of the computation occupies a cell, so it can be read and edited in place. A Python script returns only its answer.
  • 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 are native to this substrate, and they cover a large share of what gets asked of business data.

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.

The delta echo works 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 does not reply to your edit, revert it, or re-apply it.

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). We also 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, so it stays an observation until we do. 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. 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. sheetkit adds the tool surface: github.com/ingram-technologies/sheetkit.

References