Three ways an agent can touch a model — and the one it reaches for

openpyxl on a binary .xlsx, raw .deepcell XML, or the deepcell CLI. Same task, three very different affordances. Here's why the agent picks the CLI.

5 min readDeepCell Team

Give a coding agent a financial model and a job — "add a downside scenario, then tell me where FCF turns negative" — and the first thing it decides, silently, is how to touch the file. That choice is made before a single number changes, and it determines everything that follows: how many round-trips the job takes, whether the edits are safe, and whether you can trust the result.

There are three ways to touch a model that Claude might reach for. They look interchangeable from the outside. They are not.

Option 1: openpyxl on a binary .xlsx#

This is the reflex for anyone who's automated a spreadsheet in Python. It is also the worst fit for an agent, for reasons that compound.

The file is opaque. A .xlsx is a zipped bundle of XML parts. The agent can't cat it, can't grep it, can't diff it. To see one value it has to write and run a script:

from openpyxl import load_workbook
wb = load_workbook("model.xlsx", data_only=True)
print(wb["Model"]["C7"].value)

Every observation is a round-trip through code it authored. Want the next cell? Another script, or a longer one that guesses at the layout.

The formulas are dead. openpyxl hands you either the formula string or the last value Excel cached (data_only=True) — never a live recalculation. Change an input and every dependent cell is stale until something re-opens the workbook in Excel or LibreOffice. The agent adds a downside scenario and then genuinely cannot see what it did to FCF without booting a calculation engine it doesn't have in-process.

There are no semantics. A cell is Model!C7. Nothing says this is 2027 revenue under the base case. The agent reconstructs meaning from position and prays the layout doesn't shift a row. Merge a cell, insert a column, and its mental map silently rots.

Writing back is lossy. Preserving styles, merged ranges, named ranges, and charts through an openpyxl round-trip is a minefield; plenty gets dropped or mangled. And there's no history — the edit is a binary overwrite with no record of what changed or why.

openpyxl earns its place in exactly one job: getting data out of a legacy workbook and into something better. As a working surface for an agent, it's a last resort.

Option 2: hand-editing the .deepcell XML#

A .deepcell is plain XML — greppable, diffable, git-native. So the obvious next thought is: skip the tooling, let the agent read and write the file directly.

For reading, this is genuinely good. The agent can open the file, scan the ItemDefs and CalcDefs, and build an accurate mental model without running anything. Structure is legible in a way a binary blob never is.

For writing, it's a trap. The entire value of the format — dependency tracking, constraint enforcement, Status and Context rules, schema validation — lives in the calc engine, not in the raw text. Hand-edit the XML and you sail straight past all of it. It's trivially easy to write an internally inconsistent document:

  • a formula referencing an Item that doesn't exist,
  • a Value that violates its own ItemDef guardrail,
  • a dimension tuple that no longer resolves.

Nothing catches you, because nothing ran. And batch changes across many dimensioned values as raw string surgery is precisely the kind of fiddly, namespace-sensitive work where an agent introduces an off-by-one it won't notice until much later.

Direct XML is the right tool for reading structure or a single surgical patch. It's a poor primary write path.

Option 3: the deepcell CLI (or the in-process tool)#

This is the interface the agent actually wants, because it was designed around the affordances an agent has:

Read cheaply and structured. ls, cat, describe, and query inspect the model at the right altitude with no code to author — and query returns resolved, live-calculated values with their dimensions attached. The agent asks "what's FCF in 2027 under downside?" and gets a number that reflects the edit it just made.

Write through the engine. defs apply and batch-edit go through Jingwei, so dependency recalculation, constraint checks, and schema validation all run on every change. The agent cannot silently corrupt the document — an illegal edit comes back as an error, not a landmine.

Versioned by construction. Every commit carries a title and rationale, so the file's history reads as a chain of why, not a binary overwrite. The seam between "the agent did this" and "I did this" stays legible after the fact.

Self-describing and predictable. Every CLI command is a thin pass-through to a Jingwei endpoint — Click flags mirror the pydantic fields one-to-one. The agent discovers capabilities through --help and the in-product guide topics instead of reverse-engineering a file format. And the CLI, the MCP deepcell(command) tool, and the subagents' in-process tool all dispatch to the same service layer, so behavior is identical whether Claude is at a terminal or orchestrating a build.

The same task, three times#

openpyxl + .xlsxraw .deepcell XMLdeepcell CLI/tool
Read a valuewrite & run a scriptread the filequery
See live results after an edit❌ needs Excel/LibreOffice❌ bypasses the engine✅ recalculated
Illegal edit caught✅ validation error
Preserves structure/styleslossymanual
Versioned with rationalegit only
Agent verdictingestion onlyread / one-offpreferred

The through-line is simple. openpyxl makes the agent guess and hope. Raw XML lets it read but not safely write. The CLI turns a modeling task into a sequence of legible, validated, reversible commands — which is exactly the shape an agent is good at.

That's the whole thesis behind a new file format: the format is the source of truth, and the CLI is how you touch it without breaking it. Reach for raw XML when you only need to read. Drop to openpyxl only to drag data out of a legacy workbook. For everything else — the actual work of building and changing a model — the agent reaches for the CLI, and it's right to.