spice-mcp 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,185 @@
1
+ """
2
+ Verification Service - Verifies tables exist in Dune with persistent caching.
3
+
4
+ This service provides lazy verification of table existence, caching results
5
+ to avoid repeated queries. Cache persists across server restarts.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ import time
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ from ..adapters.dune.client import DuneAdapter
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Cache entry expires after 1 week (604800 seconds)
20
+ CACHE_TTL_SECONDS = 604800
21
+
22
+
23
+ class VerificationService:
24
+ """
25
+ Service for verifying table existence in Dune with persistent caching.
26
+
27
+ Verifies tables exist before returning them to users, ensuring only
28
+ queryable tables are surfaced. Uses persistent cache to avoid repeated
29
+ verification queries.
30
+ """
31
+
32
+ def __init__(self, cache_path: Path, dune_adapter: DuneAdapter):
33
+ """
34
+ Initialize verification service.
35
+
36
+ Args:
37
+ cache_path: Path to JSON file for persistent cache storage
38
+ dune_adapter: DuneAdapter instance for querying table existence
39
+ """
40
+ self.cache_path = cache_path
41
+ self.dune_adapter = dune_adapter
42
+ self._cache: dict[str, dict[str, Any]] = self._load_cache()
43
+
44
+ def verify_tables_batch(
45
+ self, tables: list[tuple[str, str]]
46
+ ) -> dict[str, bool]:
47
+ """
48
+ Verify multiple tables exist in Dune.
49
+
50
+ Uses cache for fast lookups, queries Dune only for uncached tables.
51
+ Results are cached for future use.
52
+
53
+ Args:
54
+ tables: List of (schema, table) tuples to verify
55
+
56
+ Returns:
57
+ Dict mapping "schema.table" -> bool (exists or not)
58
+ """
59
+ results: dict[str, bool] = {}
60
+ to_check: list[tuple[str, str]] = []
61
+
62
+ # Check cache first
63
+ for schema, table in tables:
64
+ fqn = f"{schema}.{table}"
65
+ cached = self._get_cached(fqn)
66
+ if cached is not None:
67
+ results[fqn] = cached
68
+ else:
69
+ to_check.append((schema, table))
70
+
71
+ # Verify uncached tables
72
+ if to_check:
73
+ logger.info(f"Verifying {len(to_check)} uncached tables")
74
+ for schema, table in to_check:
75
+ try:
76
+ exists = self._verify_single(schema, table)
77
+ fqn = f"{schema}.{table}"
78
+ results[fqn] = exists
79
+ self._cache_result(fqn, exists)
80
+ except Exception as e:
81
+ # Do not hard-cache transient failures as negative results.
82
+ # Leave the table unverified so callers can choose to keep it.
83
+ logger.warning(
84
+ f"Failed to verify {schema}.{table}: {e}. Skipping cache and leaving unverified."
85
+ )
86
+ # Intentionally omit from results and cache on failure
87
+
88
+ return results
89
+
90
+ def _verify_single(self, schema: str, table: str) -> bool:
91
+ """
92
+ Verify a single table exists using lightweight DESCRIBE query.
93
+
94
+ Uses SHOW COLUMNS which is fast and doesn't require full table scan.
95
+
96
+ Args:
97
+ schema: Schema name
98
+ table: Table name
99
+
100
+ Returns:
101
+ True if table exists, False otherwise
102
+ """
103
+ try:
104
+ # Use describe_table which internally uses SHOW COLUMNS
105
+ # This is lightweight and fast
106
+ self.dune_adapter.describe_table(schema, table)
107
+ return True
108
+ except Exception:
109
+ # If describe fails, table doesn't exist
110
+ return False
111
+
112
+ def _get_cached(self, table: str) -> bool | None:
113
+ """
114
+ Get verification result from cache if fresh.
115
+
116
+ Args:
117
+ table: Fully qualified table name (schema.table)
118
+
119
+ Returns:
120
+ bool if cached and fresh, None if cache miss or stale
121
+ """
122
+ if table not in self._cache:
123
+ return None
124
+
125
+ entry = self._cache[table]
126
+ timestamp = entry.get("timestamp", 0)
127
+ age = time.time() - timestamp
128
+
129
+ if age < CACHE_TTL_SECONDS:
130
+ return entry.get("exists", False)
131
+ else:
132
+ # Cache entry is stale, remove it
133
+ del self._cache[table]
134
+ self._save_cache()
135
+ return None
136
+
137
+ def _cache_result(self, table: str, exists: bool) -> None:
138
+ """
139
+ Cache verification result with current timestamp.
140
+
141
+ Args:
142
+ table: Fully qualified table name
143
+ exists: Whether table exists
144
+ """
145
+ self._cache[table] = {
146
+ "exists": exists,
147
+ "timestamp": time.time(),
148
+ }
149
+ self._save_cache()
150
+
151
+ def _load_cache(self) -> dict[str, dict[str, Any]]:
152
+ """
153
+ Load verification cache from disk.
154
+
155
+ Returns:
156
+ Dict mapping table -> cache entry
157
+ """
158
+ if not self.cache_path.exists():
159
+ return {}
160
+
161
+ try:
162
+ with open(self.cache_path, encoding="utf-8") as f:
163
+ cache = json.load(f)
164
+ # Validate cache structure
165
+ if isinstance(cache, dict):
166
+ return cache
167
+ return {}
168
+ except Exception as e:
169
+ logger.warning(f"Failed to load verification cache: {e}")
170
+ return {}
171
+
172
+ def _save_cache(self) -> None:
173
+ """Persist verification cache to disk."""
174
+ try:
175
+ self.cache_path.parent.mkdir(parents=True, exist_ok=True)
176
+ with open(self.cache_path, "w", encoding="utf-8") as f:
177
+ json.dump(self._cache, f, indent=2)
178
+ except Exception as e:
179
+ logger.warning(f"Failed to save verification cache: {e}")
180
+
181
+ def clear_cache(self) -> None:
182
+ """Clear verification cache (useful for testing or forced refresh)."""
183
+ self._cache = {}
184
+ if self.cache_path.exists():
185
+ self.cache_path.unlink()
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: spice-mcp
3
+ Version: 0.1.5
4
+ Summary: mcp server built ontop of dune api endpoint
5
+ Author-email: Evan-Kim2028 <ekcopersonal@gmail.com>
6
+ License-File: LICENSE
7
+ Classifier: Operating System :: OS Independent
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Typing :: Typed
12
+ Requires-Python: >=3.13
13
+ Requires-Dist: aiohttp>=3.9.5
14
+ Requires-Dist: fastmcp>=0.3.0
15
+ Requires-Dist: mcp>=0.9.0
16
+ Requires-Dist: polars>=1.35.1
17
+ Requires-Dist: requests>=2.31.0
18
+ Requires-Dist: rich-argparse>=1.5.2
19
+ Requires-Dist: rich>=13.3.3
20
+ Description-Content-Type: text/markdown
21
+
22
+ # spice-mcp
23
+
24
+ [![PyPI version](https://img.shields.io/pypi/v/spice-mcp.svg)](https://pypi.org/project/spice-mcp/)
25
+ <a href="https://glama.ai/mcp/servers/@Evan-Kim2028/spice-mcp">
26
+ <img width="380" height="200" src="https://glama.ai/mcp/servers/@Evan-Kim2028/spice-mcp/badge" alt="Spice MCP server" />
27
+ </a>
28
+
29
+ An MCP server that provides AI agents with direct access to [Dune Analytics](https://dune.com/) data. Execute queries, discover schemas and tables, and manage saved queries—all through a clean, type-safe interface optimized for AI workflows.
30
+
31
+ **Discover High-Quality Tables**: Leverages [Dune Spellbook](https://github.com/duneanalytics/spellbook), Dune's official GitHub repository of curated dbt models, to surface verified, production-ready tables with rich metadata.
32
+
33
+ ## Why spice-mcp?
34
+
35
+ - **Agent-friendly**: Designed for AI agents using the Model Context Protocol (MCP)
36
+ - **High-Quality Discovery**: Leverages Dune Spellbook's GitHub repository to find verified, production-ready tables with rich metadata
37
+ - **Efficient**: Polars-first pipeline keeps data lazy until needed, reducing memory usage
38
+ - **Discovery**: Built-in tools to explore Dune's extensive blockchain datasets from both Dune API and Spellbook
39
+ - **Type-safe**: Fully typed parameters and responses with FastMCP
40
+ - **Reproducible**: Automatic query history logging and SQL artifact storage
41
+
42
+ ## Quick Start
43
+
44
+ 1. **Install**:
45
+ ```bash
46
+ uv pip install spice-mcp
47
+ ```
48
+
49
+ 2. **Set API key** (choose one method):
50
+ - **Option A**: Create a `.env` file in your project root:
51
+ ```bash
52
+ echo "DUNE_API_KEY=your-api-key-here" > .env
53
+ ```
54
+ - **Option B**: Export in your shell:
55
+ ```bash
56
+ export DUNE_API_KEY=your-api-key-here
57
+ ```
58
+
59
+ 3. **Use with Cursor IDE**:
60
+ Add to Cursor Settings → MCP Servers:
61
+ ```json
62
+ {
63
+ "name": "spice-mcp",
64
+ "command": "spice-mcp",
65
+ "env": {
66
+ "DUNE_API_KEY": "your-dune-api-key-here"
67
+ }
68
+ }
69
+ ```
70
+
71
+ **Note**: Query history logging is enabled by default. Logs are saved to `logs/queries.jsonl` (or `~/.spice_mcp/logs/queries.jsonl` if not in a project directory). To customize paths, set `SPICE_QUERY_HISTORY` and `SPICE_ARTIFACT_ROOT` environment variables.
72
+
73
+ ## Core Tools
74
+
75
+ | Tool | Description | Key Parameters |
76
+ |------|-------------|----------------|
77
+ | `dune_query` | Execute queries by ID, URL, or raw SQL | `query` (str), `parameters` (object), `limit` (int), `offset` (int), `format` (`preview\|raw\|metadata\|poll`), `refresh` (bool), `timeout_seconds` (float) |
78
+ | `dune_query_info` | Get metadata for a saved query | `query` (str - ID or URL) |
79
+ | `dune_discover` | Unified discovery across Dune API and Spellbook (returns verified tables only). **Leverages Dune Spellbook GitHub repository** for high-quality, curated tables. | `keyword` (str\|list), `schema` (str), `limit` (int), `source` (`dune\|spellbook\|both`), `include_columns` (bool) |
80
+ | `dune_describe_table` | Get column metadata for a table | `schema` (str), `table` (str) |
81
+ | `dune_health_check` | Verify API key and configuration | (no parameters) |
82
+ | `dune_query_create` | Create a new saved query | `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
83
+ | `dune_query_update` | Update an existing saved query | `query_id` (int), `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
84
+ | `dune_query_fork` | Fork an existing saved query | `source_query_id` (int), `name` (str) |
85
+
86
+ ## Resources
87
+
88
+ - `spice:history/tail/{n}` — View last N lines of query history (1-1000)
89
+ - `spice:artifact/{sha}` — Retrieve stored SQL by SHA-256 hash
90
+
91
+ ## What is Dune?
92
+
93
+ [Dune](https://dune.com/) is a crypto data platform providing curated blockchain datasets and a public API. It aggregates on-chain data from Ethereum, Solana, Polygon, and other chains into queryable SQL tables. See the [Dune Docs](https://dune.com/docs) for more information.
94
+
95
+ ## What is Dune Spellbook?
96
+
97
+ [Dune Spellbook](https://github.com/duneanalytics/spellbook) is Dune's official GitHub repository containing thousands of curated dbt models. These models represent high-quality, production-ready tables that are:
98
+
99
+ - **Verified**: All tables are verified to exist in Dune before being returned
100
+ - **Well-documented**: Rich metadata including column descriptions and types
101
+ - **Maintained**: Regularly updated by the Dune community and team
102
+ - **Production-ready**: Used by analysts and dashboards across the ecosystem
103
+
104
+ spice-mcp automatically clones and parses the Spellbook repository to discover these high-quality tables, parsing dbt config blocks to resolve actual Dune table names and verifying their existence before returning them to you.
105
+
106
+ ## Installation
107
+
108
+ **From PyPI** (recommended):
109
+ ```bash
110
+ uv pip install spice-mcp
111
+ ```
112
+
113
+ **From source**:
114
+ ```bash
115
+ git clone https://github.com/Evan-Kim2028/spice-mcp.git
116
+ cd spice-mcp
117
+ uv sync
118
+ uv pip install -e .
119
+ ```
120
+
121
+ **Requirements**: Python 3.13+
122
+
123
+ ## Documentation
124
+
125
+ - [Tool Reference](docs/tools.md) — Complete tool documentation with parameters
126
+ - [Architecture](docs/architecture.md) — Code structure and design patterns
127
+ - [Discovery Guide](docs/discovery.md) — How to explore Dune schemas and tables
128
+ - [Dune API Guide](docs/dune_api.md) — Understanding Dune's data structure
129
+ - [Configuration](docs/config.md) — Environment variables and settings
130
+
131
+ ## License
132
+
133
+ See [LICENSE](LICENSE) file for details.
@@ -7,33 +7,36 @@ spice_mcp/adapters/http_client.py,sha256=CYgSKAsx-5c-uxaNIBCBTgQdaoBe5J3dJvnw8iq
7
7
  spice_mcp/adapters/dune/__init__.py,sha256=nspEuDpVOktAxm8B066s-d0LwouCYGpvNEexi0mRMN8,386
8
8
  spice_mcp/adapters/dune/admin.py,sha256=yxOueVz-rmgC-ZFbT06k59G24yRgYjiEkZlall5hXNQ,3157
9
9
  spice_mcp/adapters/dune/cache.py,sha256=7ykT58WN1yHGIN2uV3t7fWOqGb1VJdCvf3I-xZwsv74,4304
10
- spice_mcp/adapters/dune/client.py,sha256=JPrQZ_dtaPcGf6lYUguDxGweOtxG-8qMqOiXuhWL9QA,9122
11
- spice_mcp/adapters/dune/extract.py,sha256=3jrXqlak4Kpl11y7C3L1AhFv09lQ-2pBqmrh-0Mwlm0,28836
10
+ spice_mcp/adapters/dune/client.py,sha256=zle19bU-I3AzpOF1cp_hEaZUFNQV1RWhtl1LAxObk0g,9052
11
+ spice_mcp/adapters/dune/extract.py,sha256=67x-WCaP13vbMsTKnqNSOVbMs6Dsf0QHi2fLHduYTBI,30405
12
12
  spice_mcp/adapters/dune/helpers.py,sha256=BgDKr_g-UqmU2hoMb0ejQZHta_NbKwR1eDJp33sJYNk,227
13
+ spice_mcp/adapters/dune/query_wrapper.py,sha256=4dk8D8KJKWoBhuMLzDGupRrXGlG-N0cbM6HCs8wMFvE,2925
13
14
  spice_mcp/adapters/dune/transport.py,sha256=eRP-jPY2ZXxvTX9HSjIFqFUlbIzXspgH95jBFoTlpaQ,1436
14
15
  spice_mcp/adapters/dune/types.py,sha256=57TMX07u-Gq4BYwRAuZV0xI81nVXgtpp7KBID9YbKyQ,1195
15
16
  spice_mcp/adapters/dune/typing_utils.py,sha256=EpWneGDn-eQdo6lkLuESR09KXkDj9OqGz8bEF3JaFkM,574
16
17
  spice_mcp/adapters/dune/urls.py,sha256=bcuPERkFQduRTT2BrgzVhoFrMn-Lkvw9NmktcBZYEig,3902
18
+ spice_mcp/adapters/dune/user_agent.py,sha256=c6Kt4zczbuT9mapDoh8-3sgm268MUtvyIRxDF9yJwXQ,218
17
19
  spice_mcp/adapters/spellbook/__init__.py,sha256=D2cdVtSUbmAJdbPRvAyKxYS4-wUQ3unXyX4ZFYxenuk,150
18
- spice_mcp/adapters/spellbook/explorer.py,sha256=Q3UfEGlALizCDeeW_ZZVRRedzwMXiknmxwSkeOnxxgc,11515
20
+ spice_mcp/adapters/spellbook/explorer.py,sha256=nfecYztzxfBXp9b9IKcP4dVICCnPzhHvPfNJKFhaKxA,14856
19
21
  spice_mcp/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
22
  spice_mcp/core/errors.py,sha256=jlfTuyRaAaA_oU07KUk-1pDAAa43KG0BbZc5CINXtoE,3256
21
23
  spice_mcp/core/models.py,sha256=i0C_-UE16OWyyZo_liooEJeYvbChE5lpK80aN2OF4lk,1795
22
24
  spice_mcp/core/ports.py,sha256=nEdeA3UH7v0kB_hbguMrpDljb9EhSxUAO0SdhjpoijQ,1618
23
25
  spice_mcp/logging/query_history.py,sha256=doE9lod64uzJxlA2XzHH2-VAmC6WstYAkQ0taEAxiIM,4315
24
26
  spice_mcp/mcp/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
25
- spice_mcp/mcp/server.py,sha256=NNTjn5f_OLNd8zSnNsx73WhHzNGW_SBJoCrdzkgOGP4,26275
27
+ spice_mcp/mcp/server.py,sha256=oi0RRaSithCgUt0Qt6Tb3gks32LN0dOOwN7kX-BrN7s,32263
26
28
  spice_mcp/mcp/tools/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
27
29
  spice_mcp/mcp/tools/base.py,sha256=zJkVxLgXR48iZcJeng8cZ2rXvbyicagoGlMN7BK7Img,1041
28
- spice_mcp/mcp/tools/execute_query.py,sha256=CucjxoBT22VUS-QJV1JrDjoywu2nd13fL3Lfs1qytUg,16093
30
+ spice_mcp/mcp/tools/execute_query.py,sha256=K1YpuQGwvVM20A4_h9zNlkeG37J7jbY7BPzLm6vPAsY,16033
29
31
  spice_mcp/observability/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
32
  spice_mcp/observability/logging.py,sha256=ceJUEpKGpf5PAgPBmpB49zjqhdGCAESfLemFUhDSmI8,529
31
33
  spice_mcp/service_layer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
34
  spice_mcp/service_layer/discovery_service.py,sha256=202O0SzCZGQukd9kb2JYfarLygZHgiXlHqp_nTAdrWA,730
33
35
  spice_mcp/service_layer/query_admin_service.py,sha256=4q1NAAuTui7cm83Aq2rFDLIzKTHX17yzbSoSJyCmLbI,1356
34
36
  spice_mcp/service_layer/query_service.py,sha256=q0eAVW5I3sUxm29DgzPN_cH3rZEzmKwmdE3Xj4qP9lI,3878
35
- spice_mcp-0.1.3.dist-info/METADATA,sha256=a44b2EfhkPbQSS_PEoU1LF4Kkr2RfSD1gS6gGlEMmnQ,9343
36
- spice_mcp-0.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
37
- spice_mcp-0.1.3.dist-info/entry_points.txt,sha256=4XiXX13Vy-oiUJwlcO_82OltBaxFnEnkJ-76sZGm5os,56
38
- spice_mcp-0.1.3.dist-info/licenses/LICENSE,sha256=r0GNDnDY1RSkVQp7kEEf6MQU21OrNGJkxUHIsv6eyLk,1079
39
- spice_mcp-0.1.3.dist-info/RECORD,,
37
+ spice_mcp/service_layer/verification_service.py,sha256=dPA88p9zKqg62bNjN_4c5QFEUBHCWjZph8pn2a5zrUI,6057
38
+ spice_mcp-0.1.5.dist-info/METADATA,sha256=ag4hjEMz-qxvImxJPxpsQN_0F9BOUdbk6TF8zAXW5I4,6093
39
+ spice_mcp-0.1.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
40
+ spice_mcp-0.1.5.dist-info/entry_points.txt,sha256=4XiXX13Vy-oiUJwlcO_82OltBaxFnEnkJ-76sZGm5os,56
41
+ spice_mcp-0.1.5.dist-info/licenses/LICENSE,sha256=r0GNDnDY1RSkVQp7kEEf6MQU21OrNGJkxUHIsv6eyLk,1079
42
+ spice_mcp-0.1.5.dist-info/RECORD,,
@@ -1,198 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: spice-mcp
3
- Version: 0.1.3
4
- Summary: MCP server for Dune Analytics data access
5
- Author-email: Evan-Kim2028 <ekcopersonal@gmail.com>
6
- License-File: LICENSE
7
- Classifier: Operating System :: OS Independent
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3 :: Only
10
- Classifier: Programming Language :: Python :: 3.13
11
- Classifier: Typing :: Typed
12
- Requires-Python: >=3.13
13
- Requires-Dist: aiohttp>=3.9.5
14
- Requires-Dist: fastmcp>=0.3.0
15
- Requires-Dist: mcp>=0.9.0
16
- Requires-Dist: polars>=1.35.1
17
- Requires-Dist: requests>=2.31.0
18
- Requires-Dist: rich-argparse>=1.5.2
19
- Requires-Dist: rich>=13.3.3
20
- Description-Content-Type: text/markdown
21
-
22
- # spice-mcp
23
-
24
- spice-mcp is an MCP server for [Dune](https://dune.com/) 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. Results are Polars-first in Python and compact, token-efficient in MCP responses.
25
-
26
- [![PyPI version](https://img.shields.io/pypi/v/spice-mcp.svg)](https://pypi.org/project/spice-mcp/)
27
- <a href="https://glama.ai/mcp/servers/@Evan-Kim2028/spice-mcp">
28
- <img width="380" height="200" src="https://glama.ai/mcp/servers/@Evan-Kim2028/spice-mcp/badge" alt="Spice MCP server" />
29
- </a>
30
-
31
- Requirements: Python 3.13+
32
-
33
- This project uses FastMCP for typed, decorator-registered tools and resources.
34
-
35
- ## Highlights
36
- - Polars LazyFrame-first pipeline: results stay lazy until explicitly materialized
37
- - Ports/adapters layering for maintainable integrations ([docs/architecture.md](docs/architecture.md))
38
- - Discovery utilities (find schemas/tables, describe columns)
39
- - JSONL query history + SQL artifacts (SHA-256) for reproducibility
40
- - Rich MCP surface: query info/run, discovery, health, and Dune admin (create/update/fork)
41
-
42
- ## What is Dune?
43
- [Dune](https://dune.com/) is a crypto data platform providing curated blockchain datasets and a public API to run and fetch query results. See the [Dune Docs](https://dune.com/docs) and [Dune API](https://dune.com/docs/api/) for full details.
44
-
45
- ## Quick Start
46
- - Export `DUNE_API_KEY` in your shell (the server can also load a local `.env`; set `SPICE_MCP_SKIP_DOTENV=1` to skip during tests).
47
- - Install from PyPI: `uv pip install spice-mcp`
48
- - Or install from source:
49
- - `uv sync` then `uv pip install -e .`
50
- - Start the FastMCP stdio server:
51
- - `python -m spice_mcp.mcp.server --env PYTHONPATH=$(pwd)/src`
52
- - or install the console script via `uv tool install .` and run `spice-mcp`.
53
-
54
- ## Cursor IDE Setup
55
-
56
- To use spice-mcp with Cursor IDE:
57
-
58
- 1. **Install the MCP Server**:
59
- ```bash
60
- # Install from PyPI (recommended)
61
- uv pip install spice-mcp
62
-
63
- # Or install from source
64
- uv sync
65
- uv pip install -e .
66
-
67
- # Or install via uv tool (creates console script)
68
- uv tool install .
69
- ```
70
-
71
- 2. **Configure Cursor**:
72
- - Open Cursor Settings → MCP Servers
73
- - Add new MCP server configuration:
74
- ```json
75
- {
76
- "name": "spice-mcp",
77
- "command": "spice-mcp",
78
- "env": {
79
- "DUNE_API_KEY": "your-dune-api-key-here"
80
- },
81
- "disabled": false
82
- }
83
- ```
84
- Alternatively, if you prefer running from source:
85
- ```json
86
- {
87
- "name": "spice-mcp",
88
- "command": "python",
89
- "args": ["-m", "spice_mcp.mcp.server"],
90
- "env": {
91
- "PYTHONPATH": "/path/to/your/spice-mcp/src",
92
- "DUNE_API_KEY": "your-dune-api-key-here"
93
- },
94
- "disabled": false
95
- }
96
- ```
97
-
98
- 3. **Restart Cursor** to load the MCP server
99
-
100
- 4. **Verify Connection**:
101
- - Open Cursor and use the command palette (Cmd/Ctrl + Shift + P)
102
- - Search for "MCP" or "spice" commands
103
- - Test with `dune_health_check` to verify the connection
104
-
105
- 5. **Available Tools in Cursor**:
106
- - `dune_query`: Run Dune queries by ID, URL, or raw SQL
107
- - `dune_find_tables`: Search schemas and list tables
108
- - `dune_describe_table`: Get column metadata
109
- - `dune_health_check`: Verify API connection
110
-
111
- **Tip**: Create a `.env` file in your project root with `DUNE_API_KEY=your-key-here` for easier configuration.
112
-
113
- ## MCP Tools and Features
114
-
115
- All tools expose typed parameters, titles, and tags; failures return a consistent error envelope.
116
-
117
- - `dune_query_info` (Query Info, tags: dune, query)
118
- - Fetch saved-query metadata by ID/URL (name, parameters, tags, SQL, version).
119
-
120
- - `dune_query` (Run Dune Query, tags: dune, query)
121
- - Execute by ID/URL/raw SQL with parameters. Supports `refresh`, `max_age`, `limit/offset`, `sample_count`, `sort_by`, `columns`, and `format` = `preview|raw|metadata|poll`; accepts `timeout_seconds`.
122
-
123
- - `dune_health_check` (Health Check, tag: health)
124
- - Checks API key presence, query-history path, logging enabled; best-effort template check when configured.
125
-
126
- - `dune_find_tables` (Find Tables, tags: dune, schema)
127
- - Search schemas by keyword and/or list tables in a schema (`limit`).
128
-
129
- - `dune_describe_table` (Describe Table, tags: dune, schema)
130
- - Column metadata for `schema.table` (Dune types + Polars inferred dtypes when available).
131
-
132
- - Dune Admin tools (tags: dune, admin)
133
- - `dune_query_create(name, query_sql, description?, tags?, parameters?)`
134
- - `dune_query_update(query_id, name?, query_sql?, description?, tags?, parameters?)`
135
- - `dune_query_fork(source_query_id, name?)`
136
-
137
- ### Resources
138
- - `spice:history/tail/{n}` — tail last N lines of query history (1..1000)
139
- - `spice:artifact/{sha}` — fetch stored SQL by 64-hex SHA-256
140
- - `spice:sui/events_preview/{hours}/{limit}/{packages}` — Sui events preview (JSON)
141
- - `spice:sui/package_overview/{hours}/{timeout_seconds}/{packages}` — Sui overview (JSON)
142
-
143
- ## Resources
144
-
145
- - `spice:history/tail/{n}`
146
- - Last `n` lines from the query-history JSONL, clamped to [1, 1000]
147
-
148
- - `spice:artifact/{sha}`
149
- - Returns stored SQL for the SHA-256 (validated as 64 lowercase hex)
150
-
151
- Tests
152
- - Offline/unit tests (no network) live under `tests/offline/` and `tests/http_stubbed/`.
153
- - Live tests under `tests/live/` are skipped by default; enable with `SPICE_TEST_LIVE=1` and a valid `DUNE_API_KEY`.
154
- - Comprehensive scripted runner (tiered):
155
- - Run all tiers: `python tests/scripts/comprehensive_test_runner.py`
156
- - Select tiers: `python tests/scripts/comprehensive_test_runner.py -t 1 -t 3`
157
- - Stop on first failure: `python tests/scripts/comprehensive_test_runner.py --stop`
158
- - Optional JUnit export: `python tests/scripts/comprehensive_test_runner.py --junit tests/scripts/report.xml`
159
- - Pytest directly (offline/default): `uv run pytest -q -m "not live" --cov=src/spice_mcp --cov-report=term-missing`
160
-
161
- Core Tools (with parameters)
162
- - `dune_query`
163
- - Use: Preview/query results by ID, URL, or raw SQL (Polars preview + Dune metadata/pagination).
164
- - 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').
165
- - Output: `type`, `rowcount`, `columns`, `data_preview`, `execution_id`, `duration_ms`, `metadata?`, `next_uri?`, `next_offset?`.
166
- - `dune_find_tables`
167
- - Use: Search schemas by keyword and/or list tables for a schema.
168
- - Params: `keyword?` (string), `schema?` (string), `limit?` (int)
169
- - Output: `schemas?` (string[]), `tables?` (string[])
170
- - `dune_describe_table`
171
- - Use: Column descriptions for `schema.table` via SHOW + fallback to 1-row sample inference.
172
- - Params: `schema` (string), `table` (string)
173
- - Output: `columns` ([{ name, dune_type?, polars_dtype?, extra?, comment? }])
174
- - `sui_package_overview`
175
- - Use: Small-window overview for Sui packages (events/transactions/objects) with timeout handling.
176
- - Params: `packages` (string[]), `hours?` (int, default 72), `timeout_seconds?` (number, default 30)
177
- - Output: best-effort counts and previews; may include `*_timeout`/`*_error`
178
- - `dune_health_check`
179
- - Use: Verify API key presence and logging paths
180
- - Output: `api_key_present`, `query_history_path`, `logging_enabled`, `status`
181
-
182
- ## Docs
183
- - See [docs/index.md](docs/index.md) for full documentation:
184
- - Dune API structure and capabilities: [docs/dune_api.md](docs/dune_api.md)
185
- - Discovery patterns and examples: [docs/discovery.md](docs/discovery.md)
186
-
187
- - Tool reference and schemas: [docs/tools.md](docs/tools.md)
188
- - Codex CLI + tooling integration: [docs/codex_cli.md](docs/codex_cli.md), [docs/codex_cli_tools.md](docs/codex_cli_tools.md)
189
- - Architecture overview: [docs/architecture.md](docs/architecture.md)
190
- - Installation and configuration: [docs/installation.md](docs/installation.md), [docs/config.md](docs/config.md)
191
- - Development and linting: [docs/development.md](docs/development.md)
192
-
193
- Notes
194
- - Legacy Spice code now lives under `src/spice_mcp/adapters/dune` (extract, cache, urls, types).
195
- - Ports and models live in `src/spice_mcp/core`; services consume ports and are exercised by FastMCP tools.
196
- - Query history and SQL artefacts are always-on (see `src/spice_mcp/logging/query_history.py`).
197
- - To bypass dot-env loading during tests/CI, export `SPICE_MCP_SKIP_DOTENV=1`.
198
- - LazyFrames everywhere: eager `.collect()` or `pl.DataFrame` usage outside dedicated helpers is blocked by `tests/style/test_polars_lazy.py`; materialization helpers live in `src/spice_mcp/polars_utils.py`.