datajunction 0.0.181__tar.gz → 0.0.183__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.
Files changed (184) hide show
  1. {datajunction-0.0.181 → datajunction-0.0.183}/PKG-INFO +1 -1
  2. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/__about__.py +1 -1
  3. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/cli.py +105 -37
  4. datajunction-0.0.183/datajunction/skills/datajunction-api.md +232 -0
  5. datajunction-0.0.183/datajunction/skills/datajunction-query.md +248 -0
  6. datajunction-0.0.183/datajunction/skills/datajunction-repo.md +692 -0
  7. datajunction-0.0.183/datajunction/skills/datajunction-semantic-model.md +379 -0
  8. datajunction-0.0.183/datajunction/skills/datajunction.md +253 -0
  9. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test_cli.py +42 -26
  10. datajunction-0.0.183/tests/test_skills.py +193 -0
  11. datajunction-0.0.181/datajunction/skills/datajunction.md +0 -1823
  12. {datajunction-0.0.181 → datajunction-0.0.183}/.coveragerc +0 -0
  13. {datajunction-0.0.181 → datajunction-0.0.183}/.gitignore +0 -0
  14. {datajunction-0.0.181 → datajunction-0.0.183}/.pre-commit-config.yaml +0 -0
  15. {datajunction-0.0.181 → datajunction-0.0.183}/LICENSE.txt +0 -0
  16. {datajunction-0.0.181 → datajunction-0.0.183}/Makefile +0 -0
  17. {datajunction-0.0.181 → datajunction-0.0.183}/README.md +0 -0
  18. {datajunction-0.0.181 → datajunction-0.0.183}/claude_desktop_config.example.json +0 -0
  19. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/__init__.py +0 -0
  20. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/_base.py +0 -0
  21. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/_internal.py +0 -0
  22. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/admin.py +0 -0
  23. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/builder.py +0 -0
  24. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/client.py +0 -0
  25. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/compile.py +0 -0
  26. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/deployment.py +0 -0
  27. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/exceptions.py +0 -0
  28. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/mcp/__init__.py +0 -0
  29. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/mcp/cli.py +0 -0
  30. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/mcp/config.py +0 -0
  31. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/models.py +0 -0
  32. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/nodes.py +0 -0
  33. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/rendering.py +0 -0
  34. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/init_system_nodes.py +0 -0
  35. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/date.yaml +0 -0
  36. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/deployment.yaml +0 -0
  37. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/dimension_link.yaml +0 -0
  38. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/distinct_node_authors.yaml +0 -0
  39. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/distinct_node_editors.yaml +0 -0
  40. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/dj.yaml +0 -0
  41. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/is_active.yaml +0 -0
  42. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/materialization.yaml +0 -0
  43. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/median_deployment_duration_seconds.yaml +0 -0
  44. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/median_dim_links_per_node.yaml +0 -0
  45. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/median_dimension_indegree.yaml +0 -0
  46. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/median_downstream_count.yaml +0 -0
  47. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/median_revisions_per_node.yaml +0 -0
  48. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/median_upstream_count.yaml +0 -0
  49. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/namespace.yaml +0 -0
  50. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/node_revision.yaml +0 -0
  51. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/node_type.yaml +0 -0
  52. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/nodes.yaml +0 -0
  53. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/number_of_branches.yaml +0 -0
  54. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/number_of_deployments.yaml +0 -0
  55. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/number_of_materializations.yaml +0 -0
  56. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/number_of_namespaces.yaml +0 -0
  57. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/number_of_nodes.yaml +0 -0
  58. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/number_of_orphan_nodes.yaml +0 -0
  59. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/number_of_unused_dimensions.yaml +0 -0
  60. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/repo.yaml +0 -0
  61. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/seed/nodes/user.yaml +0 -0
  62. {datajunction-0.0.181 → datajunction-0.0.183}/datajunction/tags.py +0 -0
  63. {datajunction-0.0.181 → datajunction-0.0.183}/pyproject.toml +0 -0
  64. {datajunction-0.0.181 → datajunction-0.0.183}/setup.cfg +0 -0
  65. {datajunction-0.0.181 → datajunction-0.0.183}/tests/__init__.py +0 -0
  66. {datajunction-0.0.181 → datajunction-0.0.183}/tests/conftest.py +0 -0
  67. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/deploy0/dj.yaml +0 -0
  68. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/deploy0/roads/companies.yaml +0 -0
  69. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/deploy0/roads/companies_dim.yaml +0 -0
  70. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/deploy0/roads/contractor.yaml +0 -0
  71. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/deploy0/roads/contractors.yaml +0 -0
  72. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/deploy0/roads/us_state.yaml +0 -0
  73. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/deploy0/roads/us_states.yaml +0 -0
  74. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/dj.yaml +0 -0
  75. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/avg_length_of_employment.metric.yaml +0 -0
  76. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/avg_repair_price.metric.yaml +0 -0
  77. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/avg_time_to_dispatch.metric.yaml +0 -0
  78. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/contractor.dimension.yaml +0 -0
  79. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/contractors.source.yaml +0 -0
  80. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/date.source.yaml +0 -0
  81. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/date_dim.dimension.yaml +0 -0
  82. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/dispatcher.dimension.yaml +0 -0
  83. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/dispatchers.source.yaml +0 -0
  84. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/hard_hat.dimension.yaml +0 -0
  85. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/hard_hat_state.source.yaml +0 -0
  86. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/hard_hats.source.yaml +0 -0
  87. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/local_hard_hats.dimension.yaml +0 -0
  88. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/municipality.source.yaml +0 -0
  89. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/municipality_dim.dimension.yaml +0 -0
  90. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/municipality_municipality_type.source.yaml +0 -0
  91. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/municipality_type.source.yaml +0 -0
  92. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/national_level_agg.transform.yaml +0 -0
  93. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/num_repair_orders.metric.yaml +0 -0
  94. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/regional_level_agg.transform.yaml +0 -0
  95. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/regional_repair_efficiency.metric.yaml +0 -0
  96. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/repair_order.dimension.yaml +0 -0
  97. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/repair_order_details.source.yaml +0 -0
  98. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/repair_order_transform.transform.yaml +0 -0
  99. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/repair_orders.source.yaml +0 -0
  100. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/repair_orders_cube.cube.yaml +0 -0
  101. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/repair_type.source.yaml +0 -0
  102. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/total_repair_cost.metric.yaml +0 -0
  103. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/total_repair_order_discounts.metric.yaml +0 -0
  104. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/us_region.source.yaml +0 -0
  105. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/us_state.dimension.yaml +0 -0
  106. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project1/roads/us_states.source.yaml +0 -0
  107. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project10/dj.yaml +0 -0
  108. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/avg_length_of_employment.metric.yaml +0 -0
  109. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/avg_repair_price.metric.yaml +0 -0
  110. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/avg_time_to_dispatch.metric.yaml +0 -0
  111. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/contractor.dimension.yaml +0 -0
  112. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/contractors.source.yaml +0 -0
  113. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/date.source.yaml +0 -0
  114. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/date_dim.dimension.yaml +0 -0
  115. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/dispatcher.dimension.yaml +0 -0
  116. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/dispatchers.source.yaml +0 -0
  117. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/dj.yaml +0 -0
  118. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/hard_hat.dimension.yaml +0 -0
  119. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/hard_hat_state.source.yaml +0 -0
  120. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/hard_hats.source.yaml +0 -0
  121. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/local_hard_hats.dimension.yaml +0 -0
  122. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/municipality.source.yaml +0 -0
  123. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/municipality_dim.dimension.yaml +0 -0
  124. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/municipality_municipality_type.source.yaml +0 -0
  125. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/municipality_type.source.yaml +0 -0
  126. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/national_level_agg.transform.yaml +0 -0
  127. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/num_repair_orders.metric.yaml +0 -0
  128. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/regional_level_agg.transform.yaml +0 -0
  129. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/regional_repair_efficiency.metric.yaml +0 -0
  130. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/repair_order.dimension.yaml +0 -0
  131. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/repair_order_details.source.yaml +0 -0
  132. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/repair_order_transform.transform.yaml +0 -0
  133. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/repair_orders.source.yaml +0 -0
  134. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/repair_orders_cube.cube.yaml +0 -0
  135. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/repair_type.source.yaml +0 -0
  136. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/total_repair_cost.metric.yaml +0 -0
  137. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/total_repair_order_discounts.metric.yaml +0 -0
  138. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/us_region.source.yaml +0 -0
  139. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/us_state.dimension.yaml +0 -0
  140. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project11/us_states.source.yaml +0 -0
  141. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project12/dj.yaml +0 -0
  142. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project12/roads/companies.source.yaml +0 -0
  143. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project12/roads/companies_dim.dimension.yaml +0 -0
  144. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project12/roads/contractor.dimension.yaml +0 -0
  145. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project12/roads/contractors.source.yaml +0 -0
  146. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project12/roads/us_state.dimension.yaml +0 -0
  147. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project12/roads/us_states.source.yaml +0 -0
  148. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project2/dj.yaml +0 -0
  149. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project2/some_node.source.yaml +0 -0
  150. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project3/dj.yaml +0 -0
  151. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project3/some_node.yaml +0 -0
  152. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project4/dj.yaml +0 -0
  153. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project4/very/very/deeply/nested/namespace/some_node.source.yaml +0 -0
  154. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project5/dj.yaml +0 -0
  155. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project5/some_node.a.b.c.source.yaml +0 -0
  156. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project6/dj.yaml +0 -0
  157. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project6/roads/contractor.dimension.yaml +0 -0
  158. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project6/roads/contractors.source.yaml +0 -0
  159. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project7/dj.yaml +0 -0
  160. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project7/roads/contractor.dimension.yaml +0 -0
  161. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project7/roads/contractors.source.yaml +0 -0
  162. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project8/dj.yaml +0 -0
  163. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project9/dj.yaml +0 -0
  164. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project9/roads/companies.source.yaml +0 -0
  165. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project9/roads/companies_dim.dimension.yaml +0 -0
  166. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project9/roads/contractor.dimension.yaml +0 -0
  167. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project9/roads/contractors.source.yaml +0 -0
  168. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project9/roads/us_state.dimension.yaml +0 -0
  169. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples/project9/roads/us_states.source.yaml +0 -0
  170. {datajunction-0.0.181 → datajunction-0.0.183}/tests/examples.py +0 -0
  171. {datajunction-0.0.181 → datajunction-0.0.183}/tests/mcp/README.md +0 -0
  172. {datajunction-0.0.181 → datajunction-0.0.183}/tests/mcp/__init__.py +0 -0
  173. {datajunction-0.0.181 → datajunction-0.0.183}/tests/mcp/test_cli.py +0 -0
  174. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test__internal.py +0 -0
  175. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test_admin.py +0 -0
  176. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test_base.py +0 -0
  177. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test_builder.py +0 -0
  178. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test_client.py +0 -0
  179. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test_compile.py +0 -0
  180. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test_deploy.py +0 -0
  181. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test_generated_client.py +0 -0
  182. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test_integration.py +0 -0
  183. {datajunction-0.0.181 → datajunction-0.0.183}/tests/test_models.py +0 -0
  184. {datajunction-0.0.181 → datajunction-0.0.183}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datajunction
3
- Version: 0.0.181
3
+ Version: 0.0.183
4
4
  Summary: DataJunction client library for connecting to a DataJunction server
5
5
  Project-URL: repository, https://github.com/DataJunction/dj
6
6
  Author-email: DataJunction Authors <yian.shang@gmail.com>
@@ -2,4 +2,4 @@
2
2
  Version for Hatch
3
3
  """
4
4
 
5
- __version__ = "0.0.181"
5
+ __version__ = "0.0.183"
@@ -5,7 +5,7 @@ import json
5
5
  import logging
6
6
  import os
7
7
  from pathlib import Path
8
- from typing import Optional
8
+ from typing import Any, Optional
9
9
 
10
10
  from rich import box
11
11
  from rich.console import Console, Group
@@ -1620,52 +1620,115 @@ class DJCLI:
1620
1620
  output_dir.mkdir(parents=True, exist_ok=True)
1621
1621
 
1622
1622
  console.print(
1623
- "[bold]📚 Installing DJ skill[/bold]\n",
1623
+ "[bold]📚 Installing DJ skills[/bold]\n",
1624
1624
  )
1625
1625
 
1626
- # Load bundled skill from package
1627
- dir_name = "datajunction"
1628
- skill_dir = output_dir / dir_name
1629
- skill_file = skill_dir / "SKILL.md"
1630
-
1631
- console.print(f"Installing [cyan]{dir_name}[/cyan]...")
1632
-
1633
- # Find bundled skill file
1634
1626
  from datajunction import __version__ as dj_version
1635
1627
  from importlib.resources import files
1636
1628
 
1637
- try:
1638
- # Read bundled skill markdown using importlib.resources (Python 3.9+)
1639
- skill_file_path = files("datajunction").joinpath(
1640
- "skills/datajunction.md",
1641
- )
1642
- bundled_skill = skill_file_path.read_text(encoding="utf-8")
1629
+ # All bundled DJ skills. ``datajunction`` is the core concepts
1630
+ # skill (always loaded); the others are audience-specific
1631
+ # extensions that compose on top of it.
1632
+ bundled_skills: list[dict[str, Any]] = [
1633
+ {
1634
+ "name": "datajunction",
1635
+ "filename": "datajunction.md",
1636
+ "description": "Core DataJunction concepts and vocabulary",
1637
+ "keywords": [
1638
+ "DataJunction",
1639
+ "DJ",
1640
+ "semantic layer",
1641
+ "dimension link",
1642
+ "star schema",
1643
+ "node types",
1644
+ ],
1645
+ },
1646
+ {
1647
+ "name": "datajunction-query",
1648
+ "filename": "datajunction-query.md",
1649
+ "description": "Querying DJ metrics via MCP tools and APIs",
1650
+ "keywords": [
1651
+ "query metric",
1652
+ "generate SQL",
1653
+ "get metric data",
1654
+ "search_nodes",
1655
+ "build_metric_sql",
1656
+ "common dimensions",
1657
+ ],
1658
+ },
1659
+ {
1660
+ "name": "datajunction-semantic-model",
1661
+ "filename": "datajunction-semantic-model.md",
1662
+ "description": "DJ semantic modeling: query-to-nodes decomposition, ratio decomposition, naming, ownership",
1663
+ "keywords": [
1664
+ "semantic modeling",
1665
+ "decompose query",
1666
+ "ratio metric",
1667
+ "derived metric",
1668
+ "base metric",
1669
+ "metric naming",
1670
+ "namespace organization",
1671
+ "node ownership",
1672
+ ],
1673
+ },
1674
+ {
1675
+ "name": "datajunction-repo",
1676
+ "filename": "datajunction-repo.md",
1677
+ "description": "Authoring DJ nodes via YAML in a git-backed repository",
1678
+ "keywords": [
1679
+ "YAML nodes",
1680
+ "repo-backed namespace",
1681
+ "feature branch",
1682
+ "git workflow",
1683
+ "cube YAML",
1684
+ "metric YAML",
1685
+ "temporal partition",
1686
+ ],
1687
+ },
1688
+ {
1689
+ "name": "datajunction-api",
1690
+ "filename": "datajunction-api.md",
1691
+ "description": "Direct REST API authoring of DJ nodes for exploration / prototyping",
1692
+ "keywords": [
1693
+ "DJ API",
1694
+ "REST API",
1695
+ "curl",
1696
+ "POST nodes",
1697
+ "API authoring",
1698
+ "prototyping",
1699
+ ],
1700
+ },
1701
+ ]
1702
+
1703
+ missing: list[str] = []
1704
+ for skill in bundled_skills:
1705
+ dir_name = skill["name"]
1706
+ skill_dir = output_dir / dir_name
1707
+ skill_file = skill_dir / "SKILL.md"
1708
+
1709
+ console.print(f"Installing [cyan]{dir_name}[/cyan]...")
1710
+
1711
+ try:
1712
+ skill_file_path = files("datajunction").joinpath(
1713
+ f"skills/{skill['filename']}",
1714
+ )
1715
+ bundled_skill = skill_file_path.read_text(encoding="utf-8")
1716
+ except FileNotFoundError: # pragma: no cover
1717
+ missing.append(skill["filename"])
1718
+ continue
1643
1719
 
1644
- # Create directory
1645
1720
  skill_dir.mkdir(parents=True, exist_ok=True)
1646
1721
 
1647
- # Write SKILL.md
1648
1722
  with open(skill_file, "w") as f:
1649
1723
  f.write(bundled_skill)
1650
1724
 
1651
- # Write metadata
1652
1725
  metadata_file = skill_dir / "metadata.json"
1653
1726
  with open(metadata_file, "w") as f:
1654
1727
  metadata = {
1655
- "name": "datajunction",
1728
+ "name": skill["name"],
1656
1729
  "version": dj_version,
1657
- "description": "Comprehensive DataJunction semantic layer guide",
1658
- "keywords": [
1659
- "DataJunction",
1660
- "DJ",
1661
- "semantic layer",
1662
- "dimension link",
1663
- "metric",
1664
- "SQL generation",
1665
- "YAML nodes",
1666
- "git workflow",
1667
- "repo-backed namespace",
1668
- ],
1730
+ "description": skill["description"],
1731
+ "keywords": skill["keywords"],
1669
1732
  "metadata": {
1670
1733
  "source": "bundled",
1671
1734
  "dj_version": dj_version,
@@ -1681,13 +1744,14 @@ class DJCLI:
1681
1744
  f" [dim]└─ metadata.json (v{dj_version})[/dim]\n",
1682
1745
  )
1683
1746
 
1747
+ if missing: # pragma: no cover
1684
1748
  console.print(
1685
- f"[bold green] Skill installed to {output_dir}[/bold green]\n",
1749
+ f"[red] Bundled skills not found: {', '.join(missing)}. "
1750
+ f"Please ensure datajunction is properly installed.[/red]",
1686
1751
  )
1687
-
1688
- except FileNotFoundError: # pragma: no cover
1752
+ else:
1689
1753
  console.print(
1690
- "[red] Bundled skill not found. Please ensure datajunction is properly installed.[/red]",
1754
+ f"[bold green] Skills installed to {output_dir}[/bold green]\n",
1691
1755
  )
1692
1756
 
1693
1757
  # Install subagent if requested
@@ -1704,9 +1768,13 @@ name: dj
1704
1768
  description: >
1705
1769
  DataJunction semantic layer expert. Use proactively for any DataJunction
1706
1770
  or DJ work — querying metrics, exploring nodes and dimensions, building
1707
- SQL, understanding lineage, and semantic layer design.
1771
+ SQL, understanding lineage, authoring metrics, and semantic layer design.
1708
1772
  skills:
1709
1773
  - datajunction
1774
+ - datajunction-query
1775
+ - datajunction-semantic-model
1776
+ - datajunction-repo
1777
+ - datajunction-api
1710
1778
  model: inherit
1711
1779
  ---
1712
1780
  """
@@ -0,0 +1,232 @@
1
+ ---
2
+ name: datajunction-api
3
+ description: |
4
+ Activate this skill when authoring DataJunction (DJ) nodes via the REST
5
+ API directly (curl, HTTP clients) — typically for exploration, ad-hoc
6
+ prototyping, or namespaces that aren't repo-backed. For modeling
7
+ decisions and the decomposition workflow, invoke `datajunction-semantic-model`.
8
+ For repo-backed YAML authoring (the production path), invoke
9
+ `datajunction-repo`. For concepts, invoke `datajunction`.
10
+ Keywords:
11
+ - DJ API, REST API, curl
12
+ - POST nodes/metric, POST nodes/dimension
13
+ - create metric, create dimension, create cube
14
+ - API approach, direct API changes
15
+ - prototyping, exploration
16
+ user-invocable: false
17
+ ---
18
+
19
+ # DataJunction Direct API Authoring
20
+
21
+ Direct REST API authoring for DJ nodes. Use this skill for quick exploration, prototyping, or working in namespaces that don't use the repo-backed workflow.
22
+
23
+ For the modeling work upstream of any authoring (decomposition, naming, ratio decomposition, etc.), see `datajunction-semantic-model`. For the production-path equivalent of these patterns in YAML, see `datajunction-repo`.
24
+
25
+ ## When to Use the API Approach
26
+
27
+ **✅ MUST use repo workflow instead** (`datajunction-repo`) for:
28
+ - Namespaces configured as repo-backed and read-only (`git_only: true`) — direct API changes are rejected
29
+
30
+ **✅ Should use repo workflow** for:
31
+ - Production changes (review required)
32
+ - Multi-node changes (related metrics/dimensions)
33
+ - Team environments (multiple contributors)
34
+ - Audit-trail requirements
35
+ - Complex refactoring
36
+
37
+ **✅ API workflow is appropriate** for:
38
+ - Quick exploration and prototyping
39
+ - Ad-hoc analysis
40
+ - Single-user, non-production namespaces
41
+ - Temporary metrics
42
+ - Non-production experiments
43
+ - Only when the target namespace is NOT read-only repo-backed
44
+
45
+ ---
46
+
47
+ ## Checking if a Namespace Is Repo-Backed
48
+
49
+ Before authoring via API, verify the target namespace allows it.
50
+
51
+ **Best approach — `get_node_details` MCP tool** (`datajunction-query` skill):
52
+
53
+ ```
54
+ get_node_details(name="finance.total_revenue")
55
+ ```
56
+
57
+ The response will include git repository information:
58
+ ```
59
+ Git Repository:
60
+ Repo: owner/dj-finance
61
+ Branch: main
62
+ Default Branch: main
63
+ → This namespace is repo-backed (use git workflow for changes)
64
+ ```
65
+
66
+ **Alternative — REST API** (shows read-only status):
67
+ ```bash
68
+ curl -b ~/.dj/cookies.txt -X GET $DJ_URL/namespaces/finance/git
69
+
70
+ # Response:
71
+ {
72
+ "github_repo_path": "owner/dj-finance",
73
+ "git_branch": "main",
74
+ "default_branch": "main",
75
+ "git_path": "nodes/",
76
+ "git_only": true ← If true, namespace is read-only (API changes blocked)
77
+ }
78
+ ```
79
+
80
+ **Decision tree:**
81
+ - **If git info is present AND `git_only: true`**: MUST use repo workflow (API changes will fail)
82
+ - **If git info is present AND `git_only: false`**: Can use either workflow
83
+ - **If git info is null**: Use API workflow (direct POST/PATCH)
84
+
85
+ ---
86
+
87
+ ## Creating a Metric
88
+
89
+ ### Metric Structure
90
+
91
+ ```sql
92
+ SELECT <aggregation_expression> AS <metric_name>
93
+ FROM <single_node>
94
+ ```
95
+
96
+ Metrics select a **single expression** from a **single source, transform, or dimension node**. They cannot contain WHERE clauses — use CASE WHEN instead. See `datajunction-semantic-model` for the modeling rationale.
97
+
98
+ ### Metric Metadata Fields
99
+
100
+ **Required:**
101
+ - `name` — Fully qualified metric name (e.g., `finance.total_revenue`)
102
+ - `query` — SQL aggregation expression
103
+
104
+ **Recommended:**
105
+ - `description` — Human-readable description
106
+ - `metric_metadata.direction` — `higher_is_better` / `lower_is_better` / `neutral`
107
+ - `metric_metadata.unit` — `dollar` / `unitless` (**⚠️ NOT `count`** — server rejects)
108
+ - `mode` — `draft` / `published`
109
+ - `required_dimensions` — Dimensions required for this metric to make sense
110
+ - `owners` — List of email addresses (prefer team emails)
111
+
112
+ ### Examples
113
+
114
+ **COUNT**:
115
+ ```bash
116
+ curl -b ~/.dj/cookies.txt -X POST $DJ_URL/nodes/metric/ \
117
+ -H 'Content-Type: application/json' \
118
+ -d '{
119
+ "name": "finance.num_transactions",
120
+ "description": "Total number of transactions",
121
+ "query": "SELECT COUNT(transaction_id) AS num_transactions FROM finance.transactions",
122
+ "owners": ["data-platform-team@company.com"],
123
+ "mode": "published"
124
+ }'
125
+ ```
126
+
127
+ **SUM**:
128
+ ```bash
129
+ curl -b ~/.dj/cookies.txt -X POST $DJ_URL/nodes/metric/ \
130
+ -H 'Content-Type: application/json' \
131
+ -d '{
132
+ "name": "finance.total_revenue",
133
+ "description": "Total revenue from all transactions",
134
+ "query": "SELECT SUM(amount_usd) AS total_revenue FROM finance.transactions",
135
+ "metric_metadata": {
136
+ "direction": "higher_is_better",
137
+ "unit": "dollar"
138
+ },
139
+ "owners": ["finance-data-team@company.com"],
140
+ "mode": "published"
141
+ }'
142
+ ```
143
+
144
+ **Conditional aggregation** (CASE WHEN, not WHERE):
145
+ ```bash
146
+ curl -b ~/.dj/cookies.txt -X POST $DJ_URL/nodes/metric/ \
147
+ -H 'Content-Type: application/json' \
148
+ -d '{
149
+ "name": "finance.completed_revenue",
150
+ "description": "Revenue from completed non-refund transactions",
151
+ "query": "
152
+ SELECT SUM(
153
+ CASE
154
+ WHEN status = '\''completed'\'' AND refund_flag = false
155
+ THEN amount_usd
156
+ ELSE 0
157
+ END
158
+ ) AS completed_revenue
159
+ FROM finance.transactions
160
+ ",
161
+ "metric_metadata": {
162
+ "direction": "higher_is_better",
163
+ "unit": "dollar"
164
+ },
165
+ "owners": ["finance-data-team@company.com"],
166
+ "mode": "published"
167
+ }'
168
+ ```
169
+
170
+ **Ratio over base metrics** (decompose first, then derive — see `datajunction-semantic-model`):
171
+ ```bash
172
+ # Step 1: create the base metrics (one curl each)
173
+ curl -X POST $DJ_URL/nodes/metric/ -d '{
174
+ "name": "finance.clicks",
175
+ "query": "SELECT COUNT_IF(event = '\''click'\'') FROM finance.events",
176
+ "owners": ["marketing@company.com"],
177
+ "mode": "published"
178
+ }'
179
+
180
+ curl -X POST $DJ_URL/nodes/metric/ -d '{
181
+ "name": "finance.impressions",
182
+ "query": "SELECT COUNT_IF(event = '\''impression'\'') FROM finance.events",
183
+ "owners": ["marketing@company.com"],
184
+ "mode": "published"
185
+ }'
186
+
187
+ # Step 2: derived ratio metric referencing the base metrics
188
+ curl -X POST $DJ_URL/nodes/metric/ -d '{
189
+ "name": "finance.conversion_rate",
190
+ "description": "Click-through rate as percentage",
191
+ "query": "SELECT finance.clicks * 100.0 / NULLIF(finance.impressions, 0)",
192
+ "metric_metadata": {
193
+ "direction": "higher_is_better",
194
+ "unit": "unitless"
195
+ },
196
+ "owners": ["marketing@company.com"],
197
+ "mode": "published"
198
+ }'
199
+ ```
200
+
201
+ DJ automatically handles divide-by-zero, but `NULLIF()` is extra safety.
202
+
203
+ ---
204
+
205
+ ## Creating Other Node Types via API
206
+
207
+ Same pattern as metrics — POST JSON to the appropriate endpoint:
208
+
209
+ - `POST /nodes/source/` — source nodes (catalog/schema/table refs)
210
+ - `POST /nodes/dimension/` — dimension nodes
211
+ - `POST /nodes/transform/` — transform nodes
212
+ - `POST /nodes/cube/` — cubes (metric + dimension combinations)
213
+
214
+ For the YAML-equivalent shapes of each, see `datajunction-repo` — the field set is the same, just expressed in JSON instead of YAML.
215
+
216
+ ---
217
+
218
+ ## Updating and Deleting Nodes
219
+
220
+ **Update** (PATCH):
221
+ ```bash
222
+ curl -b ~/.dj/cookies.txt -X PATCH $DJ_URL/nodes/finance.total_revenue/ \
223
+ -H 'Content-Type: application/json' \
224
+ -d '{"description": "Updated description"}'
225
+ ```
226
+
227
+ **Deactivate** (soft delete):
228
+ ```bash
229
+ curl -b ~/.dj/cookies.txt -X DELETE $DJ_URL/nodes/finance.total_revenue/
230
+ ```
231
+
232
+ Deactivated nodes can be revived. For hard-delete (irreversible), use the `dj` CLI: `dj delete-node finance.total_revenue --hard`.
@@ -0,0 +1,248 @@
1
+ ---
2
+ name: datajunction-query
3
+ description: |
4
+ Activate this skill for querying DataJunction (DJ) — finding nodes,
5
+ generating SQL, fetching metric data, exploring lineage, visualizing
6
+ results — via the DJ UI, MCP tools, or REST/GraphQL APIs. For core DJ
7
+ concepts and vocabulary, invoke `datajunction`. For modeling decisions
8
+ (what shape something should take), invoke `datajunction-semantic-model`.
9
+ For authoring nodes, invoke `datajunction-repo` (YAML) or
10
+ `datajunction-api` (REST).
11
+ Keywords:
12
+ - query metric, query metrics
13
+ - generate SQL, build metric SQL
14
+ - get metric data, fetch metric
15
+ - available dimensions, common dimensions
16
+ - search_nodes, get_node_details, get_node_lineage
17
+ - get_common, build_metric_sql, get_metric_data
18
+ - visualize metrics
19
+ - MCP tools, DJ API, GraphQL
20
+ - DJ UI, web UI, browse
21
+ user-invocable: false
22
+ ---
23
+
24
+ # DataJunction Query
25
+
26
+ Consumer-side workflow for DJ: find existing nodes, build SQL to query them, fetch data, visualize results. For the underlying concepts (node types, dimension links, star schema), invoke the `datajunction` skill.
27
+
28
+ ## DJ UI (Web)
29
+
30
+ For interactive exploration — browsing namespaces, inspecting a node's lineage / SQL / available dimensions, building queries by clicking dimensions on/off — the DJ web UI is usually the fastest path. It's hosted at the same URL as the DJ server (e.g. `https://your-dj-server.example.com/`).
31
+
32
+ **Use the UI when:**
33
+ - The user wants to *look around* — browse what exists, read a node's description, explore lineage visually
34
+ - They want to build a query interactively and copy the SQL out
35
+ - They're sharing a node or query with a teammate (UI URLs are shareable links)
36
+
37
+ **Use the MCP tools / API (below) when:**
38
+ - You need programmatic access — pulling node names into a script, generating SQL as part of a workflow, fetching data into a notebook
39
+ - The interaction is "give me this answer," not "let me explore"
40
+
41
+ Suggest opening the UI when the user's question is exploratory and you don't yet know the right node name. Suggest MCP tools when you have a name in hand and need data, lineage, or generated SQL.
42
+
43
+ ## Discovery & Exploration (Use MCP Tools)
44
+
45
+ **When you need to explore the DJ semantic layer, use these MCP tools:**
46
+
47
+ ### Find Available Nodes
48
+
49
+ **Use MCP tool**: `search_nodes`
50
+ - Search for metrics, dimensions, cubes by name or namespace
51
+ - Filter by node type
52
+ - Returns list of matching nodes
53
+
54
+ **Example**: `search_nodes(query="revenue", node_type="metric", namespace="finance")`
55
+
56
+ ### Get Node Details
57
+
58
+ **Use MCP tool**: `get_node_details`
59
+ - Get comprehensive information about a specific node
60
+ - Returns: SQL definition, description, available dimensions, lineage, tags
61
+ - Input: Full node name (e.g., "finance.total_revenue")
62
+
63
+ **Example**: `get_node_details(name="finance.total_revenue")`
64
+
65
+ **What you get**:
66
+ - Description and SQL definition
67
+ - All available dimensions (via dimension links)
68
+ - Metric metadata (unit, direction, required dimensions)
69
+ - Upstream dependencies
70
+ - Tags and collections
71
+
72
+ ### Check Common Dimensions
73
+
74
+ **Use MCP tool**: `get_common`
75
+ - Find dimensions shared across multiple metrics, or metrics shared across multiple dimensions (bidirectional)
76
+ - Essential before querying multiple metrics together
77
+ - Returns only entries shared by ALL of the specified inputs
78
+
79
+ **Example**: `get_common(metrics=["finance.total_revenue", "growth.daily_active_users"])`
80
+
81
+ **When to use**: Always check this before building queries with multiple metrics!
82
+
83
+ ### Get Node Lineage
84
+
85
+ **Use MCP tool**: `get_node_lineage`
86
+ - Get upstream dependencies (what data sources this uses)
87
+ - Get downstream dependencies (what will break if you change this)
88
+ - Direction: "upstream", "downstream", or "both"
89
+
90
+ **Example**: `get_node_lineage(node_name="finance.total_revenue", direction="both")`
91
+
92
+ ---
93
+
94
+ ## Querying & SQL Generation (Use MCP Tools)
95
+
96
+ **When you need to query metrics or generate SQL, use these MCP tools:**
97
+
98
+ ### Build Metric SQL
99
+
100
+ **Use MCP tool**: `build_metric_sql`
101
+ - Generate executable SQL for querying metrics
102
+ - Supports filters, dimensions, ordering, limits
103
+ - Returns SQL query and metadata
104
+
105
+ **Example**:
106
+ ```
107
+ build_metric_sql(
108
+ metrics=["finance.total_revenue"],
109
+ dimensions=["core.date.date"],
110
+ filters=["core.date.date >= '2024-01-01'"],
111
+ orderby=["core.date.date ASC"],
112
+ limit=100,
113
+ dialect="trino",
114
+ )
115
+ ```
116
+
117
+ **Returns**:
118
+ - Generated SQL for specified engine
119
+ - Output columns with types
120
+ - Cube name (if materialized cube used)
121
+
122
+ **Performance**: always pass a filter on a date/time dimension. When the upstream cube has a temporal partition declared on that dimension, DJ pushes the filter down to the partition column, limiting the data scanned. Without a time filter, queries scan the full underlying table.
123
+
124
+ (Don't pass `include_temporal_filters=True` here — that's a parameter on `get_query_plan` for inspecting partition templates in materialization-plan SQL, not for live queries.)
125
+
126
+ ### Get Metric Data
127
+
128
+ **Use MCP tool**: `get_metric_data`
129
+ - Execute query and get actual data
130
+ - Returns query results as rows
131
+ - **Scan-cost guardrail**: materialized cubes always run; ad-hoc queries
132
+ (e.g. against Trino) run only if DJ's scan estimate is under the safety
133
+ threshold. Over-threshold queries are refused up front and the estimate
134
+ is surfaced so you can narrow the query (tighter date range, fewer
135
+ dimensions, lower limit) and retry.
136
+
137
+ **Example**:
138
+ ```
139
+ get_metric_data(
140
+ metrics=["finance.total_revenue", "finance.transaction_count"],
141
+ dimensions=["core.date.date", "core.region.region_name"],
142
+ filters=["core.date.date >= '2024-01-01'"],
143
+ orderby=["core.date.date ASC"],
144
+ limit=1000
145
+ )
146
+ ```
147
+
148
+ **Best practices**:
149
+ - Always set a reasonable `limit`
150
+ - Use specific date range filters — they drive partition pushdown and
151
+ keep the scan estimate below the guardrail threshold
152
+ - Check common dimensions first for multi-metric queries
153
+ - If you're not sure how heavy a query will be, run `get_query_plan`
154
+ first (see below) — it returns the scan estimate without executing
155
+
156
+ ### Visualize Metrics
157
+
158
+ **Use MCP tool**: `visualize_metrics`
159
+ - Query metrics and generate ASCII chart visualization
160
+ - Creates terminal-friendly charts (line, bar, scatter)
161
+ - Same scan-cost guardrails as `get_metric_data`
162
+
163
+ **Example**:
164
+ ```
165
+ visualize_metrics(
166
+ metrics=["finance.total_revenue"],
167
+ dimensions=["core.date.date"],
168
+ filters=["core.date.date >= '2024-01-01'"],
169
+ orderby=["core.date.date ASC"],
170
+ limit=90,
171
+ chart_type="line"
172
+ )
173
+ ```
174
+
175
+ **Chart types**:
176
+ - `line`: Time series (default)
177
+ - `bar`: Categorical comparisons
178
+ - `scatter`: Correlation analysis
179
+
180
+ ### Pre-Flight: Get Query Plan
181
+
182
+ **Use MCP tool**: `get_query_plan`
183
+ - Returns how DJ decomposes the requested metrics into grain groups and
184
+ components, AND the scan estimate, without executing anything
185
+ - Use it before `get_metric_data` / `visualize_metrics` if you suspect
186
+ the query might be expensive — the same scan estimate that drives the
187
+ refusal guardrail is surfaced here, so you can iterate on filters
188
+ until the estimate is reasonable
189
+
190
+ **Example**:
191
+ ```
192
+ get_query_plan(
193
+ metrics=["finance.total_revenue"],
194
+ dimensions=["core.date.date"],
195
+ filters=["core.date.date >= '2024-01-01'"],
196
+ )
197
+ ```
198
+
199
+ **When to use**:
200
+ - "Will this query work?" — check before fetching
201
+ - "Why is this fetch refused?" — re-run the same args here to see the
202
+ estimate
203
+ - "What grain group will this go through?" — useful for understanding
204
+ multi-metric queries
205
+
206
+ ---
207
+
208
+ ## API Reference (Educational Context)
209
+
210
+ The MCP tools call the DJ REST and GraphQL APIs under the hood. Here's what they're doing:
211
+
212
+ ### REST API Endpoints
213
+
214
+ **Node discovery**:
215
+ ```bash
216
+ GET /nodes?node_type=metric&namespace=finance
217
+ GET /nodes/{node_name}
218
+ ```
219
+
220
+ **SQL generation** (⚠️ Always use V3):
221
+ ```bash
222
+ GET /sql/metrics/v3 # Generate query SQL
223
+ GET /sql/measures/v3 # Generate pre-aggregation SQL
224
+ ```
225
+
226
+ **Dimension compatibility**:
227
+ ```bash
228
+ GET /metrics/common/dimensions
229
+ ```
230
+
231
+ ### GraphQL API
232
+
233
+ Available at `/graphql`:
234
+
235
+ ```graphql
236
+ query {
237
+ nodes(nodeType: METRIC, namespace: "finance") {
238
+ name
239
+ description
240
+ dimensions { name type }
241
+ }
242
+
243
+ commonDimensions(nodes: ["finance.revenue", "growth.users"]) {
244
+ name
245
+ type
246
+ }
247
+ }
248
+ ```