# Excel MCP Server — Product Spec
## Vision
A self-evolving data analytics toolkit for non-technical users. Users drop Excel files into a dashboard, ask Claude questions in plain English, and build up a personalized library of reusable analysis tools — all without writing code.
## Target User
- Non-technical professional (e.g. "Dave")
- Has Excel files they analyze regularly
- Has Claude Desktop installed
- Comfortable dragging files and talking to AI
- Does NOT want to: write code, edit config files, use terminal
## Architecture
```
Claude Desktop (MCP Client)
↓ stdio
MCP Server (Python, FastMCP)
↓ shared state
SQLite DB (~/.excel-mcp/data.db) — ingested Excel data
Tool Registry (~/.excel-mcp/tools/) — saved analyses & custom tools
↓ REST API
Frontend (localhost:8765) — dashboard for data + tool management
```
## Core Components
### 1. MCP Server (FastMCP, stdio transport)
**Core Tools (always available, not editable):**
| Tool | Purpose |
|------|---------|
| `list_datasets` | Show all loaded tables with row/column counts |
| `describe_dataset` | Column names, types, sample values, basic stats for a table |
| `query` | Run any read-only SQL against the SQLite DB |
| `summarize` | Quick statistical summary of a table or column |
**Meta Tools (for creating/managing user tools):**
| Tool | Purpose |
|------|---------|
| `save_analysis` | Save a SQL template as a reusable named tool |
| `create_tool` | Create a custom Python tool (sandboxed) |
| `list_my_tools` | Show all user-created tools |
| `edit_tool` | Update an existing saved analysis or custom tool |
| `delete_tool` | Remove a user-created tool |
| `test_tool` | Run a tool with given parameters and return results |
**Dynamic Tools:**
- On startup, server reads `~/.excel-mcp/tools/` directory
- Each JSON file becomes a registered MCP tool
- Saved analyses = SQL templates with parameter substitution
- Custom tools = sandboxed Python functions
### 2. Frontend (Single-page dashboard, localhost:8765)
**Tech:** Single HTML file served by FastAPI. Vanilla JS + minimal CSS (Pico CSS or similar). No build step, no npm.
**Pages/Sections:**
**Data Manager:**
- Drag-and-drop upload zone for Excel/CSV files
- Table list: name, source file, rows, columns, last updated
- Click table → preview (first 50 rows), column info, basic stats
- Delete table button
- Re-upload / refresh button
**Tool Workshop:**
- Three sections: Core Tools (read-only), Saved Analyses (editable), Custom Tools (editable)
- Each tool shows: name, description, parameter schema, created date
- Actions per tool: Edit, Delete, Test
- Test UI: auto-generated form from parameter schema → run → show results
- "Create New" button (opens a form for saved analysis creation)
- Saved analyses show the SQL template
- Custom tools have "View Code" toggle
**Activity Log (nice-to-have):**
- Recent queries and tool runs
- Helps Dave see what Claude has been doing
### 3. Data Ingestion
**Excel Ingest Flow:**
1. User drops file (via frontend upload or configured DATA_DIR scan)
2. Parse with openpyxl/pandas
3. Header detection: use first row with >60% non-null values as headers
4. Clean column names: strip whitespace, lowercase, spaces → underscores
5. Each sheet → separate SQLite table named `{filename}__{sheet_name}`
6. Store metadata in `_meta` table (source file, sheet, ingest time, row count)
7. If table already exists, replace it (re-upload = refresh)
**Supported formats:** .xlsx, .xls, .csv
### 4. Tool Registry
**Storage:** `~/.excel-mcp/tools/` directory, one JSON file per tool.
**Saved Analysis schema:**
```json
{
"id": "big_invoices_last_week",
"type": "saved_analysis",
"name": "Big Invoices Last Week",
"description": "Show invoices over a threshold from the past 7 days",
"created_at": "2026-02-12T17:00:00Z",
"updated_at": "2026-02-12T17:00:00Z",
"sql_template": "SELECT * FROM {table} WHERE amount > :min_amount AND date >= date('now', '-7 days')",
"parameters": {
"table": {"type": "string", "description": "Table to query", "required": true},
"min_amount": {"type": "number", "description": "Minimum invoice amount", "default": 5000}
}
}
```
**Custom Tool schema:**
```json
{
"id": "flag_duplicate_invoices",
"type": "custom_tool",
"name": "Flag Duplicate Invoices",
"description": "Find potential duplicate invoices (same client, amount, within N days)",
"created_at": "2026-02-12T17:00:00Z",
"updated_at": "2026-02-12T17:00:00Z",
"python_code": "def run(db, client_col='client', amount_col='amount', date_col='date', days=3):\n ...",
"parameters": {
"table": {"type": "string", "required": true},
"days": {"type": "integer", "default": 3, "description": "Days proximity threshold"}
}
}
```
### 5. Security & Sandboxing
**SQL queries:**
- Read-only (SELECT only, block INSERT/UPDATE/DELETE/DROP/ALTER)
- Timeout: 10 seconds
- Max rows: 10,000
**Custom Python tools:**
- Allowed imports: pandas, numpy, datetime, json, math, statistics, re, collections
- Blocked: os, subprocess, shutil, pathlib, sys, importlib, eval, exec, __import__
- No file writes outside `~/.excel-mcp/output/`
- No network access
- Execution timeout: 30 seconds
### 6. Installation
**install.sh script:**
1. Check for Python 3.10+ (guide to install if missing)
2. Check for `uv` (install if missing)
3. Create `~/.excel-mcp/` directory structure
4. Clone/download the project
5. Create virtualenv, install dependencies
6. Detect Claude Desktop config location (macOS vs other)
7. Inject MCP server config into `claude_desktop_config.json`
8. Optionally set DATA_DIR if user provides a path
9. Print success message with next steps
**Claude Desktop config injection:**
```json
{
"mcpServers": {
"excel-analytics": {
"command": "uv",
"args": ["run", "--directory", "~/.excel-mcp/excel-mcp", "python", "-m", "excel_mcp"],
"env": {
"DATA_DIR": "~/Documents"
}
}
}
}
```
## Directory Structure
```
excel-mcp/
├── pyproject.toml
├── README.md
├── LICENSE (MIT)
├── install.sh
├── excel_mcp/
│ ├── __init__.py
│ ├── __main__.py # Entry point
│ ├── server.py # FastMCP server + FastAPI app
│ ├── config.py # Paths, constants, settings
│ ├── database.py # SQLite connection, query execution
│ ├── ingest.py # Excel/CSV parsing and loading
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── core_tools.py # list_datasets, describe, query, summarize
│ │ ├── meta_tools.py # save_analysis, create_tool, manage, etc.
│ │ └── dynamic_loader.py # Load user tools from disk, register with MCP
│ ├── sandbox.py # Code validation and safe execution
│ ├── api/
│ │ ├── __init__.py
│ │ └── routes.py # REST API for frontend
│ └── static/
│ └── index.html # Single-file frontend dashboard
├── tests/
│ ├── test_ingest.py
│ ├── test_tools.py
│ └── test_sandbox.py
└── sample_data/
└── demo.xlsx # Sample file for testing
```
## REST API Endpoints (for frontend)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/` | Serve dashboard HTML |
| GET | `/api/datasets` | List all tables |
| GET | `/api/datasets/{table}` | Table details + preview |
| DELETE | `/api/datasets/{table}` | Drop a table |
| POST | `/api/upload` | Upload Excel/CSV file |
| GET | `/api/tools` | List all tools (core + user) |
| GET | `/api/tools/{id}` | Tool details |
| POST | `/api/tools` | Create new tool |
| PUT | `/api/tools/{id}` | Update tool |
| DELETE | `/api/tools/{id}` | Delete tool |
| POST | `/api/tools/{id}/test` | Run tool with params |
| GET | `/api/health` | Health check |
## Dependencies
- `mcp` — MCP SDK
- `fastapi` — REST API + frontend serving
- `uvicorn` — ASGI server
- `pandas` — Data manipulation
- `openpyxl` — Excel reading
- `tabulate` — Markdown table formatting
## User Config
`~/.excel-mcp/config.json`:
```json
{
"data_dir": "~/Documents",
"port": 8765,
"auto_scan": false,
"max_rows_preview": 50,
"query_timeout_seconds": 10
}
```
## Success Criteria
1. Dave can install with one terminal command (with Blake's help)
2. Dave can drag Excel files into the dashboard
3. Dave can ask Claude questions about his data immediately
4. Dave can tell Claude to save analyses as reusable tools
5. Dave can see and manage his tools in the dashboard
6. Dave never needs to write code, SQL, or edit config files
7. Everything runs locally — Dave's data never leaves his machine