A multi-source DuckDB query + transformation-pipeline MCP server.
Project description
Spelunk
A multi-source DuckDB query + transformation-pipeline MCP server. Point it at files (CSV/Parquet/JSON/Excel) and databases (SQLite/PostgreSQL/MySQL), and an agent like Claude Code can query across all of them — and build step-by-step pipelines — through one DuckDB engine.
Architecture in one line
A single DuckDB session is both the query engine and the workspace: every source is ATTACHed
(databases) or scanned (files) into one connection, so one query can join a Parquet file to a
Postgres table to a result you built two steps ago — all in DuckDB SQL.
| Path | Role |
|---|---|
spelunk/core/duck.py |
DuckSession — the one DuckDB connection: query / profile / export / catalog / drop / lineage / replay + introspection. |
spelunk/core/sources.py |
Source registry — maps a spec to a DuckDB attach/scan. DuckDB-only: a source it can't attach (e.g. SQL Server) is rejected. |
spelunk/core/guard.py |
sqlglot AST safety: read-only enforcement (assert_read_only). |
spelunk/mcp/server.py |
Thin FastMCP wrapper over DuckSession. |
tests |
Acceptance tests. |
Tools
query(sql, name, flow?) # run a read-only SELECT over sources + results; store the full
# result as table `name` for immediate reuse. The ONE tool for
# looking and building — every result is named and chainable.
profile(sql, flow?) # per-column stats (null_rate, min/max/mean/std, percentiles, top/freq)
export(target, fmt, path) # write a saved result name OR a full SELECT to csv/json/parquet
catalog(flow?) # list flows, or the results in one flow
drop(name?, flow?) # drop one result, or a whole flow
lineage(name?, flow?) # provenance DAG: the SQL + deps that built a result (or a whole flow)
replay(flow?, into?) # rebuild a flow from its recorded SQL, in dependency order
Discovery resources: db://tables (queryable source objects) and db://{table} (columns, PK,
sample, row count). A flow is an isolated result namespace (a DuckDB schema); give each
concurrent line of analysis its own flow.
Install & run
The fastest path is uvx — no clone, no venv. It fetches Spelunk
into an ephemeral environment and runs the spelunk command. The package is published as
spelunk-mcp (the command is spelunk), so pass it via --from:
uvx --from spelunk-mcp spelunk \
--source sales=./data/sales.parquet \
--source sqlite:///path/to/app.db \
--session-dir .spelunk_session # omit for an ephemeral (non-durable) workspace
Want the latest commit instead of the released version? Point uvx straight at the repo:
uvx --from git+https://github.com/shaunak2512/spelunk spelunk --source sales=./data/sales.parquet
Either way, wire it into Claude Code with a .mcp.json:
{
"mcpServers": {
"spelunk": {
"command": "uvx",
"args": ["--from", "spelunk-mcp", "spelunk", "--source", "sales=./data/sales.parquet"]
}
}
}
Prefer a local checkout? python -m spelunk.mcp.server --source ... is equivalent to the spelunk
command.
Sources auto-detect by extension/scheme; prefix with name= to set the catalog/view name.
Optional resource guards: --memory-limit 4GB, --temp-dir <dir>, --max-temp-size 50GB.
DuckDB is out-of-core, so a source larger than RAM is the normal case — scans read on demand and
buffering operators spill to the temp directory.
Dev
uv sync --extra dev
uv run --extra dev python -m pytest -q
uv run --extra dev ruff check spelunk/
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spelunk_mcp-0.1.0.tar.gz.
File metadata
- Download URL: spelunk_mcp-0.1.0.tar.gz
- Upload date:
- Size: 130.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7bc7bd0c48050121748659d62220b5abdaef21f9e36e7577469501c265a9268c
|
|
| MD5 |
1c4f053a28ecd948143a0083071a8ec8
|
|
| BLAKE2b-256 |
2edb820a918c01d9bb648ac7b00cf8b7bb9194744337a7871039732f3d69b867
|
Provenance
The following attestation bundles were made for spelunk_mcp-0.1.0.tar.gz:
Publisher:
publish.yml on shaunak2512/spelunk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spelunk_mcp-0.1.0.tar.gz -
Subject digest:
7bc7bd0c48050121748659d62220b5abdaef21f9e36e7577469501c265a9268c - Sigstore transparency entry: 2158587925
- Sigstore integration time:
-
Permalink:
shaunak2512/spelunk@4913c76620add4a2f0679b21e03e7ba3430262fe -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/shaunak2512
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4913c76620add4a2f0679b21e03e7ba3430262fe -
Trigger Event:
push
-
Statement type:
File details
Details for the file spelunk_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: spelunk_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 38.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a9ff4982ccbb4270b321d6a9c26ac40f273bbed78773cb8e310981af24074bb6
|
|
| MD5 |
41263da7e8069d520dddb3f160d8b140
|
|
| BLAKE2b-256 |
4df7f054c1f37bfc2e978e66173d31eb5875e22108d29c189819477f7dc4d1bb
|
Provenance
The following attestation bundles were made for spelunk_mcp-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on shaunak2512/spelunk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spelunk_mcp-0.1.0-py3-none-any.whl -
Subject digest:
a9ff4982ccbb4270b321d6a9c26ac40f273bbed78773cb8e310981af24074bb6 - Sigstore transparency entry: 2158587970
- Sigstore integration time:
-
Permalink:
shaunak2512/spelunk@4913c76620add4a2f0679b21e03e7ba3430262fe -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/shaunak2512
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4913c76620add4a2f0679b21e03e7ba3430262fe -
Trigger Event:
push
-
Statement type: