excel-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@excel-mcp List all sheets in sales_report.xlsx"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
excel-mcp
An MCP (Model Context Protocol) server for reading Excel workbooks (.xlsx). Built with FastMCP and openpyxl.
Features
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
Related MCP server: Excel MCP Server
Usage with Claude Desktop
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.
Running manually
uvx --from git+https://github.com/urjeetpatel/excel_mcp_server excel-mcpTools
All tools accept a file_path parameter — the full path to the .xlsx file.
Tool | Description |
| Returns the names of all sheets in the workbook |
| Returns all named tables with name, sheet, and cell range |
| Returns all pivot tables with location and source range info |
| Returns cell data from a sheet; optionally scoped to a range (e.g. |
| Returns headers and row data from a named Excel table |
| Exports a sheet to a CSV file with configurable delimiter; supports inline return |
| Exports a sheet to a plain-text delimited file; supports inline return |
| Exports a sheet to a padded Markdown table; supports inline return |
| Creates a new blank Excel file at the given path |
| Adds a new sheet to an existing Excel file |
| Adds a 2D array of data to a sheet, starting at a specified cell |
| Adds a table to a sheet over a given cell range |
| Sets the value of a single cell in a sheet |
| Sets a formula in a single cell in a sheet |
Write tool details
create_blank_file(file_path)
Creates a new blank Excel file at the specified path.
add_sheet(file_path, sheet_name)
Adds a new sheet to the Excel file. Fails if the sheet already exists.
add_data_to_sheet(file_path, sheet_name, data, start_cell="A1")
Adds a 2D array of data to the specified sheet, starting at the given cell (default A1).
add_table_to_sheet(file_path, sheet_name, table_name, ref)
Adds a table to the specified sheet, covering the given cell range (e.g. "A1:D10").
set_cell_value(file_path, sheet_name, cell, value)
Sets the value of a single cell (e.g. C5) in the specified sheet.
set_cell_formula(file_path, sheet_name, cell, formula)
Sets a formula (e.g. "=SUM(A1:A10)") in a single cell in the specified sheet.
Tool details
list_sheets(file_path)
["Sheet1", "Sheet2"]list_tables(file_path)
[{ "name": "SalesTable", "sheet": "Sheet1", "ref": "A1:D20" }]list_pivot_tables(file_path)
[{
"name": "PivotTable1",
"sheet": "Summary",
"ref": "A1:C10",
"source_sheet": "RawData",
"source_ref": "A1:F500"
}]get_sheet_data(file_path, sheet_name, cell_range?)
{
"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).
get_table_data(file_path, table_name)
{
"table": "PeopleTable",
"sheet": "Sheet1",
"ref": "A1:C4",
"headers": ["Name", "Age", "City"],
"rows": [["Alice", 30, "New York"], ["Bob", 25, "Chicago"]]
}Export tools
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.
export_sheet_to_csv(file_path, sheet_name, output_path, delimiter?, cell_range?)
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 }export_sheet_to_text(file_path, sheet_name, output_path, delimiter?, cell_range?)
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 }export_sheet_to_markdown(file_path, sheet_name, output_path, cell_range?)
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 }Local development
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 srcProject Structure
src/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 helpersNotes
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.
License
GPL-3.0-or-later
Available Tools
14 toolsadd_data_to_sheetC
Add data to a sheet starting at the given cell.
Inputs: data (required), file_path (required), sheet_name (required), start_cell. Returns: object
add_sheetB
Add a new sheet to the Excel file.
Inputs: file_path (required), sheet_name (required). Returns: object
add_table_to_sheetC
Add a table to a sheet over the given cell range (ref).
Inputs: ref (required), file_path (required), sheet_name (required), table_name (required). Returns: object
create_blank_fileC
Create a new blank Excel file at the given path.
Inputs: file_path (required). Returns: object
export_sheet_to_csvA
Export a single Excel sheet to a CSV file.
file_path: path to the source .xlsx workbook
sheet_name: name of the sheet to export
output_path: destination file path (e.g. "/tmp/data.csv"), or "return inline" to return the content directly without saving
delimiter: column separator — "comma" (default), "pipe", or "tab"
cell_range: optional Excel range string (e.g. "A1:D10"). When omitted the entire used range is exported.
Returns a dict with:
output_path: path of the file written (omitted when returning inline)
content: CSV text (only present when returning inline)
sheet: sheet name
range: the range that was exported
rows_written: number of rows written
Inputs: file_path (required), sheet_name (required), output_path (required), delimiter, +1 more. Returns: object
export_sheet_to_markdownA
Export a single Excel sheet to a Markdown table.
The first row is treated as the header row and a separator line is inserted beneath it. All column widths are padded to align the table.
file_path: path to the source .xlsx workbook
sheet_name: name of the sheet to export
output_path: destination file path (e.g. "/tmp/data.md"), or "return inline" to return the content directly without saving
cell_range: optional Excel range string (e.g. "A1:D10"). When omitted the entire used range is exported.
Returns a dict with:
output_path: path of the file written (omitted when returning inline)
content: markdown text (only present when returning inline)
sheet: sheet name
range: the range that was exported
rows_written: number of rows written (including header)
Inputs: file_path (required), sheet_name (required), output_path (required), cell_range. Returns: object
export_sheet_to_textA
Export a single Excel sheet to a plain-text delimited file.
Values are joined with the chosen delimiter with no CSV quoting applied, making output easy to read as a flat text file.
file_path: path to the source .xlsx workbook
sheet_name: name of the sheet to export
output_path: destination file path (e.g. "/tmp/data.txt"), or "return inline" to return the content directly without saving
delimiter: column separator — "pipe" (default), "comma", or "tab"
cell_range: optional Excel range string (e.g. "A1:D10"). When omitted the entire used range is exported.
Returns a dict with:
output_path: path of the file written (omitted when returning inline)
content: text content (only present when returning inline)
sheet: sheet name
range: the range that was exported
rows_written: number of rows written
Inputs: file_path (required), sheet_name (required), output_path (required), delimiter, +1 more. Returns: object
get_sheet_dataB
Return cell data from a sheet.
sheet_name: name of the sheet to read
cell_range: optional Excel range string (e.g. "A1:D10"). When omitted the entire used range is returned.
Returns a dict with:
sheet: sheet name
range: the range that was read
rows: list of rows; each row is a list of cell values
Inputs: file_path (required), sheet_name (required), cell_range. Returns: object
get_table_dataB
Return data from a named Excel table.
table_name: the display name of the table (from list_tables)
Returns a dict with:
table: table name
sheet: sheet the table lives on
ref: cell range
headers: first row (column headers)
rows: remaining rows as list of lists
Inputs: file_path (required), table_name (required). Returns: object
list_pivot_tablesA
Return every pivot table in the workbook.
Each entry contains:
name: pivot table name
sheet: sheet the pivot table is rendered on
ref: cell range of the pivot table output
source_sheet: data source sheet (if determinable, else null)
source_ref: data source range (if determinable, else null)
Inputs: file_path (required). Returns: result
list_sheetsA
Return the names of all sheets in the workbook.
Inputs: file_path (required). Returns: result
list_tablesA
Return every named table in the workbook.
Each entry contains:
name: display name of the table
sheet: sheet the table lives on
ref: cell range (e.g. "A1:D20")
Inputs: file_path (required). Returns: result
set_cell_formulaC
Set a formula in a single cell in a sheet (formula should start with '=').
Inputs: cell (required), formula (required), file_path (required), sheet_name (required). Returns: object
set_cell_valueC
Set the value of a single cell in a sheet.
Inputs: cell (required), value (required), file_path (required), sheet_name (required). Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 14 tools
Each tool targets a distinct resource or action: listing vs reading vs exporting vs writing are clearly separated, and the three export tools differ by output format. Even the similar get_sheet_data and get_table_data are unambiguous because one returns raw sheet cells and the other returns a named table with headers.
Tool names follow a consistent verb_noun pattern: list_*, get_*, export_sheet_to_*, add_*, and set_cell_*. The naming style is uniform and predictable across the entire set.
14 tools is well within the ideal range for an Excel-focused MCP server. Each tool covers a distinct operation and the set feels appropriately scoped without unnecessary duplication.
The server covers the core Excel lifecycle: discovering structure, reading data, exporting, creating files, adding sheets/data/tables, and setting cell values or formulas. Minor gaps exist—such as no delete/rename/clear operations or updating existing tables—but agents can accomplish most common workflows.
Related MCP Connectors
The Excel engine for AI agents: read, check and edit Excel models over MCP.
Excel analytics: inspect, query (JSON rows), charts, and JSON-to-xlsx workbook writing.
Query, join, profile, clean and convert CSV/JSON/Parquet with server-side DuckDB over MCP.
Open, inspect, filter, edit and convert xlsx and csv files from your AI chat. Processing is local.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables reading and searching Excel files through MCP-compatible clients. Provides tools to retrieve workbook metadata, read sheet contents, and search across all sheets using absolute file paths.1MIT
- AlicenseNot gradedqualityDmaintenanceEnables reading and writing Excel files (text, formulas, and images on Windows) via MCP tools with pagination support.3MIT
- AlicenseAqualityDmaintenanceMCP server for reading and writing Excel (xlsx) files using SheetJS, providing tools to list sheets, read data, create files, add sheets, and update cells.511 npmMIT
- AlicenseNot gradedqualityDmaintenanceProvides full read and write access to Excel workbooks (sheets, cell ranges, tables, formulas, formatting, and cross-workbook references) via MCP, running locally or as an HTTP/SSE service.17 npmMIT