The Problem The repository supplies an Excel MCP server and an Excel‑AI agent runner that let LLMs read, edit, and automate spreadsheets, yet the codebase has no test suite, no CI/CD gate, and only a single lockfile‑free manifest. A change can ship with no signal that existing behaviour still holds, so regressions reach production undetected.
What This Does The repo is a portfolio of four loosely‑coupled projects:
- excel_mcp (8 code files) – defines ~30 MCP tools (
excel_mcp/schemas.py,excel_mcp/excel_server.py,excel_mcp/helpers.py) that expose read/write operations to a running server. - excel_agent (4 code files) – contains the agent runner (
excel_agent/agent_runner.py,excel_agent/config.py,excel_agent/reasoning_models.py) that orchestrates LLM calls, retries, and task execution. - demo (15 files, 6 code files) – two demo fronts: a web‑based Excel Assistant (
demo/ExcelAssistant/) and a Slack workflow (demo/SlackExcelWorkflow/). - evals (304 files, mostly data) – a verification harness with SpreadsheetBench cases and comparison scripts (
evals/comparison.py).
The two primary entry points are chat in demo/ExcelAssistant/server.py:79 (reaches 21 functions, called from outside the repo) and main in demo/SlackExcelWorkflow/excel_agent_bot.py:178 (reaches 30 functions, called once from the Slack handler). The lifespan hook in demo/ExcelAssistant/server.py:63 starts the MCP server on start‑up.
How It Is Wired Execution flows as follows:
- Entry → server start –
lifespanlaunches the MCP SSE server (excel_mcp/excel_server.py) on127.0.0.1:8765. - Agent runner –
ExcelAgentRunner.run_excel_agent(excel_agent/agent_runner.py) sends a prompt to the LLM, then iteratively calls MCP tools (get_sheet,set_range_data,auto_fill, etc.). - Tool actions – most functions end with
to_jsonorToolError;ToolErroris invoked from 31 places,to_jsonfrom 30, indicating a broad error‑wrapping pattern. - External side‑effects –
save_workbook_async→calculate_formulas→_calculate_formulas_excel_mac(subprocess.run) writes the workbook back to disk;run_evaluationcreates output directories (mainpath).
Key hubs and their import counts (Ca = callers, Ce = callees, instability = Ce/(Ca+Ce)):
| Module | Ca | Ce | Instability |
|---|---|---|---|
excel_mcp/excel_server | 4 | 6 | 0.6 |
excel_agent/agent_runner | 3 | 4 | 0.57 |
excel_agent/config | 4 | 0 | 0 |
excel_mcp/helpers | 2 | 2 | 0.5 |
excel_mcp/sessions | 2 | 2 | 0.5 |
The most connected functions are ToolError (31 callers), to_json (30), get_sheet (14), and parse_range (7). Changing any of these ripples widely because of the high branching density and deep nesting described below.
How To Use It
Setup (from README.md):
conda create -n excel
conda activate excel
conda install python=3.11
pip install -r requirements.txt
pip install -e .
Start the MCP server (from README.md):
import asyncio
from excel_mcp.excel_server import mcp
async def run_mcp_server():
await mcp.run_sse_async(host="127.0.0.1", port=8765)
asyncio.run(run_mcp_server())
Run the Excel agent (example from README.md):
from excel_agent.agent_runner import ExcelAgentRunner, TaskInput
from excel_agent.config import ExperimentConfig
message = "your prompt to edit the file"
input_file = Path("path/to/input.xlsx")
output_file = Path("path/to/output.xlsx")
runner = ExcelAgentRunner(
config=ExperimentConfig(model="openrouter:openai/gpt-5.1"),
mcp_server_url="http://127.0.0.1:8765/sse",
)
task_input = TaskInput(
instruction=message,
input_file=str(input_file),
output_file=str(output_file),
)
agent_response = await runner.run_excel_agent(task_input)
Configuration – the model name is supplied to ExperimentConfig; no additional environment variables are required beyond what the package manager resolves.
Real‑World Use A finance team can ask an LLM to “add a quarterly totals row and format the header in bold,” the agent forwards the request to the MCP server, which calls set_range_values and auto_fill. The runner retries on ToolError, and the resulting workbook is saved alongside the original, ready for downstream reporting.
Code Health & Issues
Measured static‑analysis findings (15 total, 2 high, 13 medium, 0 low):
- Deep nesting x6 –
excel_mcp/excel_server.py,excel_agent/agent_runner.py,excel_mcp/helpers.py; max indentation depth 9 makes control flow hard to follow. - Broad exception handling x5 –
excel_mcp/excel_server.py,excel_mcp/models.py,excel_mcp/sessions.py; bareexceptswallows errors indiscriminately. - Oversized file x3 –
excel_mcp/excel_server.py(872 loc),excel_mcp/helpers.py,demo/ExcelAssistant/src/App.jsx; a single file holds too much logic, ripple effects are wide. - High branching density –
excel_mcp/formatting.py; 54 branch points over 170 lines, decision‑heavy logic should be decomposed.
Code‑health audit (4 findings, each with a prescribed fix):
- [HIGH] Add a test suite – 20 source files, no test files exist; any change ships without regression signal.
- [HIGH] Commit a lockfile beside the manifest –
pyproject.tomlhas no corresponding lockfile; tested artifact may differ from shipped artifact. - [HIGH] Add a CI/CD workflow – no CI configuration; every merge runs untested.
- [MEDIUM] Enable Dependabot or Renovate – 3 manifest files, no update bot configured; advisories remain unpatched until manual audit.
The Bottom Line The repo delivers a functional Excel‑MCP server and agent runner that can automate spreadsheet tasks via LLMs, and the evaluation harness gives concrete performance data. However, the absence of tests, CI, and a lockfile makes production‑grade changes risky, and the codebase suffers from deep nesting, broad exception handling, and oversized files that hinder maintenance. It is suitable for prototyping or internal tooling where the above gaps can be tolerated, but not for open‑source or regulated deployment without addressing the health findings.