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:

  1. Entry → server startlifespan launches the MCP SSE server (excel_mcp/excel_server.py) on 127.0.0.1:8765.
  2. Agent runnerExcelAgentRunner.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.).
  3. Tool actions – most functions end with to_json or ToolError; ToolError is invoked from 31 places, to_json from 30, indicating a broad error‑wrapping pattern.
  4. External side‑effectssave_workbook_asynccalculate_formulas_calculate_formulas_excel_mac (subprocess.run) writes the workbook back to disk; run_evaluation creates output directories (main path).

Key hubs and their import counts (Ca = callers, Ce = callees, instability = Ce/(Ca+Ce)):

ModuleCaCeInstability
excel_mcp/excel_server460.6
excel_agent/agent_runner340.57
excel_agent/config400
excel_mcp/helpers220.5
excel_mcp/sessions220.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 x6excel_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 x5excel_mcp/excel_server.py, excel_mcp/models.py, excel_mcp/sessions.py; bare except swallows errors indiscriminately.
  • Oversized file x3excel_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 densityexcel_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 manifestpyproject.toml has 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.