Provides tools for exploring Sui blockchain packages, including querying events, transactions, and objects, with package overview and activity analysis capabilities through Dune Analytics.
spice-mcp
spice-mcp is an MCP server for Dune Analytics. It wraps a curated subset of the original Spice client inside a clean architecture (core models/ports → adapters.dune → service layer → FastMCP tools) and adds agent-friendly workflows for discovery and Sui package exploration. Results are Polars-first in Python and compact, token-efficient in MCP responses.
Requirements: Python 3.13+
This project uses FastMCP for typed, decorator-registered tools and resources.
Highlights
Polars LazyFrame-first pipeline: results stay lazy until explicitly materialized
Ports/adapters layering for maintainable integrations (docs/architecture.md)
Discovery utilities (find schemas/tables, describe columns)
Sui package workflows (events/transactions/objects) with safe defaults
JSONL query history + SQL artifacts (SHA-256) for reproducibility
Rich MCP surface: query info/run, discovery, health, Sui, and Dune admin (create/update/fork)
What is Dune?
Dune is a crypto data platform providing curated blockchain datasets and a public API to run and fetch query results. See the Dune Docs and Dune API for full details.
Quick Start
Export
DUNE_API_KEYin your shell (the server can also load a local.env; setSPICE_MCP_SKIP_DOTENV=1to skip during tests).Install dependencies (
uv syncorpip install -e .).Start the FastMCP stdio server:
python -m spice_mcp.mcp.server --env PYTHONPATH=$(pwd)/srcor install the console script via
uv tool install .and runspice-mcp.
MCP Tools and Features
All tools expose typed parameters, titles, and tags; failures return a consistent error envelope.
dune_query_info(Query Info, tags: dune, query)Fetch saved-query metadata by ID/URL (name, parameters, tags, SQL, version).
dune_query(Run Dune Query, tags: dune, query)Execute by ID/URL/raw SQL with parameters. Supports
refresh,max_age,limit/offset,sample_count,sort_by,columns, andformat=preview|raw|metadata|poll; acceptstimeout_seconds.
dune_health_check(Health Check, tag: health)Checks API key presence, query-history path, logging enabled; best-effort template check when configured.
dune_find_tables(Find Tables, tags: dune, schema)Search schemas by keyword and/or list tables in a schema (
limit).
dune_describe_table(Describe Table, tags: dune, schema)Column metadata for
schema.table(Dune types + Polars inferred dtypes when available).
sui_package_overview(Sui Package Overview, tag: sui)Compact Sui activity overview for
packages[]withhours(default 72) andtimeout_seconds.
Dune Admin tools (tags: dune, admin)
dune_query_create(name, query_sql, description?, tags?, parameters?)dune_query_update(query_id, name?, query_sql?, description?, tags?, parameters?)dune_query_fork(source_query_id, name?)
Resources
spice:history/tail/{n}— tail last N lines of query history (1..1000)spice:artifact/{sha}— fetch stored SQL by 64-hex SHA-256spice:sui/events_preview/{hours}/{limit}/{packages}— Sui events preview (JSON)spice:sui/package_overview/{hours}/{timeout_seconds}/{packages}— Sui overview (JSON)
Resources
spice:history/tail/{n}Last
nlines from the query-history JSONL, clamped to [1, 1000]
spice:artifact/{sha}Returns stored SQL for the SHA-256 (validated as 64 lowercase hex)
Tests
Offline/unit tests (no network) live under
tests/offline/andtests/http_stubbed/.Live tests under
tests/live/are skipped by default; enable withSPICE_TEST_LIVE=1and a validDUNE_API_KEY.Comprehensive scripted runner (tiered):
Run all tiers:
python tests/scripts/comprehensive_test_runner.pySelect tiers:
python tests/scripts/comprehensive_test_runner.py -t 1 -t 3Stop on first failure:
python tests/scripts/comprehensive_test_runner.py --stopOptional JUnit export:
python tests/scripts/comprehensive_test_runner.py --junit tests/scripts/report.xml
Pytest directly (offline/default):
uv run pytest -q -m "not live" --cov=src/spice_mcp --cov-report=term-missing
Core Tools (with parameters)
dune_queryUse: Preview/query results by ID, URL, or raw SQL (Polars preview + Dune metadata/pagination).
Params:
query(string),parameters?(object),performance?('medium'|'large'),limit?(int),offset?(int),sort_by?(string),columns?(string[]),sample_count?(int),refresh?(bool),max_age?(number),timeout_seconds?(number),format?('preview'|'raw'|'metadata').Output:
type,rowcount,columns,data_preview,execution_id,duration_ms,metadata?,next_uri?,next_offset?.
dune_find_tablesUse: Search schemas by keyword and/or list tables for a schema.
Params:
keyword?(string),schema?(string),limit?(int)Output:
schemas?(string[]),tables?(string[])
dune_describe_tableUse: Column descriptions for
schema.tablevia SHOW + fallback to 1-row sample inference.Params:
schema(string),table(string)Output:
columns([{ name, dune_type?, polars_dtype?, extra?, comment? }])
sui_package_overviewUse: Small-window overview for Sui packages (events/transactions/objects) with timeout handling.
Params:
packages(string[]),hours?(int, default 72),timeout_seconds?(number, default 30)Output: best-effort counts and previews; may include
*_timeout/*_error
dune_health_checkUse: Verify API key presence and logging paths
Output:
api_key_present,query_history_path,logging_enabled,status
Docs
See docs/index.md for full documentation:
Dune API structure and capabilities: docs/dune_api.md
Discovery patterns and examples: docs/discovery.md
Sui package workflows: docs/sui_packages.md
Tool reference and schemas: docs/tools.md
Codex CLI + tooling integration: docs/codex_cli.md, docs/codex_cli_tools.md
Architecture overview: docs/architecture.md
Installation and configuration: docs/installation.md, docs/config.md
Development and linting: docs/development.md
Notes
Legacy Spice code now lives under
src/spice_mcp/adapters/dune(extract, cache, urls, types).Ports and models live in
src/spice_mcp/core; services consume ports and are exercised by FastMCP tools.Query history and SQL artefacts are always-on (see
src/spice_mcp/logging/query_history.py).To bypass dot-env loading during tests/CI, export
SPICE_MCP_SKIP_DOTENV=1.LazyFrames everywhere: eager
.collect()orpl.DataFrameusage outside dedicated helpers is blocked bytests/style/test_polars_lazy.py; materialization helpers live insrc/spice_mcp/polars_utils.py.