@malloy-publisher/server 0.0.87 → 0.0.89

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/build.ts +26 -0
  2. package/dist/app/api-doc.yaml +126 -5
  3. package/dist/app/assets/RenderedResult-BAZuT25g-QakVAbYy.js +2 -0
  4. package/dist/app/assets/{index-BbW5TZg_.js → index-Bq29VQqL.js} +2 -2
  5. package/dist/app/assets/{index-2xWCh-ya.css → index-CcIq0aEZ.css} +1 -1
  6. package/dist/app/assets/index-DZMePHJ5.js +251 -0
  7. package/dist/app/assets/{index-CIfV3yj1.js → index-TslDWlxH.js} +6 -6
  8. package/dist/app/assets/{index.umd-x-naS8R7.js → index.umd-BN4_E5KD.js} +259 -259
  9. package/dist/app/assets/mui-BEbinrI-.js +161 -0
  10. package/dist/app/assets/vendor-c5ypKtDW.js +17 -0
  11. package/dist/app/index.html +4 -2
  12. package/dist/instrumentation.js +67818 -35196
  13. package/dist/server.js +80404 -82231
  14. package/package.json +11 -5
  15. package/publisher.config.json +1 -1
  16. package/src/config.ts +20 -0
  17. package/src/constants.ts +14 -0
  18. package/src/controller/connection.controller.ts +21 -4
  19. package/src/controller/package.controller.ts +52 -2
  20. package/src/controller/schedule.controller.ts +3 -3
  21. package/src/controller/watch-mode.controller.ts +83 -0
  22. package/src/errors.ts +2 -1
  23. package/src/logger.ts +9 -0
  24. package/src/server.ts +33 -19
  25. package/src/service/connection.ts +159 -161
  26. package/src/service/model.ts +6 -6
  27. package/src/service/package.spec.ts +12 -10
  28. package/src/service/package.ts +15 -8
  29. package/src/service/project.ts +77 -36
  30. package/src/service/project_store.spec.ts +83 -56
  31. package/src/service/project_store.ts +330 -50
  32. package/src/utils.ts +0 -18
  33. package/tests/harness/mcp_test_setup.ts +5 -5
  34. package/dist/app/assets/RenderedResult-BAZuT25g-BMU632YI.js +0 -2
  35. package/dist/app/assets/index-C7whj6wK.js +0 -432
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@malloy-publisher/server",
3
3
  "description": "Malloy Publisher Server",
4
- "version": "0.0.87",
4
+ "version": "0.0.89",
5
5
  "main": "dist/server.js",
6
6
  "bin": {
7
7
  "malloy-publisher": "dist/server.js"
@@ -10,17 +10,19 @@
10
10
  "access": "public"
11
11
  },
12
12
  "scripts": {
13
- "test": "bun test ./**/*.spec.ts",
14
- "build": "bun generate-api-types && NODE_ENV=production && bun build:app && bun build src/server.ts --outdir dist --target node --format cjs --external='@malloydata/db-duckdb' --external='@malloydata/malloy' --external='@malloydata/malloy-sql' --external='@malloydata/render' --external='@malloydata/db-bigquery' --external='@malloydata/db-mysql' --external='@malloydata/db-postgres' --external='@malloydata/db-snowflake' --external='@malloydata/db-trino' && NODE_ENV=production bun build src/instrumentation.ts --outdir dist --target node --format cjs && bun run build:app && echo '#!/usr/bin/env node' | cat - dist/server.js > temp && mv temp dist/server.js && chmod +x dist/server.js",
13
+ "test": "bun test --timeout 100000",
14
+ "build": "bun generate-api-types && bun build:app && NODE_ENV=production bun run build.ts",
15
+ "build:server-only": "bun generate-api-types && NODE_ENV=production bun run build.ts",
15
16
  "start": "NODE_ENV=production node ./dist/server.js",
16
17
  "start:dev": "NODE_ENV=development bun --watch src/server.ts",
17
18
  "start:instrumented": "node --require ./dist/instrumentation.js ./dist/server.js",
18
19
  "lint": "bunx eslint \"./src/**/*.ts\"",
19
20
  "format": "bunx prettier --write '**/*.{ts,tsx}'",
20
- "build:app": "cd ../app && bun run build:server && rm -rf ../server/dist/app && mkdir -p ../server/dist && cp -r dist ../server/dist/app",
21
+ "build:app": "cd ../app && NODE_ENV=production bunx vite build",
21
22
  "generate-api-types": "bunx openapi-typescript ../../api-doc.yaml --output src/api.ts"
22
23
  },
23
24
  "dependencies": {
25
+ "@google-cloud/storage": "^7.16.0",
24
26
  "@malloydata/db-bigquery": "^0.0.295",
25
27
  "@malloydata/db-duckdb": "^0.0.295",
26
28
  "@malloydata/db-mysql": "^0.0.295",
@@ -36,8 +38,11 @@
36
38
  "@opentelemetry/sdk-metrics": "^2.0.0",
37
39
  "@opentelemetry/sdk-node": "^0.200.0",
38
40
  "@opentelemetry/sdk-trace-node": "^2.0.0",
41
+ "adm-zip": "^0.5.16",
39
42
  "async-mutex": "^0.5.0",
43
+ "aws-sdk": "^2.1692.0",
40
44
  "body-parser": "^1.20.2",
45
+ "chokidar": "^4.0.3",
41
46
  "class-transformer": "^0.5.1",
42
47
  "class-validator": "^0.14.1",
43
48
  "cors": "^2.8.5",
@@ -48,6 +53,7 @@
48
53
  "morgan": "^1.10.0",
49
54
  "node-cron": "^3.0.3",
50
55
  "recursive-readdir": "^2.2.3",
56
+ "simple-git": "^3.28.0",
51
57
  "uuid": "^11.0.3"
52
58
  },
53
59
  "devDependencies": {
@@ -56,6 +62,7 @@
56
62
  "@eslint/eslintrc": "^3.3.1",
57
63
  "@eslint/js": "^9.23.0",
58
64
  "@faker-js/faker": "^9.4.0",
65
+ "@types/adm-zip": "^0.5.7",
59
66
  "@types/cors": "^2.8.12",
60
67
  "@types/express": "^4.17.14",
61
68
  "@types/morgan": "^1.9.9",
@@ -65,7 +72,6 @@
65
72
  "@types/supertest": "^6.0.2",
66
73
  "@typescript-eslint/eslint-plugin": "^8.6.0",
67
74
  "@typescript-eslint/parser": "^8.6.0",
68
- "aws-sdk": "^2.1692.0",
69
75
  "eslint-config-prettier": "^9.1.0",
70
76
  "eslint-plugin-prettier": "^5.2.1",
71
77
  "mock-aws-s3": "^4.0.2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "frozenConfig": false,
3
3
  "projects": {
4
- "malloy-samples": "./malloy-samples"
4
+ "malloy-samples": "https://github.com/ms2data/malloy-samples"
5
5
  }
6
6
  }
package/src/config.ts ADDED
@@ -0,0 +1,20 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { PUBLISHER_CONFIG_NAME } from "./constants";
4
+
5
+ type FilesystemPath = `./${string}` | `../${string}` | `/${string}`;
6
+ type GcsPath = `gs://${string}`;
7
+ export type PublisherConfig = {
8
+ frozenConfig: boolean;
9
+ projects: Record<string, FilesystemPath | GcsPath>;
10
+ };
11
+
12
+ export const getPublisherConfig = (serverRoot: string): PublisherConfig => {
13
+ const publisherConfigPath = path.join(serverRoot, PUBLISHER_CONFIG_NAME);
14
+ return JSON.parse(fs.readFileSync(publisherConfigPath, "utf8"));
15
+ };
16
+
17
+ export const isPublisherConfigFrozen = (serverRoot: string) => {
18
+ const publisherConfig = getPublisherConfig(serverRoot);
19
+ return Boolean(publisherConfig.frozenConfig);
20
+ };
package/src/constants.ts CHANGED
@@ -1,2 +1,16 @@
1
+ import fs from "fs";
1
2
  export const API_PREFIX = "/api/v0";
2
3
  export const README_NAME = "README.md";
4
+ export const PUBLISHER_CONFIG_NAME = "publisher.config.json";
5
+ export const PACKAGE_MANIFEST_NAME = "publisher.json";
6
+ export const CONNECTIONS_MANIFEST_NAME = "publisher.connections.json";
7
+ export const MODEL_FILE_SUFFIX = ".malloy";
8
+ export const NOTEBOOK_FILE_SUFFIX = ".malloynb";
9
+ export const ROW_LIMIT = 1000;
10
+
11
+ export let publisherPath = "/etc/publisher";
12
+ try {
13
+ fs.accessSync(publisherPath, fs.constants.W_OK);
14
+ } catch (e) {
15
+ publisherPath = "/tmp/publisher";
16
+ }
@@ -1,6 +1,9 @@
1
- import { RunSQLOptions, TestableConnection } from "@malloydata/malloy";
1
+ import {
2
+ RunSQLOptions,
3
+ TableSourceDef,
4
+ TestableConnection,
5
+ } from "@malloydata/malloy";
2
6
  import { Connection, PersistSQLResults } from "@malloydata/malloy/connection";
3
- import { TableSourceDef } from "../../../../../malloy/packages/malloy/dist";
4
7
  import { components } from "../api";
5
8
  import { ConnectionError } from "../errors";
6
9
  import { logger } from "../logger";
@@ -11,6 +14,7 @@ import {
11
14
  import { ProjectStore } from "../service/project_store";
12
15
 
13
16
  type ApiConnection = components["schemas"]["Connection"];
17
+ type ApiConnectionStatus = components["schemas"]["ConnectionStatus"];
14
18
  type ApiSqlSource = components["schemas"]["SqlSource"];
15
19
  type ApiTableSource = components["schemas"]["TableSource"];
16
20
  type ApiQueryData = components["schemas"]["QueryData"];
@@ -57,15 +61,28 @@ export class ConnectionController {
57
61
  return getTablesForSchema(connection, schemaName);
58
62
  }
59
63
 
60
- public async testConnection(projectName: string, connectionName: string) {
64
+ public async testConnection(
65
+ projectName: string,
66
+ connectionName: string,
67
+ ): Promise<ApiConnectionStatus> {
61
68
  const project = await this.projectStore.getProject(projectName, false);
62
69
  const connection = project.getMalloyConnection(
63
70
  connectionName,
64
71
  ) as Connection;
65
72
  try {
66
73
  await (connection as TestableConnection).test();
74
+ return {
75
+ status: "ok",
76
+ errorMessage: "",
77
+ };
67
78
  } catch (error) {
68
- throw new ConnectionError((error as Error).message);
79
+ if (error instanceof ConnectionError) {
80
+ return {
81
+ status: "failed",
82
+ errorMessage: error.message,
83
+ };
84
+ }
85
+ throw error;
69
86
  }
70
87
  }
71
88
 
@@ -1,4 +1,5 @@
1
1
  import { components } from "../api";
2
+ import { publisherPath } from "../constants";
2
3
  import { BadRequestError, FrozenConfigError } from "../errors";
3
4
  import { ProjectStore } from "../service/project_store";
4
5
 
@@ -22,8 +23,12 @@ export class PackageController {
22
23
  reload: boolean,
23
24
  ): Promise<ApiPackage> {
24
25
  const project = await this.projectStore.getProject(projectName, false);
25
- const p = await project.getPackage(packageName, reload);
26
- return p.getPackageMetadata();
26
+ const _package = await project.getPackage(packageName, reload);
27
+ const packageLocation = _package.getPackageMetadata().location;
28
+ if (reload && packageLocation) {
29
+ await this.downloadPackage(projectName, packageName, packageLocation);
30
+ }
31
+ return _package.getPackageMetadata();
27
32
  }
28
33
 
29
34
  async addPackage(projectName: string, body: ApiPackage) {
@@ -34,6 +39,9 @@ export class PackageController {
34
39
  throw new BadRequestError("Package name is required");
35
40
  }
36
41
  const project = await this.projectStore.getProject(projectName, false);
42
+ if (body.location) {
43
+ await this.downloadPackage(projectName, body.name, body.location);
44
+ }
37
45
  return project.addPackage(body.name);
38
46
  }
39
47
 
@@ -54,6 +62,48 @@ export class PackageController {
54
62
  throw new FrozenConfigError();
55
63
  }
56
64
  const project = await this.projectStore.getProject(projectName, false);
65
+ if (body.location) {
66
+ await this.downloadPackage(projectName, packageName, body.location);
67
+ }
57
68
  return project.updatePackage(packageName, body);
58
69
  }
70
+
71
+ private async downloadPackage(
72
+ projectName: string,
73
+ packageName: string,
74
+ packageLocation: string,
75
+ ) {
76
+ const absoluteTargetPath = `${publisherPath}/${projectName}/${packageName}`;
77
+ if (
78
+ packageLocation.startsWith("https://") ||
79
+ packageLocation.startsWith("git@")
80
+ ) {
81
+ await this.projectStore.downloadGitHubDirectory(
82
+ packageLocation,
83
+ absoluteTargetPath,
84
+ );
85
+ } else if (packageLocation.startsWith("gs://")) {
86
+ await this.projectStore.downloadGcsDirectory(
87
+ packageLocation,
88
+ projectName,
89
+ absoluteTargetPath,
90
+ );
91
+ } else if (packageLocation.startsWith("s3://")) {
92
+ await this.projectStore.downloadS3Directory(
93
+ packageLocation,
94
+ projectName,
95
+ absoluteTargetPath,
96
+ );
97
+ } else if (packageLocation.startsWith("/")) {
98
+ if (packageLocation.endsWith(".zip")) {
99
+ packageLocation =
100
+ await this.projectStore.unzipProject(packageLocation);
101
+ }
102
+ await this.projectStore.mountLocalDirectory(
103
+ packageLocation,
104
+ absoluteTargetPath,
105
+ projectName,
106
+ );
107
+ }
108
+ }
59
109
  }
@@ -14,8 +14,8 @@ export class ScheduleController {
14
14
  projectName: string,
15
15
  packageName: string,
16
16
  ): Promise<ApiSchedule[]> {
17
- const project = await this.projectStore.getProject(projectName);
18
- const p = await project.getPackage(packageName);
19
- return p.listSchedules();
17
+ const project = await this.projectStore?.getProject?.(projectName);
18
+ const p = await project?.getPackage?.(packageName);
19
+ return p?.listSchedules?.();
20
20
  }
21
21
  }
@@ -0,0 +1,83 @@
1
+ import chokidar, { FSWatcher } from "chokidar";
2
+ import { RequestHandler } from "express";
3
+ import path from "path";
4
+ import { components } from "../api";
5
+ import { logger } from "../logger";
6
+ import { ProjectStore } from "../service/project_store";
7
+
8
+ type StartWatchReq = components["schemas"]["StartWatchRequest"];
9
+ type WatchStatusRes = components["schemas"]["WatchStatus"];
10
+ type Handler<Req = {}, Res = void> = RequestHandler<{}, Res, Req>;
11
+
12
+ export class WatchModeController {
13
+ watchingPath: string | null;
14
+ watchingProjectName: string | null;
15
+ watcher: FSWatcher;
16
+
17
+ constructor(private projectStore: ProjectStore) {
18
+ this.watchingPath = null;
19
+ this.watchingProjectName = null;
20
+ }
21
+
22
+ public getWatchStatus: Handler<{}, WatchStatusRes> = async (_req, res) => {
23
+ return res.json({
24
+ enabled: !!this.watchingPath,
25
+ watchingPath: this.watchingPath,
26
+ projectName: this.watchingProjectName ?? undefined,
27
+ });
28
+ };
29
+
30
+ public startWatching: Handler<StartWatchReq> = async (req, res) => {
31
+ const projectManifest = await ProjectStore.reloadProjectManifest(
32
+ this.projectStore.serverRootPath,
33
+ );
34
+ this.watchingProjectName = req.body.projectName;
35
+ this.watchingPath = path.join(
36
+ this.projectStore.serverRootPath,
37
+ projectManifest.projects[req.body.projectName],
38
+ );
39
+ this.watcher = chokidar.watch(this.watchingPath, {
40
+ ignored: (path, stats) =>
41
+ !!stats?.isFile() &&
42
+ !path.endsWith(".malloy") &&
43
+ !path.endsWith(".md"),
44
+ ignoreInitial: true,
45
+ });
46
+ const reloadProject = async () => {
47
+ // Overwrite the project with it's existing metadata to trigger a re-read
48
+ const project = await this.projectStore.getProject(
49
+ req.body.projectName,
50
+ true,
51
+ );
52
+ await this.projectStore.addProject(project.metadata);
53
+ logger.info(`Reloaded ${req.body.projectName}`);
54
+ };
55
+
56
+ this.watcher.on("add", async (path) => {
57
+ logger.info(
58
+ `Detected new file ${path}, reloading ${req.body.projectName}`,
59
+ );
60
+ await reloadProject();
61
+ });
62
+ this.watcher.on("unlink", async (path) => {
63
+ logger.info(
64
+ `Detected deletion of ${path}, reloading ${req.body.projectName}`,
65
+ );
66
+ await reloadProject();
67
+ });
68
+ this.watcher.on("change", async (path) => {
69
+ logger.info(
70
+ `Detected change on ${path}, reloading ${req.body.projectName}`,
71
+ );
72
+ await reloadProject();
73
+ });
74
+ res.json();
75
+ };
76
+
77
+ public stopWatchMode: Handler = async (_req, res) => {
78
+ this.watcher.close();
79
+ this.watchingPath = null;
80
+ this.watchingProjectName = null;
81
+ res.json();
82
+ };
83
+ }
package/src/errors.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { MalloyError } from "@malloydata/malloy";
2
+ import { PUBLISHER_CONFIG_NAME } from "./constants";
2
3
 
3
4
  export function internalErrorToHttpError(error: Error) {
4
5
  if (error instanceof BadRequestError) {
@@ -84,7 +85,7 @@ export class ModelCompilationError extends Error {
84
85
 
85
86
  export class FrozenConfigError extends Error {
86
87
  constructor(
87
- message = "Publisher config can't be updated when publisher.config.json has `frozenConfig: true`",
88
+ message = `Publisher config can't be updated when ${PUBLISHER_CONFIG_NAME} has { "frozenConfig": true }`,
88
89
  ) {
89
90
  super(message);
90
91
  }
package/src/logger.ts CHANGED
@@ -20,11 +20,20 @@ export const logger = winston.createLogger({
20
20
 
21
21
  export const loggerMiddleware: RequestHandler = (req, res, next) => {
22
22
  const startTime = performance.now();
23
+ const resJson = res.json;
24
+ res.json = (body: unknown) => {
25
+ res.locals.body = body;
26
+ return resJson.call(res, body);
27
+ };
23
28
  res.on("finish", () => {
24
29
  const endTime = performance.now();
25
30
  logger.info(`${req.method} ${req.url}`, {
26
31
  statusCode: res.statusCode,
27
32
  duration: endTime - startTime,
33
+ payload: req.body,
34
+ response: res.locals.body,
35
+ params: req.params,
36
+ query: req.query,
28
37
  });
29
38
  });
30
39
  next();
package/src/server.ts CHANGED
@@ -12,6 +12,7 @@ import { ModelController } from "./controller/model.controller";
12
12
  import { PackageController } from "./controller/package.controller";
13
13
  import { QueryController } from "./controller/query.controller";
14
14
  import { ScheduleController } from "./controller/schedule.controller";
15
+ import { WatchModeController } from "./controller/watch-mode.controller";
15
16
  import { internalErrorToHttpError, NotImplementedError } from "./errors";
16
17
  import { logger, loggerMiddleware } from "./logger";
17
18
  import { initializeMcpServer } from "./mcp/server";
@@ -62,7 +63,7 @@ function parseArgs() {
62
63
  parseArgs();
63
64
 
64
65
  const PUBLISHER_PORT = Number(process.env.PUBLISHER_PORT || 4000);
65
- const PUBLISHER_HOST = process.env.PUBLISHER_HOST || "localhost";
66
+ const PUBLISHER_HOST = process.env.PUBLISHER_HOST || "0.0.0.0";
66
67
  const MCP_PORT = Number(process.env.MCP_PORT || 4040);
67
68
  const MCP_ENDPOINT = "/mcp";
68
69
  // Find the app directory - handle NPX vs local execution
@@ -83,6 +84,7 @@ app.use(loggerMiddleware);
83
84
  app.use(cors());
84
85
 
85
86
  const projectStore = new ProjectStore(SERVER_ROOT);
87
+ const watchModeController = new WatchModeController(projectStore);
86
88
  const connectionController = new ConnectionController(projectStore);
87
89
  const modelController = new ModelController(projectStore);
88
90
  const packageController = new PackageController(projectStore);
@@ -90,7 +92,7 @@ const databaseController = new DatabaseController(projectStore);
90
92
  const queryController = new QueryController(projectStore);
91
93
  const scheduleController = new ScheduleController(projectStore);
92
94
 
93
- const mcpApp = express();
95
+ export const mcpApp = express();
94
96
 
95
97
  mcpApp.use(MCP_ENDPOINT, express.json());
96
98
  mcpApp.use(MCP_ENDPOINT, cors());
@@ -206,6 +208,17 @@ const setVersionIdError = (res: express.Response) => {
206
208
  app.use(cors());
207
209
  app.use(bodyParser.json());
208
210
 
211
+ app.get(`${API_PREFIX}/status`, async (_req, res) => {
212
+ res.status(200).json({
213
+ timestamp: Date.now(),
214
+ projects: await projectStore.listProjects(),
215
+ });
216
+ });
217
+
218
+ app.get(`${API_PREFIX}/watch-mode/status`, watchModeController.getWatchStatus);
219
+ app.post(`${API_PREFIX}/watch-mode/start`, watchModeController.startWatching);
220
+ app.post(`${API_PREFIX}/watch-mode/stop`, watchModeController.stopWatchMode);
221
+
209
222
  app.get(`${API_PREFIX}/projects`, async (_req, res) => {
210
223
  try {
211
224
  res.status(200).json(await projectStore.listProjects());
@@ -218,7 +231,9 @@ app.get(`${API_PREFIX}/projects`, async (_req, res) => {
218
231
 
219
232
  app.post(`${API_PREFIX}/projects`, async (req, res) => {
220
233
  try {
221
- res.status(200).json(await projectStore.addProject(req.body));
234
+ logger.info("Adding project", { body: req.body });
235
+ const project = await projectStore.addProject(req.body);
236
+ res.status(200).json(await project.serialize());
222
237
  } catch (error) {
223
238
  logger.error(error);
224
239
  const { json, status } = internalErrorToHttpError(error as Error);
@@ -232,7 +247,7 @@ app.get(`${API_PREFIX}/projects/:projectName`, async (req, res) => {
232
247
  req.params.projectName,
233
248
  req.query.reload === "true",
234
249
  );
235
- res.status(200).json(project.metadata);
250
+ res.status(200).json(await project.serialize());
236
251
  } catch (error) {
237
252
  logger.error(error);
238
253
  const { json, status } = internalErrorToHttpError(error as Error);
@@ -242,7 +257,8 @@ app.get(`${API_PREFIX}/projects/:projectName`, async (req, res) => {
242
257
 
243
258
  app.patch(`${API_PREFIX}/projects/:projectName`, async (req, res) => {
244
259
  try {
245
- res.status(200).json(await projectStore.updateProject(req.body));
260
+ const project = await projectStore.updateProject(req.body);
261
+ res.status(200).json(await project.serialize());
246
262
  } catch (error) {
247
263
  logger.error(error);
248
264
  const { json, status } = internalErrorToHttpError(error as Error);
@@ -252,9 +268,8 @@ app.patch(`${API_PREFIX}/projects/:projectName`, async (req, res) => {
252
268
 
253
269
  app.delete(`${API_PREFIX}/projects/:projectName`, async (req, res) => {
254
270
  try {
255
- res.status(200).json(
256
- await projectStore.deleteProject(req.params.projectName),
257
- );
271
+ const project = await projectStore.deleteProject(req.params.projectName);
272
+ res.status(200).json(await project.serialize());
258
273
  } catch (error) {
259
274
  logger.error(error);
260
275
  const { json, status } = internalErrorToHttpError(error as Error);
@@ -296,12 +311,11 @@ app.get(
296
311
  `${API_PREFIX}/projects/:projectName/connections/:connectionName/test`,
297
312
  async (req, res) => {
298
313
  try {
299
- res.status(200).json(
300
- await connectionController.testConnection(
301
- req.params.projectName,
302
- req.params.connectionName,
303
- ),
314
+ const connectionStatus = await connectionController.testConnection(
315
+ req.params.projectName,
316
+ req.params.connectionName,
304
317
  );
318
+ res.status(200).json(connectionStatus);
305
319
  } catch (error) {
306
320
  logger.error(error);
307
321
  const { json, status } = internalErrorToHttpError(error as Error);
@@ -445,9 +459,11 @@ app.get(`${API_PREFIX}/projects/:projectName/packages`, async (req, res) => {
445
459
 
446
460
  app.post(`${API_PREFIX}/projects/:projectName/packages`, async (req, res) => {
447
461
  try {
448
- res.status(200).json(
449
- await packageController.addPackage(req.params.projectName, req.body),
462
+ const _package = await packageController.addPackage(
463
+ req.params.projectName,
464
+ req.body,
450
465
  );
466
+ res.status(200).json(_package?.getPackageMetadata());
451
467
  } catch (error) {
452
468
  logger.error(error);
453
469
  const { json, status } = internalErrorToHttpError(error as Error);
@@ -705,6 +721,7 @@ app.use(
705
721
  );
706
722
 
707
723
  const mainServer = http.createServer(app);
724
+
708
725
  mainServer.listen(PUBLISHER_PORT, PUBLISHER_HOST, () => {
709
726
  const address = mainServer.address() as AddressInfo;
710
727
  logger.info(
@@ -716,9 +733,6 @@ mainServer.listen(PUBLISHER_PORT, PUBLISHER_HOST, () => {
716
733
  );
717
734
  }
718
735
  });
719
-
720
- const mcpHttpServer = mcpApp.listen(MCP_PORT, PUBLISHER_HOST, () => {
736
+ mcpApp.listen(MCP_PORT, PUBLISHER_HOST, () => {
721
737
  logger.info(`MCP server listening at http://${PUBLISHER_HOST}:${MCP_PORT}`);
722
738
  });
723
-
724
- export { app, mainServer as httpServer, mcpApp, mcpHttpServer };