Metadata-Version: 2.4
Name: search-excel-mcp
Version: 0.1.0
Summary: Local MCP server for querying Excel files (heavy merged cells, multi-language text) via DuckDB and substring search. No LLM/embedding required.
Project-URL: Homepage, https://github.com/your-org/search-excel-mcp
Project-URL: Issues, https://github.com/your-org/search-excel-mcp/issues
Author-email: Nguyen Si Hung <nsh.astec.96@gmail.com>
License: MIT
License-File: LICENSE
Keywords: cjk,claude,duckdb,excel,fastmcp,japanese,mcp,rag
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Database
Classifier: Topic :: Office/Business :: Office Suites
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: duckdb>=0.10
Requires-Dist: fastmcp>=0.2
Requires-Dist: openpyxl>=3.1
Requires-Dist: platformdirs>=4
Requires-Dist: pyarrow>=14
Description-Content-Type: text/markdown

# search-excel-mcp

Local MCP server that lets any MCP client (Claude Code, Cursor, Claude Desktop, Cline, …) query complex Excel files (heavy merged cells, large size, multi-language text including CJK) via SQL and substring search — **no API keys, no embeddings, no GPU**.

Built with [FastMCP](https://github.com/jlowin/fastmcp) + [DuckDB](https://duckdb.org) + [openpyxl](https://openpyxl.readthedocs.io) + [pyarrow](https://arrow.apache.org/).

---

## Why this exists

Documentation-style Excel files (external design docs, report specs, requirements sheets) typically have thousands of merged cells per sheet, 20–100 MB per file, and multi-language text where most FTS tokenizers fail.

Standard RAG (embeddings + vector store) needs an LLM or embedding model that you can't always run locally. Your MCP client is the reasoning layer; this server just gives it accurate structured access to cells via DuckDB.

## Install

```bash
pip install search-excel-mcp
# or run without install:
uvx search-excel-mcp
```

## Configure your MCP client

Add to the client's MCP config file. Common locations:

| Client | Config path |
|---|---|
| Claude Code | `~/.claude/mcp.json` (global) or `<project>/.mcp.json` (project-local) |
| Cursor | `~/.cursor/mcp.json` (global) or `<project>/.cursor/mcp.json` (project-local) |
| Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Cline / other | see your client's docs |

Config entry (same JSON shape for all):

```json
{
  "mcpServers": {
    "search-excel": {
      "command": "uvx",
      "args": ["search-excel-mcp"],
      "env": {
        "SEARCH_EXCEL_DATA_DIR": "/absolute/path/to/your/excel/folder"
      }
    }
  }
}
```

Only `SEARCH_EXCEL_DATA_DIR` is required. The DuckDB cache is placed under your OS-standard app data folder (e.g. `~/Library/Application Support/search-excel-mcp/data.duckdb` on macOS) — override with `SEARCH_EXCEL_DB` if you want it elsewhere.

Restart your MCP client → the `search-excel` server appears.

## Use

Drop `.xlsx` files into your data folder, then ask your agent:

> "ingest all the files in the data folder"
> "find rows that mention '保存ボタン' in any file"
> "list all sheets in report-2024.xlsx"
> "show me sheet 5"

The agent routes to the right MCP tool. DuckDB is built incrementally — already-ingested files are skipped unless you re-ingest with `reset=true`.

## CLI (optional)

The same package installs CLI tools for batch use:

```bash
# Ingest everything under $SEARCH_EXCEL_DATA_DIR
search-excel-ingest

# Ingest a single file or folder
search-excel-ingest --input /path/to/file.xlsx
search-excel-ingest --input /path/to/folder

# Filter / reset
search-excel-ingest --only "report"
search-excel-ingest --reset

# Validate ingest accuracy (samples random cells against the source xlsx)
search-excel-validate --samples 20
```

## Environment variables

| Variable | Default | Purpose |
|---|---|---|
| `SEARCH_EXCEL_DATA_DIR` | `./data` (cwd-relative) | Where your `.xlsx` files live — set this for MCP usage |
| `SEARCH_EXCEL_DB` | OS user data dir | DuckDB cache file |
| `SEARCH_EXCEL_REPORTS_DIR` | OS user data dir / reports | Ingest/validation log output |
| `SEARCH_EXCEL_WORKERS` | `min(4, cpu_count)` | Parallel ingest worker count |

`<OS user data dir>` resolves per platform via [`platformdirs`](https://pypi.org/project/platformdirs/):
- **macOS**: `~/Library/Application Support/search-excel-mcp/`
- **Linux**: `~/.local/share/search-excel-mcp/` (or `$XDG_DATA_HOME`)
- **Windows**: `%LOCALAPPDATA%\search-excel-mcp\`

## MCP tools

| Tool | Purpose |
|---|---|
| `ingest_file(path, reset?)` | Ingest a single `.xlsx` file |
| `ingest_folder(path?, reset?)` | Ingest every `.xlsx` under a folder (recursive) |
| `list_files()` | All ingested files |
| `list_sheets(file?)` | Sheets, optionally filtered |
| `search_text(keyword, file?, sheet?, limit?)` | Substring search across cells (CJK-friendly) |
| `search_sheets(keyword, limit?)` | Which sheets discuss a topic, ranked by hit count |
| `get_sheet_cells(sheet_id, row/col range, ...)` | Read a slice of a sheet |
| `get_sheet_text(sheet_id, max_chars?)` | Deduplicated text overview of a sheet |
| `query_sql(sql, limit?)` | Read-only SELECT against DuckDB |
| `delete_file(file_id)` | Remove one file's data |
| `clear_db(confirm)` | Wipe all data (preview unless `confirm=true`) |
| `db_stats()` | Counts and overall stats |

## Accuracy notes

- **No LLM in the pipeline** → deterministic.
- Cell formatting (font color, bold, fill) is **not** extracted — only text values. Files that mark changes with red font lose that signal here.
- Read-only MCP for queries; ingest tools mutate DuckDB only.
- `search-excel-validate` samples cells from the source xlsx and checks them against the DB.
- `search_text` uses ILIKE (substring) instead of DuckDB FTS so CJK works reliably.

## License

MIT
