An MCP (Model Context Protocol) server for reading Excel workbooks (.xlsx). Built with FastMCP and openpyxl.
- List all sheets in a workbook
- List all named tables across sheets
- List all pivot tables with source range info
- Read raw cell data from any sheet or optional cell range
- Read structured data (headers + rows) from named Excel tables
- Export any sheet to CSV, plain-text (delimited), or Markdown — save to file or return inline
No installation required. Add the following to your claude_desktop_config.json:
{
"mcpServers": {
"excel-mcp": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/urjeetpatel/excel_mcp_server",
"excel-mcp"
]
}
}
}uvx will pull the package directly from GitHub and run it in an isolated environment — no pip install or virtual environment setup needed.
uvx --from git+https://github.com/urjeetpatel/excel_mcp_server excel-mcpAll tools accept a file_path parameter — the full path to the .xlsx file.
| Tool | Description |
|---|---|
list_sheets |
Returns the names of all sheets in the workbook |
list_tables |
Returns all named tables with name, sheet, and cell range |
list_pivot_tables |
Returns all pivot tables with location and source range info |
get_sheet_data |
Returns cell data from a sheet; optionally scoped to a range (e.g. A1:D10) |
get_table_data |
Returns headers and row data from a named Excel table |
export_sheet_to_csv |
Exports a sheet to a CSV file with configurable delimiter; supports inline return |
export_sheet_to_text |
Exports a sheet to a plain-text delimited file; supports inline return |
export_sheet_to_markdown |
Exports a sheet to a padded Markdown table; supports inline return |
create_blank_file |
Creates a new blank Excel file at the given path |
add_sheet |
Adds a new sheet to an existing Excel file |
add_data_to_sheet |
Adds a 2D array of data to a sheet, starting at a specified cell |
add_table_to_sheet |
Adds a table to a sheet over a given cell range |
set_cell_value |
Sets the value of a single cell in a sheet |
set_cell_formula |
Sets a formula in a single cell in a sheet |
Creates a new blank Excel file at the specified path.
Adds a new sheet to the Excel file. Fails if the sheet already exists.
Adds a 2D array of data to the specified sheet, starting at the given cell (default A1).
Adds a table to the specified sheet, covering the given cell range (e.g. "A1:D10").
Sets the value of a single cell (e.g. C5) in the specified sheet.
Sets a formula (e.g. "=SUM(A1:A10)") in a single cell in the specified sheet.
["Sheet1", "Sheet2"][{ "name": "SalesTable", "sheet": "Sheet1", "ref": "A1:D20" }][{
"name": "PivotTable1",
"sheet": "Summary",
"ref": "A1:C10",
"source_sheet": "RawData",
"source_ref": "A1:F500"
}]{
"sheet": "Sheet1",
"range": "A1:D10",
"rows": [["Name", "Age", "City"], ["Alice", 30, "New York"]]
}cell_rangeis optional. When omitted, the full used range is returned.- Range strings are case-insensitive (
a1:d10==A1:D10).
{
"table": "PeopleTable",
"sheet": "Sheet1",
"ref": "A1:C4",
"headers": ["Name", "Age", "City"],
"rows": [["Alice", 30, "New York"], ["Bob", 25, "Chicago"]]
}All three export tools share a common pattern:
output_path— path to write the output file, or"return inline"to skip writing and return the content directly in the response.cell_range— optional Excel range string (e.g.A1:D10). When omitted, the full used range is exported.
When saving to a file the response contains output_path. When returning inline, output_path is replaced by content.
Exports a sheet using Python's csv module (values containing the delimiter or newlines are properly quoted).
delimiter options: "comma" (default), "pipe", "tab".
{ "output_path": "/tmp/data.csv", "sheet": "Sheet1", "range": "A1:C4", "rows_written": 4 }Inline variant (output_path = "return inline"):
{ "content": "Name,Age,City\r\nAlice,30,New York\r\n...", "sheet": "Sheet1", "range": "A1:C4", "rows_written": 4 }Exports a sheet as a plain delimited text file — values are joined with the delimiter with no CSV quoting, making output easy to read or pipe into other tools.
delimiter options: "pipe" (default), "comma", "tab".
{ "output_path": "/tmp/data.txt", "sheet": "Sheet1", "range": "A1:C4", "rows_written": 4 }Inline variant:
{ "content": "Name|Age|City\nAlice|30|New York\n...", "sheet": "Sheet1", "range": "A1:C4", "rows_written": 4 }Exports a sheet as a padded Markdown table. The first row is used as the header; a separator line is inserted beneath it. All columns are padded to align.
{ "output_path": "/tmp/data.md", "sheet": "Sheet1", "range": "A1:C4", "rows_written": 4 }Inline variant:
{ "content": "| Name | Age | City |\n| ----- | --- | -------- |\n| Alice | 30 | New York |\n...", "sheet": "Sheet1", "range": "A1:C4", "rows_written": 4 }Requires Python >= 3.14 and uv.
git clone https://github.com/urjeetpatel/excel_mcp_server
cd excel_mcp_server
uv sync --group dev
# Run tests
uv run pytest
# Type check
uv run mypy srcsrc/excel_mcp/
├── __init__.py # Exposes main() entry point
├── server.py # FastMCP server and tool registration
├── workbook.py # Workbook loading helpers
└── tools/
├── __init__.py
├── read.py # Read tool implementations
└── export.py # Export tool implementations (CSV, text, Markdown)
tests/
├── conftest.py # pytest fixtures (builds workbooks programmatically)
├── test_read.py # Tests for read tools
├── test_export.py # Tests for export tools
└── test_workbook.py # Tests for workbook helpers
- Workbooks are opened in
data_only=Truemode — formula results are read, not formula strings. - Export tools support
"return inline"asoutput_pathto return content directly to the agent without touching the filesystem.
GPL-3.0-or-later