Skip to main content
Glama

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-mcp

Tools

All 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

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_range is 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 src

Project 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 helpers

Notes

  • Workbooks are opened in data_only=True mode — formula results are read, not formula strings.

  • Export tools support "return inline" as output_path to return content directly to the agent without touching the filesystem.

License

GPL-3.0-or-later

Available Tools

14 tools
add_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

    A3.7/5.0

    Scored across 14 tools

    Disambiguation5/5

    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.

    Naming Consistency5/5

    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.

    Tool Count5/5

    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.

    Completeness4/5

    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

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables 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.
      1
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      MCP server for reading and writing Excel (xlsx) files using SheetJS, providing tools to list sheets, read data, create files, add sheets, and update cells.
      5
      11 npm
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Provides 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 npm
      MIT