spice-mcp 0.1.3__tar.gz → 0.1.5__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 (163) hide show
  1. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/.gitignore +2 -0
  2. spice_mcp-0.1.5/PKG-INFO +133 -0
  3. spice_mcp-0.1.5/README.md +112 -0
  4. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/docs/codex_cli.md +2 -2
  5. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/docs/discovery.md +11 -4
  6. spice_mcp-0.1.5/docs/tools.md +118 -0
  7. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/pyproject.toml +2 -2
  8. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/client.py +22 -33
  9. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/extract.py +49 -5
  10. spice_mcp-0.1.5/src/spice_mcp/adapters/dune/query_wrapper.py +86 -0
  11. spice_mcp-0.1.5/src/spice_mcp/adapters/dune/user_agent.py +9 -0
  12. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/spellbook/explorer.py +84 -1
  13. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/mcp/server.py +199 -99
  14. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/mcp/tools/execute_query.py +19 -25
  15. spice_mcp-0.1.5/src/spice_mcp/service_layer/verification_service.py +185 -0
  16. spice_mcp-0.1.5/tests/fastmcp/test_dune_query_schema_validation.py +139 -0
  17. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/fastmcp/test_server_fastmcp.py +4 -4
  18. spice_mcp-0.1.5/tests/integration/test_spellbook_discovery.py +289 -0
  19. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/integration/test_user_journeys.py +6 -5
  20. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/mcp/test_tool_contracts.py +5 -4
  21. spice_mcp-0.1.5/tests/offline/test_show_rewrite.py +37 -0
  22. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/tools/test_additional_mcp_tools.py +14 -10
  23. spice_mcp-0.1.5/tests/tools/test_dbt_config_verification.py +169 -0
  24. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/tools/test_unified_discover.py +70 -0
  25. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/uv.lock +1 -1
  26. spice_mcp-0.1.3/.hypothesis/constants/03230d036f66298e +0 -4
  27. spice_mcp-0.1.3/.hypothesis/constants/06eb11a7027a88cd +0 -4
  28. spice_mcp-0.1.3/.hypothesis/constants/0f801575b114e021 +0 -4
  29. spice_mcp-0.1.3/.hypothesis/constants/1eeb4d051122e156 +0 -4
  30. spice_mcp-0.1.3/.hypothesis/constants/366c70be49a7442b +0 -4
  31. spice_mcp-0.1.3/.hypothesis/constants/4753a088eccf5421 +0 -4
  32. spice_mcp-0.1.3/.hypothesis/constants/4a324fb33357cad2 +0 -4
  33. spice_mcp-0.1.3/.hypothesis/constants/4cc858fdf36f013d +0 -4
  34. spice_mcp-0.1.3/.hypothesis/constants/5405706eff340311 +0 -4
  35. spice_mcp-0.1.3/.hypothesis/constants/6cfc775ad89a5b6a +0 -4
  36. spice_mcp-0.1.3/.hypothesis/constants/71bc6f8cf28bcc19 +0 -4
  37. spice_mcp-0.1.3/.hypothesis/constants/851c74a7cccd312b +0 -4
  38. spice_mcp-0.1.3/.hypothesis/constants/8c85c7b24d65d86a +0 -4
  39. spice_mcp-0.1.3/.hypothesis/constants/8d1eb008907ca218 +0 -4
  40. spice_mcp-0.1.3/.hypothesis/constants/9da684ed8a9b8d9c +0 -4
  41. spice_mcp-0.1.3/.hypothesis/constants/adc83b19e793491b +0 -4
  42. spice_mcp-0.1.3/.hypothesis/constants/ae79689a9c0be3fc +0 -4
  43. spice_mcp-0.1.3/.hypothesis/constants/c9f1afd2a84c92ac +0 -4
  44. spice_mcp-0.1.3/.hypothesis/constants/d427659fa4d8fdef +0 -4
  45. spice_mcp-0.1.3/.hypothesis/constants/d946c4cbd3f551fb +0 -4
  46. spice_mcp-0.1.3/.hypothesis/constants/da39a3ee5e6b4b0d +0 -4
  47. spice_mcp-0.1.3/.hypothesis/constants/df167ab23556209e +0 -4
  48. spice_mcp-0.1.3/.hypothesis/constants/e5d028d6ca3bc6c6 +0 -4
  49. spice_mcp-0.1.3/.hypothesis/constants/ea30e4add0a6171c +0 -4
  50. spice_mcp-0.1.3/.hypothesis/constants/ebbaf5778e99a3e9 +0 -4
  51. spice_mcp-0.1.3/.hypothesis/constants/ebfda9399227f401 +0 -4
  52. spice_mcp-0.1.3/.hypothesis/constants/f8058dd8f3978a9f +0 -4
  53. spice_mcp-0.1.3/.hypothesis/examples/04e6b3400353b141/3297e56a00db6e8d +0 -0
  54. spice_mcp-0.1.3/.hypothesis/examples/04e6b3400353b141/5206044c103f45ca +0 -2
  55. spice_mcp-0.1.3/.hypothesis/examples/04e6b3400353b141/8c16f33e2460573e +0 -1
  56. spice_mcp-0.1.3/.hypothesis/examples/04e6b3400353b141/c67ab7f30dd41ebd +0 -1
  57. spice_mcp-0.1.3/.hypothesis/examples/3297e56a00db6e8d/1236c0a13f38a176 +0 -0
  58. spice_mcp-0.1.3/.hypothesis/examples/5206044c103f45ca/e4201915c9a55aa8 +0 -1
  59. spice_mcp-0.1.3/.hypothesis/examples/8c16f33e2460573e/05f4853fe973fb8f +0 -1
  60. spice_mcp-0.1.3/.hypothesis/examples/8c16f33e2460573e/57bd1d7010b0a3e5 +0 -1
  61. spice_mcp-0.1.3/.hypothesis/examples/8c16f33e2460573e/6c3ff7a3026f98fd +0 -1
  62. spice_mcp-0.1.3/.hypothesis/examples/8c16f33e2460573e/ccd03e0f6bc2e1a2 +0 -1
  63. spice_mcp-0.1.3/.hypothesis/examples/8c16f33e2460573e/d179779eaa64628d +0 -1
  64. spice_mcp-0.1.3/.hypothesis/examples/c67ab7f30dd41ebd/31f5e37d90db6ce6 +0 -1
  65. spice_mcp-0.1.3/.hypothesis/unicode_data/15.1.0/charmap.json.gz +0 -0
  66. spice_mcp-0.1.3/.hypothesis/unicode_data/15.1.0/codec-utf-8.json.gz +0 -0
  67. spice_mcp-0.1.3/PKG-INFO +0 -198
  68. spice_mcp-0.1.3/README.md +0 -177
  69. spice_mcp-0.1.3/docs/tools.md +0 -88
  70. spice_mcp-0.1.3/scripts/bridgez/make_circle_comparison.py +0 -114
  71. spice_mcp-0.1.3/scripts/codex_tools_doctor.sh +0 -104
  72. spice_mcp-0.1.3/tests/integration/test_spellbook_discovery.py +0 -214
  73. spice_mcp-0.1.3/tests/offline/test_show_rewrite.py +0 -24
  74. spice_mcp-0.1.3/tests/scripts/comprehensive_test_runner.py +0 -164
  75. spice_mcp-0.1.3/tests/scripts/run_tests.py +0 -25
  76. spice_mcp-0.1.3/tests/scripts/test_api_health.py +0 -424
  77. spice_mcp-0.1.3/tests/scripts/test_cache_functionality.py +0 -197
  78. spice_mcp-0.1.3/tests/scripts/test_data_types.py +0 -727
  79. spice_mcp-0.1.3/tests/scripts/test_dune_connectivity.py +0 -93
  80. spice_mcp-0.1.3/tests/scripts/test_dune_query_execution.py +0 -170
  81. spice_mcp-0.1.3/tests/scripts/test_error_handling.py +0 -200
  82. spice_mcp-0.1.3/tests/scripts/test_mcp_simulation.py +0 -1166
  83. spice_mcp-0.1.3/tests/scripts/test_mcp_tools.py +0 -133
  84. spice_mcp-0.1.3/tests/scripts/test_performance.py +0 -654
  85. spice_mcp-0.1.3/tests/scripts/test_query_lifecycle.py +0 -534
  86. spice_mcp-0.1.3/tests/scripts/test_resilience.py +0 -945
  87. spice_mcp-0.1.3/tests/scripts/test_resource_management.py +0 -376
  88. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/.python-version +0 -0
  89. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/CONTRIBUTING.md +0 -0
  90. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/LICENSE +0 -0
  91. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/docs/architecture.md +0 -0
  92. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/docs/codex_cli_tools.md +0 -0
  93. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/docs/config.md +0 -0
  94. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/docs/development.md +0 -0
  95. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/docs/dune_api.md +0 -0
  96. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/docs/index.md +0 -0
  97. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/docs/installation.md +0 -0
  98. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/pytest.ini +0 -0
  99. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/__init__.py +0 -0
  100. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/__init__.py +0 -0
  101. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/__init__.py +0 -0
  102. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/admin.py +0 -0
  103. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/cache.py +0 -0
  104. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/helpers.py +0 -0
  105. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/transport.py +0 -0
  106. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/types.py +0 -0
  107. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/typing_utils.py +0 -0
  108. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/urls.py +0 -0
  109. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/http_client.py +0 -0
  110. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/adapters/spellbook/__init__.py +0 -0
  111. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/config.py +0 -0
  112. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/core/__init__.py +0 -0
  113. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/core/errors.py +0 -0
  114. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/core/models.py +0 -0
  115. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/core/ports.py +0 -0
  116. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/logging/query_history.py +0 -0
  117. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/mcp/__init__.py +0 -0
  118. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/mcp/tools/__init__.py +0 -0
  119. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/mcp/tools/base.py +0 -0
  120. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/observability/__init__.py +0 -0
  121. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/observability/logging.py +0 -0
  122. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/polars_utils.py +0 -0
  123. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/py.typed +0 -0
  124. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/service_layer/__init__.py +0 -0
  125. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/service_layer/discovery_service.py +0 -0
  126. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/service_layer/query_admin_service.py +0 -0
  127. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/src/spice_mcp/service_layer/query_service.py +0 -0
  128. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/cassettes/.gitkeep +0 -0
  129. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/config/environments.yaml +0 -0
  130. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/config/test_queries.yaml +0 -0
  131. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/conftest.py +0 -0
  132. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/fastmcp/test_resources_and_validation.py +0 -0
  133. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/fastmcp/test_server_mcp_extras.py +0 -0
  134. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/http_stubbed/test_age.py +0 -0
  135. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/http_stubbed/test_errors.py +0 -0
  136. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/http_stubbed/test_pagination.py +0 -0
  137. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/integration/__init__.py +0 -0
  138. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/live/test_live_basic.py +0 -0
  139. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/live/test_live_sui.py +0 -0
  140. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/mcp/conftest.py +0 -0
  141. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/offline/test_cache.py +0 -0
  142. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/offline/test_cache_consistency.py +0 -0
  143. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/offline/test_discovery.py +0 -0
  144. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/offline/test_dune_adapter.py +0 -0
  145. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/offline/test_edge_cases.py +0 -0
  146. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/offline/test_parsing.py +0 -0
  147. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/offline/test_query_history.py +0 -0
  148. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/offline/test_timeout.py +0 -0
  149. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/offline/test_typing_utils.py +0 -0
  150. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/offline/test_urls.py +0 -0
  151. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/property/__init__.py +0 -0
  152. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/property/test_property_based.py +0 -0
  153. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/style/test_polars_lazy.py +0 -0
  154. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/support/api_client.py +0 -0
  155. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/support/helpers.py +0 -0
  156. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/support/query_factory.py +0 -0
  157. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/support/test_data.py +0 -0
  158. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/tools/test_execute_query_tool.py +0 -0
  159. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/tools/test_health_tool.py +0 -0
  160. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/tools/test_query_service.py +0 -0
  161. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/tools/test_schemas.py +0 -0
  162. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/validation/__init__.py +0 -0
  163. {spice_mcp-0.1.3 → spice_mcp-0.1.5}/tests/validation/test_error_messages.py +0 -0
@@ -24,3 +24,5 @@ debug_api.py
24
24
 
25
25
  # Local testing environment
26
26
  test_spice_mcp/
27
+ scripts/
28
+ test_mcp_cursor/
@@ -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.
@@ -0,0 +1,112 @@
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
+ **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.
11
+
12
+ ## Why spice-mcp?
13
+
14
+ - **Agent-friendly**: Designed for AI agents using the Model Context Protocol (MCP)
15
+ - **High-Quality Discovery**: Leverages Dune Spellbook's GitHub repository to find verified, production-ready tables with rich metadata
16
+ - **Efficient**: Polars-first pipeline keeps data lazy until needed, reducing memory usage
17
+ - **Discovery**: Built-in tools to explore Dune's extensive blockchain datasets from both Dune API and Spellbook
18
+ - **Type-safe**: Fully typed parameters and responses with FastMCP
19
+ - **Reproducible**: Automatic query history logging and SQL artifact storage
20
+
21
+ ## Quick Start
22
+
23
+ 1. **Install**:
24
+ ```bash
25
+ uv pip install spice-mcp
26
+ ```
27
+
28
+ 2. **Set API key** (choose one method):
29
+ - **Option A**: Create a `.env` file in your project root:
30
+ ```bash
31
+ echo "DUNE_API_KEY=your-api-key-here" > .env
32
+ ```
33
+ - **Option B**: Export in your shell:
34
+ ```bash
35
+ export DUNE_API_KEY=your-api-key-here
36
+ ```
37
+
38
+ 3. **Use with Cursor IDE**:
39
+ Add to Cursor Settings → MCP Servers:
40
+ ```json
41
+ {
42
+ "name": "spice-mcp",
43
+ "command": "spice-mcp",
44
+ "env": {
45
+ "DUNE_API_KEY": "your-dune-api-key-here"
46
+ }
47
+ }
48
+ ```
49
+
50
+ **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.
51
+
52
+ ## Core Tools
53
+
54
+ | Tool | Description | Key Parameters |
55
+ |------|-------------|----------------|
56
+ | `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) |
57
+ | `dune_query_info` | Get metadata for a saved query | `query` (str - ID or URL) |
58
+ | `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) |
59
+ | `dune_describe_table` | Get column metadata for a table | `schema` (str), `table` (str) |
60
+ | `dune_health_check` | Verify API key and configuration | (no parameters) |
61
+ | `dune_query_create` | Create a new saved query | `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
62
+ | `dune_query_update` | Update an existing saved query | `query_id` (int), `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
63
+ | `dune_query_fork` | Fork an existing saved query | `source_query_id` (int), `name` (str) |
64
+
65
+ ## Resources
66
+
67
+ - `spice:history/tail/{n}` — View last N lines of query history (1-1000)
68
+ - `spice:artifact/{sha}` — Retrieve stored SQL by SHA-256 hash
69
+
70
+ ## What is Dune?
71
+
72
+ [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.
73
+
74
+ ## What is Dune Spellbook?
75
+
76
+ [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:
77
+
78
+ - **Verified**: All tables are verified to exist in Dune before being returned
79
+ - **Well-documented**: Rich metadata including column descriptions and types
80
+ - **Maintained**: Regularly updated by the Dune community and team
81
+ - **Production-ready**: Used by analysts and dashboards across the ecosystem
82
+
83
+ 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.
84
+
85
+ ## Installation
86
+
87
+ **From PyPI** (recommended):
88
+ ```bash
89
+ uv pip install spice-mcp
90
+ ```
91
+
92
+ **From source**:
93
+ ```bash
94
+ git clone https://github.com/Evan-Kim2028/spice-mcp.git
95
+ cd spice-mcp
96
+ uv sync
97
+ uv pip install -e .
98
+ ```
99
+
100
+ **Requirements**: Python 3.13+
101
+
102
+ ## Documentation
103
+
104
+ - [Tool Reference](docs/tools.md) — Complete tool documentation with parameters
105
+ - [Architecture](docs/architecture.md) — Code structure and design patterns
106
+ - [Discovery Guide](docs/discovery.md) — How to explore Dune schemas and tables
107
+ - [Dune API Guide](docs/dune_api.md) — Understanding Dune's data structure
108
+ - [Configuration](docs/config.md) — Environment variables and settings
109
+
110
+ ## License
111
+
112
+ See [LICENSE](LICENSE) file for details.
@@ -54,8 +54,8 @@ 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 schemas
58
- - `mcp__spice_mcp_beta__dune_find_tables {"keyword": "dex"}`
57
+ - Find schemas and tables
58
+ - `mcp__spice_mcp_beta__dune_discover {"keyword": "dex", "source": "dune"}`
59
59
  - Describe a table
60
60
  - `mcp__spice_mcp_beta__dune_describe_table {"schema": "dex", "table": "trades"}`
61
61
  - Query preview (with metadata/pagination)
@@ -2,20 +2,24 @@ Catalog Discovery
2
2
 
3
3
  Summary
4
4
  - There is no public REST endpoint to browse the full catalog. Discovery is best achieved using Dune SQL primitives and fallback probes.
5
+ - **Native SHOW statements are preferred** - they're faster than information_schema queries. See issue #10 for details.
5
6
 
6
7
  Approach
7
8
  - Schemas
8
9
  - SHOW SCHEMAS
9
10
  - SHOW SCHEMAS LIKE '%keyword%'
11
+ - ⚠️ Avoid: information_schema.schemata (slower, causes lag)
10
12
  - Tables
11
13
  - SHOW TABLES FROM <schema>
12
14
  - If SHOW is blocked, probe candidate names via SELECT 1 FROM <schema>.<table> LIMIT 1
15
+ - ⚠️ Avoid: information_schema.tables (slower, causes lag)
13
16
  - Columns
14
17
  - SHOW COLUMNS FROM <schema>.<table>
15
18
  - Fallback: SELECT * FROM <schema>.<table> LIMIT 1, infer columns and Polars dtypes client-side
16
- - INFORMATION_SCHEMA
17
- - Some deployments allow: information_schema.schemata/tables/columns
18
- - If blocked, use SHOW + probes
19
+ - INFORMATION_SCHEMA (Deprecated)
20
+ - Previously used for portability, but causes performance issues
21
+ - Native SHOW statements are now used directly (faster, no lag)
22
+ - Kept as fallback only if SHOW is blocked
19
23
 
20
24
 
21
25
  Helpers in this repo
@@ -23,5 +27,8 @@ Helpers in this repo
23
27
  - `find_schemas(keyword)`, `list_tables(schema, limit)`, `describe_table(schema, table)`
24
28
 
25
29
  MCP Tools
26
- - dune_find_tables: search schemas and list tables
30
+ - dune_discover: **PRIMARY discovery tool** - unified search across Dune API and Spellbook, returns verified tables only
31
+ - Automatically parses dbt configs from Spellbook models to resolve actual Dune table names
32
+ - Verifies tables exist in Dune before returning (uses persistent cache)
33
+ - Filters out non-existent tables
27
34
  - dune_describe_table: describe columns with SHOW + fallback
@@ -0,0 +1,118 @@
1
+ Tools Reference
2
+
3
+ All tools are exposed by the MCP server started with `spice-mcp`.
4
+
5
+ 1) dune_discover
6
+ - Purpose: **PRIMARY discovery tool** for finding tables in Dune. Searches both Dune API and Spellbook repository. Returns ONLY verified, queryable tables.
7
+ - ⚠️ **IMPORTANT**: Always use this tool instead of querying `information_schema` directly (which is slow and causes lag).
8
+ - Input schema:
9
+ - keyword?: string | string[] — search term(s) to find schemas/tables (e.g., "walrus", ["layerzero", "dex"])
10
+ - schema?: string — schema name to list tables from (e.g., "dex", "sui_walrus")
11
+ - limit?: integer (default 50) — maximum number of tables to return
12
+ - source?: 'dune' | 'spellbook' | 'both' (default 'both') — where to search
13
+ - include_columns?: boolean (default true) — include column details for Spellbook models
14
+ - Output fields:
15
+ - schemas: string[] — matching schema names
16
+ - tables: array of table objects, each with:
17
+ - schema: string — Spellbook subproject name (for Spellbook models) or Dune schema name
18
+ - table: string — Spellbook model name (for Spellbook models) or Dune table name
19
+ - fully_qualified_name: string — schema.table format
20
+ - source: 'dune' | 'spellbook'
21
+ - dune_schema?: string — actual Dune schema name (for Spellbook models, parsed from dbt config)
22
+ - dune_alias?: string — actual Dune table alias (for Spellbook models, parsed from dbt config)
23
+ - dune_table?: string — verified, queryable Dune table name (e.g., "sui_walrus.base_table")
24
+ - verified?: boolean — true (all returned tables are verified to exist)
25
+ - columns?: array — column details (if include_columns=true)
26
+ - source: string — the source parameter used
27
+ - message?: string — helpful message if no tables found
28
+ - Features:
29
+ - Automatically parses dbt configs from Spellbook models to resolve actual Dune table names
30
+ - Verifies tables exist in Dune before returning (uses persistent cache)
31
+ - Filters out non-existent tables
32
+ - Examples:
33
+ - Search for walrus tables (returns verified tables only):
34
+ - `dune_discover {"keyword":"walrus"}`
35
+ - → Returns tables with `dune_table` field like "sui_walrus.base_table"
36
+ - Use discovered table to query:
37
+ - `dune_query {"query":"SELECT * FROM sui_walrus.base_table LIMIT 10"}`
38
+ - Search only Spellbook:
39
+ - `dune_discover {"keyword":["layerzero","bridge"],"source":"spellbook"}`
40
+ - List tables in a schema:
41
+ - `dune_discover {"schema":"dex"}`
42
+
43
+ 2) dune_query
44
+ - Purpose: Execute Dune queries (ID, URL, raw SQL) and return a compact preview plus Dune metadata/pagination hints.
45
+ - ⚠️ **IMPORTANT**: Always use `dune_discover` FIRST to find verified table names. Do not guess table names or query `information_schema` directly.
46
+ - Input schema:
47
+ - query: string (required) — Query ID, URL, or raw SQL using tables from `dune_discover`
48
+ - parameters?: object
49
+ - performance?: 'medium' | 'large'
50
+ - limit?: integer, offset?: integer, sort_by?: string, columns?: string[], sample_count?: integer
51
+ - refresh?: boolean, max_age?: number, timeout_seconds?: number
52
+ - format?: 'preview' | 'raw' | 'metadata' | 'poll' (preview by default)
53
+ - extras?: object (e.g., allow_partial_results, ignore_max_datapoints_per_request)
54
+ - Output fields:
55
+ - type: 'preview' | 'metadata' | 'raw' | 'execution'
56
+ - rowcount: number, columns: string[]
57
+ - data_preview: object[] (first rows)
58
+ - execution_id: string, duration_ms: number
59
+ - metadata?: structured Dune metadata / execution state / error hints
60
+ - next_uri?: string, next_offset?: number
61
+ - Errors: `{ "ok": false, "error": { code, message, data: { suggestions }, context? } }`
62
+ - Examples:
63
+ - Workflow: discover → query:
64
+ - `dune_discover {"keyword":"walrus"}` → get `dune_table="sui_walrus.base_table"`
65
+ - `dune_query {"query":"SELECT * FROM sui_walrus.base_table LIMIT 10"}`
66
+ - Preview latest metadata without rows:
67
+ - `dune_query {"query":"4388","format":"metadata"}`
68
+ - Preview first rows and metadata:
69
+ - `dune_query {"query":"4388","limit":10}`
70
+
71
+ Logging & Artifacts
72
+ - Successful calls are written to a JSONL audit log (see `docs/config.md` for path configuration via `QueryHistory`).
73
+ - The canonical SQL is stored as a deduplicated artefact keyed by SHA‑256 (for raw SQL and query IDs/URLs), enabling reproducibility and offline review.
74
+ - Result caching is handled by `adapters.dune.cache` (parquet files) and can be tuned via `SPICE_CACHE_*` environment variables.
75
+
76
+
77
+ 3) dune_describe_table
78
+ - Purpose: Describe columns for a schema.table (SHOW + fallback to 1-row SELECT inference).
79
+ - Input schema:
80
+ - schema: string
81
+ - table: string
82
+ - Output fields:
83
+ - columns: [{ name, dune_type?, polars_dtype?, extra?, comment? }]
84
+ - Errors follow the standard MCP envelope.
85
+
86
+ 3) dune_health_check
87
+ - Purpose: Basic environment and logging readiness check.
88
+ - Output fields: ok, api_key_present, status
89
+
90
+ 4) dune_query_info
91
+ - Purpose: Fetch Dune query object metadata (name, description, tags, parameter schema, SQL).
92
+ - Input schema:
93
+ - query: string — ID or URL
94
+ - Output fields:
95
+ - ok: boolean, status: number, query_id: number
96
+ - name?: string, description?: string, tags?: string[], parameters?: object[], version?: number, query_sql?: string
97
+
98
+ 5) dune_query_create
99
+ - Purpose: Create a saved Dune query.
100
+ - Input schema:
101
+ - name: string (required)
102
+ - query_sql: string (required)
103
+ - description?: string, tags?: string[], parameters?: object[]
104
+ - Output: Dune query object
105
+
106
+ 6) dune_query_update
107
+ - Purpose: Update a saved Dune query.
108
+ - Input schema:
109
+ - query_id: integer (required)
110
+ - name?: string, query_sql?: string, description?: string, tags?: string[], parameters?: object[]
111
+ - Output: Dune query object
112
+
113
+ 7) dune_query_fork
114
+ - Purpose: Fork an existing saved Dune query.
115
+ - Input schema:
116
+ - source_query_id: integer (required)
117
+ - name?: string (new name)
118
+ - Output: Dune query object
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "spice-mcp"
3
- version = "0.1.3"
4
- description = "MCP server for Dune Analytics data access"
3
+ version = "0.1.5"
4
+ description = "mcp server built ontop of dune api endpoint"
5
5
  authors = [
6
6
  { name = "Evan-Kim2028", email = "ekcopersonal@gmail.com" }
7
7
  ]
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
- import re
5
4
  import time
6
5
  from collections.abc import Mapping, Sequence
7
6
  from typing import Any
@@ -24,6 +23,10 @@ from ...polars_utils import collect_preview
24
23
  from ..http_client import HttpClient, HttpClientConfig
25
24
  from . import extract, urls
26
25
 
26
+ # Use wrapper to avoid FastMCP detecting overloads in extract.query()
27
+ # Note: We still import extract for _determine_input_type and other non-overloaded functions
28
+ from .query_wrapper import execute_query as _execute_dune_query
29
+
27
30
 
28
31
  class DuneAdapter(QueryExecutor, CatalogExplorer):
29
32
  """Thin façade around the vendored extract module."""
@@ -43,11 +46,10 @@ class DuneAdapter(QueryExecutor, CatalogExplorer):
43
46
  self._ensure_api_key()
44
47
  start = time.time()
45
48
  q = request.query
46
- if isinstance(q, str):
47
- q_rewritten = _maybe_rewrite_show_sql(q)
48
- if q_rewritten is not None:
49
- q = q_rewritten
50
- result = extract.query(
49
+ # Use native SHOW statements directly - they're faster than information_schema queries
50
+ # See issue #10: https://github.com/Evan-Kim2028/spice-mcp/issues/10
51
+ # Removed rewrite to avoid performance issues with information_schema queries
52
+ result = _execute_dune_query(
51
53
  query_or_execution=q,
52
54
  verbose=False,
53
55
  refresh=request.refresh,
@@ -123,9 +125,10 @@ class DuneAdapter(QueryExecutor, CatalogExplorer):
123
125
  pass
124
126
 
125
127
  url = urls.get_query_results_url(query_id, parameters=params, csv=False)
128
+ from .user_agent import get_user_agent
126
129
  headers = {
127
130
  "X-Dune-API-Key": self._api_key(),
128
- "User-Agent": extract.get_user_agent(),
131
+ "User-Agent": get_user_agent(),
129
132
  }
130
133
  try:
131
134
  resp = self._http.request("GET", url, headers=headers)
@@ -195,9 +198,11 @@ class DuneAdapter(QueryExecutor, CatalogExplorer):
195
198
  # Internal helpers --------------------------------------------------------------
196
199
  def _run_sql(self, sql: str, *, limit: int | None = None) -> pl.DataFrame:
197
200
  self._ensure_api_key()
198
- sql_eff = _maybe_rewrite_show_sql(sql) or sql
199
- df = extract.query(
200
- sql_eff,
201
+ # Use native SHOW statements directly - they're faster than information_schema queries
202
+ # See issue #10: https://github.com/Evan-Kim2028/spice-mcp/issues/10
203
+ sql_eff = sql
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,
@@ -228,28 +233,12 @@ def _build_preview(lf: pl.LazyFrame, columns: list[str], rowcount: int) -> Resul
228
233
 
229
234
 
230
235
  def _maybe_rewrite_show_sql(sql: str) -> str | None:
231
- """Rewrite certain SHOW statements to information_schema SELECTs for portability.
232
-
233
- This allows running discovery-style commands through the parameterized raw SQL
234
- template which expects SELECT statements.
236
+ """DEPRECATED: This function is no longer used.
237
+
238
+ Native SHOW statements are now used directly as they're faster than
239
+ information_schema queries in Dune. See issue #10 for details.
240
+
241
+ This function is kept for backward compatibility but is not called.
235
242
  """
236
- s = sql.strip()
237
- m = re.match(r"^SHOW\s+SCHEMAS\s+LIKE\s+'([^']+)'\s*;?$", s, flags=re.IGNORECASE)
238
- if m:
239
- pat = m.group(1)
240
- return (
241
- "SELECT schema_name AS Schema FROM information_schema.schemata "
242
- f"WHERE schema_name LIKE '{pat}'"
243
- )
244
- if re.match(r"^SHOW\s+SCHEMAS\s*;?$", s, flags=re.IGNORECASE):
245
- return "SELECT schema_name AS Schema FROM information_schema.schemata"
246
-
247
- m = re.match(r"^SHOW\s+TABLES\s+FROM\s+([A-Za-z0-9_\.]+)\s*;?$", s, flags=re.IGNORECASE)
248
- if m:
249
- schema = m.group(1)
250
- return (
251
- "SELECT table_name AS Table FROM information_schema.tables "
252
- f"WHERE table_schema = '{schema}'"
253
- )
254
-
243
+ # Function body kept for reference but not executed
255
244
  return None
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import io
4
+ import os
4
5
  import time
5
6
  from typing import TYPE_CHECKING, overload
6
- import os
7
7
 
8
8
  from ..http_client import HttpClient
9
9
  from . import cache as _cache
@@ -289,6 +289,14 @@ def query(
289
289
 
290
290
  # execute or retrieve query
291
291
  if query_id:
292
+ # Check if this is a parameterized query (raw SQL via template or parameterized query)
293
+ # For parameterized queries, results don't exist until execution, so skip GET attempt
294
+ is_parameterized = (
295
+ parameters is not None
296
+ and len(parameters) > 0
297
+ and ('query' in parameters or any(k != 'query' for k in parameters))
298
+ )
299
+
292
300
  if cache and load_from_cache and not refresh:
293
301
  cache_result, cache_execution = _cache.load_from_cache(
294
302
  execute_kwargs, result_kwargs, output_kwargs
@@ -301,7 +309,8 @@ def query(
301
309
  age = get_query_latest_age(**execute_kwargs, verbose=verbose) # type: ignore
302
310
  if age is None or age > max_age:
303
311
  refresh = True
304
- if not refresh:
312
+ # Skip GET results attempt for parameterized queries - they need execution first
313
+ if not refresh and not is_parameterized:
305
314
  df = get_results(**execute_kwargs, **result_kwargs)
306
315
  if df is not None:
307
316
  return process_result(df, execution, **output_kwargs)
@@ -334,9 +343,44 @@ def query(
334
343
  return execution
335
344
 
336
345
 
337
- @overload
338
- @overload
339
- @overload
346
+ if TYPE_CHECKING:
347
+ @overload
348
+ def _process_result(
349
+ df: pl.DataFrame,
350
+ execution: Execution | None,
351
+ execute_kwargs: ExecuteKwargs,
352
+ result_kwargs: RetrievalKwargs,
353
+ cache: bool,
354
+ save_to_cache: bool,
355
+ cache_dir: str | None,
356
+ include_execution: Literal[False],
357
+ ) -> pl.DataFrame: ...
358
+
359
+ @overload
360
+ def _process_result(
361
+ df: pl.DataFrame,
362
+ execution: Execution | None,
363
+ execute_kwargs: ExecuteKwargs,
364
+ result_kwargs: RetrievalKwargs,
365
+ cache: bool,
366
+ save_to_cache: bool,
367
+ cache_dir: str | None,
368
+ include_execution: Literal[True],
369
+ ) -> tuple[pl.DataFrame, Execution]: ...
370
+
371
+ @overload
372
+ def _process_result(
373
+ df: pl.DataFrame,
374
+ execution: Execution | None,
375
+ execute_kwargs: ExecuteKwargs,
376
+ result_kwargs: RetrievalKwargs,
377
+ cache: bool,
378
+ save_to_cache: bool,
379
+ cache_dir: str | None,
380
+ include_execution: bool,
381
+ ) -> pl.DataFrame | tuple[pl.DataFrame, Execution]: ...
382
+
383
+
340
384
  def _process_result(
341
385
  df: pl.DataFrame,
342
386
  execution: Execution | None,