spice-mcp 0.1.2__tar.gz → 0.1.4__tar.gz

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.

Potentially problematic release.


This version of spice-mcp might be problematic. Click here for more details.

Files changed (139) hide show
  1. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/.gitignore +3 -0
  2. spice_mcp-0.1.4/PKG-INFO +121 -0
  3. spice_mcp-0.1.4/README.md +100 -0
  4. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/docs/architecture.md +3 -4
  5. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/docs/codex_cli.md +7 -13
  6. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/docs/config.md +1 -1
  7. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/docs/development.md +3 -3
  8. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/docs/discovery.md +0 -4
  9. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/docs/index.md +1 -1
  10. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/docs/tools.md +1 -14
  11. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/pyproject.toml +1 -1
  12. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/dune/__init__.py +4 -2
  13. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/dune/cache.py +2 -34
  14. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/dune/client.py +9 -4
  15. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/dune/extract.py +79 -633
  16. spice_mcp-0.1.4/src/spice_mcp/adapters/dune/query_wrapper.py +86 -0
  17. spice_mcp-0.1.4/src/spice_mcp/adapters/dune/user_agent.py +9 -0
  18. spice_mcp-0.1.4/src/spice_mcp/adapters/spellbook/__init__.py +6 -0
  19. spice_mcp-0.1.4/src/spice_mcp/adapters/spellbook/explorer.py +313 -0
  20. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/config.py +1 -1
  21. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/core/models.py +0 -8
  22. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/core/ports.py +0 -15
  23. spice_mcp-0.1.4/src/spice_mcp/mcp/server.py +797 -0
  24. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/mcp/tools/base.py +1 -1
  25. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/mcp/tools/execute_query.py +32 -63
  26. spice_mcp-0.1.4/test_mcp_cursor/.env.example +9 -0
  27. spice_mcp-0.1.4/test_mcp_cursor/CURSOR_SETUP.md +159 -0
  28. spice_mcp-0.1.4/test_mcp_cursor/INSTALL.md +71 -0
  29. spice_mcp-0.1.4/test_mcp_cursor/QUICK_START.md +96 -0
  30. spice_mcp-0.1.4/test_mcp_cursor/README.md +38 -0
  31. spice_mcp-0.1.4/test_mcp_cursor/TEST_RESULTS.md +50 -0
  32. spice_mcp-0.1.4/test_mcp_cursor/cursor_mcp_config.json +11 -0
  33. spice_mcp-0.1.4/test_mcp_cursor/setup_cursor_mcp.sh +58 -0
  34. spice_mcp-0.1.4/test_mcp_cursor/test_issue_8_scenarios.py +335 -0
  35. spice_mcp-0.1.4/test_mcp_cursor/test_real_api.py +394 -0
  36. spice_mcp-0.1.4/tests/fastmcp/test_dune_query_schema_validation.py +139 -0
  37. spice_mcp-0.1.4/tests/fastmcp/test_resources_and_validation.py +58 -0
  38. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/fastmcp/test_server_fastmcp.py +34 -27
  39. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/fastmcp/test_server_mcp_extras.py +1 -3
  40. spice_mcp-0.1.4/tests/integration/__init__.py +2 -0
  41. spice_mcp-0.1.4/tests/integration/test_spellbook_discovery.py +214 -0
  42. spice_mcp-0.1.4/tests/integration/test_user_journeys.py +327 -0
  43. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/mcp/conftest.py +2 -21
  44. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/mcp/test_tool_contracts.py +0 -16
  45. spice_mcp-0.1.4/tests/offline/test_cache_consistency.py +253 -0
  46. spice_mcp-0.1.4/tests/offline/test_edge_cases.py +256 -0
  47. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/offline/test_typing_utils.py +4 -6
  48. spice_mcp-0.1.4/tests/property/__init__.py +2 -0
  49. spice_mcp-0.1.4/tests/property/test_property_based.py +195 -0
  50. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_mcp_simulation.py +0 -1
  51. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_mcp_tools.py +4 -5
  52. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/support/helpers.py +2 -7
  53. spice_mcp-0.1.4/tests/tools/test_additional_mcp_tools.py +117 -0
  54. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/tools/test_execute_query_tool.py +6 -15
  55. spice_mcp-0.1.4/tests/tools/test_unified_discover.py +183 -0
  56. spice_mcp-0.1.4/tests/validation/__init__.py +2 -0
  57. spice_mcp-0.1.4/tests/validation/test_error_messages.py +206 -0
  58. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/uv.lock +1 -1
  59. spice_mcp-0.1.2/PKG-INFO +0 -193
  60. spice_mcp-0.1.2/README.md +0 -172
  61. spice_mcp-0.1.2/debug_mcp_tool.py +0 -147
  62. spice_mcp-0.1.2/debug_template_query.py +0 -147
  63. spice_mcp-0.1.2/docs/commands.md +0 -41
  64. spice_mcp-0.1.2/issue_diagnostics.py +0 -76
  65. spice_mcp-0.1.2/src/spice_mcp/mcp/server.py +0 -527
  66. spice_mcp-0.1.2/src/spice_mcp/mcp/tools/sui_package_overview.py +0 -56
  67. spice_mcp-0.1.2/src/spice_mcp/service_layer/sui_service.py +0 -131
  68. spice_mcp-0.1.2/test_semaphore_fix.py +0 -101
  69. spice_mcp-0.1.2/test_sync_fix.py +0 -55
  70. spice_mcp-0.1.2/tests/fastmcp/test_resources_and_validation.py +0 -49
  71. spice_mcp-0.1.2/tests/mcp/test_resources.py +0 -53
  72. spice_mcp-0.1.2/tests/tools/test_additional_mcp_tools.py +0 -82
  73. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/.python-version +0 -0
  74. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/CONTRIBUTING.md +0 -0
  75. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/LICENSE +0 -0
  76. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/docs/codex_cli_tools.md +0 -0
  77. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/docs/dune_api.md +0 -0
  78. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/docs/installation.md +0 -0
  79. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/pytest.ini +0 -0
  80. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/scripts/bridgez/make_circle_comparison.py +0 -0
  81. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/scripts/codex_tools_doctor.sh +0 -0
  82. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/__init__.py +0 -0
  83. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/__init__.py +0 -0
  84. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/dune/admin.py +0 -0
  85. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/dune/helpers.py +0 -0
  86. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/dune/transport.py +0 -0
  87. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/dune/types.py +0 -0
  88. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/dune/typing_utils.py +0 -0
  89. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/dune/urls.py +0 -0
  90. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/adapters/http_client.py +0 -0
  91. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/core/__init__.py +0 -0
  92. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/core/errors.py +0 -0
  93. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/logging/query_history.py +0 -0
  94. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/mcp/__init__.py +0 -0
  95. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/mcp/tools/__init__.py +0 -0
  96. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/observability/__init__.py +0 -0
  97. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/observability/logging.py +0 -0
  98. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/polars_utils.py +0 -0
  99. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/py.typed +0 -0
  100. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/service_layer/__init__.py +0 -0
  101. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/service_layer/discovery_service.py +0 -0
  102. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/service_layer/query_admin_service.py +0 -0
  103. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/src/spice_mcp/service_layer/query_service.py +0 -0
  104. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/cassettes/.gitkeep +0 -0
  105. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/config/environments.yaml +0 -0
  106. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/config/test_queries.yaml +0 -0
  107. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/conftest.py +0 -0
  108. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/http_stubbed/test_age.py +0 -0
  109. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/http_stubbed/test_errors.py +0 -0
  110. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/http_stubbed/test_pagination.py +0 -0
  111. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/live/test_live_basic.py +0 -0
  112. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/live/test_live_sui.py +0 -0
  113. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/offline/test_cache.py +0 -0
  114. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/offline/test_discovery.py +0 -0
  115. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/offline/test_dune_adapter.py +0 -0
  116. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/offline/test_parsing.py +0 -0
  117. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/offline/test_query_history.py +0 -0
  118. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/offline/test_show_rewrite.py +0 -0
  119. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/offline/test_timeout.py +0 -0
  120. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/offline/test_urls.py +0 -0
  121. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/comprehensive_test_runner.py +0 -0
  122. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/run_tests.py +0 -0
  123. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_api_health.py +0 -0
  124. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_cache_functionality.py +0 -0
  125. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_data_types.py +0 -0
  126. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_dune_connectivity.py +0 -0
  127. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_dune_query_execution.py +0 -0
  128. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_error_handling.py +0 -0
  129. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_performance.py +0 -0
  130. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_query_lifecycle.py +0 -0
  131. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_resilience.py +0 -0
  132. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/scripts/test_resource_management.py +0 -0
  133. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/style/test_polars_lazy.py +0 -0
  134. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/support/api_client.py +0 -0
  135. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/support/query_factory.py +0 -0
  136. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/support/test_data.py +0 -0
  137. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/tools/test_health_tool.py +0 -0
  138. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/tools/test_query_service.py +0 -0
  139. {spice_mcp-0.1.2 → spice_mcp-0.1.4}/tests/tools/test_schemas.py +0 -0
@@ -21,3 +21,6 @@ bin/
21
21
  .github/
22
22
  .factory/
23
23
  debug_api.py
24
+
25
+ # Local testing environment
26
+ test_spice_mcp/
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.4
2
+ Name: spice-mcp
3
+ Version: 0.1.4
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
+ [![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
+ ## Why spice-mcp?
32
+
33
+ - **Agent-friendly**: Designed for AI agents using the Model Context Protocol (MCP)
34
+ - **Efficient**: Polars-first pipeline keeps data lazy until needed, reducing memory usage
35
+ - **Discovery**: Built-in tools to explore Dune's extensive blockchain datasets
36
+ - **Type-safe**: Fully typed parameters and responses with FastMCP
37
+ - **Reproducible**: Automatic query history logging and SQL artifact storage
38
+
39
+ ## Quick Start
40
+
41
+ 1. **Install**:
42
+ ```bash
43
+ uv pip install spice-mcp
44
+ ```
45
+
46
+ 2. **Set API key** (choose one method):
47
+ - **Option A**: Create a `.env` file in your project root:
48
+ ```bash
49
+ echo "DUNE_API_KEY=your-api-key-here" > .env
50
+ ```
51
+ - **Option B**: Export in your shell:
52
+ ```bash
53
+ export DUNE_API_KEY=your-api-key-here
54
+ ```
55
+
56
+ 3. **Use with Cursor IDE**:
57
+ Add to Cursor Settings → MCP Servers:
58
+ ```json
59
+ {
60
+ "name": "spice-mcp",
61
+ "command": "spice-mcp",
62
+ "env": {
63
+ "DUNE_API_KEY": "your-dune-api-key-here"
64
+ }
65
+ }
66
+ ```
67
+
68
+ **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.
69
+
70
+ ## Core Tools
71
+
72
+ | Tool | Description | Key Parameters |
73
+ |------|-------------|----------------|
74
+ | `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) |
75
+ | `dune_query_info` | Get metadata for a saved query | `query` (str - ID or URL) |
76
+ | `dune_discover` | Unified discovery across Dune API and Spellbook | `keyword` (str\|list), `schema` (str), `limit` (int), `source` (`dune\|spellbook\|both`), `include_columns` (bool) |
77
+ | `dune_find_tables` | Search schemas and list tables | `keyword` (str), `schema` (str), `limit` (int) |
78
+ | `dune_describe_table` | Get column metadata for a table | `schema` (str), `table` (str) |
79
+ | `spellbook_find_models` | Search Spellbook dbt models | `keyword` (str\|list), `schema` (str), `limit` (int), `include_columns` (bool) |
80
+ | `dune_health_check` | Verify API key and configuration | (no parameters) |
81
+ | `dune_query_create` | Create a new saved query | `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
82
+ | `dune_query_update` | Update an existing saved query | `query_id` (int), `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
83
+ | `dune_query_fork` | Fork an existing saved query | `source_query_id` (int), `name` (str) |
84
+
85
+ ## Resources
86
+
87
+ - `spice:history/tail/{n}` — View last N lines of query history (1-1000)
88
+ - `spice:artifact/{sha}` — Retrieve stored SQL by SHA-256 hash
89
+
90
+ ## What is Dune?
91
+
92
+ [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.
93
+
94
+ ## Installation
95
+
96
+ **From PyPI** (recommended):
97
+ ```bash
98
+ uv pip install spice-mcp
99
+ ```
100
+
101
+ **From source**:
102
+ ```bash
103
+ git clone https://github.com/Evan-Kim2028/spice-mcp.git
104
+ cd spice-mcp
105
+ uv sync
106
+ uv pip install -e .
107
+ ```
108
+
109
+ **Requirements**: Python 3.13+
110
+
111
+ ## Documentation
112
+
113
+ - [Tool Reference](docs/tools.md) — Complete tool documentation with parameters
114
+ - [Architecture](docs/architecture.md) — Code structure and design patterns
115
+ - [Discovery Guide](docs/discovery.md) — How to explore Dune schemas and tables
116
+ - [Dune API Guide](docs/dune_api.md) — Understanding Dune's data structure
117
+ - [Configuration](docs/config.md) — Environment variables and settings
118
+
119
+ ## License
120
+
121
+ See [LICENSE](LICENSE) file for details.
@@ -0,0 +1,100 @@
1
+ # spice-mcp
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/spice-mcp.svg)](https://pypi.org/project/spice-mcp/)
4
+ <a href="https://glama.ai/mcp/servers/@Evan-Kim2028/spice-mcp">
5
+ <img width="380" height="200" src="https://glama.ai/mcp/servers/@Evan-Kim2028/spice-mcp/badge" alt="Spice MCP server" />
6
+ </a>
7
+
8
+ 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.
9
+
10
+ ## Why spice-mcp?
11
+
12
+ - **Agent-friendly**: Designed for AI agents using the Model Context Protocol (MCP)
13
+ - **Efficient**: Polars-first pipeline keeps data lazy until needed, reducing memory usage
14
+ - **Discovery**: Built-in tools to explore Dune's extensive blockchain datasets
15
+ - **Type-safe**: Fully typed parameters and responses with FastMCP
16
+ - **Reproducible**: Automatic query history logging and SQL artifact storage
17
+
18
+ ## Quick Start
19
+
20
+ 1. **Install**:
21
+ ```bash
22
+ uv pip install spice-mcp
23
+ ```
24
+
25
+ 2. **Set API key** (choose one method):
26
+ - **Option A**: Create a `.env` file in your project root:
27
+ ```bash
28
+ echo "DUNE_API_KEY=your-api-key-here" > .env
29
+ ```
30
+ - **Option B**: Export in your shell:
31
+ ```bash
32
+ export DUNE_API_KEY=your-api-key-here
33
+ ```
34
+
35
+ 3. **Use with Cursor IDE**:
36
+ Add to Cursor Settings → MCP Servers:
37
+ ```json
38
+ {
39
+ "name": "spice-mcp",
40
+ "command": "spice-mcp",
41
+ "env": {
42
+ "DUNE_API_KEY": "your-dune-api-key-here"
43
+ }
44
+ }
45
+ ```
46
+
47
+ **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.
48
+
49
+ ## Core Tools
50
+
51
+ | Tool | Description | Key Parameters |
52
+ |------|-------------|----------------|
53
+ | `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) |
54
+ | `dune_query_info` | Get metadata for a saved query | `query` (str - ID or URL) |
55
+ | `dune_discover` | Unified discovery across Dune API and Spellbook | `keyword` (str\|list), `schema` (str), `limit` (int), `source` (`dune\|spellbook\|both`), `include_columns` (bool) |
56
+ | `dune_find_tables` | Search schemas and list tables | `keyword` (str), `schema` (str), `limit` (int) |
57
+ | `dune_describe_table` | Get column metadata for a table | `schema` (str), `table` (str) |
58
+ | `spellbook_find_models` | Search Spellbook dbt models | `keyword` (str\|list), `schema` (str), `limit` (int), `include_columns` (bool) |
59
+ | `dune_health_check` | Verify API key and configuration | (no parameters) |
60
+ | `dune_query_create` | Create a new saved query | `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
61
+ | `dune_query_update` | Update an existing saved query | `query_id` (int), `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
62
+ | `dune_query_fork` | Fork an existing saved query | `source_query_id` (int), `name` (str) |
63
+
64
+ ## Resources
65
+
66
+ - `spice:history/tail/{n}` — View last N lines of query history (1-1000)
67
+ - `spice:artifact/{sha}` — Retrieve stored SQL by SHA-256 hash
68
+
69
+ ## What is Dune?
70
+
71
+ [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.
72
+
73
+ ## Installation
74
+
75
+ **From PyPI** (recommended):
76
+ ```bash
77
+ uv pip install spice-mcp
78
+ ```
79
+
80
+ **From source**:
81
+ ```bash
82
+ git clone https://github.com/Evan-Kim2028/spice-mcp.git
83
+ cd spice-mcp
84
+ uv sync
85
+ uv pip install -e .
86
+ ```
87
+
88
+ **Requirements**: Python 3.13+
89
+
90
+ ## Documentation
91
+
92
+ - [Tool Reference](docs/tools.md) — Complete tool documentation with parameters
93
+ - [Architecture](docs/architecture.md) — Code structure and design patterns
94
+ - [Discovery Guide](docs/discovery.md) — How to explore Dune schemas and tables
95
+ - [Dune API Guide](docs/dune_api.md) — Understanding Dune's data structure
96
+ - [Configuration](docs/config.md) — Environment variables and settings
97
+
98
+ ## License
99
+
100
+ See [LICENSE](LICENSE) file for details.
@@ -24,8 +24,8 @@
24
24
  ## Layers
25
25
 
26
26
  ### Core (`src/spice_mcp/core`)
27
- - `models.py` – dataclass representations for query requests, previews, metadata, schema descriptions, and Sui artefacts.
28
- - `ports.py` – protocols defining the boundaries (`QueryExecutor`, `CatalogExplorer`, `SuiInspector`).
27
+ - `models.py` – dataclass representations for query requests, previews, metadata, schema descriptions.
28
+ - `ports.py` – protocols defining the boundaries (`QueryExecutor`, `CatalogExplorer`).
29
29
  - `errors.py` – MCP-oriented error categorisation and envelope helpers (`error_response`).
30
30
 
31
31
  These modules contain zero infrastructure concerns; they define the shapes that higher layers orchestrate.
@@ -39,13 +39,12 @@ These modules contain zero infrastructure concerns; they define the shapes that
39
39
  ### Service Layer (`src/spice_mcp/service_layer`)
40
40
  - `query_service.py` – orchestrates `QueryExecutor` calls and shapes the dictionaries returned to MCP tools. Handles `performance` passthrough, metadata merging, and preview formatting.
41
41
  - `discovery_service.py` – thin façade around `CatalogExplorer`, returning friendlier lists for schema/table introspection.
42
- - `sui_service.py` – composes `QueryService` to deliver Sui-specific behaviours (events preview, package overview defaults).
43
42
 
44
43
  Services are pure Python and easily testable with stubbed ports (see `tests/tools/test_query_service.py`).
45
44
 
46
45
  ### MCP Integration (`src/spice_mcp/mcp`)
47
46
  - `server.py` – FastMCP stdio bridge. Lazily initialises configuration, adapters, services, tools, and resources. Provides resource URIs and tools while keeping stdout clean.
48
- - `tools/execute_query.py`, `tools/sui_package_overview.py` – thin glue calling into services, handling logging/audit trails, and shaping agent-friendly responses.
47
+ - `tools/execute_query.py` – thin glue calling into services, handling logging/audit trails, and shaping agent-friendly responses.
49
48
  - Uses `QueryHistory` (`src/spice_mcp/logging/query_history.py`) for JSONL audit trails and SQL artefact storage.
50
49
 
51
50
  ### Observability (`src/spice_mcp/observability`)
@@ -1,7 +1,7 @@
1
1
  Codex CLI MCP Setup (spice_mcp_beta)
2
2
 
3
3
  Goal
4
- - Register the spice-mcp server as an MCP provider named `spice_mcp_beta` for Codex CLI, so you can call tools like `dune_query` and use the Sui command URIs.
4
+ - Register the spice-mcp server as an MCP provider named `spice_mcp_beta` for Codex CLI, so you can call tools like `dune_query`.
5
5
 
6
6
  Prereqs
7
7
  - Dune API key in your shell (e.g., export `DUNE_API_KEY=...`)
@@ -54,23 +54,17 @@ Verify configuration
54
54
  - `codex mcp list` should list `spice_mcp_beta` with the python command and no secrets in Env.
55
55
 
56
56
  Try some tools
57
- - Find Sui schemas
58
- - `mcp__spice_mcp_beta__dune_find_tables {"keyword": "sui"}`
59
- - Describe `sui.transactions`
60
- - `mcp__spice_mcp_beta__dune_describe_table {"schema": "sui", "table": "transactions"}`
57
+ - Find schemas
58
+ - `mcp__spice_mcp_beta__dune_find_tables {"keyword": "dex"}`
59
+ - Describe a table
60
+ - `mcp__spice_mcp_beta__dune_describe_table {"schema": "dex", "table": "trades"}`
61
61
  - Query preview (with metadata/pagination)
62
62
  - `mcp__spice_mcp_beta__dune_query {"query": "4388", "limit": 5}`
63
- - `mcp__spice_mcp_beta__dune_query {"query": "SELECT * FROM sui.events LIMIT 5"}`
64
- MCP command URIs (resources)
65
- - You can read command-style resources for safe defaults (3-day windows, small LIMITs):
66
- - Events preview: `spice:sui/events_preview/72/50/0xcaf6...,0x2c8d...`
67
- - Package overview (cmd): `spice:sui/package_overview/72/30/0xcaf6...,0x2c8d...`
68
- - Global preview (no package filter): `spice:sui/events_preview/72/50/_`
69
- - See `docs/commands.md` for details. (Your client must support reading MCP resources.)
63
+ - `mcp__spice_mcp_beta__dune_query {"query": "SELECT * FROM dex.trades LIMIT 5"}`
70
64
 
71
65
  Notes & troubleshooting
72
66
  - Secret safety: Never store `DUNE_API_KEY` in Codex config; use `shell_environment_policy.inherit=["DUNE_API_KEY"]` or set it in your shell.
73
67
  - Missing key error: If you see `DUNE_API_KEY required`, export it in your shell and relaunch. The server also attempts to load `.env` from the project or home directory as a fallback.
74
68
  - FastMCP stdout: This server disables FastMCP banners/logging to keep stdio clean; if handshakes fail, ensure you used the exact `python -m spice_mcp.mcp.server` and PYTHONPATH as shown.
75
- - Heavy scans: Start with `format="metadata"` on `dune_query`, use `performance="large"`, and small `limit` with a recent time window; the Sui command URIs default to safe windows and limits.
69
+ - Heavy scans: Start with `format="metadata"` on `dune_query`, use `performance="large"`, and small `limit` with a recent time window.
76
70
  - Tests/CI: set `SPICE_MCP_SKIP_DOTENV=1` to stop `_ensure_initialized` from reading local `.env` files when the key is intentionally absent.
@@ -15,7 +15,7 @@ Optional
15
15
  - `SPICE_LOGGING_ENABLED`: true/false (default: true)
16
16
  - Timeouts
17
17
  - `SPICE_TIMEOUT_SECONDS`: default polling timeout (seconds)
18
- - `SPICE_MAX_CONCURRENT_QUERIES`: max concurrent dune_query executions (default: 5)
18
+ - `SPICE_MAX_CONCURRENT_QUERIES`: reserved for future concurrency control (default: 5, not currently enforced)
19
19
  - Raw SQL
20
20
  - `SPICE_RAW_SQL_QUERY_ID`: ID of the template query used to execute raw SQL (default: 4060379). Health is reported by `dune_health_check` when set.
21
21
 
@@ -4,7 +4,7 @@ This document summarizes local development workflows for spice-mcp.
4
4
 
5
5
  ## Tooling
6
6
 
7
- - Tests: `pytest` (+ `pytest-asyncio`, `responses`, `aioresponses`, `vcrpy`)
7
+ - Tests: `pytest` (+ `responses`, `vcrpy`, `hypothesis`)
8
8
  - Lint/Format: `ruff` (lint + import sort + formatting)
9
9
  - Types: `mypy`
10
10
 
@@ -27,7 +27,7 @@ Enable live tests explicitly:
27
27
  - `tests/offline/`: pure unit tests; no network.
28
28
  - `tests/http_stubbed/`: HTTP boundary via `responses`.
29
29
  - `tests/tools/`: service + MCP tool unit tests.
30
- - `tests/mcp/`: FastMCP contract tests with stubbed services (preview/raw/metadata, Sui resources).
30
+ - `tests/mcp/`: FastMCP contract tests with stubbed services (preview/raw/metadata).
31
31
  - `tests/fastmcp/`: registration/metadata smoke tests for FastMCP wiring.
32
32
  - `tests/style/`: static safety checks (lazyframe enforcement, etc.).
33
33
  - `tests/live/`: opt-in integrations; require `SPICE_TEST_LIVE=1` and `DUNE_API_KEY`.
@@ -36,7 +36,7 @@ Most unit/contract tests construct stub implementations of the ports in `src/spi
36
36
 
37
37
  ## Style
38
38
 
39
- - Follow Ruff + ruff-format defaults (88 cols, py39).
39
+ - Follow Ruff + ruff-format defaults (88 cols, py313).
40
40
  - Prefer small, focused functions and explicit typing in `src/`.
41
41
  - Tests prioritize readability over strict typing.
42
42
  - Export `SPICE_MCP_SKIP_DOTENV=1` when running tests that expect the API key to be unset.
@@ -17,10 +17,6 @@ Approach
17
17
  - Some deployments allow: information_schema.schemata/tables/columns
18
18
  - If blocked, use SHOW + probes
19
19
 
20
- Sui Examples
21
- - Find Sui schemas: SHOW SCHEMAS LIKE '%sui%'
22
- - Confirm core tables: SELECT * FROM sui.events/transactions/checkpoints/objects LIMIT 1
23
- - SHOW TABLES works on some Sui-related schemas (e.g., sui_base), but not all
24
20
 
25
21
  Helpers in this repo
26
22
  - `src/spice_mcp/service_layer/discovery_service.py` provides service wrappers around the Dune adapter:
@@ -7,7 +7,7 @@ spice-mcp Documentation
7
7
  - Architecture Overview: `docs/architecture.md`
8
8
  - Dune API Overview: `docs/dune_api.md`
9
9
  - Catalog Discovery Guide: `docs/discovery.md`
10
- - Sui Packages (Deepbook) Workflow: `docs/sui_packages.md`
10
+
11
11
  - Codex CLI MCP Setup: `docs/codex_cli.md`
12
12
  - Development & Testing: `docs/development.md`
13
13
 
@@ -53,20 +53,7 @@ Logging & Artifacts
53
53
  - columns: [{ name, dune_type?, polars_dtype?, extra?, comment? }]
54
54
  - Errors follow the standard MCP envelope.
55
55
 
56
- 4) sui_package_overview
57
- - Purpose: Preview Sui package activity (events, transactions, objects) over a time window; timeouts supported.
58
- - Input schema:
59
- - packages: string[] (required)
60
- - hours?: integer (default 72)
61
- - timeout_seconds?: number (default 30)
62
- - Output fields (best-effort):
63
- - ok: boolean
64
- - events_count?, events_preview?, events_timeout?, events_error?
65
- - transactions_count?, transactions_preview?, transactions_timeout?, transactions_error?
66
- - objects_count?, objects_preview?, objects_timeout?, objects_error?
67
- - On error: { ok: false, error: { … } }
68
-
69
- 5) dune_health_check
56
+ 4) dune_health_check
70
57
  - Purpose: Basic environment and logging readiness check.
71
58
  - Output fields: ok, api_key_present, status
72
59
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "spice-mcp"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "MCP server for Dune Analytics data access"
5
5
  authors = [
6
6
  { name = "Evan-Kim2028", email = "ekcopersonal@gmail.com" }
@@ -2,9 +2,11 @@
2
2
 
3
3
  This module provides a thin façade used by the new service layer while
4
4
  keeping the battle-tested logic that the original spice client offered.
5
+
6
+ Only synchronous interfaces are exposed.
5
7
  """
6
8
 
7
9
  from . import urls # re-export for callers needing low-level helpers
8
- from .extract import async_query, query # noqa: F401
10
+ from .extract import query # noqa: F401
9
11
 
10
- __all__ = ["query", "async_query", "urls"]
12
+ __all__ = ["query", "urls"]
@@ -51,39 +51,7 @@ def load_from_cache(
51
51
  return None, execution
52
52
 
53
53
 
54
- async def async_load_from_cache(
55
- execute_kwargs: _extract.ExecuteKwargs,
56
- result_kwargs: _extract.RetrievalKwargs,
57
- output_kwargs: _extract.OutputKwargs,
58
- ) -> tuple[
59
- pl.DataFrame | tuple[pl.DataFrame, Execution] | None, Execution | None
60
- ]:
61
- # get latest execution
62
- execution = await _extract.async_get_latest_execution(execute_kwargs)
63
- if execution is None:
64
- return None, None
65
-
66
- # build cache path
67
- cache_path = _build_cache_path(
68
- execution=execution,
69
- execute_kwargs=execute_kwargs,
70
- result_kwargs=result_kwargs,
71
- cache_dir=output_kwargs['cache_dir'],
72
- )
73
-
74
- # load cache result
75
- if os.path.exists(cache_path):
76
- import polars as pl
77
-
78
- if result_kwargs['verbose']:
79
- print('loading dune query result from cache')
80
- df = await pl.scan_parquet(cache_path).collect_async()
81
- if output_kwargs['include_execution']:
82
- return (df, execution), execution
83
- else:
84
- return df, execution
85
- else:
86
- return None, execution
54
+ ## Removed async cache loader; synchronous load_from_cache should be used exclusively.
87
55
 
88
56
 
89
57
  def save_to_cache(
@@ -113,7 +81,7 @@ def save_to_cache(
113
81
  # save to cache
114
82
  tmp_path = (
115
83
  cache_path + '_tmp_' + secrets.token_hex(8)
116
- ) # add for if running in parallel
84
+ ) # add tmp suffix to avoid conflicts during writes
117
85
  df.write_parquet(tmp_path)
118
86
  shutil.move(tmp_path, cache_path)
119
87
 
@@ -24,6 +24,10 @@ from ...polars_utils import collect_preview
24
24
  from ..http_client import HttpClient, HttpClientConfig
25
25
  from . import extract, urls
26
26
 
27
+ # Use wrapper to avoid FastMCP detecting overloads in extract.query()
28
+ # Note: We still import extract for _determine_input_type and other non-overloaded functions
29
+ from .query_wrapper import execute_query as _execute_dune_query
30
+
27
31
 
28
32
  class DuneAdapter(QueryExecutor, CatalogExplorer):
29
33
  """Thin façade around the vendored extract module."""
@@ -47,7 +51,7 @@ class DuneAdapter(QueryExecutor, CatalogExplorer):
47
51
  q_rewritten = _maybe_rewrite_show_sql(q)
48
52
  if q_rewritten is not None:
49
53
  q = q_rewritten
50
- result = extract.query(
54
+ result = _execute_dune_query(
51
55
  query_or_execution=q,
52
56
  verbose=False,
53
57
  refresh=request.refresh,
@@ -123,9 +127,10 @@ class DuneAdapter(QueryExecutor, CatalogExplorer):
123
127
  pass
124
128
 
125
129
  url = urls.get_query_results_url(query_id, parameters=params, csv=False)
130
+ from .user_agent import get_user_agent
126
131
  headers = {
127
132
  "X-Dune-API-Key": self._api_key(),
128
- "User-Agent": extract.get_user_agent(),
133
+ "User-Agent": get_user_agent(),
129
134
  }
130
135
  try:
131
136
  resp = self._http.request("GET", url, headers=headers)
@@ -196,8 +201,8 @@ class DuneAdapter(QueryExecutor, CatalogExplorer):
196
201
  def _run_sql(self, sql: str, *, limit: int | None = None) -> pl.DataFrame:
197
202
  self._ensure_api_key()
198
203
  sql_eff = _maybe_rewrite_show_sql(sql) or sql
199
- df = extract.query(
200
- sql_eff,
204
+ df = _execute_dune_query(
205
+ query_or_execution=sql_eff,
201
206
  verbose=False,
202
207
  performance="medium",
203
208
  timeout_seconds=self.config.default_timeout_seconds,