@malloy-publisher/server 0.0.90 → 0.0.92

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 (35) hide show
  1. package/.prettierignore +1 -0
  2. package/build.ts +9 -0
  3. package/dist/app/api-doc.yaml +3 -0
  4. package/dist/app/assets/{index-2BGzlUQa.js → index-DYO_URL-.js} +100 -100
  5. package/dist/app/assets/{index-D1X7Y0Ve.js → index-Dg-zTLb3.js} +1 -1
  6. package/dist/app/assets/{index.es49-D9XPPIF9.js → index.es50-CDMydA2o.js} +1 -1
  7. package/dist/app/assets/mui-UpaxdnvH.js +159 -0
  8. package/dist/app/index.html +2 -2
  9. package/dist/server.js +307 -98
  10. package/eslint.config.mjs +8 -0
  11. package/package.json +2 -1
  12. package/publisher.config.json +25 -5
  13. package/src/config.ts +25 -2
  14. package/src/constants.ts +1 -1
  15. package/src/controller/package.controller.ts +1 -0
  16. package/src/controller/watch-mode.controller.ts +19 -4
  17. package/src/data_styles.ts +10 -3
  18. package/src/mcp/prompts/index.ts +9 -1
  19. package/src/mcp/resources/package_resource.ts +2 -1
  20. package/src/mcp/resources/source_resource.ts +1 -0
  21. package/src/mcp/resources/view_resource.ts +1 -0
  22. package/src/server.ts +9 -5
  23. package/src/service/connection.ts +17 -4
  24. package/src/service/db_utils.ts +19 -11
  25. package/src/service/model.ts +2 -0
  26. package/src/service/package.spec.ts +76 -54
  27. package/src/service/project.ts +160 -45
  28. package/src/service/project_store.spec.ts +477 -165
  29. package/src/service/project_store.ts +319 -69
  30. package/src/service/scheduler.ts +3 -2
  31. package/src/utils.ts +0 -1
  32. package/tests/harness/e2e.ts +60 -58
  33. package/tests/harness/uris.ts +21 -24
  34. package/tests/integration/mcp/mcp_resource.integration.spec.ts +10 -0
  35. package/dist/app/assets/mui-YektUyEU.js +0 -161
@@ -4,18 +4,18 @@ import path from "path";
4
4
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
5
5
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
6
6
  import type {
7
- Request,
8
- Notification,
9
- Result,
7
+ Request,
8
+ Notification,
9
+ Result,
10
10
  } from "@modelcontextprotocol/sdk/types.js";
11
11
 
12
12
  /**
13
13
  * E2E environment descriptor returned by {@link startE2E}.
14
14
  */
15
15
  export interface E2EEnv {
16
- httpServer: http.Server;
17
- serverUrl: string;
18
- mcpClient: Client<Request, Notification, Result>;
16
+ httpServer: http.Server;
17
+ serverUrl: string;
18
+ mcpClient: Client<Request, Notification, Result>;
19
19
  }
20
20
 
21
21
  let originalServerRoot: string | undefined;
@@ -26,65 +26,67 @@ let originalServerRoot: string | undefined;
26
26
  * once its suite finishes.
27
27
  */
28
28
  export async function startE2E(): Promise<E2EEnv & { stop(): Promise<void> }> {
29
- //--------------------------------------------------------------------------
30
- // 1. Set SERVER_ROOT so ProjectStore loader finds publisher.config.json
31
- //--------------------------------------------------------------------------
32
- originalServerRoot = process.env.SERVER_ROOT;
33
- const serverPackageDir = path.resolve(__dirname, "../../../"); // packages/server
34
- process.env.SERVER_ROOT = serverPackageDir;
29
+ //--------------------------------------------------------------------------
30
+ // 1. Set SERVER_ROOT so ProjectStore loader finds publisher.config.json
31
+ //--------------------------------------------------------------------------
32
+ originalServerRoot = process.env.SERVER_ROOT;
33
+ const serverPackageDir = path.resolve(__dirname, "../../../"); // packages/server
34
+ process.env.SERVER_ROOT = serverPackageDir;
35
35
 
36
- //--------------------------------------------------------------------------
37
- // 2. Dynamically import the configured Express app AFTER env var mutation
38
- //--------------------------------------------------------------------------
39
- const { mcpApp } = await import("../../src/server");
36
+ //--------------------------------------------------------------------------
37
+ // 2. Dynamically import the configured Express app AFTER env var mutation
38
+ //--------------------------------------------------------------------------
39
+ const { mcpApp } = await import("../../src/server");
40
40
 
41
- //--------------------------------------------------------------------------
42
- // 3. Start HTTP server on a predictable high port (4042 by default)
43
- //--------------------------------------------------------------------------
44
- const TEST_PORT = Number(process.env.MCP_PORT || 4040) + 2;
41
+ //--------------------------------------------------------------------------
42
+ // 3. Start HTTP server on a predictable high port (4042 by default)
43
+ //--------------------------------------------------------------------------
44
+ const TEST_PORT = Number(process.env.MCP_PORT || 4040) + 2;
45
45
 
46
- const httpServer: http.Server = await new Promise<http.Server>((resolve, reject) => {
47
- const srv = http
48
- .createServer(mcpApp)
49
- .listen(TEST_PORT, "127.0.0.1", () => resolve(srv));
46
+ const httpServer: http.Server = await new Promise<http.Server>(
47
+ (resolve, reject) => {
48
+ const srv = http
49
+ .createServer(mcpApp)
50
+ .listen(TEST_PORT, "127.0.0.1", () => resolve(srv));
50
51
 
51
- srv.on("error", (err: NodeJS.ErrnoException) => {
52
- /* c8 ignore next 3 */
53
- console.error("[E2E] server listen error", err);
54
- reject(err);
55
- });
56
- });
52
+ srv.on("error", (err: NodeJS.ErrnoException) => {
53
+ /* c8 ignore next 3 */
54
+ console.error("[E2E] server listen error", err);
55
+ reject(err);
56
+ });
57
+ },
58
+ );
57
59
 
58
- const serverUrl = `http://127.0.0.1:${TEST_PORT}`;
60
+ const serverUrl = `http://127.0.0.1:${TEST_PORT}`;
59
61
 
60
- //--------------------------------------------------------------------------
61
- // 4. Connect MCP client over HTTP streaming bridge
62
- //--------------------------------------------------------------------------
63
- const mcpClient = new Client<Request, Notification, Result>({
64
- name: "mcp-e2e-client",
65
- version: "1.0",
66
- });
62
+ //--------------------------------------------------------------------------
63
+ // 4. Connect MCP client over HTTP streaming bridge
64
+ //--------------------------------------------------------------------------
65
+ const mcpClient = new Client<Request, Notification, Result>({
66
+ name: "mcp-e2e-client",
67
+ version: "1.0",
68
+ });
67
69
 
68
- const transport = new StreamableHTTPClientTransport(
69
- new URL(`${serverUrl}/mcp`),
70
- );
70
+ const transport = new StreamableHTTPClientTransport(
71
+ new URL(`${serverUrl}/mcp`),
72
+ );
71
73
 
72
- await mcpClient.connect(transport);
74
+ await mcpClient.connect(transport);
73
75
 
74
- //--------------------------------------------------------------------------
75
- // 5. Return env + graceful stop helper
76
- //--------------------------------------------------------------------------
77
- const stop = async (): Promise<void> => {
78
- try {
79
- await mcpClient.close();
80
- } finally {
81
- httpServer.closeAllConnections?.();
82
- await new Promise<void>((r) => httpServer.close(() => r()));
83
- // Restore SERVER_ROOT
84
- if (originalServerRoot === undefined) delete process.env.SERVER_ROOT;
85
- else process.env.SERVER_ROOT = originalServerRoot;
86
- }
87
- };
76
+ //--------------------------------------------------------------------------
77
+ // 5. Return env + graceful stop helper
78
+ //--------------------------------------------------------------------------
79
+ const stop = async (): Promise<void> => {
80
+ try {
81
+ await mcpClient.close();
82
+ } finally {
83
+ httpServer.closeAllConnections?.();
84
+ await new Promise<void>((r) => httpServer.close(() => r()));
85
+ // Restore SERVER_ROOT
86
+ if (originalServerRoot === undefined) delete process.env.SERVER_ROOT;
87
+ else process.env.SERVER_ROOT = originalServerRoot;
88
+ }
89
+ };
88
90
 
89
- return { httpServer, serverUrl, mcpClient, stop };
90
- }
91
+ return { httpServer, serverUrl, mcpClient, stop };
92
+ }
@@ -1,31 +1,28 @@
1
1
  export interface FixtureUris {
2
- project: string;
3
- package: string;
4
- flightsModel: string;
5
- source: string;
6
- view: string;
7
- query: string;
8
- notebook: string;
2
+ project: string;
3
+ package: string;
4
+ flightsModel: string;
5
+ source: string;
6
+ view: string;
7
+ query: string;
8
+ notebook: string;
9
9
  }
10
10
 
11
11
  /**
12
12
  * Return canonical URIs used across integration tests, parametrised by project
13
13
  * and package. Most suites will call this with defaults.
14
14
  */
15
- export function malloyUris(
16
- project = "home",
17
- pkg = "faa",
18
- ): FixtureUris {
19
- const base = `malloy://project/${project}`;
20
- const pkgUri = `${base}/package/${pkg}`;
21
- const model = `${pkgUri}/models/flights.malloy`;
22
- return {
23
- project: base,
24
- package: pkgUri,
25
- flightsModel: model,
26
- source: `${model}/sources/flights`,
27
- view: `${model}/sources/flights/views/flights_by_month`,
28
- query: `${model}/queries/flights_by_carrier`,
29
- notebook: `${pkgUri}/notebooks/overview.malloynb`,
30
- };
31
- }
15
+ export function malloyUris(project = "home", pkg = "faa"): FixtureUris {
16
+ const base = `malloy://project/${project}`;
17
+ const pkgUri = `${base}/package/${pkg}`;
18
+ const model = `${pkgUri}/models/flights.malloy`;
19
+ return {
20
+ project: base,
21
+ package: pkgUri,
22
+ flightsModel: model,
23
+ source: `${model}/sources/flights`,
24
+ view: `${model}/sources/flights/views/flights_by_month`,
25
+ query: `${model}/queries/flights_by_carrier`,
26
+ notebook: `${pkgUri}/notebooks/overview.malloynb`,
27
+ };
28
+ }
@@ -631,6 +631,16 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
631
631
  expect(errorPayload.suggestions).toBeDefined();
632
632
  expect(Array.isArray(errorPayload.suggestions)).toBe(true);
633
633
  expect(errorPayload.suggestions.length).toBeGreaterThan(0);
634
+
635
+ const expectedSuggestions = [
636
+ "Verify the identifier or URI",
637
+ "project 'malloy-samples'",
638
+ "is spelled correctly",
639
+ "Check capitalization and path separators.",
640
+ ];
641
+ for (const chunk of errorPayload.suggestions) {
642
+ expect(expectedSuggestions).toContain(chunk);
643
+ }
634
644
  // Check suggestion content for project not found
635
645
  expect(errorPayload.suggestions[0]).toContain(
636
646
  "Verify the identifier or URI (project 'malloy-samples') is spelled correctly",