Excel MCP Server for manipulating Excel files
Project description
A Model Context Protocol (MCP) server that lets you manipulate Excel files without needing Microsoft Excel installed. Create, read, and modify Excel workbooks with your AI agent.
Features
- 📊 Excel Operations: Create, read, update workbooks and worksheets
- 📈 Data Manipulation: Formulas, formatting, charts, pivot tables, and Excel tables
- 🔍 Data Validation: Built-in validation for ranges, formulas, and data integrity
- 🎨 Formatting: Font styling, colors, borders, alignment, and conditional formatting
- 📋 Table Operations: Create and manage Excel tables with custom styling
- 📊 Chart Creation: Generate various chart types (line, bar, pie, scatter, etc.)
- 🔄 Pivot Tables: Create dynamic pivot tables for data analysis
- 🔧 Sheet Management: Copy, rename, delete worksheets with ease
- 🔌 Triple transport support: stdio, SSE (deprecated), and streamable HTTP
- 🌐 Remote & Local: Works both locally and as a remote service
Development and CI parity
Install dev extras so local runs match PR and release gates (docs/architecture/ci-cd-packaging-governance.md):
pip install -e ".[dev]"
python -m pytest
hatch build
python -m twine check dist/*
From the repository root with uv (uses uv.lock if present):
uv sync --extra dev
uv run python -m pytest
uv run hatch build
uv run python -m twine check dist/*
The PyPI distribution name is excel-com-mcp (same as [project].name in pyproject.toml). A legacy console entrypoint excel-mcp-server is also installed. Examples below use excel-com-mcp for uvx and MCP JSON so they stay aligned with manifest.json → server.mcp_config (command / args).
Operator documentation map
| Artifact | Purpose |
|---|---|
| This README | Transports, env vars, filepath rules (disk vs SharePoint URL), Cursor/uv local MCP setup, allowlists |
TOOLS.md |
Per-tool reference; same filepath and workbook_transport rules apply to every workbook tool |
manifest.json |
MCP catalog metadata and mcp_config (uvx excel-com-mcp stdio) |
.cursor/mcp.json |
Optional Cursor workspace server using ${workspaceFolder} + uv run --project … (see Stdio Transport) |
CHANGELOG.md |
Version-to-version release notes and breaking changes |
docs/plan/transport-routing/IMPLEMENTATION-ROADMAP.md |
Epic/story delivery status for workbook routing |
Upgrading from 0.2.x
Epic 11 / ADR 0008 (release 0.3.0): save_after_write is removed from mutating tools—call save_workbook when you need disk persistence. Read tools are COM-first (same as writes) when workbook_transport is auto or com and Excel has the workbook open; for on-disk snapshots use workbook_transport=file on reads or save first. New lifecycle tools: excel_open_workbook, excel_close_workbook (Windows + COM). Details: CHANGELOG.md, TOOLS.md, ADR 0008.
Upgrading from 0.3.x
Epic 12 / ADR 0009 (release 0.4.0): excel_list_open_workbooks lists workbooks open in Excel and returns exact full_name locators for get_workbook_metadata, reads, and writes (replaces ad-hoc VBA/Immediate discovery). COM-only; see TOOLS.md and CHANGELOG.md.
Usage
The server supports three transport methods:
1. Stdio Transport (for local use)
uvx excel-com-mcp stdio
{
"mcpServers": {
"excel": {
"command": "uvx",
"args": ["excel-com-mcp", "stdio"]
}
}
}
Local clone in Cursor (this repo): MCP often starts uv with no project working directory, so uv run --extra com … fails with *“--extra com has no effect when used outside of a project* and *program not found*. Pass the project explicitly with **--project** (absolute path to the folder that contains pyproject.toml`):
{
"mcpServers": {
"excel-mcp-local": {
"command": "uv",
"args": [
"run",
"--project",
"C:/Users/YOU/mcp/excel-mcp-server",
"--extra",
"com",
"excel-com-mcp",
"stdio"
]
}
}
}
On Windows you can use C:\\\\Users\\\\YOU\\\\... instead of forward slashes. Omit "com" on non-Windows installs. You can add "cwd" with the same path as a hint for other tools, but --project is what fixes uv run.
If Cursor logs Failed to spawn: excel-com-mcp / program not found, the client did not resolve the project for uv run: add --project with an absolute path to this repo (or use the workspace file .cursor/mcp.json, which passes ${workspaceFolder}). Relying on cwd alone is not enough in many Cursor builds.
2. SSE Transport (Server-Sent Events - Deprecated)
uvx excel-com-mcp sse
SSE transport connection:
{
"mcpServers": {
"excel": {
"url": "http://localhost:8000/sse",
}
}
}
3. Streamable HTTP Transport (Recommended for remote connections)
uvx excel-com-mcp streamable-http
Streamable HTTP transport connection:
{
"mcpServers": {
"excel": {
"url": "http://localhost:8000/mcp",
}
}
}
Environment Variables & File Path Handling
SSE and Streamable HTTP Transports
When running the server with the SSE or Streamable HTTP protocols, you must set the EXCEL_FILES_PATH environment variable on the server side. This variable tells the server where to read and write Excel files.
- If not set, it defaults to
./excel_files. - With these transports, tool
filepathvalues must be relative to that directory (e.g.reports/q1.xlsx); absolute paths and directory traversal are rejected.
You can also set the FASTMCP_PORT environment variable to control the port the server listens on (default is 8017 if not set).
- Example (Windows PowerShell):
$env:EXCEL_FILES_PATH="E:\MyExcelFiles" $env:FASTMCP_PORT="8007" uvx excel-com-mcp streamable-http
- Example (Linux/macOS):
EXCEL_FILES_PATH=/path/to/excel_files FASTMCP_PORT=8007 uvx excel-com-mcp streamable-http
Stdio Transport
When using the stdio protocol, the file path is provided with each tool call, so you do not need to set EXCEL_FILES_PATH on the server. The server will use the path sent by the client for each operation.
Workbook identity for COM (Windows): filepath may be a local absolute path or a SharePoint-style https:// URL that matches Workbook.FullName in a running Excel instance. Opening the file, Microsoft 365 sign-in, and sync are handled by Excel / Office; the MCP does not perform SharePoint or Graph OAuth. For read/write via the file backend (openpyxl), use a real filesystem path. When EXCEL_MCP_ALLOWED_PATHS is enabled, https locators also require EXCEL_MCP_ALLOWED_URL_PREFIXES (see below).
SharePoint / Microsoft 365: use the same string as Excel’s FullName
Excel often reports cloud-backed workbooks with an https://…sharepoint.com/… identity even when a synced copy exists on disk. The MCP compares your filepath to COM Workbook.FullName after normalization (ADR 0006). If they differ, routing fails (Workbook not open…), and auto may fall back to the file backend and hit Permission denied while Excel holds the file.
- In Excel: Alt+F11 → Immediate window → run:
? ActiveWorkbook.FullNameand press Enter. - Use that exact returned string (including
https://) asfilepathon tools. - Prefer
workbook_transport=com(orautoonce the identity matches and open detection can see the workbook) for edits in the Excel session.
Use the local absolute path only when FullName is a normal file path, or when you intentionally use the file backend (e.g. headless openpyxl).
Path normalization (resolve_target)
Internally, workbook targets are normalized with resolve_target in excel_mcp.path_resolution (single entry point for FR-1 / future COM path comparison). It uses os.path.realpath for stable absolute paths; relative resolution order (search_roots, then cwd) is documented in that module.
- stdio, allowlist off: tool paths must still be absolute; the server returns
os.path.normpathonly (legacy behavior for existing clients). - stdio, allowlist on and SSE / streamable HTTP: paths are finalized with
resolve_targetbefore jail and allowlist checks.
Integrators can reuse path_is_allowed / assert_path_allowed from excel_mcp.path_policy so file and future COM backends share the same policy.
Optional path allowlist (EXCEL_MCP_ALLOWED_PATHS)
For tighter control (aligned with FR-11), set EXCEL_MCP_ALLOWED_PATHS to one or more allowed directory roots, separated by os.pathsep (semicolon on Windows, colon on macOS/Linux—the same rule as the PATH environment variable). Whitespace around each entry is trimmed; ~ is expanded per entry.
When unset or blank, behavior matches the pre-fork defaults: stdio accepts any absolute path (subject to existing validation), and SSE/HTTP use only the EXCEL_FILES_PATH jail.
When set:
- stdio: each workbook path is resolved with
resolve_target, then must lie inside at least one listed root (directory containment, same idea as the remote jail). - SSE / streamable HTTP: the resolved path must be inside
EXCEL_FILES_PATHand inside at least one allowlist root (intersection).
If the variable is non-empty but no root path resolves (typos, missing drive letters, unreadable paths), the allowlist is treated as active with zero valid roots and paths are rejected until the environment is corrected (fail-closed).
Examples:
# Windows: two roots (note the semicolon)
$env:EXCEL_MCP_ALLOWED_PATHS = "E:\Workbooks;E:\SharedTemplates"
uvx excel-com-mcp stdio
# Linux / macOS: colon-separated
EXCEL_MCP_ALLOWED_PATHS=/var/excel-in:/var/excel-out uvx excel-com-mcp stdio
URL prefix allowlist for cloud locators (EXCEL_MCP_ALLOWED_URL_PREFIXES)
When EXCEL_MCP_ALLOWED_PATHS is set (path allowlist on), https:// cloud workbook locators must also match at least one entry in EXCEL_MCP_ALLOWED_URL_PREFIXES, or they are rejected (fail-closed). This is separate from directory containment: filesystem roots do not authorize arbitrary SharePoint URLs.
- Segments are separated with semicolons (
;) on every platform (notPATH-styleos.pathsep:https://URLs contain colons, so colon-separated lists break on Linux/macOS). - Each segment is a prefix URL (scheme
https, host, and optional path) after the same canonicalization as workbook URLs. Prefer a trailing slash on path prefixes (e.g.https://contoso.sharepoint.com/sites/Team/) so only that hierarchy is allowed. - If the path allowlist is on but this variable is missing, empty, or has no valid
httpsentries, https workbook targets are denied.
When the path allowlist is off, https locators are accepted without EXCEL_MCP_ALLOWED_URL_PREFIXES (subject to normal https validation in get_excel_path).
Workbook transport and COM policy (not MCP wire transport)
These environment variables control workbook routing (file-backed openpyxl path vs COM automation when wired in later stories). They do not select the MCP client↔server wire transport (stdio, SSE, or streamable HTTP); that is configured by how you launch the server (see above). See ADR 0001 for the vocabulary split.
| Variable | Meaning |
|---|---|
EXCEL_MCP_TRANSPORT |
Workbook mode: auto, file, or com (case-insensitive). Default auto when unset or empty. Invalid values raise at read time. Parsed by excel_mcp.routing.read_workbook_transport. |
EXCEL_MCP_COM_STRICT |
When 1 / true / yes (case-insensitive): strict COM policy. When 0 / false / no, or explicitly relaxed: non-strict. Unset or empty defaults to strict (True). Parsed by read_com_strict. |
EXCEL_MCP_COM_ALLOW_FILE_FALLBACK |
When 1 / true / yes: operators allow documented file fallback in scenarios where non-strict routing would apply (ADR 0005). Unset or empty: False. Parsed by read_com_allow_file_fallback. |
EXCEL_MCP_ALLOWED_URL_PREFIXES |
When EXCEL_MCP_ALLOWED_PATHS is set: required for https workbook locators. Semicolon-separated https URL prefixes on all OSes. See URL prefix allowlist. |
Effective strictness for the router is effective_com_strict(): False if file fallback is allowed or EXCEL_MCP_COM_STRICT is explicitly falsy; otherwise True. Allowing file fallback forces non-strict effective behavior whenever that flag is on.
Optional MCP tool parameters (workbook routing)
Most workbook tools accept optional workbook_transport (auto | file | com). When omitted, transport defaults to EXCEL_MCP_TRANSPORT. Discovery (excel_list_open_workbooks) and lifecycle tools excel_open_workbook / excel_close_workbook do not use this parameter (they are COM-only per ADR 0009 / ADR 0008). For persistence after mutations, call save_workbook (ADR 0008). These names refer to workbook execution routing (ADR 0001), not MCP wire transport.
Routing observability
Routed workbook operations (via execute_routed_workbook_operation in excel_mcp.routing.routed_dispatch) emit one JSON object per dispatch on logger excel-mcp.routing at INFO (no stdout). Fields follow ADR 0001 vocabulary:
- workbook_transport — requested mode:
auto,file, orcom. - workbook_backend — resolved backend after the selection matrix:
fileorcom. - routing_reason — stable reason string from
RoutingBackend(e.g.forced_file,full_name_match,auto_workbook_not_open_file,v1_file_forced). - duration_ms — wall time for resolve plus executed file I/O (when applicable).
- workbook_path — redacted path (basename only by default; set
EXCEL_MCP_LOG_FULL_PATHS=1for full path in break-glass scenarios). - operation_name — routed contract method name (e.g.
read_range_with_metadata). - mcp_tool_name — optional registered MCP tool name when supplied by the caller.
- v1_file_forced —
truewhen ADR 0004 forces the file backend for a tool (chart / pivot v1) regardless ofauto→COM for other writes.
ADR 0008 / ADR 0003 — COM-first reads and when disk matters
Read-class tools use the same routing as writes (COM-first when transport=auto/com, the workbook matches an open host, and COM is viable; otherwise openpyxl file path). Live grid reads therefore follow Excel when COM is selected, not necessarily the last saved file. If you rely on on-disk snapshots (external tools, workbook_transport=file), or after COM writes you need the file to match the host, call save_workbook before file-backed operations. create_chart / create_pivot_table remain file-forced (ADR 0004). Historical “file-default reads” (ADR 0007) are superseded; see docs/architecture/adr/0008-com-first-default-and-file-lifecycle-tools.md and docs/architecture/adr/0003-read-path-com-parity.md.
FR-9 — Protected View, read-only, duplicate instances
COM operations return clear, fail-closed errors when Excel blocks writes (e.g. Protected View, read-only). If multiple open workbooks resolve to the same path across Excel instances, routing fails closed with an error asking the operator to close duplicates or use a single Excel instance.
NFR-2 — routing latency
p95 routing overhead is not continuously benchmarked in CI. See docs/performance/routing-nfr2-note.md for an honest scope note and an optional local micro-benchmark idea.
NFR-4 — elevation
The server does not request administrator elevation by default (COM automation targets the user’s normal Excel session; see also the blueprint §5 “do not start Excel as admin without explicit user opt-in”).
Planning / delivery status: workbook transport epics and stories are tracked in docs/plan/transport-routing/IMPLEMENTATION-ROADMAP.md. Phases 1–9 and Epic 11 (COM-first reads, explicit save_workbook, lifecycle tools, docs/tests) are delivered per that roadmap. Epic 7 remains the vertical slice for COM write-class parity, save_workbook, FR-9 errors, and ADR 0004 v1 file-forced create_chart / create_pivot_table (logs: routing_reason v1_file_forced when applicable).
Optional Windows COM ([com]): to install pywin32 for COM-backed workbook routing, use pip install excel-com-mcp[com] (or the equivalent for your installer). pywin32 is distributed under the PSF License Agreement (same terms as CPython).
COM execution threading (Windows)
COM apartment rules require Excel automation from a consistent thread. The server uses excel_mcp.com_executor.ComThreadExecutor: a single worker thread pulls jobs from a queue; submit(fn, *args, **kwargs) runs fn on that thread and blocks the caller until the result is ready (or an exception is propagated), so synchronous MCP tool handlers stay compatible without turning every tool async. On Windows, that worker thread calls pythoncom.CoInitialize() before processing jobs and CoUninitialize() on shutdown so COM APIs such as GetActiveObject("Excel.Application") behave like a normal main-thread script (without this, automation from a plain background thread often fails even when Excel is running). The executor does not start Excel by itself. For tests or clean process teardown, call shutdown(wait=True); abrupt exit may still cut off in-flight work—see the module docstring on com_executor for limitations (including no reentrant submit from inside a job on the worker).
Windows manual smoke (COM write path)
- Install the optional stack:
pip install "excel-com-mcp[com]"(or your package equivalent) sopywin32is available. - Start Microsoft Excel manually and open the target
.xlsxusing File → Open (a running instance with the workbook loaded is required; the server does not launch Excel). - Run the MCP server (e.g. stdio) on the same Windows machine with routing env vars as needed (defaults:
EXCEL_MCP_TRANSPORT=autowhen unset). - Call
write_data_to_excelwith an absolute path to that file,workbook_transport=comorauto, and a smalldatagrid; withauto, the workbook must be detected as open in Excel for COM to win. - Optional: call
excel_list_open_workbookswhen you need the exactfilepath/FullNameExcel reports (replacing VBA Immediate); thenget_workbook_metadata/ reads with that string. - Optional: use
excel_open_workbook(orcreate_workbook(..., open_in_excel=true)) so Excel has the path open for auto→COM routing. - Read tools (e.g.
read_data_from_excel) use the same COM/file matrix; after COM writes, callsave_workbookif you need the on-disk file to match the host. - Confirm routing in
excel-mcp.log: one JSON line per dispatch withworkbook_backendcomand a stablerouting_reason(e.g.full_name_match/forced_com) for writes routed to COM. - For release-style sign-off, follow
docs/plan/transport-routing/MANUAL-WINDOWS-RC-CHECKLIST.md(Protected View, read-only, duplicate instance, save-then-read, chart/pivotv1_file_forcedrows).
CI locally (contributors)
GitHub Actions runs the same gates as PR CI via .github/workflows/reusable-validate-and-test.yml: editable install with dev extras, pytest, hatch build, and twine check dist/*.
python -m pip install --upgrade pip
pip install -e ".[dev]"
pytest
hatch build
twine check dist/*
No Excel or Windows COM is required for this default path (Linux CI).
Available Tools
The server provides a comprehensive set of Excel manipulation tools. See TOOLS.md for complete documentation of all available tools.
Star History
License
MIT License - see LICENSE for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file excel_com_mcp-0.4.0.tar.gz.
File metadata
- Download URL: excel_com_mcp-0.4.0.tar.gz
- Upload date:
- Size: 62.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f4621bfccdcbdf67ce1468c20e399a555a504d3d84ea8e89f49a023b7164bf4
|
|
| MD5 |
677e5a9e38c9e7e637b486966e75f27c
|
|
| BLAKE2b-256 |
9c435e91f6d07839ca586e3c9738936f10887aa9f5c40d70defdba5b42285e0e
|
Provenance
The following attestation bundles were made for excel_com_mcp-0.4.0.tar.gz:
Publisher:
release-pypi-publish.yml on benvdbergh/excel-mcp-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
excel_com_mcp-0.4.0.tar.gz -
Subject digest:
5f4621bfccdcbdf67ce1468c20e399a555a504d3d84ea8e89f49a023b7164bf4 - Sigstore transparency entry: 1395267304
- Sigstore integration time:
-
Permalink:
benvdbergh/excel-mcp-server@6a53540066325ee27cb1c2dfd81340a97c6cc376 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/benvdbergh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi-publish.yml@6a53540066325ee27cb1c2dfd81340a97c6cc376 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file excel_com_mcp-0.4.0-py3-none-any.whl.
File metadata
- Download URL: excel_com_mcp-0.4.0-py3-none-any.whl
- Upload date:
- Size: 77.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd7cead0677f34ed5fb887883605004290625fb0672bcd6739670d4c68d4f6b0
|
|
| MD5 |
e6252cf9eb66c6780f5af6d2cda97b21
|
|
| BLAKE2b-256 |
bb0564983626b689577065e6b102fe5f58eccd7aa8a6286684224bc77f60687d
|
Provenance
The following attestation bundles were made for excel_com_mcp-0.4.0-py3-none-any.whl:
Publisher:
release-pypi-publish.yml on benvdbergh/excel-mcp-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
excel_com_mcp-0.4.0-py3-none-any.whl -
Subject digest:
dd7cead0677f34ed5fb887883605004290625fb0672bcd6739670d4c68d4f6b0 - Sigstore transparency entry: 1395267319
- Sigstore integration time:
-
Permalink:
benvdbergh/excel-mcp-server@6a53540066325ee27cb1c2dfd81340a97c6cc376 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/benvdbergh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi-publish.yml@6a53540066325ee27cb1c2dfd81340a97c6cc376 -
Trigger Event:
workflow_dispatch
-
Statement type: