@malloy-publisher/server 0.0.234 → 0.0.236

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 (318) hide show
  1. package/dist/app/api-doc.yaml +47 -17
  2. package/dist/app/assets/{EnvironmentPage-DTZQ4Gxc.js → EnvironmentPage-BsAnavYN.js} +1 -1
  3. package/dist/app/assets/{HomePage-C5mlDPXK.js → HomePage-CADE138j.js} +1 -1
  4. package/dist/app/assets/{LightMode-DGNmhG0u.js → LightMode-Cfh7KzN8.js} +1 -1
  5. package/dist/app/assets/{MainPage-CVL_wmP4.js → MainPage-CO3pRlnV.js} +1 -1
  6. package/dist/app/assets/{MaterializationsPage-DmzMBCpy.js → MaterializationsPage-p9YjkRXZ.js} +1 -1
  7. package/dist/app/assets/{ModelPage-Dbvf4QbB.js → ModelPage-C1OSTv-x.js} +1 -1
  8. package/dist/app/assets/{PackagePage-DxdHc2Qs.js → PackagePage-e4kN75YR.js} +1 -1
  9. package/dist/app/assets/{RouteError-OJdT4tCd.js → RouteError-CzbfOkng.js} +1 -1
  10. package/dist/app/assets/{ThemeEditorPage-Bk7s0KXY.js → ThemeEditorPage-CciagFTq.js} +1 -1
  11. package/dist/app/assets/{WorkbookPage-j_vCWdN3.js → WorkbookPage-DNWmkCXa.js} +1 -1
  12. package/dist/app/assets/{core-Rj_4rRnA.es-DoIfLxDJ.js → core-Rj_4rRnA.es-BZyvITuO.js} +1 -1
  13. package/dist/app/assets/{index-B_jKMR35.js → index-CH2AcDzc.js} +4 -4
  14. package/dist/app/assets/{index-hw-xn0X7.js → index-DQa463gC.js} +2 -2
  15. package/dist/app/assets/{index-DWIe_hK0.js → index-DQpV7MyA.js} +1 -1
  16. package/dist/app/assets/{index-BabP-V-S.js → index-DzaYbhnD.js} +1 -1
  17. package/dist/app/assets/{index-D-rDyK11.js → index-VBbcc8s6.js} +1 -1
  18. package/dist/app/index.html +1 -1
  19. package/dist/package_load_worker.mjs +8 -1
  20. package/dist/server.mjs +471 -54
  21. package/package.json +16 -13
  22. package/.eslintrc.json +0 -44
  23. package/.prettierignore +0 -1
  24. package/.prettierrc +0 -4
  25. package/build.ts +0 -97
  26. package/eslint.config.mjs +0 -76
  27. package/k6-tests/README.md +0 -144
  28. package/k6-tests/bun.lock +0 -797
  29. package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
  30. package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
  31. package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
  32. package/k6-tests/load-test/load-test-crud.ts +0 -142
  33. package/k6-tests/load-test/load-test.ts +0 -784
  34. package/k6-tests/package.json +0 -27
  35. package/k6-tests/scripts/run-with-otel.sh +0 -50
  36. package/k6-tests/smoke-test/smoke-test.ts +0 -158
  37. package/k6-tests/tsconfig.json +0 -29
  38. package/k6-tests/utils/client_factory.ts +0 -128
  39. package/k6-tests/utils/common.ts +0 -489
  40. package/k6-tests/utils/logger.ts +0 -62
  41. package/k6-tests/utils/types.d.ts +0 -35
  42. package/malloy_mcp.dxt +0 -0
  43. package/publisher.config.example.bigquery.json +0 -33
  44. package/publisher.config.example.duckdb.json +0 -23
  45. package/publisher.config.json +0 -23
  46. package/scripts/bake-duckdb-extensions.js +0 -110
  47. package/src/config.spec.ts +0 -1535
  48. package/src/config.theme.spec.ts +0 -178
  49. package/src/config.ts +0 -1142
  50. package/src/constants.ts +0 -105
  51. package/src/controller/compile.controller.ts +0 -40
  52. package/src/controller/connection.controller.spec.ts +0 -849
  53. package/src/controller/connection.controller.ts +0 -901
  54. package/src/controller/database.controller.ts +0 -24
  55. package/src/controller/materialization.controller.spec.ts +0 -293
  56. package/src/controller/materialization.controller.ts +0 -319
  57. package/src/controller/model.controller.ts +0 -164
  58. package/src/controller/package.controller.spec.ts +0 -273
  59. package/src/controller/package.controller.ts +0 -306
  60. package/src/controller/query.controller.ts +0 -152
  61. package/src/controller/theme.controller.ts +0 -83
  62. package/src/controller/watch-mode.controller.ts +0 -243
  63. package/src/data_styles.ts +0 -71
  64. package/src/default-publisher.config.json +0 -23
  65. package/src/ducklake_version.spec.ts +0 -163
  66. package/src/ducklake_version.ts +0 -153
  67. package/src/errors.spec.ts +0 -119
  68. package/src/errors.ts +0 -237
  69. package/src/filter_deprecation.spec.ts +0 -64
  70. package/src/filter_deprecation.ts +0 -42
  71. package/src/health.spec.ts +0 -123
  72. package/src/health.ts +0 -233
  73. package/src/heap_check.spec.ts +0 -144
  74. package/src/heap_check.ts +0 -144
  75. package/src/instrumentation.ts +0 -214
  76. package/src/json_utils.spec.ts +0 -51
  77. package/src/json_utils.ts +0 -33
  78. package/src/logger.spec.ts +0 -210
  79. package/src/logger.ts +0 -251
  80. package/src/malloy_pin_prereqs.spec.ts +0 -25
  81. package/src/materialization_metrics.spec.ts +0 -169
  82. package/src/materialization_metrics.ts +0 -346
  83. package/src/mcp/error_messages.spec.ts +0 -35
  84. package/src/mcp/error_messages.ts +0 -183
  85. package/src/mcp/handler_utils.spec.ts +0 -108
  86. package/src/mcp/handler_utils.ts +0 -251
  87. package/src/mcp/mcp_constants.ts +0 -6
  88. package/src/mcp/query_envelope.spec.ts +0 -229
  89. package/src/mcp/query_envelope.ts +0 -240
  90. package/src/mcp/server.protocol.spec.ts +0 -250
  91. package/src/mcp/server.ts +0 -87
  92. package/src/mcp/skills/build_skills_bundle.spec.ts +0 -51
  93. package/src/mcp/skills/build_skills_bundle.ts +0 -176
  94. package/src/mcp/skills/skills_bundle.json +0 -1
  95. package/src/mcp/skills/skills_bundle.spec.ts +0 -171
  96. package/src/mcp/tool_response.spec.ts +0 -108
  97. package/src/mcp/tool_response.ts +0 -138
  98. package/src/mcp/tools/compile_tool.spec.ts +0 -315
  99. package/src/mcp/tools/compile_tool.ts +0 -208
  100. package/src/mcp/tools/docs_search/build_docs_index.ts +0 -132
  101. package/src/mcp/tools/docs_search/malloy_docs_index.json +0 -1
  102. package/src/mcp/tools/docs_search_tool.spec.ts +0 -32
  103. package/src/mcp/tools/docs_search_tool.ts +0 -128
  104. package/src/mcp/tools/embedding_index.spec.ts +0 -1236
  105. package/src/mcp/tools/embedding_index.ts +0 -808
  106. package/src/mcp/tools/execute_query_tool.spec.ts +0 -294
  107. package/src/mcp/tools/execute_query_tool.ts +0 -316
  108. package/src/mcp/tools/get_context_eval.ts +0 -275
  109. package/src/mcp/tools/get_context_tool.spec.ts +0 -592
  110. package/src/mcp/tools/get_context_tool.ts +0 -585
  111. package/src/mcp/tools/reload_package_tool.spec.ts +0 -232
  112. package/src/mcp/tools/reload_package_tool.ts +0 -132
  113. package/src/mcp_config.spec.ts +0 -919
  114. package/src/mcp_config.ts +0 -425
  115. package/src/oom_guards.integration.spec.ts +0 -269
  116. package/src/package_load/package_load_pool.spec.ts +0 -252
  117. package/src/package_load/package_load_pool.ts +0 -931
  118. package/src/package_load/package_load_worker.ts +0 -1034
  119. package/src/package_load/protocol.ts +0 -364
  120. package/src/package_load/rpc_wait_accountant.spec.ts +0 -109
  121. package/src/package_load/rpc_wait_accountant.ts +0 -76
  122. package/src/package_load_metrics.spec.ts +0 -114
  123. package/src/package_load_metrics.ts +0 -127
  124. package/src/path_safety.spec.ts +0 -158
  125. package/src/path_safety.ts +0 -146
  126. package/src/pg_helpers.spec.ts +0 -223
  127. package/src/pg_helpers.ts +0 -52
  128. package/src/query_cap_metrics.spec.ts +0 -89
  129. package/src/query_cap_metrics.ts +0 -116
  130. package/src/query_concurrency.spec.ts +0 -247
  131. package/src/query_concurrency.ts +0 -237
  132. package/src/query_metadata_metrics.ts +0 -49
  133. package/src/query_param_utils.ts +0 -29
  134. package/src/query_timeout.spec.ts +0 -224
  135. package/src/query_timeout.ts +0 -179
  136. package/src/runtime/publisher.js +0 -328
  137. package/src/server-old.ts +0 -988
  138. package/src/server.ts +0 -2059
  139. package/src/service/annotations.spec.ts +0 -118
  140. package/src/service/annotations.ts +0 -91
  141. package/src/service/authorize.spec.ts +0 -154
  142. package/src/service/authorize.ts +0 -498
  143. package/src/service/authorize_integration.spec.ts +0 -2041
  144. package/src/service/build_plan.spec.ts +0 -685
  145. package/src/service/build_plan.ts +0 -797
  146. package/src/service/compile_authorize.spec.ts +0 -85
  147. package/src/service/compile_fragment_techniques.spec.ts +0 -156
  148. package/src/service/connection.spec.ts +0 -2360
  149. package/src/service/connection.ts +0 -2187
  150. package/src/service/connection_config.spec.ts +0 -734
  151. package/src/service/connection_config.ts +0 -899
  152. package/src/service/connection_federation.spec.ts +0 -184
  153. package/src/service/connection_fingerprint.spec.ts +0 -102
  154. package/src/service/connection_service.spec.ts +0 -711
  155. package/src/service/connection_service.ts +0 -291
  156. package/src/service/cron_evaluator.spec.ts +0 -79
  157. package/src/service/cron_evaluator.ts +0 -105
  158. package/src/service/db_utils.spec.ts +0 -1085
  159. package/src/service/db_utils.ts +0 -1502
  160. package/src/service/duckdb_instance_isolation.spec.ts +0 -137
  161. package/src/service/ducklake_lazy_attach.spec.ts +0 -110
  162. package/src/service/embedding_provider.spec.ts +0 -329
  163. package/src/service/embedding_provider.ts +0 -236
  164. package/src/service/environment.ts +0 -2191
  165. package/src/service/environment_admission.spec.ts +0 -344
  166. package/src/service/environment_compile.spec.ts +0 -197
  167. package/src/service/environment_store.spec.ts +0 -2274
  168. package/src/service/environment_store.ts +0 -2434
  169. package/src/service/environment_store_anchoring.spec.ts +0 -107
  170. package/src/service/environment_store_clone.spec.ts +0 -350
  171. package/src/service/explore_visibility.spec.ts +0 -434
  172. package/src/service/exports_probe.spec.ts +0 -107
  173. package/src/service/extension_fetch_policy.spec.ts +0 -256
  174. package/src/service/filter.spec.ts +0 -458
  175. package/src/service/filter.ts +0 -341
  176. package/src/service/filter_bypass.spec.ts +0 -418
  177. package/src/service/filter_integration.spec.ts +0 -1004
  178. package/src/service/freshness.spec.ts +0 -183
  179. package/src/service/freshness.ts +0 -112
  180. package/src/service/gcs_s3_utils.ts +0 -427
  181. package/src/service/given.ts +0 -105
  182. package/src/service/givens_integration.spec.ts +0 -219
  183. package/src/service/manifest_loader.spec.ts +0 -189
  184. package/src/service/manifest_loader.ts +0 -156
  185. package/src/service/materialization_build_session.spec.ts +0 -435
  186. package/src/service/materialization_build_session.ts +0 -681
  187. package/src/service/materialization_config_validation.spec.ts +0 -99
  188. package/src/service/materialization_config_validation.ts +0 -120
  189. package/src/service/materialization_eligibility.spec.ts +0 -158
  190. package/src/service/materialization_eligibility.ts +0 -305
  191. package/src/service/materialization_schedule_surface.spec.ts +0 -256
  192. package/src/service/materialization_scheduler.spec.ts +0 -289
  193. package/src/service/materialization_scheduler.ts +0 -304
  194. package/src/service/materialization_serve_transform.spec.ts +0 -1003
  195. package/src/service/materialization_serve_transform.ts +0 -779
  196. package/src/service/materialization_service.spec.ts +0 -2373
  197. package/src/service/materialization_service.ts +0 -2342
  198. package/src/service/materialization_test_fixtures.ts +0 -249
  199. package/src/service/model.spec.ts +0 -1157
  200. package/src/service/model.ts +0 -3370
  201. package/src/service/model_limits.spec.ts +0 -209
  202. package/src/service/model_limits.ts +0 -131
  203. package/src/service/model_storage_serve.spec.ts +0 -193
  204. package/src/service/model_storage_serve_joins.spec.ts +0 -193
  205. package/src/service/package.spec.ts +0 -612
  206. package/src/service/package.ts +0 -1799
  207. package/src/service/package_manifest.spec.ts +0 -230
  208. package/src/service/package_manifest.ts +0 -241
  209. package/src/service/package_memory_governor.spec.ts +0 -173
  210. package/src/service/package_memory_governor.ts +0 -233
  211. package/src/service/package_quote_bound_tables.spec.ts +0 -171
  212. package/src/service/package_race.spec.ts +0 -208
  213. package/src/service/package_reload_safety.spec.ts +0 -193
  214. package/src/service/package_rollback.spec.ts +0 -190
  215. package/src/service/package_worker_path.spec.ts +0 -645
  216. package/src/service/path_injection.spec.ts +0 -39
  217. package/src/service/persist_annotation_validation.spec.ts +0 -89
  218. package/src/service/persist_annotation_validation.ts +0 -59
  219. package/src/service/persistence_policy.spec.ts +0 -374
  220. package/src/service/proxy.spec.ts +0 -414
  221. package/src/service/proxy.ts +0 -248
  222. package/src/service/query_boundary.spec.ts +0 -470
  223. package/src/service/query_metadata.spec.ts +0 -408
  224. package/src/service/query_metadata.ts +0 -492
  225. package/src/service/query_metadata_identity.spec.ts +0 -149
  226. package/src/service/query_text.spec.ts +0 -79
  227. package/src/service/query_text.ts +0 -43
  228. package/src/service/quoting.spec.ts +0 -135
  229. package/src/service/quoting.ts +0 -78
  230. package/src/service/resolve_environment.ts +0 -15
  231. package/src/service/restricted_mode.spec.ts +0 -299
  232. package/src/service/source_extraction.ts +0 -225
  233. package/src/service/theme_store.ts +0 -199
  234. package/src/storage/DatabaseInterface.ts +0 -198
  235. package/src/storage/StorageManager.ts +0 -113
  236. package/src/storage/duckdb/ConnectionRepository.ts +0 -143
  237. package/src/storage/duckdb/DuckDBConnection.ts +0 -123
  238. package/src/storage/duckdb/DuckDBRepository.ts +0 -208
  239. package/src/storage/duckdb/EnvironmentRepository.ts +0 -148
  240. package/src/storage/duckdb/MaterializationRepository.spec.ts +0 -191
  241. package/src/storage/duckdb/MaterializationRepository.ts +0 -336
  242. package/src/storage/duckdb/PackageRepository.ts +0 -135
  243. package/src/storage/duckdb/schema.ts +0 -229
  244. package/src/stream_helpers.spec.ts +0 -280
  245. package/src/stream_helpers.ts +0 -162
  246. package/src/telemetry.ts +0 -20
  247. package/src/test_helpers/metrics_harness.ts +0 -166
  248. package/src/theme_key_parity.spec.ts +0 -57
  249. package/src/utils.ts +0 -29
  250. package/tests/fixtures/authorize-compile/model.malloy +0 -9
  251. package/tests/fixtures/authorize-compile/publisher.json +0 -4
  252. package/tests/fixtures/html-pages-nopublic/model.malloy +0 -1
  253. package/tests/fixtures/html-pages-nopublic/publisher.json +0 -5
  254. package/tests/fixtures/html-pages-test/data.csv +0 -3
  255. package/tests/fixtures/html-pages-test/public/assets/app.css +0 -3
  256. package/tests/fixtures/html-pages-test/public/barehtml.html +0 -4
  257. package/tests/fixtures/html-pages-test/public/bodymeta.html +0 -8
  258. package/tests/fixtures/html-pages-test/public/data.json +0 -1
  259. package/tests/fixtures/html-pages-test/public/fitcomment.html +0 -11
  260. package/tests/fixtures/html-pages-test/public/index.html +0 -9
  261. package/tests/fixtures/html-pages-test/public/nohead.html +0 -8
  262. package/tests/fixtures/html-pages-test/public/notfit.html +0 -13
  263. package/tests/fixtures/html-pages-test/public/slides.html +0 -12
  264. package/tests/fixtures/html-pages-test/public/sub/page2.html +0 -9
  265. package/tests/fixtures/html-pages-test/public/unterminated.html +0 -10
  266. package/tests/fixtures/html-pages-test/publisher.json +0 -5
  267. package/tests/fixtures/html-pages-test/report.malloy +0 -1
  268. package/tests/fixtures/persist-multi-level/data/orders.csv +0 -5
  269. package/tests/fixtures/persist-multi-level/multi_level.malloy +0 -18
  270. package/tests/fixtures/persist-multi-level/publisher.json +0 -5
  271. package/tests/fixtures/persist-schedule-test/data/orders.csv +0 -5
  272. package/tests/fixtures/persist-schedule-test/persist_schedule_test.malloy +0 -11
  273. package/tests/fixtures/persist-schedule-test/publisher.json +0 -7
  274. package/tests/fixtures/persist-schedule-warn-test/data/orders.csv +0 -5
  275. package/tests/fixtures/persist-schedule-warn-test/persist_warn_test.malloy +0 -11
  276. package/tests/fixtures/persist-schedule-warn-test/publisher.json +0 -6
  277. package/tests/fixtures/persist-test/data/orders.csv +0 -5
  278. package/tests/fixtures/persist-test/persist_test.malloy +0 -11
  279. package/tests/fixtures/persist-test/publisher.json +0 -5
  280. package/tests/fixtures/publisher.config.json +0 -15
  281. package/tests/fixtures/query-givens/data/orders.csv +0 -7
  282. package/tests/fixtures/query-givens/model.malloy +0 -34
  283. package/tests/fixtures/query-givens/publisher.json +0 -5
  284. package/tests/fixtures/xlsx/database.xlsx +0 -0
  285. package/tests/harness/mcp_test_setup.ts +0 -332
  286. package/tests/harness/rest_e2e.ts +0 -68
  287. package/tests/integration/authorize/compile_authorize_http.integration.spec.ts +0 -92
  288. package/tests/integration/concurrent_package/concurrent_package.integration.spec.ts +0 -280
  289. package/tests/integration/duckdb_storage/duckdb_storage.integration.spec.ts +0 -138
  290. package/tests/integration/first_boot/readiness_line.integration.spec.ts +0 -177
  291. package/tests/integration/html_pages/html_pages.integration.spec.ts +0 -440
  292. package/tests/integration/legacy_routes/legacy_routes.integration.spec.ts +0 -259
  293. package/tests/integration/materialization/environment_list.integration.spec.ts +0 -133
  294. package/tests/integration/materialization/freshness_gate.integration.spec.ts +0 -292
  295. package/tests/integration/materialization/manifest_binding.integration.spec.ts +0 -430
  296. package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -441
  297. package/tests/integration/materialization/orchestrated_rebind.integration.spec.ts +0 -150
  298. package/tests/integration/materialization/reference_manifest.integration.spec.ts +0 -251
  299. package/tests/integration/materialization/schedule_edit.integration.spec.ts +0 -126
  300. package/tests/integration/materialization/scheduler.integration.spec.ts +0 -200
  301. package/tests/integration/materialization/scheduler_recovery.integration.spec.ts +0 -172
  302. package/tests/integration/materialization/scheduler_transitions.integration.spec.ts +0 -256
  303. package/tests/integration/materialization/scheduler_wiring.integration.spec.ts +0 -143
  304. package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -438
  305. package/tests/integration/mcp/mcp_get_context_semantic.integration.spec.ts +0 -235
  306. package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -98
  307. package/tests/integration/query_givens/query_givens.integration.spec.ts +0 -146
  308. package/tests/integration/query_givens/query_givens_authorize.integration.spec.ts +0 -121
  309. package/tests/integration/sdk_givens/sdk_givens.integration.spec.ts +0 -110
  310. package/tests/integration/watch-mode/watch_mode.integration.spec.ts +0 -421
  311. package/tests/unit/controller/theme.controller.spec.ts +0 -141
  312. package/tests/unit/duckdb/attached_databases.test.ts +0 -1243
  313. package/tests/unit/duckdb/duckdb_connection.test.ts +0 -181
  314. package/tests/unit/duckdb/legacy_schema_migration.test.ts +0 -197
  315. package/tests/unit/duckdb/repositories.test.ts +0 -208
  316. package/tests/unit/ducklake/ducklake.test.ts +0 -952
  317. package/tests/unit/service/theme_store.spec.ts +0 -139
  318. package/tsconfig.json +0 -32
@@ -1,251 +0,0 @@
1
- import { MalloyError } from "@malloydata/malloy";
2
- import { EnvironmentStore } from "../service/environment_store";
3
- import {
4
- AccessDeniedError,
5
- BadRequestError,
6
- PackageNotFoundError,
7
- ModelNotFoundError,
8
- ModelCompilationError,
9
- EnvironmentNotFoundError,
10
- NotQueryableError,
11
- PayloadTooLargeError,
12
- QueryTimeoutError,
13
- ServiceUnavailableError,
14
- } from "../errors";
15
- import {
16
- getNotFoundError,
17
- getInternalError,
18
- getMalloyErrorDetails,
19
- type ErrorDetails,
20
- } from "./error_messages";
21
- import type { Model } from "../service/model";
22
- import type { Environment } from "../service/environment";
23
- import { logger } from "../logger";
24
-
25
- /**
26
- * Remediation for a ServiceUnavailableError, shared by every caller that homes
27
- * one. The error covers both the memory governor and the concurrency slots, so
28
- * the wording names both rather than only memory.
29
- */
30
- export const BACK_PRESSURE_SUGGESTIONS: readonly string[] = [
31
- "Retry once the publisher is below its configured memory or concurrency limit.",
32
- "If this persists, raise the limit or scale up the pod.",
33
- ];
34
-
35
- /**
36
- * Turns a thrown error into agent-facing ErrorDetails, homed by error class.
37
- *
38
- * Without this, a tool that funnels every failure through getMalloyErrorDetails
39
- * dresses each one up as a Malloy language problem: a missing package tells the
40
- * agent to check its Malloy syntax, and a back-pressure rejection sends it
41
- * hunting for a typo instead of retrying.
42
- *
43
- * Only the classes that really are about the caller's Malloy or request reach
44
- * the Malloy advice; anything else is reported as internal rather than blamed
45
- * on the model. Routing by class and not by message is deliberate:
46
- * getMalloyErrorDetails refines on message patterns that the shipped engine
47
- * does not emit (its syntax pattern wants "no viable alternative at input"
48
- * where a real error says "unexpected '@'"), so "did it refine?" would send
49
- * genuine compile errors to the internal branch.
50
- *
51
- * @param operation The operation that failed (e.g. 'compile', 'reloadPackage').
52
- * @param identifier The resource it failed on (e.g. 'env/package').
53
- */
54
- export function classifyToolError(
55
- operation: string,
56
- identifier: string,
57
- error: unknown,
58
- ): ErrorDetails {
59
- if (
60
- error instanceof EnvironmentNotFoundError ||
61
- error instanceof PackageNotFoundError ||
62
- error instanceof ModelNotFoundError ||
63
- // A query-boundary denial is a deliberate 404, and reporting it as a
64
- // server fault would tell the caller to contact support about a boundary
65
- // that is working. getNotFoundError also drops the source name this class
66
- // is careful not to confirm.
67
- error instanceof NotQueryableError
68
- ) {
69
- return getNotFoundError(identifier);
70
- }
71
- if (error instanceof ServiceUnavailableError) {
72
- // Back-pressure: surface the server's own message so the caller knows to
73
- // retry rather than to go edit its Malloy.
74
- return {
75
- message: error.message,
76
- suggestions: [...BACK_PRESSURE_SUGGESTIONS],
77
- } satisfies ErrorDetails;
78
- }
79
- if (
80
- error instanceof QueryTimeoutError ||
81
- error instanceof PayloadTooLargeError
82
- ) {
83
- // The query was too slow or too big. Both classes already carry their own
84
- // "refine the query" remediation in the message, so the suggestions say
85
- // the part the message leaves out and the internal branch got backwards:
86
- // this is not transient, and retrying it unchanged fails identically.
87
- // That distinction is the whole reason these are 504/413 and not the
88
- // retryable 503 (see the QueryTimeoutError docstring in errors.ts).
89
- return {
90
- message: error.message,
91
- suggestions: [
92
- "This is not transient. The same query will fail the same way, so change the query rather than retrying it.",
93
- "If it is already minimal, raise the configured cap named in the message.",
94
- ],
95
- } satisfies ErrorDetails;
96
- }
97
- if (
98
- // A raw engine error: what a bad query throws (syntax, undefined name,
99
- // field not found). executeQuery's catch sees these, so they must keep
100
- // the Malloy advice.
101
- error instanceof MalloyError ||
102
- // The same thing wrapped: what a failed compile or reload throws.
103
- error instanceof ModelCompilationError ||
104
- // An #(authorize) denial, whose message getMalloyErrorDetails recognizes.
105
- error instanceof AccessDeniedError ||
106
- // A malformed request is the caller's to fix, not ours to retry.
107
- error instanceof BadRequestError
108
- ) {
109
- return getMalloyErrorDetails(operation, identifier, error);
110
- }
111
- // Everything else (a filesystem EACCES, a TypeError, a worker crash, a
112
- // timeout) is not the caller's Malloy. Telling them to check their syntax
113
- // sends them to edit a model that is fine.
114
- return getInternalError(operation, error);
115
- }
116
-
117
- /**
118
- * Fetches and validates the Package and Model instances needed for query execution.
119
- * Handles errors related to package/model access and initial compilation.
120
- *
121
- * The resolved Environment comes back with the model because a query needs it for
122
- * more than the lookup: it owns the connection configs the per-query metadata
123
- * layers are read from.
124
- *
125
- * @returns An object containing the Model instance or a pre-formatted ErrorDetails object.
126
- */
127
- export async function getModelForQuery(
128
- environmentStore: EnvironmentStore,
129
- environmentName: string,
130
- packageName: string,
131
- modelPath: string,
132
- ): Promise<
133
- { model: Model; environment: Environment } | { error: ErrorDetails }
134
- > {
135
- try {
136
- const environment = await environmentStore.getEnvironment(
137
- environmentName,
138
- false,
139
- );
140
- // Shed load before any disk / DB work; mirrors the HTTP query
141
- // controllers so MCP traffic obeys the same back-pressure rules.
142
- environment.assertCanAdmitQuery();
143
- const pkg = await environment.getPackage(packageName, false);
144
- const model = pkg.getModel(modelPath);
145
- if (!model || model.getModelType() === "notebook") {
146
- // Ensure it's actually a model
147
- const details = getNotFoundError(
148
- `model '${modelPath}' in package '${packageName}' for environment '${environmentName}'`,
149
- );
150
- return { error: details };
151
- }
152
- // Attempt to get the model definition early to catch initial compilation errors
153
- await model.getModel(); // This might throw ModelCompilationError
154
- return { model, environment };
155
- } catch (error) {
156
- // Handle errors during package/model access or initial compilation
157
- let errorDetails: ErrorDetails;
158
- if (error instanceof EnvironmentNotFoundError) {
159
- errorDetails = getNotFoundError(`environment '${environmentName}'`);
160
- } else if (error instanceof PackageNotFoundError) {
161
- errorDetails = getNotFoundError(
162
- `package '${packageName}' in environment '${environmentName}'`,
163
- );
164
- } else if (error instanceof ModelNotFoundError) {
165
- errorDetails = getNotFoundError(
166
- `model '${modelPath}' in package '${packageName}' for environment '${environmentName}'`,
167
- );
168
- } else if (error instanceof ModelCompilationError) {
169
- errorDetails = getMalloyErrorDetails(
170
- "executeQuery (load model)",
171
- `${environmentName}/${packageName}/${modelPath}`,
172
- error,
173
- );
174
- } else if (error instanceof AccessDeniedError) {
175
- // An #(authorize) denial during model setup. Funnel through
176
- // getMalloyErrorDetails (which recognizes the access-denied message and
177
- // gives supply-the-givens guidance) so a 403 never surfaces as an
178
- // opaque internal error. Defensive: the gate fires during query
179
- // execution today, not setup, but this keeps every error class homed.
180
- errorDetails = getMalloyErrorDetails(
181
- "executeQuery (load model)",
182
- `${environmentName}/${packageName}/${modelPath}`,
183
- error,
184
- );
185
- } else if (error instanceof ServiceUnavailableError) {
186
- // Back-pressure: don't dress this up as a 404/500. Surface the
187
- // server's own message so the MCP caller knows to retry.
188
- errorDetails = {
189
- message: error.message,
190
- suggestions: [...BACK_PRESSURE_SUGGESTIONS],
191
- } satisfies ErrorDetails;
192
- } else {
193
- // Unexpected error during setup
194
- errorDetails = getInternalError("executeQuery (Setup)", error);
195
- }
196
- logger.error(
197
- `[MCP Server Error] Error accessing package/model for query: ${environmentName}/${packageName}/${modelPath}`,
198
- { error },
199
- );
200
- return { error: errorDetails };
201
- }
202
- }
203
-
204
- /**
205
- * Constructs a malloy:// URI string identifying the model a tool response came
206
- * from. Attached as `uri` metadata on tool results (get-context, execute-query,
207
- * docs-search); it is not a fetchable MCP resource.
208
- *
209
- * @param components The URI parts (environment, package, and optionally the model).
210
- * @param fragment Optional fragment identifier (e.g., "result", "get-context").
211
- * @returns A malloy:// URI string.
212
- */
213
- export function buildMalloyUri(
214
- components: {
215
- environment?: string;
216
- package?: string;
217
- resourceType?: "models";
218
- resourceName?: string;
219
- },
220
- fragment?: string,
221
- ): string {
222
- let path = "/environment/";
223
-
224
- if (components.environment) {
225
- path += encodeURIComponent(components.environment);
226
- } else {
227
- // Default to 'home' if not provided, consistent with current behavior
228
- path += "home";
229
- }
230
-
231
- if (components.package) {
232
- path += "/package/" + encodeURIComponent(components.package);
233
- }
234
-
235
- if (components.resourceType) {
236
- path += "/" + components.resourceType;
237
- if (components.resourceName) {
238
- path += "/" + encodeURIComponent(components.resourceName);
239
- }
240
- }
241
-
242
- // The URL constructor normalizes malloy://path to malloy:///path, so we
243
- // build the string manually to keep the malloy://environment/... shape.
244
- let uriString = "malloy:/" + path; // Start with one slash after scheme
245
-
246
- if (fragment) {
247
- uriString += "#" + fragment;
248
- }
249
-
250
- return uriString;
251
- }
@@ -1,6 +0,0 @@
1
- export const MCP_ERROR_MESSAGES = {
2
- // Protocol-level errors (validation)
3
- MISSING_REQUIRED_PARAMS:
4
- "Either 'query' or both 'sourceName' and 'queryName' must be provided",
5
- MUTUALLY_EXCLUSIVE_PARAMS: "Cannot provide both 'query' and 'queryName'",
6
- } as const;
@@ -1,229 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import type * as Malloy from "@malloydata/malloy-interfaces";
3
- import {
4
- buildQueryEnvelope,
5
- serializeEnvelope,
6
- MAX_RESULT_CHARS,
7
- } from "./query_envelope";
8
-
9
- const rows = (n: number) =>
10
- Array.from({ length: n }, (_, i) => ({ id: i, name: `row ${i}` }));
11
-
12
- /** A minimal Malloy.Result: only the metadata the envelope reads. */
13
- const result = (extra: Partial<Malloy.Result> = {}): Malloy.Result =>
14
- ({
15
- schema: { fields: [{ kind: "dimension", name: "id" }] },
16
- connection_name: "duckdb",
17
- ...extra,
18
- }) as Malloy.Result;
19
-
20
- /**
21
- * The field names here are Credible's, on purpose: a data app authored against
22
- * Publisher is served through Credible, and an agent that sees one shape while
23
- * developing and another in production has to learn both. These assertions are
24
- * the contract that keeps them the same, so renaming a field should fail here.
25
- */
26
- describe("buildQueryEnvelope", () => {
27
- it("returns flat rows plus Credible's metadata block", () => {
28
- const e = buildQueryEnvelope(rows(3), 1000, result());
29
- expect(e.rows).toHaveLength(3);
30
- expect(e._meta.connection_name).toBe("duckdb");
31
- expect(e._meta.schema.fields).toHaveLength(1);
32
- expect(e._meta.annotations).toEqual([]);
33
- });
34
-
35
- it("carries the Malloy metadata that flat rows drop", () => {
36
- // Field types and render tags live in the schema, not the rows, so an
37
- // agent would otherwise need a second verbose call to reach them.
38
- const e = buildQueryEnvelope(rows(1), 1000, {
39
- ...result(),
40
- query_timezone: "UTC",
41
- model_annotations: [{ value: "# dashboard" }],
42
- } as Malloy.Result);
43
- expect(e._meta.query_timezone).toBe("UTC");
44
- expect(e._meta.model_annotations).toEqual([{ value: "# dashboard" }]);
45
- });
46
-
47
- it("omits metadata keys the result did not carry", () => {
48
- const e = buildQueryEnvelope(rows(1), 1000, result());
49
- expect("query_timezone" in e._meta).toBe(false);
50
- expect("source_annotations" in e._meta).toBe(false);
51
- });
52
-
53
- /**
54
- * The correctness fix, and the one field a client cannot compute for itself:
55
- * the cap depends on server config and on the query's own LIMIT.
56
- */
57
- describe("_limit_hit", () => {
58
- it("is true when the row count lands exactly on the cap", () => {
59
- const e = buildQueryEnvelope(rows(1000), 1000, result());
60
- expect(e._limit_hit).toBe(true);
61
- expect(e._query_row_limit).toBe(1000);
62
- expect(e.warning).toContain("not a complete result");
63
- });
64
-
65
- it("is false when the result came in under the cap", () => {
66
- const e = buildQueryEnvelope(rows(999), 1000, result());
67
- expect(e._limit_hit).toBe(false);
68
- expect(e.warning).toBeUndefined();
69
- });
70
-
71
- /**
72
- * The false positive this guards. resolveModelQueryRowLimit folds the
73
- * query's own limit:/top: into the same cap, so equality holds every time
74
- * an author limited the query deliberately. Three of the eight views in
75
- * the bundled storefront example use `top:`, so a modelled top-N would
76
- * otherwise report its complete answer as cut off, and the Contract rule
77
- * would tell an agent that a top-N is "not the answer".
78
- */
79
- it("is false when the author's own limit: produced the cap", () => {
80
- const e = buildQueryEnvelope(
81
- rows(10),
82
- 10,
83
- result(),
84
- [],
85
- undefined,
86
- "query",
87
- );
88
- expect(e._limit_hit).toBe(false);
89
- expect(e._limit_source).toBe("query");
90
- expect(e.warning).toBeUndefined();
91
- });
92
-
93
- it("still fires when the silent server default produced the cap", () => {
94
- const e = buildQueryEnvelope(
95
- rows(1000),
96
- 1000,
97
- result(),
98
- [],
99
- undefined,
100
- "server_default",
101
- );
102
- expect(e._limit_hit).toBe(true);
103
- expect(e._limit_source).toBe("server_default");
104
- expect(e.warning).toContain("not a complete result");
105
- });
106
-
107
- it("reports the source even when the cap was not reached", () => {
108
- const e = buildQueryEnvelope(
109
- rows(3),
110
- 10,
111
- result(),
112
- [],
113
- undefined,
114
- "query",
115
- );
116
- expect(e._limit_hit).toBe(false);
117
- expect(e._limit_source).toBe("query");
118
- });
119
-
120
- it("is false when no cap was applied", () => {
121
- // rowLimit 0 means uncapped; equality against 0 would otherwise call
122
- // an empty result "limited".
123
- expect(buildQueryEnvelope([], 0, result())._limit_hit).toBe(false);
124
- });
125
-
126
- it("does not call an empty result limited", () => {
127
- expect(buildQueryEnvelope([], 1000, result())._limit_hit).toBe(false);
128
- });
129
- });
130
-
131
- /**
132
- * compactResult is raw driver output and DuckDB returns count() as a BigInt,
133
- * so a plain JSON.stringify throws on the most common query anyone writes.
134
- */
135
- it("serializes BigInt values instead of throwing", () => {
136
- const e = buildQueryEnvelope([{ c: 150930n }], 1000, result());
137
- expect(() => serializeEnvelope(e)).not.toThrow();
138
- expect(JSON.parse(serializeEnvelope(e)).rows[0].c).toBe(150930);
139
- });
140
-
141
- describe("payload truncation", () => {
142
- it("drops rows to fit and reports both counts", () => {
143
- const e = buildQueryEnvelope(rows(200), 100_000, result(), [], 3_000);
144
- expect(e._rows_truncated).toBe(true);
145
- expect(e._total_rows).toBe(200);
146
- expect(e._returned_rows).toBe((e.rows as unknown[]).length);
147
- expect(e._returned_rows).toBeGreaterThan(0);
148
- expect(e._returned_rows).toBeLessThan(200);
149
- expect(e.warning).toContain("of 200 rows");
150
- });
151
-
152
- it("keeps the finished payload under the cap", () => {
153
- // The bug this pins: setting the marker fields and the warning AFTER
154
- // the search pushes the payload back over the limit the truncation
155
- // existed to respect.
156
- const e = buildQueryEnvelope(rows(400), 100_000, result(), [], 3_000);
157
- expect(serializeEnvelope(e).length).toBeLessThanOrEqual(3_000);
158
- });
159
-
160
- /**
161
- * Reachable because the hard ceiling is maxBytes (50MB), so one row with a
162
- * large text column passes assertWithinModelResponseLimits and lands here.
163
- * "Showing 0 of N rows" reads as an empty result set, and an agent will
164
- * report "no rows matched" for a row that was merely too big to send.
165
- */
166
- it("says no rows fit rather than implying nothing matched", () => {
167
- const big = [{ doc: "z".repeat(5_000) }, { doc: "small" }];
168
- const e = buildQueryEnvelope(big, 1000, result(), [], 2_000);
169
- expect((e.rows as unknown[]).length).toBe(0);
170
- expect(e._returned_rows).toBe(0);
171
- expect(e._total_rows).toBe(2);
172
- expect(e.warning).toContain("No rows fit the result size limit");
173
- expect(e.warning).toContain("NOT an empty result");
174
- expect(e.warning).not.toContain("Showing 0 of");
175
- });
176
-
177
- it("keeps the zero-row payload under the cap too", () => {
178
- // The longer of the two wordings is what the search measures, so
179
- // swapping it in afterwards must not push the payload back over.
180
- const big = [{ doc: "z".repeat(5_000) }, { doc: "small" }];
181
- const e = buildQueryEnvelope(big, 1000, result(), [], 2_000);
182
- expect(serializeEnvelope(e).length).toBeLessThanOrEqual(2_000);
183
- });
184
-
185
- it("still reports the row cap when no rows fit", () => {
186
- // Both shortenings apply at once; neither may swallow the other.
187
- const big = [{ doc: "z".repeat(5_000) }, { doc: "y".repeat(5_000) }];
188
- const e = buildQueryEnvelope(big, 2, result(), [], 2_000);
189
- expect(e._limit_hit).toBe(true);
190
- expect(e.warning).toContain("not a complete result");
191
- expect(e.warning).toContain("No rows fit the result size limit");
192
- });
193
-
194
- it("omits the truncation fields entirely when nothing was dropped", () => {
195
- // Credible's shape: absent rather than false.
196
- const e = buildQueryEnvelope(rows(3), 1000, result());
197
- expect("_rows_truncated" in e).toBe(false);
198
- expect("_total_rows" in e).toBe(false);
199
- expect("_returned_rows" in e).toBe(false);
200
- });
201
-
202
- it("reports both shortenings when they happen together", () => {
203
- const e = buildQueryEnvelope(rows(200), 200, result(), [], 3_000);
204
- expect(e._limit_hit).toBe(true);
205
- expect(e._rows_truncated).toBe(true);
206
- // One says the query was capped, the other that the payload was.
207
- // Reporting only one would understate the loss.
208
- expect(e.warning).toContain("not a complete result");
209
- expect(e.warning).toContain("result size limit");
210
- });
211
-
212
- it("leaves an ordinary result well inside the default budget", () => {
213
- const e = buildQueryEnvelope(rows(500), 1000, result());
214
- expect("_rows_truncated" in e).toBe(false);
215
- expect(serializeEnvelope(e).length).toBeLessThan(MAX_RESULT_CHARS);
216
- });
217
- });
218
-
219
- it("passes render-tag messages through under Credible's key", () => {
220
- const e = buildQueryEnvelope(rows(1), 1000, result(), ["bad tag on x"]);
221
- expect(e.renderLogErrors).toEqual(["bad tag on x"]);
222
- });
223
-
224
- it("omits optional keys when they do not apply", () => {
225
- const e = buildQueryEnvelope(rows(1), 1000, result());
226
- expect("warning" in e).toBe(false);
227
- expect("renderLogErrors" in e).toBe(false);
228
- });
229
- });