@malloy-publisher/server 0.0.382-dev → 0.2.1

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 (175) hide show
  1. package/README.docker.md +145 -20
  2. package/README.md +65 -12
  3. package/dist/app/api-doc.html +5 -0
  4. package/dist/app/api-doc.yaml +3929 -534
  5. package/dist/app/assets/EnvironmentPage-CHq5BF4l.js +1 -0
  6. package/dist/app/assets/HomePage-CIp-pJCa.js +1 -0
  7. package/dist/app/assets/LightMode-mHHPjEeA.js +1 -0
  8. package/dist/app/assets/MainPage-CylxO5Yu.js +2 -0
  9. package/dist/app/assets/MaterializationsPage-a0vjqxib.js +1 -0
  10. package/dist/app/assets/ModelPage-Cz-MrSZQ.js +1 -0
  11. package/dist/app/assets/PackagePage-Cdg5D7AN.js +1 -0
  12. package/dist/app/assets/RouteError-B5SIyn6G.js +1 -0
  13. package/dist/app/assets/ThemeEditorPage-B62rRc-y.js +1 -0
  14. package/dist/app/assets/WorkbookPage-45cG1Gx6.js +1 -0
  15. package/dist/app/assets/{core-CjeTkq8O.es-BqRc6yhC.js → core-DyU47reh.es-DY9b3WjS.js} +1 -1
  16. package/dist/app/assets/github-dark-DenFmJkN.es-DHJKELXO.js +1 -0
  17. package/dist/app/assets/index-5eLCcNmP.css +1 -0
  18. package/dist/app/assets/index-C5ClsnS7.js +1774 -0
  19. package/dist/app/assets/index-DF9tVqwN.js +1 -0
  20. package/dist/app/assets/index-DSoCUF-f.js +2725 -0
  21. package/dist/app/assets/index-DgVRz2wm.js +2 -0
  22. package/dist/app/assets/index-P-pF8nU5.js +632 -0
  23. package/dist/app/assets/index-w_0OQJgZ.js +23 -0
  24. package/dist/app/index.html +8 -4
  25. package/dist/cpufeatures-1yrn0vtw.node +0 -0
  26. package/dist/default-publisher.config.json +23 -0
  27. package/dist/{instrumentation.js → instrumentation.mjs} +10631 -10573
  28. package/dist/package_load_worker.mjs +15219 -0
  29. package/dist/runtime/publisher.js +331 -0
  30. package/dist/{server.js → server.mjs} +141276 -74705
  31. package/dist/sshcrypto-vd2k5hq9.node +0 -0
  32. package/package.json +41 -28
  33. package/publisher.config.example.bigquery.json +33 -0
  34. package/publisher.config.example.duckdb.json +23 -0
  35. package/.eslintrc.json +0 -44
  36. package/.prettierignore +0 -1
  37. package/.prettierrc +0 -4
  38. package/build.ts +0 -38
  39. package/dist/app/assets/HomePage-Dn3E4CuB.js +0 -1
  40. package/dist/app/assets/MainPage-BzB3yoqi.js +0 -2
  41. package/dist/app/assets/ModelPage-C9O_sAXT.js +0 -1
  42. package/dist/app/assets/PackagePage-DcxKEjBX.js +0 -1
  43. package/dist/app/assets/ProjectPage-BDj307rF.js +0 -1
  44. package/dist/app/assets/RouteError-DAShbVCG.js +0 -1
  45. package/dist/app/assets/WorkbookPage-Cs_XYEaB.js +0 -1
  46. package/dist/app/assets/index-15BOvhp0.js +0 -456
  47. package/dist/app/assets/index-Bb2jqquW.js +0 -676
  48. package/dist/app/assets/index-CMlGQMcl.css +0 -1
  49. package/dist/app/assets/index-D68X76-7.js +0 -1276
  50. package/dist/app/assets/index.umd-DGBekgSu.js +0 -1145
  51. package/dxt/malloy_bridge.py +0 -354
  52. package/dxt/manifest.json +0 -22
  53. package/eslint.config.mjs +0 -76
  54. package/k6-tests/README.md +0 -144
  55. package/k6-tests/bun.lock +0 -797
  56. package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
  57. package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
  58. package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
  59. package/k6-tests/load-test/load-test-crud.ts +0 -142
  60. package/k6-tests/load-test/load-test.ts +0 -784
  61. package/k6-tests/package.json +0 -27
  62. package/k6-tests/scripts/run-with-otel.sh +0 -50
  63. package/k6-tests/smoke-test/smoke-test.ts +0 -158
  64. package/k6-tests/tsconfig.json +0 -29
  65. package/k6-tests/utils/client_factory.ts +0 -128
  66. package/k6-tests/utils/common.ts +0 -489
  67. package/k6-tests/utils/logger.ts +0 -62
  68. package/k6-tests/utils/types.d.ts +0 -35
  69. package/malloy_mcp.dxt +0 -0
  70. package/publisher.config.json +0 -33
  71. package/src/config.spec.ts +0 -850
  72. package/src/config.ts +0 -305
  73. package/src/constants.ts +0 -10
  74. package/src/controller/compile.controller.ts +0 -35
  75. package/src/controller/connection.controller.ts +0 -515
  76. package/src/controller/database.controller.ts +0 -21
  77. package/src/controller/manifest.controller.ts +0 -29
  78. package/src/controller/materialization.controller.ts +0 -125
  79. package/src/controller/model.controller.ts +0 -107
  80. package/src/controller/package.controller.ts +0 -182
  81. package/src/controller/query.controller.ts +0 -60
  82. package/src/controller/watch-mode.controller.ts +0 -98
  83. package/src/data_styles.ts +0 -71
  84. package/src/dto/connection.dto.spec.ts +0 -131
  85. package/src/dto/connection.dto.ts +0 -174
  86. package/src/dto/index.ts +0 -2
  87. package/src/dto/package.dto.spec.ts +0 -42
  88. package/src/dto/package.dto.ts +0 -12
  89. package/src/dto/validate.spec.ts +0 -76
  90. package/src/dto/validate.ts +0 -31
  91. package/src/errors.ts +0 -116
  92. package/src/health.ts +0 -189
  93. package/src/instrumentation.ts +0 -148
  94. package/src/logger.ts +0 -154
  95. package/src/mcp/error_messages.ts +0 -199
  96. package/src/mcp/handler_utils.ts +0 -239
  97. package/src/mcp/mcp_constants.ts +0 -22
  98. package/src/mcp/prompts/handlers.ts +0 -84
  99. package/src/mcp/prompts/index.ts +0 -11
  100. package/src/mcp/prompts/prompt_definitions.ts +0 -160
  101. package/src/mcp/prompts/prompt_service.ts +0 -67
  102. package/src/mcp/prompts/utils.ts +0 -62
  103. package/src/mcp/resource_metadata.ts +0 -47
  104. package/src/mcp/resources/model_resource.ts +0 -153
  105. package/src/mcp/resources/notebook_resource.ts +0 -137
  106. package/src/mcp/resources/package_resource.ts +0 -370
  107. package/src/mcp/resources/project_resource.ts +0 -184
  108. package/src/mcp/resources/query_resource.ts +0 -117
  109. package/src/mcp/resources/source_resource.ts +0 -141
  110. package/src/mcp/resources/view_resource.ts +0 -136
  111. package/src/mcp/server.ts +0 -62
  112. package/src/mcp/tools/discovery_tools.ts +0 -258
  113. package/src/mcp/tools/execute_query_tool.ts +0 -265
  114. package/src/server.ts +0 -1195
  115. package/src/service/connection.spec.ts +0 -1331
  116. package/src/service/connection.ts +0 -1511
  117. package/src/service/connection_service.spec.ts +0 -641
  118. package/src/service/connection_service.ts +0 -181
  119. package/src/service/db_utils.spec.ts +0 -714
  120. package/src/service/db_utils.ts +0 -1227
  121. package/src/service/filter.spec.ts +0 -447
  122. package/src/service/filter.ts +0 -337
  123. package/src/service/filter_integration.spec.ts +0 -825
  124. package/src/service/gcs_s3_utils.ts +0 -427
  125. package/src/service/manifest_service.spec.ts +0 -201
  126. package/src/service/manifest_service.ts +0 -106
  127. package/src/service/materialization_service.spec.ts +0 -648
  128. package/src/service/materialization_service.ts +0 -929
  129. package/src/service/materialized_table_gc.spec.ts +0 -383
  130. package/src/service/materialized_table_gc.ts +0 -279
  131. package/src/service/model.spec.ts +0 -257
  132. package/src/service/model.ts +0 -1069
  133. package/src/service/package.spec.ts +0 -236
  134. package/src/service/package.ts +0 -463
  135. package/src/service/project.ts +0 -681
  136. package/src/service/project_compile.spec.ts +0 -197
  137. package/src/service/project_store.spec.ts +0 -859
  138. package/src/service/project_store.ts +0 -1531
  139. package/src/service/quoting.ts +0 -41
  140. package/src/service/resolve_project.ts +0 -13
  141. package/src/storage/BaseRepository.ts +0 -31
  142. package/src/storage/DatabaseInterface.ts +0 -180
  143. package/src/storage/StorageManager.mock.ts +0 -59
  144. package/src/storage/StorageManager.ts +0 -210
  145. package/src/storage/duckdb/ConnectionRepository.ts +0 -141
  146. package/src/storage/duckdb/DuckDBConnection.ts +0 -177
  147. package/src/storage/duckdb/DuckDBManifestStore.ts +0 -70
  148. package/src/storage/duckdb/DuckDBRepository.ts +0 -224
  149. package/src/storage/duckdb/ManifestRepository.ts +0 -119
  150. package/src/storage/duckdb/MaterializationRepository.ts +0 -249
  151. package/src/storage/duckdb/PackageRepository.ts +0 -135
  152. package/src/storage/duckdb/ProjectRepository.ts +0 -148
  153. package/src/storage/duckdb/manifest_store.spec.ts +0 -133
  154. package/src/storage/duckdb/schema.ts +0 -147
  155. package/src/storage/ducklake/DuckLakeManifestStore.ts +0 -146
  156. package/src/utils.ts +0 -13
  157. package/tests/fixtures/persist-test/data/orders.csv +0 -5
  158. package/tests/fixtures/persist-test/persist_test.malloy +0 -11
  159. package/tests/fixtures/persist-test/publisher.json +0 -5
  160. package/tests/fixtures/publisher.config.json +0 -15
  161. package/tests/harness/e2e.ts +0 -96
  162. package/tests/harness/mcp_test_setup.ts +0 -344
  163. package/tests/harness/mocks.ts +0 -37
  164. package/tests/harness/rest_e2e.ts +0 -68
  165. package/tests/harness/uris.ts +0 -31
  166. package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -470
  167. package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -434
  168. package/tests/integration/mcp/mcp_resource.integration.spec.ts +0 -663
  169. package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -122
  170. package/tests/integration/mcp/setup.spec.ts +0 -5
  171. package/tests/unit/duckdb/attached_databases.test.ts +0 -1132
  172. package/tests/unit/ducklake/ducklake.test.ts +0 -950
  173. package/tests/unit/mcp/prompt_definitions.test.ts +0 -102
  174. package/tests/unit/mcp/prompt_happy.test.ts +0 -51
  175. package/tsconfig.json +0 -32
@@ -1,663 +0,0 @@
1
- /// <reference types="bun-types" />
2
-
3
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
- import { ErrorCode } from "@modelcontextprotocol/sdk/types.js";
5
- import { afterAll, beforeAll, describe, expect, it } from "bun:test";
6
- import {
7
- cleanupE2ETestEnvironment,
8
- McpE2ETestEnvironment,
9
- setupE2ETestEnvironment,
10
- } from "../../harness/mcp_test_setup";
11
-
12
- // Define an interface for the expected structure of package content entries
13
- interface PackageContentEntry {
14
- uri?: string;
15
- metadata?: {
16
- description?: string;
17
- // Add other expected metadata fields if known
18
- };
19
- // Add other expected fields if known
20
- }
21
-
22
- // --- Test Suite ---
23
- describe("MCP Resource Handlers (E2E Integration)", () => {
24
- let env: McpE2ETestEnvironment | null = null;
25
- let mcpClient: Client;
26
-
27
- // --- Setup/Teardown using E2E Scaffolding ---
28
- beforeAll(async () => {
29
- env = await setupE2ETestEnvironment();
30
- mcpClient = env.mcpClient;
31
- });
32
-
33
- afterAll(async () => {
34
- await cleanupE2ETestEnvironment(env);
35
- env = null;
36
- });
37
-
38
- // --- Test Constants ---
39
- const homeProjectUri = "malloy://project/malloy-samples";
40
- const faaPackageUri = "malloy://project/malloy-samples/package/faa";
41
- const flightsModelUri =
42
- "malloy://project/malloy-samples/package/faa/models/flights.malloy";
43
- const FLIGHTS_SOURCE = "flights";
44
- const FLIGHTS_CARRIER_QUERY = "flights_by_carrier";
45
- const FLIGHTS_MONTH_VIEW = "flights_by_month";
46
- const OVERVIEW_NOTEBOOK = "overview.malloynb";
47
- const nonExistentPackageUri =
48
- "malloy://project/malloy-samples/package/nonexistent";
49
- const nonExistentModelUri =
50
- "malloy://project/malloy-samples/package/faa/models/nonexistent.malloy";
51
- const nonExistentProjectUri = "malloy://project/invalid_project";
52
- const invalidUri = "invalid://format";
53
-
54
- const validSourceUri = `malloy://project/malloy-samples/package/faa/models/flights.malloy/sources/${FLIGHTS_SOURCE}`;
55
- const validQueryUri = `malloy://project/malloy-samples/package/faa/models/flights.malloy/queries/${FLIGHTS_CARRIER_QUERY}`;
56
- const validViewUri = `malloy://project/malloy-samples/package/faa/models/flights.malloy/sources/${FLIGHTS_SOURCE}/views/${FLIGHTS_MONTH_VIEW}`;
57
- const validNotebookUri = `malloy://project/malloy-samples/package/faa/notebooks/${OVERVIEW_NOTEBOOK}`;
58
- const nonExistentSourceUri = `malloy://project/malloy-samples/package/faa/models/flights.malloy/sources/non_existent_source`;
59
- const nonExistentQueryUri = `malloy://project/malloy-samples/package/faa/models/flights.malloy/queries/non_existent_query`;
60
- const nonExistentViewUri = `malloy://project/malloy-samples/package/faa/models/flights.malloy/sources/${FLIGHTS_SOURCE}/views/non_existent_view`;
61
- const nonExistentNotebookUri = `malloy://project/malloy-samples/package/faa/notebooks/non_existent.malloynb`;
62
-
63
- describe("client.listResources", () => {
64
- it(
65
- "should list all resources, including package entries (models/notebooks/sources)",
66
- async () => {
67
- console.log(
68
- "[TEST LOG] Starting: listResources - check package entries",
69
- );
70
- if (!env) throw new Error("Test environment not initialized");
71
- const result = await mcpClient.listResources({});
72
- console.log(
73
- "[TEST LOG] listResources result received (count):",
74
- result?.resources?.length ?? 0,
75
- );
76
-
77
- expect(result).toBeDefined();
78
- expect(result.resources).toBeDefined();
79
- expect(Array.isArray(result.resources)).toBe(true);
80
- expect(result.resources.length).toBeGreaterThan(0);
81
-
82
- const faaPackageEntry = result.resources.find(
83
- (r) => r.uri === faaPackageUri,
84
- );
85
- expect(faaPackageEntry).toBeDefined();
86
- expect(faaPackageEntry?.name).toBe("faa");
87
-
88
- const firstResource = result.resources[0];
89
- expect(firstResource).toBeDefined();
90
- expect(firstResource.uri).toBeDefined();
91
- expect(typeof firstResource.uri).toBe("string");
92
- expect(firstResource.uri).toMatch(/^malloy:\/\//);
93
- expect(firstResource.name).toBeDefined();
94
- expect(typeof firstResource.name).toBe("string");
95
- expect(firstResource.description).toBeDefined();
96
- expect(typeof firstResource.description).toBe("string");
97
- if (firstResource.mimeType) {
98
- expect(typeof firstResource.mimeType).toBe("string");
99
- }
100
- },
101
- { timeout: 30000 },
102
- );
103
-
104
- it("should resolve even if called with a specific URI (behavior might vary)", async () => {
105
- console.log("[TEST LOG] Starting: listResources - with URI");
106
- if (!env) throw new Error("Test environment not initialized");
107
- const result = await mcpClient.listResources({ uri: homeProjectUri });
108
- console.log(
109
- "[TEST LOG] listResources (with URI) result received (count):",
110
- result?.resources?.length ?? 0,
111
- );
112
- expect(result).toBeDefined();
113
- expect(result.resources).toBeDefined();
114
- expect(Array.isArray(result.resources)).toBe(true);
115
- expect(result.resources.length).toBeGreaterThan(0);
116
- });
117
- });
118
-
119
- describe("client.readResource (Basic Types)", () => {
120
- it("should return details for the project URI", async () => {
121
- console.log("[TEST LOG] Starting: readResource - project success");
122
- if (!env) throw new Error("Test environment not initialized");
123
- const resource = await mcpClient.readResource({ uri: homeProjectUri });
124
- console.log(
125
- "[TEST LOG] readResource (project) result received:",
126
- resource ? "object" : "null",
127
- );
128
- expect(resource).toBeDefined();
129
- expect(resource.contents).toBeDefined();
130
- expect(Array.isArray(resource.contents)).toBe(true);
131
- expect(resource.contents).toHaveLength(1);
132
- expect(resource.contents[0].type).toBe("application/json");
133
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
- const responsePayload = JSON.parse((resource.contents[0] as any).text);
135
- expect(responsePayload).toBeDefined();
136
- expect(responsePayload.definition).toBeDefined();
137
- expect(responsePayload.metadata).toBeDefined();
138
- // Check definition content - Project name is 'malloy-samples' from URI param
139
- expect(responsePayload.definition.name).toBe("malloy-samples");
140
- // Check metadata content (can be more specific if needed)
141
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
- expect(typeof responsePayload.metadata.description).toBe("string");
143
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
144
- expect(typeof responsePayload.metadata.usage).toBe("string");
145
- });
146
-
147
- it("should return details for a valid package URI", async () => {
148
- console.log("[TEST LOG] Starting: readResource - package success");
149
- if (!env) throw new Error("Test environment not initialized");
150
- const resource = await mcpClient.readResource({ uri: faaPackageUri });
151
- console.log(
152
- "[TEST LOG] readResource (package) result received:",
153
- resource ? "object" : "null",
154
- );
155
- expect(resource).toBeDefined();
156
- expect(resource.contents).toBeDefined();
157
- expect(Array.isArray(resource.contents)).toBe(true);
158
- expect(resource.contents).toHaveLength(1);
159
- expect(resource.contents[0].type).toBe("application/json");
160
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
161
- const responsePayload = JSON.parse((resource.contents[0] as any).text);
162
- expect(responsePayload).toBeDefined();
163
- expect(responsePayload.definition).toBeDefined();
164
- expect(responsePayload.metadata).toBeDefined();
165
- // Check definition content (matches old packageMetadata check)
166
- expect(responsePayload.definition.name).toBe("faa");
167
- expect(responsePayload.definition.description).toBeDefined();
168
- expect(typeof responsePayload.definition.description).toBe("string");
169
- // Check metadata content
170
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
- expect(typeof responsePayload.metadata.description).toBe("string");
172
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
173
- expect(typeof responsePayload.metadata.usage).toBe("string");
174
- });
175
-
176
- it("should return a list of contained resources for a valid package contents URI", async () => {
177
- console.log(
178
- "[TEST LOG] Starting: readResource - package contents success",
179
- );
180
- if (!env) throw new Error("Test environment not initialized");
181
- const contentsUri = `${faaPackageUri}/contents`; // Construct the contents URI
182
- const resource = await mcpClient.readResource({ uri: contentsUri });
183
-
184
- console.log(
185
- "[TEST DEBUG] Received Package Contents:",
186
- JSON.stringify(resource, null, 2),
187
- );
188
- console.log(
189
- "[TEST LOG] readResource (package contents) result received:",
190
- resource ? "object" : "null",
191
- );
192
- expect(resource).toBeDefined();
193
- expect(resource.isError).not.toBe(true);
194
- expect(resource.contents).toBeDefined();
195
- expect(Array.isArray(resource.contents)).toBe(true);
196
- expect(resource.contents).toHaveLength(1);
197
- expect(resource.contents[0].type).toBe("application/json");
198
-
199
- // The content should be a JSON array of resource definitions
200
- // Explicitly type the parsed array
201
- const contentArray = JSON.parse(
202
- (resource.contents[0] as { text: string }).text, // Use a specific type for content item
203
- ) as PackageContentEntry[];
204
- expect(Array.isArray(contentArray)).toBe(true);
205
- // Note: Package contents may be empty if models are not properly recognized
206
- // This is a known issue with the package contents handler
207
- expect(contentArray.length).toBeGreaterThanOrEqual(0);
208
-
209
- // Only check for specific entries if the array is not empty
210
- if (contentArray.length > 0) {
211
- // Check for a specific known source entry (e.g., flights.malloy)
212
- // Find the entry by URI suffix, don't assume order
213
- // Remove 'any' type from entry parameter
214
- const flightsEntry = contentArray.find((entry) =>
215
- entry?.uri?.endsWith("/sources/flights.malloy"),
216
- );
217
- if (flightsEntry) {
218
- expect(flightsEntry.uri).toBe(
219
- "malloy://project/malloy-samples/package/faa/sources/flights.malloy",
220
- );
221
- expect(flightsEntry.metadata).toBeDefined();
222
- expect(flightsEntry.metadata!.description).toContain(
223
- "Represents a table or dataset",
224
- );
225
- }
226
-
227
- // Check for a specific known notebook entry (e.g., aircraft_analysis.malloynb)
228
- // Remove 'any' type from entry parameter
229
- const notebookEntry = contentArray.find((entry) =>
230
- entry?.uri?.endsWith("/notebooks/aircraft_analysis.malloynb"),
231
- );
232
- if (notebookEntry) {
233
- expect(notebookEntry.uri).toBe(
234
- "malloy://project/malloy-samples/package/faa/notebooks/aircraft_analysis.malloynb",
235
- );
236
- expect(notebookEntry.metadata).toBeDefined();
237
- expect(notebookEntry.metadata!.description).toContain(
238
- "interactive document",
239
- );
240
- }
241
-
242
- // Check overall structure of the first item in the *original array*
243
- const firstItem = contentArray[0];
244
- expect(firstItem.uri).toBeDefined();
245
- expect(typeof firstItem.uri).toBe("string");
246
- expect(firstItem.metadata).toBeDefined();
247
- expect(typeof firstItem.metadata!.description).toBe("string");
248
- }
249
- });
250
-
251
- it("should return details for a valid model URI", async () => {
252
- console.log("[TEST LOG] Starting: readResource - model success");
253
- if (!env) throw new Error("Test environment not initialized");
254
- const resource = await mcpClient.readResource({
255
- uri: flightsModelUri,
256
- });
257
- console.log(
258
- "[TEST LOG] readResource (model) result received:",
259
- resource ? "object" : "null",
260
- );
261
- expect(resource).toBeDefined();
262
- expect(resource.isError).not.toBe(true);
263
- expect(resource.contents).toBeDefined();
264
- expect(Array.isArray(resource.contents)).toBe(true);
265
- expect(resource.contents).toHaveLength(1);
266
- expect(resource.contents[0].type).toBe("application/json");
267
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
268
- const responsePayload = JSON.parse((resource.contents[0] as any).text);
269
- expect(responsePayload).toBeDefined();
270
- expect(responsePayload.definition).toBeDefined();
271
- expect(responsePayload.metadata).toBeDefined();
272
- // Check definition content (like old compiledModel check)
273
- expect(responsePayload.definition.modelPath).toBe("flights.malloy");
274
- expect(responsePayload.definition.packageName).toBe("faa");
275
- expect(responsePayload.definition.sources).toBeDefined(); // Example check
276
- // Check metadata content
277
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
278
- expect(typeof responsePayload.metadata.description).toBe("string");
279
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
280
- expect(typeof responsePayload.metadata.usage).toBe("string");
281
- });
282
-
283
- it("should reject with InvalidParams for an invalid URI structure", async () => {
284
- console.log(
285
- "[TEST LOG] Starting: readResource - invalid URI structure",
286
- );
287
- if (!env) throw new Error("Test environment not initialized");
288
- await expect(
289
- mcpClient.readResource({ uri: invalidUri }),
290
- ).rejects.toMatchObject({
291
- code: ErrorCode.InvalidParams,
292
- message: expect.stringMatching(/Resource .* not found/i),
293
- });
294
- });
295
-
296
- it("should return application error response if package not found", async () => {
297
- console.log("[TEST LOG] Starting: readResource - package not found");
298
- if (!env) throw new Error("Test environment not initialized");
299
- const result = await mcpClient.readResource({
300
- uri: nonExistentPackageUri,
301
- });
302
- console.log(
303
- "[TEST LOG] readResource (package not found) result received:",
304
- result ? "object" : "null",
305
- );
306
- expect(result).toBeDefined();
307
- expect(result.isError).toBe(true);
308
- expect(result.contents).toBeDefined();
309
- expect(Array.isArray(result.contents)).toBe(true);
310
- expect(result.contents).toHaveLength(1);
311
- expect(result.contents[0].type).toBe("application/json");
312
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
313
- const responsePayload = JSON.parse((result.contents[0] as any).text);
314
- expect(responsePayload).toBeDefined();
315
- expect(responsePayload.error).toBeDefined();
316
- expect(responsePayload.suggestions).toBeDefined();
317
- expect(Array.isArray(responsePayload.suggestions)).toBe(true);
318
- // Adjust regex to match the actual "Resource not found: Package..." message format
319
- expect(responsePayload.error).toMatch(/^Resource not found: Package/i);
320
- expect(responsePayload.suggestions.length).toBeGreaterThan(0);
321
- });
322
-
323
- it("should return application error response if model not found", async () => {
324
- console.log("[TEST LOG] Starting: readResource - model not found");
325
- if (!env) throw new Error("Test environment not initialized");
326
- const result = await mcpClient.readResource({
327
- uri: nonExistentModelUri,
328
- });
329
- console.log(
330
- "[TEST LOG] readResource (model not found) result received:",
331
- result ? "object" : "null",
332
- );
333
- expect(result).toBeDefined();
334
- expect(result.isError).toBe(true);
335
- expect(result.contents).toBeDefined();
336
- expect(Array.isArray(result.contents)).toBe(true);
337
- expect(result.contents).toHaveLength(1);
338
- expect(result.contents[0].type).toBe("application/json");
339
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
340
- const responsePayload = JSON.parse((result.contents[0] as any).text);
341
- expect(responsePayload).toBeDefined();
342
- expect(responsePayload.error).toBeDefined();
343
- expect(responsePayload.suggestions).toBeDefined();
344
- expect(Array.isArray(responsePayload.suggestions)).toBe(true);
345
- expect(responsePayload.error).toMatch(/Resource not found/i);
346
- expect(responsePayload.suggestions.length).toBeGreaterThan(0);
347
- });
348
-
349
- it("should return application error response if project not found", async () => {
350
- console.log("[TEST LOG] Starting: readResource - project not found");
351
- if (!env) throw new Error("Test environment not initialized");
352
- const result = await mcpClient.readResource({
353
- uri: nonExistentProjectUri,
354
- });
355
- console.log(
356
- "[TEST LOG] readResource (project not found) result received:",
357
- result ? "object" : "null",
358
- );
359
- expect(result).toBeDefined();
360
- expect(result.isError).toBe(true);
361
- expect(result.contents).toBeDefined();
362
- expect(Array.isArray(result.contents)).toBe(true);
363
- expect(result.contents).toHaveLength(1);
364
- expect(result.contents[0].type).toBe("application/json");
365
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
366
- const responsePayload = JSON.parse((result.contents[0] as any).text);
367
- expect(responsePayload).toBeDefined();
368
- expect(responsePayload.error).toBeDefined();
369
- expect(responsePayload.suggestions).toBeDefined();
370
- expect(Array.isArray(responsePayload.suggestions)).toBe(true);
371
- expect(responsePayload.error).toMatch(/Resource not found/i);
372
- });
373
-
374
- it("should reject for syntactically invalid URI string", async () => {
375
- console.log(
376
- "[TEST LOG] Starting: readResource - syntactically invalid URI",
377
- );
378
- if (!env) throw new Error("Test environment not initialized");
379
- const syntacticallyInvalidUri = "i am /// not valid?";
380
- await expect(
381
- mcpClient.readResource({ uri: syntacticallyInvalidUri }),
382
- ).rejects.toMatchObject({
383
- code: expect.any(Number),
384
- message: expect.any(String),
385
- });
386
- });
387
-
388
- it("should return application error response if package contents URI references non-existent package", async () => {
389
- console.log(
390
- "[TEST LOG] Starting: readResource - package contents not found",
391
- );
392
- if (!env) throw new Error("Test environment not initialized");
393
- const nonExistentContentsUri = `${nonExistentPackageUri}/contents`;
394
- const result = await mcpClient.readResource({
395
- uri: nonExistentContentsUri,
396
- });
397
-
398
- console.log(
399
- "[TEST DEBUG] Received Package Contents (Not Found Case):",
400
- JSON.stringify(result, null, 2),
401
- );
402
- console.log(
403
- "[TEST LOG] readResource (package contents not found) result received:",
404
- result ? "object" : "null",
405
- );
406
- expect(result).toBeDefined();
407
- expect(result.isError).toBe(true);
408
- expect(result.contents).toBeDefined();
409
- expect(Array.isArray(result.contents)).toBe(true);
410
- expect(result.contents).toHaveLength(1);
411
- expect(result.contents[0].type).toBe("application/json");
412
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
413
- const responsePayload = JSON.parse((result.contents[0] as any).text);
414
- expect(responsePayload).toBeDefined();
415
- expect(responsePayload.error).toBeDefined();
416
- expect(responsePayload.error).toMatch(/Resource not found/i);
417
- expect(responsePayload.suggestions).toBeDefined();
418
- expect(Array.isArray(responsePayload.suggestions)).toBe(true);
419
- });
420
- });
421
-
422
- describe("client.readResource (Source/Query/View/Notebook)", () => {
423
- it("should get a valid source resource", async () => {
424
- if (!env) throw new Error("Test environment not initialized");
425
- const result = await mcpClient.readResource({ uri: validSourceUri });
426
- expect(result.isError).not.toBe(true);
427
- expect(result.contents).toBeDefined();
428
- expect(Array.isArray(result.contents)).toBe(true);
429
- expect(result.contents).toHaveLength(1);
430
- expect(result.contents[0].type).toBe("application/json");
431
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
432
- const responsePayload = JSON.parse((result.contents[0] as any).text);
433
- expect(responsePayload).toBeDefined();
434
- expect(responsePayload.definition).toBeDefined();
435
- expect(responsePayload.metadata).toBeDefined();
436
- expect(responsePayload.definition.name).toBe(FLIGHTS_SOURCE);
437
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
438
- expect(typeof responsePayload.metadata.description).toBe("string");
439
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
440
- expect(typeof responsePayload.metadata.usage).toBe("string");
441
- });
442
-
443
- it("should return structured app error for a query that is not found", async () => {
444
- if (!env) throw new Error("Test environment not initialized");
445
- const result = await mcpClient.readResource({ uri: validQueryUri });
446
- expect(result.isError).toBe(true);
447
- expect(result.contents).toBeDefined();
448
- expect(Array.isArray(result.contents)).toBe(true);
449
- expect(result.contents).toHaveLength(1);
450
- expect(result.contents?.[0]?.type).toBe("application/json");
451
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
452
- const errorPayload = JSON.parse((result.contents![0] as any).text);
453
- expect(errorPayload.error).toBeDefined();
454
- expect(errorPayload.error).toMatch(/Resource not found/i);
455
- // Check for the specific query name in the message
456
- expect(errorPayload.error).toMatch(
457
- new RegExp(`Query '${FLIGHTS_CARRIER_QUERY}'`),
458
- );
459
- expect(errorPayload.suggestions).toBeDefined();
460
- expect(Array.isArray(errorPayload.suggestions)).toBe(true);
461
- expect(errorPayload.suggestions.length).toBeGreaterThan(0);
462
- expect(errorPayload.suggestions[0]).toContain(
463
- "Verify the identifier or URI",
464
- ); // Check suggestion content
465
- });
466
-
467
- it("should return structured app error for a view that is not found", async () => {
468
- if (!env) throw new Error("Test environment not initialized");
469
- const result = await mcpClient.readResource({ uri: validViewUri });
470
- expect(result.isError).toBe(true);
471
- expect(result.contents).toBeDefined();
472
- expect(Array.isArray(result.contents)).toBe(true);
473
- expect(result.contents).toHaveLength(1);
474
- expect(result.contents?.[0]?.type).toBe("application/json");
475
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
476
- const errorPayload = JSON.parse((result.contents![0] as any).text);
477
- expect(errorPayload.error).toBeDefined();
478
- expect(errorPayload.error).toMatch(/Resource not found/i);
479
- // Check for the specific view name in the message
480
- expect(errorPayload.error).toMatch(
481
- new RegExp(`View '${FLIGHTS_MONTH_VIEW}'`),
482
- );
483
- expect(errorPayload.suggestions).toBeDefined();
484
- expect(Array.isArray(errorPayload.suggestions)).toBe(true);
485
- expect(errorPayload.suggestions.length).toBeGreaterThan(0);
486
- expect(errorPayload.suggestions[0]).toContain(
487
- "Verify the identifier or URI",
488
- ); // Check suggestion content
489
- });
490
-
491
- it("should return structured app error for a notebook that is not found or invalid", async () => {
492
- if (!env) throw new Error("Test environment not initialized");
493
- const result = await mcpClient.readResource({ uri: validNotebookUri });
494
- expect(result.isError).toBe(true);
495
- expect(result.contents).toBeDefined();
496
- expect(Array.isArray(result.contents)).toBe(true);
497
- expect(result.contents).toHaveLength(1);
498
- expect(result.contents?.[0]?.type).toBe("application/json");
499
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
500
- const errorPayload = JSON.parse((result.contents![0] as any).text);
501
- expect(errorPayload.error).toBeDefined();
502
- expect(errorPayload.error).toMatch(/Resource not found/i);
503
- // Check for the specific notebook name and context in the message
504
- // Adjust test to expect the generic "Resource not found" error, as the specific
505
- // "not a notebook" detail isn't easily surfaced in the standard error format.
506
- expect(errorPayload.error).toMatch(/Notebook 'overview.malloynb'/);
507
- expect(errorPayload.error).toMatch(/project 'malloy-samples'/); // Check project name
508
- expect(errorPayload.suggestions).toBeDefined();
509
- expect(Array.isArray(errorPayload.suggestions)).toBe(true);
510
- expect(errorPayload.suggestions.length).toBeGreaterThan(0);
511
- expect(errorPayload.suggestions[0]).toContain(
512
- "Verify the identifier or URI",
513
- );
514
- // This case might also hit ModelCompilationError if the .malloynb exists but is invalid
515
- // Adding a check for compilation-related suggestions could be useful if applicable
516
- });
517
-
518
- it("should return structured app error for non-existent source", async () => {
519
- if (!env) throw new Error("Test environment not initialized");
520
- const result = await mcpClient.readResource({
521
- uri: nonExistentSourceUri,
522
- });
523
- expect(result.isError).toBe(true);
524
- expect(result.contents).toBeDefined();
525
- expect(Array.isArray(result.contents)).toBe(true);
526
- expect(result.contents).toHaveLength(1);
527
- expect(result.contents?.[0]?.type).toBe("application/json");
528
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
529
- const errorPayload = JSON.parse((result.contents![0] as any).text);
530
- expect(errorPayload.error).toBeDefined();
531
- expect(errorPayload.error).toMatch(/Resource not found/i);
532
- // Check for the specific source name in the message
533
- expect(errorPayload.error).toMatch(/Source 'non_existent_source'/);
534
- // Adjust project name expectation
535
- expect(errorPayload.error).toMatch(/project 'malloy-samples'/);
536
- expect(errorPayload.suggestions).toBeDefined();
537
- expect(Array.isArray(errorPayload.suggestions)).toBe(true);
538
- expect(errorPayload.suggestions.length).toBeGreaterThan(0);
539
- expect(errorPayload.suggestions[0]).toContain(
540
- "Verify the identifier or URI",
541
- );
542
- });
543
-
544
- it("should return structured app error for non-existent query", async () => {
545
- if (!env) throw new Error("Test environment not initialized");
546
- const result = await mcpClient.readResource({
547
- uri: nonExistentQueryUri,
548
- });
549
- expect(result.isError).toBe(true);
550
- expect(result.contents).toBeDefined();
551
- expect(Array.isArray(result.contents)).toBe(true);
552
- expect(result.contents).toHaveLength(1);
553
- expect(result.contents?.[0]?.type).toBe("application/json");
554
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
555
- const errorPayload = JSON.parse((result.contents![0] as any).text);
556
- expect(errorPayload.error).toBeDefined();
557
- expect(errorPayload.error).toMatch(/Resource not found/i);
558
- // Check for the specific query name in the message
559
- expect(errorPayload.error).toMatch(/Query 'non_existent_query'/);
560
- // Adjust project name expectation
561
- expect(errorPayload.error).toMatch(/project 'malloy-samples'/);
562
- expect(errorPayload.suggestions).toBeDefined();
563
- expect(Array.isArray(errorPayload.suggestions)).toBe(true);
564
- expect(errorPayload.suggestions.length).toBeGreaterThan(0);
565
- expect(errorPayload.suggestions[0]).toContain(
566
- "Verify the identifier or URI",
567
- );
568
- });
569
-
570
- it("should return structured app error for non-existent view", async () => {
571
- if (!env) throw new Error("Test environment not initialized");
572
- const result = await mcpClient.readResource({
573
- uri: nonExistentViewUri,
574
- });
575
- expect(result.isError).toBe(true);
576
- expect(result.contents).toBeDefined();
577
- expect(Array.isArray(result.contents)).toBe(true);
578
- expect(result.contents).toHaveLength(1);
579
- expect(result.contents?.[0]?.type).toBe("application/json");
580
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
581
- const errorPayload = JSON.parse((result.contents![0] as any).text);
582
- expect(errorPayload.error).toBeDefined();
583
- expect(errorPayload.error).toMatch(/Resource not found/i);
584
- // Check for the specific view name in the message
585
- expect(errorPayload.error).toMatch(/View 'non_existent_view'/);
586
- // Adjust project name expectation
587
- expect(errorPayload.error).toMatch(/project 'malloy-samples'/);
588
- expect(errorPayload.suggestions).toBeDefined();
589
- expect(Array.isArray(errorPayload.suggestions)).toBe(true);
590
- expect(errorPayload.suggestions.length).toBeGreaterThan(0);
591
- expect(errorPayload.suggestions[0]).toContain(
592
- "Verify the identifier or URI",
593
- );
594
- });
595
-
596
- it("should return structured app error for non-existent notebook", async () => {
597
- if (!env) throw new Error("Test environment not initialized");
598
- const result = await mcpClient.readResource({
599
- uri: nonExistentNotebookUri,
600
- });
601
- expect(result.isError).toBe(true);
602
- expect(result.contents).toBeDefined();
603
- expect(Array.isArray(result.contents)).toBe(true);
604
- expect(result.contents).toHaveLength(1);
605
- expect(result.contents?.[0]?.type).toBe("application/json");
606
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
607
- const errorPayload = JSON.parse((result.contents![0] as any).text);
608
- expect(errorPayload.error).toBeDefined();
609
- expect(errorPayload.error).toMatch(/Resource not found/i);
610
- // Check for the specific notebook name and context in the message
611
- // Adjust test to expect the generic "Resource not found" error
612
- expect(errorPayload.error).toMatch(/Notebook 'non_existent.malloynb'/);
613
- // Adjust project name expectation
614
- expect(errorPayload.error).toMatch(/project 'malloy-samples'/);
615
- expect(errorPayload.suggestions).toBeDefined();
616
- expect(Array.isArray(errorPayload.suggestions)).toBe(true);
617
- expect(errorPayload.suggestions.length).toBeGreaterThan(0);
618
- expect(errorPayload.suggestions[0]).toContain(
619
- "Verify the identifier or URI",
620
- );
621
- });
622
-
623
- it("should return structured app error when requesting view from wrong source", async () => {
624
- if (!env) throw new Error("Test environment not initialized");
625
- const wrongSourceUri = `malloy://project/malloy-samples/package/faa/models/flights.malloy/sources/aircraft/views/${FLIGHTS_MONTH_VIEW}`;
626
- const result = await mcpClient.readResource({ uri: wrongSourceUri });
627
- expect(result.isError).toBe(true);
628
- expect(result.contents).toBeDefined();
629
- expect(Array.isArray(result.contents)).toBe(true);
630
- expect(result.contents).toHaveLength(1);
631
- expect(result.contents?.[0]?.type).toBe("application/json");
632
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
633
- const errorPayload = JSON.parse((result.contents![0] as any).text);
634
- expect(errorPayload.error).toBeDefined();
635
- // Adjust expectation: The primary error should be the project not found
636
- expect(errorPayload.error).toMatch(/Resource not found/i);
637
- expect(errorPayload.error).toMatch(/project 'malloy-samples'/);
638
- // Remove checks for view/source name in the error for this specific case
639
- expect(errorPayload.suggestions).toBeDefined();
640
- expect(Array.isArray(errorPayload.suggestions)).toBe(true);
641
- expect(errorPayload.suggestions.length).toBeGreaterThan(0);
642
-
643
- // The suggestions come as full sentences, so we check for expected content
644
- expect(errorPayload.suggestions.length).toBe(3);
645
- expect(errorPayload.suggestions[0]).toContain(
646
- "Verify the identifier or URI",
647
- );
648
- expect(errorPayload.suggestions[0]).toContain(
649
- "project 'malloy-samples'",
650
- );
651
- expect(errorPayload.suggestions[0]).toContain("is spelled correctly");
652
- expect(errorPayload.suggestions[0]).toContain(
653
- "Check capitalization and path separators",
654
- );
655
- expect(errorPayload.suggestions[1]).toContain(
656
- "If using a URI, ensure it follows the correct format",
657
- );
658
- expect(errorPayload.suggestions[2]).toContain(
659
- "Check if the resource exists and is correctly named",
660
- );
661
- });
662
- });
663
- });