The usual way an assistant reads a spreadsheet is to write a throwaway pandas script, run it, and read the output. That works until the file has a title row above the headers, or prices stored as text, or 40,000 rows you do not want printed into the context window. The MCP Spreadsheet server gives the model four purpose-built tools instead: describe the file, query it, add a computed column, convert it. It opens xlsx, xlsm, xlsb, ods, csv and tsv.
claude mcp add spreadsheet -- npx -y @theluckystrike/mcp-spreadsheet
Cursor, in ~/.cursor/mcp.json:
{
"mcpServers": {
"spreadsheet": {
"command": "npx",
"args": ["-y", "@theluckystrike/mcp-spreadsheet"]
}
}
}
"Open sales.xlsx and tell me what is in it" runs sheet_info: sheet names, row and column
counts, the guessed header row, a type per column, sample values and empty counts. This is the step that
handles the export nobody designed for a machine. In the audit the test file carried a title line and a
blank line above the real headers; sheet_info reported headerRow=2, three
sheets and 400 rows, with correct types and ranges. Delimiters in csv files are sniffed rather than
assumed, so a semicolon-separated European export opens without an argument.
"Which rep sold the most units in the North region? Top 5 with totals" is one call:
sheet_query
path: "/Users/you/sales.xlsx"
sheet: "Sales"
where: '[Region] = "North"'
group_by: ["Rep"]
aggregate: [{"col": "Units", "fn": "sum", "as": "total_units"}]
sort: {"col": "total_units", "dir": "desc"}
limit: 5
The where string is a small safe expression language, not eval: comparisons
= != > >= < <=, plus contains, startswith and endswith, combined with AND, OR, NOT and
parentheses. Column names with spaces go in brackets. Aggregate functions are sum, count, avg, min and
max, and sort can name an aggregate alias. Numbers written as text, such as
"$1,250.00", are read as numbers. Grouping was added after the audit measured the same
ranking taking five tool calls and 71 seconds, because the model had to fall back to python; it is now
one call.
"Add a Revenue column that is Units times Unit Price and save it as CSV next to the original" runs
sheet_add_column with the formula [Units] * [Unit Price]. The source file is
never overwritten unless you pass an output path that points at it. sheet_convert moves
between csv, xlsx and json the same way. sheet_stats gives count, empties, distinct, min,
max, sum, mean and median per column, and sheet_find searches every cell and returns cell
addresses with a preview of the row.
The audit caught a bad failure here and it was fixed. Adding a column to a 400 row sheet on the free
tier used to write 200 rows and report success, leaving a file that looked complete and was not. The
write cap now refuses: nothing is written, the tool tells you the row count and the cap, and it suggests
a way through on the free tier, such as filtering with sheet_query first and writing only
the rows you need. A partial file that looks whole is worse than no file.
Free reads files up to 5 MB and 5,000 rows, with every tool available, and writes up to 500 rows per file. Over a read limit the tool still returns the first 5,000 rows with a note saying what was left out. Pro ($19 once) removes both, up to a 50 MB file ceiling. See MCP Spreadsheet and free versus Pro.
Yes. sheet_info guesses the header row and reports which row it picked, so an export with a title line and a blank line above the real headers opens correctly without you specifying anything.
It groups. sheet_query takes group_by plus aggregate with sum, count, avg, min or max, and can sort by an aggregate alias, so top-N-by-category questions are a single call.
No, not unless you explicitly pass an output path that points at the source. sheet_add_column and sheet_convert write a new file next to the original by default.
Reads return the first 5,000 rows with a note naming what was omitted. Writes over 500 rows are refused outright rather than producing a truncated file, and the message tells you the row count, the cap and a free way round.
No. The server runs locally on your machine and reads your files directly. It makes no network requests, and it stores nothing of its own beyond the files you ask it to write.