@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.
- package/.prettierignore +1 -0
- package/build.ts +9 -0
- package/dist/app/api-doc.yaml +3 -0
- package/dist/app/assets/{index-2BGzlUQa.js → index-DYO_URL-.js} +100 -100
- package/dist/app/assets/{index-D1X7Y0Ve.js → index-Dg-zTLb3.js} +1 -1
- package/dist/app/assets/{index.es49-D9XPPIF9.js → index.es50-CDMydA2o.js} +1 -1
- package/dist/app/assets/mui-UpaxdnvH.js +159 -0
- package/dist/app/index.html +2 -2
- package/dist/server.js +307 -98
- package/eslint.config.mjs +8 -0
- package/package.json +2 -1
- package/publisher.config.json +25 -5
- package/src/config.ts +25 -2
- package/src/constants.ts +1 -1
- package/src/controller/package.controller.ts +1 -0
- package/src/controller/watch-mode.controller.ts +19 -4
- package/src/data_styles.ts +10 -3
- package/src/mcp/prompts/index.ts +9 -1
- package/src/mcp/resources/package_resource.ts +2 -1
- package/src/mcp/resources/source_resource.ts +1 -0
- package/src/mcp/resources/view_resource.ts +1 -0
- package/src/server.ts +9 -5
- package/src/service/connection.ts +17 -4
- package/src/service/db_utils.ts +19 -11
- package/src/service/model.ts +2 -0
- package/src/service/package.spec.ts +76 -54
- package/src/service/project.ts +160 -45
- package/src/service/project_store.spec.ts +477 -165
- package/src/service/project_store.ts +319 -69
- package/src/service/scheduler.ts +3 -2
- package/src/utils.ts +0 -1
- package/tests/harness/e2e.ts +60 -58
- package/tests/harness/uris.ts +21 -24
- package/tests/integration/mcp/mcp_resource.integration.spec.ts +10 -0
- package/dist/app/assets/mui-YektUyEU.js +0 -161
package/tests/harness/e2e.ts
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
60
|
+
const serverUrl = `http://127.0.0.1:${TEST_PORT}`;
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
const transport = new StreamableHTTPClientTransport(
|
|
71
|
+
new URL(`${serverUrl}/mcp`),
|
|
72
|
+
);
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
await mcpClient.connect(transport);
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
90
|
-
}
|
|
91
|
+
return { httpServer, serverUrl, mcpClient, stop };
|
|
92
|
+
}
|
package/tests/harness/uris.ts
CHANGED
|
@@ -1,31 +1,28 @@
|
|
|
1
1
|
export interface FixtureUris {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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",
|