spice-mcp 0.1.5__tar.gz → 0.1.6__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 (102) hide show
  1. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/PKG-INFO +1 -1
  2. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/pyproject.toml +1 -1
  3. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/spellbook/explorer.py +13 -16
  4. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/mcp/server.py +8 -5
  5. spice_mcp-0.1.6/tests/integration/test_dune_table_names.py +48 -0
  6. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/uv.lock +1 -1
  7. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/.gitignore +0 -0
  8. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/.python-version +0 -0
  9. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/CONTRIBUTING.md +0 -0
  10. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/LICENSE +0 -0
  11. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/README.md +0 -0
  12. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/docs/architecture.md +0 -0
  13. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/docs/codex_cli.md +0 -0
  14. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/docs/codex_cli_tools.md +0 -0
  15. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/docs/config.md +0 -0
  16. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/docs/development.md +0 -0
  17. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/docs/discovery.md +0 -0
  18. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/docs/dune_api.md +0 -0
  19. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/docs/index.md +0 -0
  20. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/docs/installation.md +0 -0
  21. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/docs/tools.md +0 -0
  22. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/pytest.ini +0 -0
  23. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/__init__.py +0 -0
  24. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/__init__.py +0 -0
  25. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/__init__.py +0 -0
  26. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/admin.py +0 -0
  27. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/cache.py +0 -0
  28. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/client.py +0 -0
  29. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/extract.py +0 -0
  30. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/helpers.py +0 -0
  31. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/query_wrapper.py +0 -0
  32. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/transport.py +0 -0
  33. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/types.py +0 -0
  34. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/typing_utils.py +0 -0
  35. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/urls.py +0 -0
  36. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/dune/user_agent.py +0 -0
  37. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/http_client.py +0 -0
  38. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/adapters/spellbook/__init__.py +0 -0
  39. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/config.py +0 -0
  40. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/core/__init__.py +0 -0
  41. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/core/errors.py +0 -0
  42. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/core/models.py +0 -0
  43. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/core/ports.py +0 -0
  44. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/logging/query_history.py +0 -0
  45. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/mcp/__init__.py +0 -0
  46. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/mcp/tools/__init__.py +0 -0
  47. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/mcp/tools/base.py +0 -0
  48. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/mcp/tools/execute_query.py +0 -0
  49. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/observability/__init__.py +0 -0
  50. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/observability/logging.py +0 -0
  51. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/polars_utils.py +0 -0
  52. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/py.typed +0 -0
  53. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/service_layer/__init__.py +0 -0
  54. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/service_layer/discovery_service.py +0 -0
  55. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/service_layer/query_admin_service.py +0 -0
  56. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/service_layer/query_service.py +0 -0
  57. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/src/spice_mcp/service_layer/verification_service.py +0 -0
  58. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/cassettes/.gitkeep +0 -0
  59. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/config/environments.yaml +0 -0
  60. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/config/test_queries.yaml +0 -0
  61. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/conftest.py +0 -0
  62. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/fastmcp/test_dune_query_schema_validation.py +0 -0
  63. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/fastmcp/test_resources_and_validation.py +0 -0
  64. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/fastmcp/test_server_fastmcp.py +0 -0
  65. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/fastmcp/test_server_mcp_extras.py +0 -0
  66. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/http_stubbed/test_age.py +0 -0
  67. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/http_stubbed/test_errors.py +0 -0
  68. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/http_stubbed/test_pagination.py +0 -0
  69. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/integration/__init__.py +0 -0
  70. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/integration/test_spellbook_discovery.py +0 -0
  71. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/integration/test_user_journeys.py +0 -0
  72. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/live/test_live_basic.py +0 -0
  73. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/live/test_live_sui.py +0 -0
  74. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/mcp/conftest.py +0 -0
  75. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/mcp/test_tool_contracts.py +0 -0
  76. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_cache.py +0 -0
  77. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_cache_consistency.py +0 -0
  78. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_discovery.py +0 -0
  79. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_dune_adapter.py +0 -0
  80. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_edge_cases.py +0 -0
  81. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_parsing.py +0 -0
  82. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_query_history.py +0 -0
  83. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_show_rewrite.py +0 -0
  84. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_timeout.py +0 -0
  85. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_typing_utils.py +0 -0
  86. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/offline/test_urls.py +0 -0
  87. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/property/__init__.py +0 -0
  88. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/property/test_property_based.py +0 -0
  89. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/style/test_polars_lazy.py +0 -0
  90. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/support/api_client.py +0 -0
  91. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/support/helpers.py +0 -0
  92. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/support/query_factory.py +0 -0
  93. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/support/test_data.py +0 -0
  94. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/tools/test_additional_mcp_tools.py +0 -0
  95. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/tools/test_dbt_config_verification.py +0 -0
  96. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/tools/test_execute_query_tool.py +0 -0
  97. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/tools/test_health_tool.py +0 -0
  98. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/tools/test_query_service.py +0 -0
  99. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/tools/test_schemas.py +0 -0
  100. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/tools/test_unified_discover.py +0 -0
  101. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/validation/__init__.py +0 -0
  102. {spice_mcp-0.1.5 → spice_mcp-0.1.6}/tests/validation/test_error_messages.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spice-mcp
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: mcp server built ontop of dune api endpoint
5
5
  Author-email: Evan-Kim2028 <ekcopersonal@gmail.com>
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "spice-mcp"
3
- version = "0.1.5"
3
+ version = "0.1.6"
4
4
  description = "mcp server built ontop of dune api endpoint"
5
5
  authors = [
6
6
  { name = "Evan-Kim2028", email = "ekcopersonal@gmail.com" }
@@ -349,13 +349,17 @@ class SpellbookExplorer(CatalogExplorer):
349
349
  return {}
350
350
 
351
351
  def _parse_sql_columns(self, sql_file: Path) -> list[TableColumn]:
352
- """Parse SQL file to extract column names from SELECT statements."""
352
+ """
353
+ Parse SQL file to extract column names from SELECT statements.
354
+
355
+ Note: This is a best-effort heuristic and may not be perfect for complex SQL.
356
+ For accurate column information, use Dune's DESCRIBE TABLE or query the actual table.
357
+ """
353
358
  try:
354
359
  with open(sql_file, encoding="utf-8") as f:
355
360
  sql = f.read()
356
361
 
357
- # Look for SELECT ... FROM patterns
358
- # Match: SELECT col1, col2, col3 FROM ...
362
+ # Look for SELECT ... FROM patterns (simple heuristic)
359
363
  select_match = re.search(
360
364
  r"SELECT\s+(.+?)\s+FROM",
361
365
  sql,
@@ -364,27 +368,20 @@ class SpellbookExplorer(CatalogExplorer):
364
368
 
365
369
  if select_match:
366
370
  cols_str = select_match.group(1)
367
- # Split by comma, but handle function calls and aliases
371
+ # Simple split - may not handle all nested cases perfectly
372
+ # This is OK since column info is optional and best-effort
368
373
  cols = []
369
374
  for col in cols_str.split(","):
370
375
  col = col.strip()
371
- # Extract column name (handle aliases: col AS alias -> col)
372
- if " AS " in col.upper():
373
- col = col.split(" AS ", 1)[0].strip()
374
- elif " " in col and not col.startswith("("):
375
- # Might be alias without AS
376
- parts = col.split()
377
- col = parts[0].strip()
378
-
379
- # Clean up function calls: function(col) -> col
380
- col = re.sub(r"^\w+\((.+)\)", r"\1", col)
376
+ # Basic cleanup - remove obvious SQL noise
377
+ col = col.split()[-1] if col else ""
381
378
  col = col.strip().strip('"').strip("'")
382
379
 
383
- if col and col not in ["*", "DISTINCT"]:
380
+ if col and col not in ["*", "DISTINCT", "FROM"]:
384
381
  cols.append(
385
382
  TableColumn(
386
383
  name=col,
387
- dune_type="VARCHAR", # Default, can't infer from SQL
384
+ dune_type="VARCHAR",
388
385
  polars_dtype="Utf8",
389
386
  )
390
387
  )
@@ -347,7 +347,7 @@ def _unified_discover_impl(
347
347
  schema: str | None = None,
348
348
  limit: int = 50,
349
349
  source: Literal["dune", "spellbook", "both"] = "both",
350
- include_columns: bool = True,
350
+ include_columns: bool = False,
351
351
  ) -> dict[str, Any]:
352
352
  """
353
353
  Unified discovery implementation that can search Dune API, Spellbook repo, or both.
@@ -496,7 +496,7 @@ def dune_discover(
496
496
  schema: str | None = None,
497
497
  limit: int = 50,
498
498
  source: Literal["dune", "spellbook", "both"] = "both",
499
- include_columns: bool = True,
499
+ include_columns: bool = False,
500
500
  ) -> dict[str, Any]:
501
501
  """
502
502
  PRIMARY discovery tool for finding tables in Dune.
@@ -520,7 +520,9 @@ def dune_discover(
520
520
  limit: Maximum number of tables to return
521
521
  source: Where to search - "dune" (Dune API only), "spellbook" (GitHub repo only),
522
522
  or "both" (default: searches both and merges results)
523
- include_columns: Whether to include column details for Spellbook models (default: True)
523
+ include_columns: Whether to include column details (default: False).
524
+ Note: Column info from Spellbook SQL is unreliable.
525
+ Use dune_describe_table on the actual Dune table for accurate columns.
524
526
 
525
527
  Returns:
526
528
  Dictionary with:
@@ -534,9 +536,10 @@ def dune_discover(
534
536
  - dune_alias: Actual Dune table alias (for Spellbook models)
535
537
  - dune_table: Verified, queryable Dune table name (e.g., "sui_walrus.base_table")
536
538
  - verified: True (all returned tables are verified to exist)
537
- - columns: Column details (for Spellbook models, if include_columns=True)
538
539
  - 'source': The source parameter used
539
540
  - 'message': Helpful message if no tables found
541
+
542
+ Note: To get accurate column information, use dune_describe_table on the dune_table value.
540
543
 
541
544
  Examples:
542
545
  # Search both sources for walrus - returns verified tables only
@@ -611,7 +614,7 @@ def _spellbook_find_models_impl(
611
614
  keyword: str | list[str] | None = None,
612
615
  schema: str | None = None,
613
616
  limit: int = 50,
614
- include_columns: bool = True,
617
+ include_columns: bool = False,
615
618
  ) -> dict[str, Any]:
616
619
  """
617
620
  Implementation for spellbook model discovery.
@@ -0,0 +1,48 @@
1
+ """Test that Spellbook discovery returns correct queryable Dune table names."""
2
+
3
+ import pytest
4
+
5
+
6
+ def test_walrus_table_discovery_returns_dune_table_names():
7
+ """Test that discovering Walrus tables returns correct dune_table field."""
8
+ from spice_mcp.mcp.server import _spellbook_find_models_impl
9
+
10
+ # Discover walrus models
11
+ result = _spellbook_find_models_impl(
12
+ keyword="walrus",
13
+ schema=None,
14
+ limit=10,
15
+ include_columns=False
16
+ )
17
+
18
+ assert "models" in result
19
+ walrus_models = result["models"]
20
+
21
+ # Should find at least the base_table and payments models
22
+ assert len(walrus_models) >= 2
23
+
24
+ # Check each model has the required fields
25
+ for model in walrus_models:
26
+ assert "dune_schema" in model
27
+ assert "dune_alias" in model
28
+ assert "dune_table" in model
29
+
30
+ # Verify format is schema.alias (not daily_spellbook.model_name)
31
+ dune_table = model["dune_table"]
32
+ assert "." in dune_table
33
+ assert not dune_table.startswith("daily_spellbook.")
34
+
35
+ # Verify specific known tables
36
+ if model["table"] == "sui_walrus_base_table":
37
+ assert model["dune_schema"] == "sui_walrus"
38
+ assert model["dune_alias"] == "base_table"
39
+ assert model["dune_table"] == "sui_walrus.base_table"
40
+ elif model["table"] == "sui_walrus_payments":
41
+ assert model["dune_schema"] == "sui_walrus"
42
+ assert model["dune_alias"] == "payments"
43
+ assert model["dune_table"] == "sui_walrus.payments"
44
+
45
+
46
+ if __name__ == "__main__":
47
+ pytest.main([__file__, "-v"])
48
+
@@ -1601,7 +1601,7 @@ wheels = [
1601
1601
 
1602
1602
  [[package]]
1603
1603
  name = "spice-mcp"
1604
- version = "0.1.4"
1604
+ version = "0.1.6"
1605
1605
  source = { editable = "." }
1606
1606
  dependencies = [
1607
1607
  { name = "aiohttp" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes