spice-mcp 0.1.4__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 (130) hide show
  1. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/.gitignore +2 -0
  2. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/PKG-INFO +18 -6
  3. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/README.md +16 -4
  4. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/docs/codex_cli.md +2 -2
  5. {spice_mcp-0.1.4 → 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.4 → spice_mcp-0.1.5}/pyproject.toml +2 -2
  8. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/client.py +13 -29
  9. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/spellbook/explorer.py +84 -1
  10. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/mcp/server.py +141 -106
  11. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/mcp/tools/execute_query.py +13 -21
  12. spice_mcp-0.1.5/src/spice_mcp/service_layer/verification_service.py +185 -0
  13. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/fastmcp/test_server_fastmcp.py +4 -4
  14. spice_mcp-0.1.5/tests/integration/test_spellbook_discovery.py +289 -0
  15. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/integration/test_user_journeys.py +6 -5
  16. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/mcp/test_tool_contracts.py +5 -4
  17. spice_mcp-0.1.5/tests/offline/test_show_rewrite.py +37 -0
  18. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/tools/test_additional_mcp_tools.py +14 -10
  19. spice_mcp-0.1.5/tests/tools/test_dbt_config_verification.py +169 -0
  20. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/tools/test_unified_discover.py +70 -0
  21. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/uv.lock +1 -1
  22. spice_mcp-0.1.4/docs/tools.md +0 -88
  23. spice_mcp-0.1.4/scripts/bridgez/make_circle_comparison.py +0 -114
  24. spice_mcp-0.1.4/scripts/codex_tools_doctor.sh +0 -104
  25. spice_mcp-0.1.4/test_mcp_cursor/.env.example +0 -9
  26. spice_mcp-0.1.4/test_mcp_cursor/CURSOR_SETUP.md +0 -159
  27. spice_mcp-0.1.4/test_mcp_cursor/INSTALL.md +0 -71
  28. spice_mcp-0.1.4/test_mcp_cursor/QUICK_START.md +0 -96
  29. spice_mcp-0.1.4/test_mcp_cursor/README.md +0 -38
  30. spice_mcp-0.1.4/test_mcp_cursor/TEST_RESULTS.md +0 -50
  31. spice_mcp-0.1.4/test_mcp_cursor/cursor_mcp_config.json +0 -11
  32. spice_mcp-0.1.4/test_mcp_cursor/setup_cursor_mcp.sh +0 -58
  33. spice_mcp-0.1.4/test_mcp_cursor/test_issue_8_scenarios.py +0 -335
  34. spice_mcp-0.1.4/test_mcp_cursor/test_real_api.py +0 -394
  35. spice_mcp-0.1.4/tests/integration/test_spellbook_discovery.py +0 -214
  36. spice_mcp-0.1.4/tests/offline/test_show_rewrite.py +0 -24
  37. spice_mcp-0.1.4/tests/scripts/comprehensive_test_runner.py +0 -164
  38. spice_mcp-0.1.4/tests/scripts/run_tests.py +0 -25
  39. spice_mcp-0.1.4/tests/scripts/test_api_health.py +0 -424
  40. spice_mcp-0.1.4/tests/scripts/test_cache_functionality.py +0 -197
  41. spice_mcp-0.1.4/tests/scripts/test_data_types.py +0 -727
  42. spice_mcp-0.1.4/tests/scripts/test_dune_connectivity.py +0 -93
  43. spice_mcp-0.1.4/tests/scripts/test_dune_query_execution.py +0 -170
  44. spice_mcp-0.1.4/tests/scripts/test_error_handling.py +0 -200
  45. spice_mcp-0.1.4/tests/scripts/test_mcp_simulation.py +0 -1166
  46. spice_mcp-0.1.4/tests/scripts/test_mcp_tools.py +0 -133
  47. spice_mcp-0.1.4/tests/scripts/test_performance.py +0 -654
  48. spice_mcp-0.1.4/tests/scripts/test_query_lifecycle.py +0 -534
  49. spice_mcp-0.1.4/tests/scripts/test_resilience.py +0 -945
  50. spice_mcp-0.1.4/tests/scripts/test_resource_management.py +0 -376
  51. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/.python-version +0 -0
  52. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/CONTRIBUTING.md +0 -0
  53. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/LICENSE +0 -0
  54. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/docs/architecture.md +0 -0
  55. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/docs/codex_cli_tools.md +0 -0
  56. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/docs/config.md +0 -0
  57. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/docs/development.md +0 -0
  58. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/docs/dune_api.md +0 -0
  59. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/docs/index.md +0 -0
  60. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/docs/installation.md +0 -0
  61. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/pytest.ini +0 -0
  62. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/__init__.py +0 -0
  63. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/__init__.py +0 -0
  64. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/__init__.py +0 -0
  65. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/admin.py +0 -0
  66. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/cache.py +0 -0
  67. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/extract.py +1 -1
  68. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/helpers.py +0 -0
  69. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/query_wrapper.py +1 -1
  70. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/transport.py +0 -0
  71. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/types.py +0 -0
  72. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/typing_utils.py +0 -0
  73. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/urls.py +0 -0
  74. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/dune/user_agent.py +0 -0
  75. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/http_client.py +0 -0
  76. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/adapters/spellbook/__init__.py +0 -0
  77. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/config.py +0 -0
  78. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/core/__init__.py +0 -0
  79. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/core/errors.py +0 -0
  80. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/core/models.py +0 -0
  81. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/core/ports.py +0 -0
  82. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/logging/query_history.py +0 -0
  83. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/mcp/__init__.py +0 -0
  84. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/mcp/tools/__init__.py +0 -0
  85. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/mcp/tools/base.py +0 -0
  86. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/observability/__init__.py +0 -0
  87. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/observability/logging.py +0 -0
  88. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/polars_utils.py +0 -0
  89. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/py.typed +0 -0
  90. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/service_layer/__init__.py +0 -0
  91. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/service_layer/discovery_service.py +0 -0
  92. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/service_layer/query_admin_service.py +0 -0
  93. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/src/spice_mcp/service_layer/query_service.py +0 -0
  94. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/cassettes/.gitkeep +0 -0
  95. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/config/environments.yaml +0 -0
  96. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/config/test_queries.yaml +0 -0
  97. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/conftest.py +0 -0
  98. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/fastmcp/test_dune_query_schema_validation.py +0 -0
  99. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/fastmcp/test_resources_and_validation.py +0 -0
  100. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/fastmcp/test_server_mcp_extras.py +0 -0
  101. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/http_stubbed/test_age.py +0 -0
  102. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/http_stubbed/test_errors.py +0 -0
  103. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/http_stubbed/test_pagination.py +0 -0
  104. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/integration/__init__.py +0 -0
  105. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/live/test_live_basic.py +0 -0
  106. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/live/test_live_sui.py +0 -0
  107. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/mcp/conftest.py +0 -0
  108. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/offline/test_cache.py +0 -0
  109. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/offline/test_cache_consistency.py +0 -0
  110. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/offline/test_discovery.py +0 -0
  111. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/offline/test_dune_adapter.py +0 -0
  112. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/offline/test_edge_cases.py +0 -0
  113. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/offline/test_parsing.py +0 -0
  114. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/offline/test_query_history.py +0 -0
  115. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/offline/test_timeout.py +0 -0
  116. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/offline/test_typing_utils.py +0 -0
  117. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/offline/test_urls.py +0 -0
  118. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/property/__init__.py +0 -0
  119. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/property/test_property_based.py +0 -0
  120. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/style/test_polars_lazy.py +0 -0
  121. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/support/api_client.py +0 -0
  122. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/support/helpers.py +0 -0
  123. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/support/query_factory.py +0 -0
  124. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/support/test_data.py +0 -0
  125. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/tools/test_execute_query_tool.py +0 -0
  126. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/tools/test_health_tool.py +0 -0
  127. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/tools/test_query_service.py +0 -0
  128. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/tools/test_schemas.py +0 -0
  129. {spice_mcp-0.1.4 → spice_mcp-0.1.5}/tests/validation/__init__.py +0 -0
  130. {spice_mcp-0.1.4 → 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/
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spice-mcp
3
- Version: 0.1.4
4
- Summary: MCP server for Dune Analytics data access
3
+ Version: 0.1.5
4
+ Summary: mcp server built ontop of dune api endpoint
5
5
  Author-email: Evan-Kim2028 <ekcopersonal@gmail.com>
6
6
  License-File: LICENSE
7
7
  Classifier: Operating System :: OS Independent
@@ -28,11 +28,14 @@ Description-Content-Type: text/markdown
28
28
 
29
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
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
+
31
33
  ## Why spice-mcp?
32
34
 
33
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
34
37
  - **Efficient**: Polars-first pipeline keeps data lazy until needed, reducing memory usage
35
- - **Discovery**: Built-in tools to explore Dune's extensive blockchain datasets
38
+ - **Discovery**: Built-in tools to explore Dune's extensive blockchain datasets from both Dune API and Spellbook
36
39
  - **Type-safe**: Fully typed parameters and responses with FastMCP
37
40
  - **Reproducible**: Automatic query history logging and SQL artifact storage
38
41
 
@@ -73,10 +76,8 @@ An MCP server that provides AI agents with direct access to [Dune Analytics](htt
73
76
  |------|-------------|----------------|
74
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) |
75
78
  | `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) |
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) |
78
80
  | `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
81
  | `dune_health_check` | Verify API key and configuration | (no parameters) |
81
82
  | `dune_query_create` | Create a new saved query | `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
82
83
  | `dune_query_update` | Update an existing saved query | `query_id` (int), `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
@@ -91,6 +92,17 @@ An MCP server that provides AI agents with direct access to [Dune Analytics](htt
91
92
 
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.
93
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
+
94
106
  ## Installation
95
107
 
96
108
  **From PyPI** (recommended):
@@ -7,11 +7,14 @@
7
7
 
8
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
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
+
10
12
  ## Why spice-mcp?
11
13
 
12
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
13
16
  - **Efficient**: Polars-first pipeline keeps data lazy until needed, reducing memory usage
14
- - **Discovery**: Built-in tools to explore Dune's extensive blockchain datasets
17
+ - **Discovery**: Built-in tools to explore Dune's extensive blockchain datasets from both Dune API and Spellbook
15
18
  - **Type-safe**: Fully typed parameters and responses with FastMCP
16
19
  - **Reproducible**: Automatic query history logging and SQL artifact storage
17
20
 
@@ -52,10 +55,8 @@ An MCP server that provides AI agents with direct access to [Dune Analytics](htt
52
55
  |------|-------------|----------------|
53
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) |
54
57
  | `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) |
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) |
57
59
  | `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
60
  | `dune_health_check` | Verify API key and configuration | (no parameters) |
60
61
  | `dune_query_create` | Create a new saved query | `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
61
62
  | `dune_query_update` | Update an existing saved query | `query_id` (int), `name` (str), `query_sql` (str), `description` (str), `tags` (list), `parameters` (list) |
@@ -70,6 +71,17 @@ An MCP server that provides AI agents with direct access to [Dune Analytics](htt
70
71
 
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.
72
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
+
73
85
  ## Installation
74
86
 
75
87
  **From PyPI** (recommended):
@@ -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.4"
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
@@ -47,10 +46,9 @@ class DuneAdapter(QueryExecutor, CatalogExplorer):
47
46
  self._ensure_api_key()
48
47
  start = time.time()
49
48
  q = request.query
50
- if isinstance(q, str):
51
- q_rewritten = _maybe_rewrite_show_sql(q)
52
- if q_rewritten is not None:
53
- q = q_rewritten
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
54
52
  result = _execute_dune_query(
55
53
  query_or_execution=q,
56
54
  verbose=False,
@@ -200,7 +198,9 @@ class DuneAdapter(QueryExecutor, CatalogExplorer):
200
198
  # Internal helpers --------------------------------------------------------------
201
199
  def _run_sql(self, sql: str, *, limit: int | None = None) -> pl.DataFrame:
202
200
  self._ensure_api_key()
203
- sql_eff = _maybe_rewrite_show_sql(sql) or sql
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
204
  df = _execute_dune_query(
205
205
  query_or_execution=sql_eff,
206
206
  verbose=False,
@@ -233,28 +233,12 @@ def _build_preview(lf: pl.LazyFrame, columns: list[str], rowcount: int) -> Resul
233
233
 
234
234
 
235
235
  def _maybe_rewrite_show_sql(sql: str) -> str | None:
236
- """Rewrite certain SHOW statements to information_schema SELECTs for portability.
237
-
238
- This allows running discovery-style commands through the parameterized raw SQL
239
- 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.
240
242
  """
241
- s = sql.strip()
242
- m = re.match(r"^SHOW\s+SCHEMAS\s+LIKE\s+'([^']+)'\s*;?$", s, flags=re.IGNORECASE)
243
- if m:
244
- pat = m.group(1)
245
- return (
246
- "SELECT schema_name AS Schema FROM information_schema.schemata "
247
- f"WHERE schema_name LIKE '{pat}'"
248
- )
249
- if re.match(r"^SHOW\s+SCHEMAS\s*;?$", s, flags=re.IGNORECASE):
250
- return "SELECT schema_name AS Schema FROM information_schema.schemata"
251
-
252
- m = re.match(r"^SHOW\s+TABLES\s+FROM\s+([A-Za-z0-9_\.]+)\s*;?$", s, flags=re.IGNORECASE)
253
- if m:
254
- schema = m.group(1)
255
- return (
256
- "SELECT table_name AS Table FROM information_schema.tables "
257
- f"WHERE table_schema = '{schema}'"
258
- )
259
-
243
+ # Function body kept for reference but not executed
260
244
  return None
@@ -133,11 +133,43 @@ class SpellbookExplorer(CatalogExplorer):
133
133
  if not schema_yml.exists():
134
134
  schema_yml = sql_file.parent.parent / "schema.yml"
135
135
 
136
+ # Parse dbt config to get actual Dune table name
137
+ config = self._parse_dbt_config(sql_file)
138
+
139
+ # Ignore templated dbt config values like "{{ target.schema }}"
140
+ def _is_templated(val: Any) -> bool:
141
+ try:
142
+ s = str(val)
143
+ except Exception:
144
+ return False
145
+ return "{{" in s and "}}" in s
146
+
147
+ raw_schema = config.get("schema")
148
+ raw_alias = config.get("alias")
149
+
150
+ dune_schema = (
151
+ raw_schema.strip() if isinstance(raw_schema, str) else raw_schema
152
+ )
153
+ dune_alias = (
154
+ raw_alias.strip() if isinstance(raw_alias, str) else raw_alias
155
+ )
156
+
157
+ # Fall back to original names when values are templated or empty
158
+ if not dune_schema or _is_templated(dune_schema):
159
+ dune_schema = schema_name
160
+ if not dune_alias or _is_templated(dune_alias):
161
+ dune_alias = model_name
162
+
163
+ dune_table = f"{dune_schema}.{dune_alias}"
164
+
136
165
  models[schema_name].append({
137
166
  "name": model_name,
138
167
  "file": sql_file,
139
168
  "schema_yml": schema_yml if schema_yml.exists() else None,
140
169
  "schema": schema_name,
170
+ "dune_schema": dune_schema,
171
+ "dune_alias": dune_alias,
172
+ "dune_table": dune_table,
141
173
  })
142
174
 
143
175
  self._models_cache = models
@@ -264,6 +296,58 @@ class SpellbookExplorer(CatalogExplorer):
264
296
 
265
297
  return []
266
298
 
299
+ def _parse_dbt_config(self, sql_file: Path) -> dict[str, str]:
300
+ """
301
+ Parse dbt config block from SQL file to extract schema and alias.
302
+
303
+ Looks for patterns like:
304
+ {{ config(schema='sui_walrus', alias='base_table') }}
305
+ {{ config(schema="sui_walrus", alias="base_table") }}
306
+
307
+ Returns dict with 'schema' and 'alias' keys, or empty dict if not found.
308
+ """
309
+ try:
310
+ with open(sql_file, encoding="utf-8") as f:
311
+ sql = f.read()
312
+
313
+ # Match dbt config block: {{ config(...) }}
314
+ # Use non-greedy match to get first config block
315
+ config_match = re.search(
316
+ r"{{\s*config\s*\((.*?)\)\s*}}",
317
+ sql,
318
+ re.IGNORECASE | re.DOTALL,
319
+ )
320
+
321
+ if not config_match:
322
+ return {}
323
+
324
+ config_content = config_match.group(1)
325
+ result: dict[str, str] = {}
326
+
327
+ # Extract schema parameter (supports single and double quotes)
328
+ schema_match = re.search(
329
+ r"schema\s*=\s*['\"]([^'\"]+)['\"]",
330
+ config_content,
331
+ re.IGNORECASE,
332
+ )
333
+ if schema_match:
334
+ result["schema"] = schema_match.group(1)
335
+
336
+ # Extract alias parameter (supports single and double quotes)
337
+ alias_match = re.search(
338
+ r"alias\s*=\s*['\"]([^'\"]+)['\"]",
339
+ config_content,
340
+ re.IGNORECASE,
341
+ )
342
+ if alias_match:
343
+ result["alias"] = alias_match.group(1)
344
+
345
+ return result
346
+ except Exception:
347
+ # On any error (file read, parsing, etc.), return empty dict
348
+ # This allows fallback to using schema_name and model_name
349
+ return {}
350
+
267
351
  def _parse_sql_columns(self, sql_file: Path) -> list[TableColumn]:
268
352
  """Parse SQL file to extract column names from SELECT statements."""
269
353
  try:
@@ -310,4 +394,3 @@ class SpellbookExplorer(CatalogExplorer):
310
394
  pass
311
395
 
312
396
  return []
313
-