mcp-server-spreadsheet
mcp-server-spreadsheet
mcp-name: io.github.marekrost/mcp-server-spreadsheet
Data-first MCP server for reading and writing spreadsheet files (.xlsx, .csv, .ods).
Key features
Multi-format — works with Excel (
.xlsx), CSV (.csv), and OpenDocument (.ods) files through a unified tool interface.Dual mode — cell-level workbook operations and a DuckDB-powered SQL query engine, interleaved freely on the same file.
Workbook essentials — worksheets, rows, columns, cells, search.
Data-only — preserves existing formatting but only reads and writes values.
Stateless — every call specifies
fileandsheetexplicitly; no handles or sessions.Atomic saves — writes go to a temp file, then
os.replace()into the target path.Type coercion on write — numeric strings become numbers, everything else is text.
SQL across sheets — JOINs, GROUP BY, aggregates, subqueries via in-memory DuckDB; mutations write back to the file.
CSV as single-sheet workbook — CSV files are treated as a workbook with one sheet named
default.
Related MCP server: SheetForge MCP
Requirements
Python 3.10+
Installation
From PyPI (recommended)
No local checkout needed — just configure your MCP client (see below).
From source (for development)
git clone https://github.com/marekrost/mcp-server-spreadsheet.git
cd mcp-server-spreadsheet
uv syncUsage
Claude Desktop
Add to your claude_desktop_config.json:
Using PyPI (recommended):
{
"mcpServers": {
"mcp-server-spreadsheet": {
"command": "uvx",
"args": ["mcp-server-spreadsheet"]
}
}
}Using local source:
{
"mcpServers": {
"mcp-server-spreadsheet": {
"command": "uv",
"args": ["run", "--directory", "/path/to/mcp-server-spreadsheet", "main.py"]
}
}
}Claude Code
Add to your .mcp.json:
Using PyPI (recommended):
{
"mcpServers": {
"mcp-server-spreadsheet": {
"command": "uvx",
"args": ["mcp-server-spreadsheet"]
}
}
}Using local source:
{
"mcpServers": {
"mcp-server-spreadsheet": {
"command": "uv",
"args": ["run", "--directory", "/path/to/mcp-server-spreadsheet", "main.py"]
}
}
}Standalone (stdio transport)
# PyPI
uvx mcp-server-spreadsheet
# Local source
uv run main.pyRestricting file access to a directory (optional)
Set MCP_SPREADSHEET_ROOT to confine all path arguments to a single directory tree. Paths outside it are rejected with a clear error returned to the agent.
{
"mcpServers": {
"mcp-server-spreadsheet": {
"command": "uvx",
"args": ["mcp-server-spreadsheet"],
"env": { "MCP_SPREADSHEET_ROOT": "/home/me/spreadsheets" }
}
}
}Unset (the default), any path the server process can access is allowed.
Format notes
Format | Sheets | Formulas | Types |
| Multiple | Preserved as strings | Native (int, float, date, bool) |
| Multiple | Not preserved | Native (int, float, date, bool) |
| Single ( | N/A | Inferred on load (int, float, text) |
Sheet management tools (add_sheet, delete_sheet, copy_sheet) raise an error for CSV files.
Tools
Workbook Operations
Tool | Description |
| List all spreadsheet files in a directory (non-recursive) |
| Create a new empty spreadsheet file (format by extension) |
| Copy an existing file to a new path |
Sheet Operations
Tool | Description |
| List all sheet names in a workbook |
| Add a new sheet (optional name and position) |
| Rename an existing sheet |
| Delete a sheet by name |
| Duplicate a sheet within a workbook (optional new name and position) |
Reading Data
Tool | Description |
| Read entire sheet as rows (optional row/column bounds) |
| Read a single cell value, e.g. |
| Read a rectangular range, e.g. |
| Get row and column count of the used range |
Writing Data
Tool | Description |
| Write a value to a single cell |
| Write a 2D array starting at a given cell |
| Append rows after the last used row |
| Insert blank or pre-filled rows at a position (shifts rows down) |
| Delete rows by index (shifts rows up) |
| Clear values in a range without removing rows/columns |
| Copy a block of cells to another location (optionally to a different sheet) |
Column Operations
Tool | Description |
| Insert blank columns at a position |
| Delete columns by index |
Search
Tool | Description |
| Search for a value or regex pattern, returns matching cell references |
Table Mode (SQL)
Tool | Description |
| Inspect column names, inferred types, row count, and sample values |
| Execute a read-only SQL |
| Execute |
SQL examples:
-- Filter and sort
SELECT name, revenue FROM Sales WHERE status = 'Active' ORDER BY revenue DESC LIMIT 20
-- Cross-sheet JOIN
SELECT o.order_id, c.name FROM Orders o JOIN Customers c ON o.customer_id = c.id
-- Aggregate
SELECT department, COUNT(*) AS n, AVG(salary) AS avg FROM Employees GROUP BY department
-- Mutate
UPDATE Sales SET status = 'Closed' WHERE quarter = 'Q1' AND revenue < 1000
DELETE FROM Logs WHERE date < '2024-01-01'Sheet names with spaces must be quoted: SELECT * FROM "Q1 Sales".
Sheets whose table doesn't start at row 1
All three SQL tools accept header_row and data_start_row. Each can be an
int (applied to every sheet) or a {sheet_name: row} mapping (sheets not
listed fall back to the default). Use header_row when column titles live
below row 1, and data_start_row when extra rows (e.g. a units row) sit
between the header and the data.
# Header on row 3, data follows immediately
sql_query(file, 'SELECT * FROM "People"', header_row=3)
# Mixed workbook: People headers at row 3, Orders header at row 1 with a
# units row at row 2.
sql_query(
file,
'SELECT * FROM "Orders" o JOIN "People" p ON o.name = p.name',
header_row={"People": 3, "Orders": 1},
data_start_row={"Orders": 3},
)sql_execute preserves rows above header_row when writing changes back.
Running tests
uv sync --group dev
uv run pytestEvery tool is exercised against .xlsx, .csv, and .ods fixtures generated into a temp directory.
Common Parameters
Every sheet-level tool accepts:
Parameter | Required | Description |
| yes | Path to the spreadsheet file (.xlsx, .csv, or .ods) |
| no | Sheet name. Defaults to the first sheet in the workbook |
All row/column indices are 1-based. Cell references use A1 notation (A1, $B$2).
Available Tools
25 toolsadd_sheetA
Add a new sheet to the workbook.
Returns the name of the newly created sheet. Not supported for CSV files.
Inputs: file (required), name, position. Returns: result
append_rowsA
Append one or more rows after the last used row in the sheet.
Values are type-coerced (numeric strings to numbers). This is the most efficient way to add data to the end of a sheet.
Inputs: file (required), data (required), sheet. Returns: result
clear_rangeA
Clear all cell values in a range without removing rows or columns.
Sets every cell in the range to null. Row/column structure is preserved.
Inputs: file (required), range_str (required), sheet. Returns: result
copy_rangeA
Copy a rectangular block of cells to another location.
Copies raw values only. The destination can be on the same sheet or a different sheet in the same workbook. Existing values at the destination are overwritten.
Inputs: file (required), source_range (required), dest_start (required), sheet, +1 more. Returns: result
copy_sheetA
Duplicate a sheet within the same workbook.
Copies all cell values. Returns the name of the new sheet. Not supported for CSV files.
Inputs: file (required), source_name (required), new_name, position. Returns: result
copy_workbookA
Copy an existing spreadsheet file to a new location.
Performs a full file copy preserving all data. The destination must not already exist. Returns the absolute path of the new file.
Inputs: source (required), destination (required). Returns: result
create_workbook_fileA
Create a new empty spreadsheet file at the given path.
The file format is determined by the extension (.xlsx, .csv, or .ods). The file must not already exist. Returns the absolute path of the created file.
Inputs: file (required), sheet_name. Returns: result
delete_columnsB
Delete one or more columns, shifting remaining columns left.
Inputs: file (required), column (required), count, sheet. Returns: result
delete_rowsB
Delete one or more rows, shifting remaining rows up.
All data in the deleted rows is permanently removed.
Inputs: file (required), row (required), count, sheet. Returns: result
delete_sheetA
Delete a sheet by name from the workbook.
All data in the sheet is permanently removed. Not supported for CSV files.
Inputs: file (required), name (required). Returns: result
describe_tableA
Inspect the structure of a sheet treated as a database table.
Returns column names, inferred data types (text, integer, number, boolean, date), total row count, and sample values from the first 3 data rows. Use this before writing SQL queries to understand the available columns and their types.
When sheet is omitted, returns a list of descriptions for all sheets.
Inputs: file (required), sheet, header_row. Returns: result
get_sheet_dimensionsA
Get the dimensions of the used range in a sheet.
Returns {"rows": N, "columns": M} where N is the number of the last used row and M is the number of the last used column. Both are 0 for an empty sheet.
Inputs: file (required), sheet.
insert_columnsB
Insert one or more blank columns, shifting existing columns right.
Inputs: file (required), column (required), count, sheet. Returns: result
insert_rowsA
Insert rows at a given position, shifting existing rows down.
If data is provided, the inserted rows are filled with those values (type-coerced). Otherwise the rows are left blank.
Inputs: file (required), row (required), count, data, +1 more. Returns: result
list_sheetsA
List all sheet names in a workbook, in workbook order.
Returns a list of sheet name strings. CSV files always return a single sheet named 'default'.
Inputs: file (required). Returns: result
list_workbooksA
List all spreadsheet files (.xlsx, .csv, .ods) in a directory (non-recursive).
Returns the full path of each file found, sorted alphabetically.
Inputs: directory (required). Returns: result
read_cellA
Read the value of a single cell.
Returns the cell's value: numbers as int/float, text as string, and empty cells as null.
Inputs: file (required), cell (required), sheet.
read_rangeA
Read a rectangular range of cells as a list of rows.
Returns a 2D array where each inner list is one row of values. Empty cells return null.
Inputs: file (required), range_str (required), sheet. Returns: result
read_sheetA
Read an entire sheet (or a bounded sub-region) as a list of rows.
Each row is a list of cell values. Empty cells appear as null. Use the optional row/column bounds to limit output for large sheets.
Inputs: file (required), sheet, start_row, end_row, +2 more. Returns: result
rename_sheetB
Rename an existing sheet in the workbook.
Returns the new sheet name on success.
Inputs: file (required), old_name (required), new_name (required). Returns: result
search_sheetA
Search all cells in a sheet for values matching a regex pattern.
Returns a list of matches, each with the cell reference and value, e.g. [{"cell": "B3", "value": "hello"}, ...]. Returns an empty list if no matches are found.
Inputs: file (required), pattern (required), sheet. Returns: result
sql_executeA
Execute a mutating SQL statement and write changes back to the file.
Supports INSERT INTO (adds rows), UPDATE (modifies cell values), and DELETE FROM (removes rows). The target sheet is determined from the SQL statement. After execution, the modified table is written back to the file atomically. Returns {"affected_rows": N}.
Inputs: file (required), sql (required), header_row.
sql_queryA
Execute a read-only SQL SELECT query against the spreadsheet data.
Every sheet in the workbook is loaded as a database table, with the header row defining column names and data rows below it. Returns results as a list of {column: value} objects.
Only SELECT (and WITH ... SELECT) statements are accepted. Use sql_execute for INSERT, UPDATE, or DELETE.
Inputs: file (required), sql (required), header_row. Returns: result
write_cellA
Write a single value to a cell.
Overwrites any existing value. The value is type-coerced: numeric strings become numbers, all else is text.
Inputs: file (required), cell (required), value (required), sheet. Returns: result
write_rangeA
Write a 2D array of values into a rectangular region.
Writing starts at start_cell and expands right and down to fit the data. Prefer this over multiple write_cell calls for efficiency.
Inputs: file (required), start_cell (required), data (required), sheet. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 25 tools
Each tool has a clearly distinct purpose with no significant overlap. For example, read_cell, read_range, and read_sheet provide progressively broader reading capabilities, while write_cell and write_range mirror this for writing. SQL tools (sql_query, sql_execute) offer a distinct query-based interface separate from direct cell operations.
Tool names follow a highly consistent verb_noun pattern throughout, such as add_sheet, clear_range, copy_sheet, delete_columns, list_sheets, read_cell, rename_sheet, and write_range. All tools use snake_case with clear, descriptive action-object pairs, making them predictable and easy to understand.
With 25 tools, the count is borderline high for a spreadsheet server, potentially overwhelming for agents. While many tools are justified for comprehensive spreadsheet operations (e.g., cell, range, sheet, and workbook-level actions), it approaches the upper limit where usability might decline due to complexity.
The toolset provides complete coverage for spreadsheet manipulation, including CRUD operations at multiple levels (cell, range, sheet, workbook), file management, structural modifications, and advanced querying via SQL. There are no obvious gaps; agents can perform all typical spreadsheet tasks without dead ends.
Maintenance
Related MCP Connectors
Query, join, profile, clean and convert CSV/JSON/Parquet with server-side DuckDB over MCP.
Hosted MCP server for AI-driven data ops. Create apps, manage schemas, and CRUD structured data.
An MCP server for search and retrieval over a local LanceDB database
Document conversion MCP server: PDF to Markdown, image OCR, spreadsheet parsing.
Related MCP Servers
- AlicenseBqualityDmaintenanceMCP server for semantic spreadsheet operations that lets LLMs create and edit Excel workbooks by describing spreadsheet intent.42MIT
- AlicenseCqualityBmaintenanceLocal-first Excel MCP server for AI agents enabling structured reads, workbook introspection, and safer .xlsx mutation without Microsoft Excel or LibreOffice.8360 PyPI3MIT
- AlicenseNot gradedqualityBmaintenanceMCP server for Google Sheets - read, write, and query spreadsheet data.648 npm2MIT
- AlicenseNot gradedqualityDmaintenanceMCP server for sandboxed, read-only SQL queries on CSV/Parquet/JSON files via DuckDB, limited to a specified directory.1MIT