title: "JSON output" sidebar_position: 7
JSON output
Commands without the screen write their results as JSON with
--format json, or as NUON (the same values, as nushell reads them)
with --format nuon; 012 export writes to a file, so its result
comes with --report json. The schemas below are stable: a later 012
may add fields, but never renames or removes one, or changes its type.
Errors are not JSON: they go to standard error as text, with the exit
statuses in Command line.
Values
A cell's value in JSON keeps what its format means: money stays money
and a date a date, so what reads it can write it back as it was. The
same form is what 012 get and the MCP server's reads return
and what 012 set --value, write_cells, write_table and
create_workbook take:
| Value | Is | Shown |
|---|---|---|
3.5, "00123", true, null |
A number in the Automatic format, text (always text, however it looks), a boolean, a blank cell | 3.5, 00123, TRUE |
{"currency": 3.5} |
An amount; "symbol": "€" for another currency than dollars |
$3.50, €3.50 |
{"percent": 0.12} |
A fraction shown as a percentage | 12% |
{"date": "2026-09-29"} |
A date, or with a time "2026-09-29T14:30:00" on the sheet's clock (an offset, Z or -06:00, converts it to the local time zone) |
9/ |
{"time": "14:30:00"} |
A time of day | 2:30:00 PM |
{"duration": "90min"} |
Elapsed time, in nushell's units (ns to wk), or written as a number of seconds |
1:30:00 |
{"size": 1500} |
A number of bytes, or written as nushell's "1.5kb" |
1.5 kB |
{"number": 1234.5, "decimals": 2} |
A number in another format than Automatic | 1,234.50 |
{"text": "00123"} |
Text, written this way or as a string | 00123 |
Any of the objects may add "decimals" (0 to 15) or "format", a
number format code shown in
the type's place ({"date": "2026-09-29", "format": "yyyy-mm-dd"}).
A value read back carries them when its cell's format isn't the one its
type gives by itself, so writing it again gives the same value and
format. An error reads as its text ("#DIV/). NUON has its own
types for sizes, durations and dates, and none
for currency or percentages.
get
012 get file ref --format json writes a cell's value alone,
or a range as a list of records named by its first row
(--no-header: by column letters), each field a value, as
Scripts describes. In NUON dates, file sizes
and durations are nushell's, and currency and percentages numbers.
set
{
"file": "book.012",
"saved": true,
"changes": [
{"kind": "cell", "sheet": "Sheet1", "item": "B7", "field": "input", "old": "12", "new": "=SUM(B1:B6)"}
],
"warnings": ["Sheet1!C2: Enter a number between 1 and 10"]
}
| Field | Is |
|---|---|
file |
The workbook's path, as given |
saved |
Whether the file was written: false with --dry-run |
changes |
What changed, as diff lists it |
warnings |
Entries a validation rule marks invalid but lets in |
recalc
{
"file": "book.012",
"errors": [
{"cell": "Q3!B7", "sheet": "Q3", "addr": "B7", "value": "#DIV/0!", "why": "Division by zero in B6/C6", "jev": false}
],
"circular": false,
"notebook_failures": []
}
errors are the formulas showing errors, sheet by sheet and row by
row; why is what the screen's context line says, or ""; jev is
set for a JEV function's error that --jev would answer. circular is
set when a formula reads its own cell. notebook_failures are the
lines --notebooks reported for cells that failed. The exit status is
1 when there are errors, as without --format.
describe
{
"sheets": [
{
"name": "Sales", "kind": "sheet", "shown": true, "hidden": false,
"used": "A1:F9", "rows": 9, "cols": 6, "cells": 54, "formulas": 9, "errors": 0,
"header_row": 1, "columns": ["Date", "Region", "Units", "Price", "Total", "Share"],
"frozen": [1, 0], "filter": "",
"tables": [{"name": "Orders", "range": "A1:F9", "columns": ["Date", "Region", "Units", "Price", "Total", "Share"]}],
"regions": [{"name": "files", "kind": "output", "range": "H1:J12"}],
"charts": [{"number": 1, "title": "Units by date", "type": "column", "data": "A1:C9", "at": "H2"}]
}
],
"names": [{"name": "Rate", "range": "Sales!B12"}]
}
| Field | Is |
|---|---|
kind |
sheet, notebook or pivot |
shown, hidden |
The sheet shown when the file was saved; a hidden tab |
used |
A1 to the last cell with contents, "" for an empty sheet; rows and cols its size |
cells, formulas, errors |
Cells with contents, formulas, and formulas showing errors; formulas and errors are -1 on sheets of over a million cells, which aren't counted |
header_row, columns |
The row guessed to name the columns (1 for the first; 0 and [] when none) and its names, "" for a blank cell |
frozen |
Frozen rows and columns |
filter |
The filtered range, or "" |
tables |
Named tables, header row included in range |
regions |
Notebook outputs (kind output) and linked files (linked, with file) sent to the sheet, with the range their rows fill |
charts |
Each chart's number (for 012 export --chart), title (its type's name when it has none), type, data range and at, the cell under its top-left corner |
pivot |
On a pivot table, {"source": "Sales!A1:F9"} |
notebook_cells |
On a notebook, each cell: number, kind (code or note), name (its output's), source, and for code state (ran, failed, not run) and error |
names |
Named ranges, range #REF! when its cells were deleted |
diff
012 diff a b --format json writes a list of changes:
| Field | Is |
|---|---|
kind |
cell, sheet, region, table, layout (another field of a sheet: widths, rules, charts), name, macro, notebook or workbook (its locale, its arithmetic) |
sheet |
The sheet, by its name in the second workbook where it has one |
item |
The cell, region, name, macro or field that changed |
field |
What about it: for a cell input, value, format or note; for a sheet added, removed, renamed or moved |
old, new |
Its values, null where it wasn't or isn't there |
export
012 export file out --report json:
{"file": "out.csv", "format": "csv", "rows": 9, "notes": ["1 formula saved as values"]}
merge-driver
012 merge-driver base ours theirs --format json writes
{"conflicts": [...]}, each with where, field, and the base,
ours and theirs versions as text ("" where it isn't there), and
what for a conflict about a whole sheet. The merge and exit status
are as without it.
MCP tools
The MCP server's tools return these, as structured
content with the same JSON as text beside it. describe with a path returns path and
describe's schema, and so does create_workbook, of the
workbook it made; without a path, describe returns the folders open
to the server and the workbooks in them, more set when there were
more than it lists:
{
"roots": ["/Users/me/Documents/budgets"],
"workbooks": [
{"path": "2026.012", "format": "012", "size": 18422, "modified": "2026-09-29T14:02:11Z"},
{"path": "bank/september.csv", "format": "CSV", "size": 5120, "modified": "2026-09-28T09:30:00Z"}
]
}
list_errors returns recalc's errors and circular.
read_range, and the range resources:
{
"range": "Sales!A1:C3", "rows": 3, "cols": 3,
"values": [["Region", "Units", "Price"], ["North", 3, {"currency": 12.5}], ["South", 5, null]],
"text": [["Region", "Units", "Price"], ["North", "3", "$12.50"], ["South", "5", ""]],
"formulas": {"C3": "=IF(B3>4, \"\", 9.75)"},
"truncated": false
}
values are values (blank is null, an error its text);
text is each cell as shown, left out with "text": false;
truncated is set when the range held more cells than max_cells, and
rows says how many came back. The result's _meta["o12/ holds what
the view draws: title (the
workbook's name), where and html.
write_cells takes each entry's input as a person types it, or its
value as a value, and format, a number format code for
the cell or, alone, for every cell of a range:
{"entries": [
{"ref": "B2", "value": {"currency": 3.5}},
{"ref": "C2", "input": "12%"},
{"ref": "D2", "value": {"date": "2026-09-29"}, "format": "yyyy-mm-dd"},
{"ref": "E2:E9", "format": "#,##0.00"}
]}
write_table (and apply_operations' write_table, and
create_workbook's data) writes rows under a header naming their
columns, in columns' order (a JSON object's keys can reach the tool
in any order), each row a record of values or a list of them in that
order, and formats each column as its first typed value, or as
formats says:
{"at": "A1", "columns": ["Item", "Price", "Bought"],
"rows": [{"Item": "Tea", "Price": {"currency": 3.5}, "Bought": {"date": "2026-09-29"}},
["Cake", 2, {"date": "2026-09-28"}]],
"formats": {"Price": "$#,##0.00"}}
Writes (write_cells, write_table, apply_operations, sort, filter) return
{"saved", "changes", "warnings"} as set does, saved false
for a dry run or a change that changed nothing; create_chart adds
"chart": {"sheet", "number", "title"}, its view in _meta as
read_range's (with name, the chart's data), and create_pivot
"sheet", the pivot's.
| Tool | Returns |
|---|---|
evaluate |
cell where it was computed, value, text as shown, error explaining an error value, and spill, a read_range result, when the formula spilled |
find |
matches, each cell, text and input, the first limit of them, and total |
run_notebook_cell |
notebook, cell, name, state (ran or failed), error, output as NUON and cut when the output was longer than 64 KB |
version
{"version": "v0.9.0", "go": "go1.27.1", "os": "darwin", "arch": "arm64"}