Skip to main content
Glama
README.md
# Excel MCP Server

A production-structured [Model Context Protocol](https://modelcontextprotocol.io) server that
gives Claude (or any MCP client) deep, tool-level control over Microsoft Excel files
(`.xlsx`, `.xlsm`, `.csv`, `.tsv`) — reading, writing, formatting, formulas, charts, pivot-style
summaries, data cleaning, statistics, and export, all exposed as **164 individual MCP tools**
across 24 modules.

---

## 1. What's actually implemented

This covers the vast majority of the original 300+ item feature spec as real, tested tools.
A handful of items have genuine platform limitations — see **Section 6** before you rely on them.

| Category | Module | Highlights |
|---|---|---|
| Workbook | `workbook_tools.py` | open/create/save/save-as/close/rename/copy/delete, properties, structural password protection, statistics |
| Worksheet | `worksheet_tools.py` | create/delete/rename/duplicate/hide/unhide/move/protect/set-active |
| Cells & ranges | `cell_tools.py` | read/write single cell & range, copy/move/clear/delete/insert, merge/unmerge, fill series, auto-fill (with formula reference shifting), hyperlinks, named ranges |
| Formulas | `formula_tools.py` | insert, find all, find broken (cached errors), replace, formula→value / value→formula, dependency extraction, formula stats, small safe local evaluator |
| Formatting | `formatting_tools.py` | font, fill, border, alignment, number formats (incl. accounting/currency/%/scientific), column width/row height, auto-fit, style presets |
| Tables | `table_tools.py` | create/delete/rename/resize/restyle native Excel Tables, convert range↔table |
| Sorting | `sort_tools.py` | multi-column, case-(in)sensitive, natural sort, custom category order |
| Filtering | `filter_tools.py` | AutoFilter, 12 filter conditions (blank/duplicate/contains/range/etc.), optional write-out |
| Search | `search_tools.py` | find/replace, regex search/replace, multi-sheet, result highlighting |
| Duplicates | `duplicate_tools.py` | find/remove with subset-column support |
| Data cleaning | `cleaning_tools.py` | trim, case conversion, null fill/remove/interpolate, split/merge columns, extract email/phone/URL/number, date-format normalization, dtype conversion, z-score/IQR anomaly detection |
| Analysis | `analysis_tools.py` | describe, correlation/covariance, frequency tables, group/aggregate, ranking, linear regression, trend forecasting, missing-value & unique-value reports |
| Statistics | `statistics_tools.py` | mean/median/mode/variance/std/IQR/skew/kurtosis, confidence intervals, z-scores, t-test, chi-square, ANOVA |
| Charts | `chart_tools.py` | native dynamic charts (bar/column/line/pie/doughnut/scatter/bubble/area/radar) **and** image-rendered charts for types Excel/openpyxl can't script (histogram/waterfall/treemap/sunburst/box/heatmap/combo) |
| Validation | `validation_tools.py` | dropdown (literal or range-sourced), number/date rules, custom formula rules, input/error messages |
| Conditional formatting | `conditional_formatting.py` | color scales, data bars, icon sets, formula rules, duplicates, top/bottom-N, above/below average, blanks |
| Freeze/view | `freeze_tools.py` | freeze/split panes, zoom, gridlines, headings |
| Images | `image_tools.py` | insert/resize/move/delete/list/export |
| Comments | `comment_tools.py` | add/read/delete/list (classic Notes) |
| Merge/split | `merge_tools.py` | merge workbooks, split workbook into files, append sheet across workbooks |
| Import | `import_tools.py` | CSV, TSV, JSON, XML, another workbook's sheet |
| Export | `export_tools.py` | CSV, JSON, HTML, Markdown, PNG snapshot, PDF (via LibreOffice), multi-sheet batch export |
| Pivot-style summaries | `pivot_tools.py` | pandas-computed pivot tables written as live sheets, calculated fields, refresh |
| Automation / analyst reports | `automation_tools.py` | batch cell/format/formula operations, KPI summary generator, ABC/Pareto analysis, monthly/quarterly/yearly period reports |

**"AI features"** (*"what caused sales to drop?"*, *"find business insights"*, etc.) are **not** a
separate tool — that's exactly what Claude does natively by chaining `describe_dataset` →
`correlation_matrix` → `forecast_trend` → `detect_anomalies` and reasoning over the JSON results.
No extra tool needed; just ask Claude the question once the file is open.

---

## 2. Project structure

```
excel-mcp/
├── server.py                 # FastMCP entrypoint, registers all tool modules
├── config.py                 # logging, path/workspace safety settings
├── requirements.txt
├── README.md
├── tools/
│   ├── workbook_tools.py
│   ├── worksheet_tools.py
│   ├── cell_tools.py
│   ├── formula_tools.py
│   ├── formatting_tools.py
│   ├── table_tools.py
│   ├── sort_tools.py
│   ├── filter_tools.py
│   ├── search_tools.py
│   ├── duplicate_tools.py
│   ├── cleaning_tools.py
│   ├── analysis_tools.py
│   ├── statistics_tools.py
│   ├── chart_tools.py
│   ├── validation_tools.py
│   ├── conditional_formatting.py
│   ├── freeze_tools.py
│   ├── image_tools.py
│   ├── comment_tools.py
│   ├── merge_tools.py
│   ├── import_tools.py
│   ├── export_tools.py
│   ├── pivot_tools.py
│   └── automation_tools.py
└── utils/
    ├── helpers.py             # path safety, workbook registry, response envelopes, pandas bridge
    └── constants.py           # number formats, regex patterns, chart type maps
```

---

## 3. Installation

**Requirements:** Python 3.10+ (3.12 recommended).

```bash
cd excel-mcp
python3 -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -r requirements.txt
```

Optional, for `export_to_pdf` only: install [LibreOffice](https://www.libreoffice.org/download/)
(`sudo apt install libreoffice` / `brew install --cask libreoffice` / Windows installer). Every
other tool works without it.

Test the server starts cleanly:

```bash
python server.py
```

It will sit waiting for an MCP client on stdio (this is expected — it's not meant to print
anything when run standalone). Press Ctrl+C to stop.

---

## 4. Claude Desktop configuration

Add this to your Claude Desktop MCP config file
(`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS,
`%APPDATA%\Claude\claude_desktop_config.json` on Windows):

```json
{
  "mcpServers": {
    "excel": {
      "command": "/absolute/path/to/excel-mcp/.venv/bin/python",
      "args": ["/absolute/path/to/excel-mcp/server.py"],
      "env": {
        "EXCEL_MCP_ROOT": "/absolute/path/to/a/folder/you/want/Claude/to/access"
      }
    }
  }
}
```

On Windows, `command` would be something like
`C:\\path\\to\\excel-mcp\\.venv\\Scripts\\python.exe`.

`EXCEL_MCP_ROOT` is optional — it's just documentation of intent for you; the server itself
validates file extensions and existence but does not hard-sandbox to that folder (see Section 7).
Restart Claude Desktop after editing the config.

---

## 5. Example prompts

- *"Open ~/Documents/Q3_Sales.xlsx and give me a KPI summary of Units and Revenue by Region."*
- *"Create a new workbook with a sales table, add a column chart, and format the header row blue with white bold text."*
- *"Find and highlight all duplicate rows in the Customers sheet based on Email."*
- *"Clean up the PhoneNumbers column — trim whitespace and extract just the digits."*
- *"Run a correlation matrix on my numeric columns and tell me what's most correlated with Revenue."*
- *"Forecast next 3 months of sales based on the Monthly Total column."*
- *"Do an ABC/Pareto analysis of my products by revenue."*
- *"Add a dropdown validation to column C with options Low/Medium/High."*
- *"Export the Summary sheet to a Markdown table."*

---

## 6. Known limitations (read before relying on these)

- **Formula calculation:** openpyxl (and therefore this server) never runs Excel's calculation
  engine. Formulas are written as strings; `evaluate_formula_locally` can only evaluate
  standalone literal expressions (no cell references), and `find_broken_formulas` /
  `convert_formulas_to_values` only see the *cached* results Excel itself last saved. To get
  fresh calculated results, open the file in Excel/LibreOffice and save once.
- **Native PivotTables:** openpyxl cannot author real PivotTable/PivotCache/Slicer XML objects —
  only Excel can write that format. `pivot_tools.py` computes the same result with pandas and
  writes it as a plain (very pivot-table-looking) sheet instead. It won't have a field-list pane
  or be "refreshable" by clicking a button in Excel, but `refresh_pivot_summary` does the same job
  from Claude.
- **VBA macros:** not implemented. openpyxl can *preserve* existing macros in a `.xlsm` file
  (`keep_vba=True`, used automatically) but cannot safely author new VBA — that's an intentional
  omission, since generated macro code is also a common malware vector.
- **Threaded (Excel 365) comments:** `comment_tools.py` writes classic "Notes," not the newer
  threaded Comments format, which uses a different XML part openpyxl doesn't support writing.
- **PDF export:** requires LibreOffice installed on the host (`export_to_pdf` will tell you if
  it's missing); there's no pure-Python way to reproduce Excel's print/pagination engine.
- **Password protection:** `protect_workbook`/`protect_sheet` add Excel's structural/UI protection
  (deters casual editing) — they do **not** encrypt the file the way Excel's "Encrypt with
  Password" does. Don't rely on this for confidentiality.
- **AutoFit:** `auto_fit_columns` is a character-count heuristic; openpyxl has no access to
  Excel's actual font-metrics engine, so results are close but not pixel-perfect.
- **Exotic chart types:** histogram/waterfall/treemap/sunburst/box-plot/heatmap/combo charts are
  rendered as static images (matplotlib), not live Excel chart objects, since openpyxl has no
  writer for them.

---

## 7. Security notes

- Every tool validates file extensions against an allow-list (`.xlsx .xlsm .xltx .xltm .csv .tsv .xlsb`).
- New-file tools (`create_workbook`, `save_workbook_as`, exports, etc.) require `overwrite=true`
  before replacing an existing file.
- `delete_workbook` requires an explicit `confirm=true` flag.
- File size is capped at 250 MB by default (`config.MAX_FILE_SIZE_MB`) to avoid pathological loads.
- This server does **not** sandbox file access to a single directory by default — it can read/write
  anywhere the OS user running it has permissions, the same way a desktop Excel installation would.
  If you want a hard boundary, run it under an OS user with restricted filesystem permissions, or
  add a path-prefix check to `utils/helpers.safe_path()` (one extra `if` statement) tying it to
  `config.WORKSPACE_ROOT`.
- All tool functions catch exceptions internally and return `{"success": false, ...}` rather than
  raising — a single bad call cannot crash the server process.

---

## 8. Testing

A quick manual smoke test (also how this server was validated during development):

```python
import asyncio
from server import mcp

async def main():
    tool = await mcp.get_tool("create_workbook")
    result = await tool.run({"file_path": "/tmp/test.xlsx", "overwrite": True})
    print(result.structured_content)

asyncio.run(main())
```

For a fuller check, list every registered tool:

```python
import asyncio
from server import mcp
print(len(asyncio.run(mcp.list_tools())))   # -> 164
```

---

## 9. Logging & error handling

- Logs go to stderr (never stdout — stdout is reserved for the MCP JSON-RPC stream) and to a
  rotating file at `~/.excel_mcp/logs/excel_mcp_server.log` (override with `EXCEL_MCP_LOG_DIR`).
- Every tool returns a consistent envelope:
  ```json
  {"success": true, "message": "...", "data": {...}}
  {"success": false, "error_type": "WriteError", "message": "..."}
  ```