The default still reads a string with typed input, which is en-US: "05.04.2026" from a dd.mm.yyyy export became 4 May, "2.500" became 2.5, and the command said nothing (#2255). `parse: false` (PR #2267) lets a caller keep the text, but a script that does not pass it still cannot notice the change. The result now names them: `converted` lists the strings typed input read as a number (dates and times included), a boolean or an error, with the cell, the input and what the cell shows, and `convertedCount` is the total. It is capped at 20 entries and 32 characters per string, so a big write does not put a second copy of its data in the result. A string written as a formula (`=1+1`) and a leading apostrophe are not conversions. `cells` and the default reading of the values are unchanged. Two `parse: false` fixes from the review of #2267: - An empty string clears the cell, as `null` and the default path do. Storing an empty text cell left a cell that is not blank: `values: [["", ""]]` gave COUNTA 2 and ISBLANK FALSE. - MCP `write_range` takes `parse`, so an agent can opt out too; the tool's description and docs/mcp.md say so. Tests: the issue's examples under both settings, the report and its cap, the empty string, and an XLSX save/reopen that keeps the text as text and the default's date as a date.
5.3 KiB
GridCraft MCP server
gridcraft-cli mcp exposes GridCraft to AI agents over the Model Context Protocol:
newline-delimited JSON-RPC 2.0 on stdin/stdout (protocol 2025-06-18; 2025-03-26 and 2024-11-05 clients are
answered in their own version). Logs go to stderr.
Two backends:
| Mode | Command | What it drives |
|---|---|---|
| Headless (default) | gridcraft-cli mcp [--in book.xlsx | --sample budget] |
An in-process engine session — no window. |
| Remote | gridcraft-cli mcp --connect 7979 |
A running app started with gridcraft --control 7979 (TCP JSON lines on 127.0.0.1, authenticated with the app's token: it is read from the app's token file or GRIDCRAFT_CONTROL_TOKEN, see control-protocol.md). |
Claude Code
claude mcp add gridcraft -- gridcraft-cli mcp
# or drive the desktop app you are looking at:
claude mcp add gridcraft-app -- gridcraft-cli mcp --connect 7979
JSON config (.mcp.json, Claude Desktop, other clients):
{
"mcpServers": {
"gridcraft": {
"command": "gridcraft-cli",
"args": ["mcp"]
}
}
}
Use "args": ["mcp", "--connect", "7979"] for the desktop app, or ["mcp", "--in", "/path/book.xlsx"] to start
with a file open.
Tools
| Tool | Arguments | Does |
|---|---|---|
execute_command |
command, params? |
Runs any engine command (cell.set, home.bold, data.removeDuplicates, …). |
list_commands |
search? |
Command ids, labels, ribbon paths, shortcuts and parameter docs. |
list_windows |
— | Open windows: window id, workbook (doc) id, title ("Book1", or "Book1:2" for a workbook's second window), unsaved changes, active, hidden. |
inspect_workbook |
— | Sheets, used ranges, tables, charts, names, selection, history. |
read_range |
range?, sheet?, formulas?, formatted? |
Rows of values (default: the used range). |
write_range |
range, values (2-D), parse? |
range.setValues; strings starting with = are formulas, other strings are read like typed input unless parse: false stores them as text. converted in the result lists the strings read as a number, date, time, boolean or error. |
set_cell / get_cell |
cell, input / cell |
Enter one cell / read value, formula, display text, style. |
evaluate_formula |
formula, cell? |
Evaluates without changing the sheet. A formula that couldn't be entered in a cell (it doesn't parse, or a function gets the wrong number of arguments) is an error saying why. |
select |
range, active? |
Sets the selection. |
format_range |
range, bold?, italic?, underline?, fontName?, fontSize?, fontColor?, fillColor?, numberFormat?, horizontalAlign?, wrapText?, borders? |
Composes the home.* formatting commands. |
insert_chart |
range, type, title?, subtype?, at? |
insert.chart. |
create_table |
range, style?, header?, name? |
insert.table. |
sort_range |
keys, range?, header? |
data.sort; keys are "B", "B desc" or {column, order}. |
filter |
column, range?, values?, custom?, top?, blanks?, clear? |
data.filterBy (turns AutoFilter on when needed). |
open_workbook / save_workbook / new_workbook |
path / path? / sample? |
Files (.xlsx, .csv, .tsv, .json, .html). |
undo / redo |
steps? |
History. |
screenshot |
path |
PNG of the window (Remote only). |
list_functions |
search?, category? |
Worksheet functions with signatures. |
Every tool that acts on a workbook (all but list_commands, list_functions, list_windows, open_workbook,
new_workbook and screenshot) also takes window or doc, an id from list_windows, and acts in that window
(for doc, the workbook's most recent window) without bringing it forward. Without them it acts in the active window.
Results are one text block holding pretty JSON; failures set isError: true with a readable message. Unknown tools,
missing required arguments and wrong argument types are JSON-RPC -32602 errors; malformed JSON is -32700; unknown
methods -32601.
Resources
gridcraft://workbook—document.inspectof the active workbook.gridcraft://commands— every command with its parameter docs.gridcraft://functions— every worksheet function.
prompts/list returns an empty list.
Example session
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}
→ {"jsonrpc":"2.0","method":"notifications/initialized"}
→ {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"write_range","arguments":{"range":"A1","values":[["Item","Cost"],["Rent",1200],["Food",400],["Total","=SUM(B2:B3)"]]}}}
→ {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"read_range","arguments":{"range":"B4"}}}
← {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"{\n \"range\": \"B4\", ... \"values\": [[1600.0]]\n}"}],"isError":false}}
Embedding
gridcraft-mcp is a library: Server::new(Box::new(Headless::new())) or
Server::new(Box::new(Remote::connect(&control_addr("7979"))?)), then Server::serve(stdin, stdout) or
Server::handle_line(line) -> Option<String>. Implement the Backend trait (call(method, params)) to drive another
host.