Skip to main content

Local MCP server for querying Excel files (heavy merged cells, multi-language text) via DuckDB and substring search. No LLM/embedding required.

Project description

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 + DuckDB + openpyxl + pyarrow.


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

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):

{
  "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:

# 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:

  • 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

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

search_excel_mcp-0.1.0.tar.gz (14.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

search_excel_mcp-0.1.0-py3-none-any.whl (19.1 kB view details)

Uploaded Python 3

File details

Details for the file search_excel_mcp-0.1.0.tar.gz.

File metadata

  • Download URL: search_excel_mcp-0.1.0.tar.gz
  • Upload date:
  • Size: 14.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.5

File hashes

Hashes for search_excel_mcp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2a9f35d4b0c3a45086dd9ee8866399cb128d35892b27523e2dd2398e912f6e2d
MD5 6149e30d341eddea084482c0839ab253
BLAKE2b-256 58f2931463a9617b09b3d0df56d386659d0fd7b9086e1cf24da0cd42a36dfaf9

See more details on using hashes here.

File details

Details for the file search_excel_mcp-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for search_excel_mcp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 156a74f29e9f416859e395315a155a1079d98e198b6a5cdad567c503d14bfa24
MD5 48ee2088fd367a882aacff022abf8929
BLAKE2b-256 2425d8680f331d7938e37c53c6c5202668885878daa8a8993c2e2825ce4d43db

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page