@malloy-publisher/server 0.0.79 → 0.0.81
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/dist/app/assets/{RenderedResult-BAZuT25g-BvrSEe60.js → RenderedResult-BAZuT25g-DO0bulFJ.js} +2 -2
- package/dist/app/assets/{index-DWXKogRO.js → index-9YQno_QP.js} +1 -1
- package/dist/app/assets/{index-0tj7kiI6.js → index-B3C-uSNk.js} +1 -1
- package/dist/app/assets/{index-EoKisyOf.js → index-D2AowBDH.js} +114 -114
- package/dist/app/assets/{index.umd-BzdkHCNs.js → index.umd-DAKfNegB.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/instrumentation.js +10262 -3
- package/dist/server.js +149449 -150333
- package/package.json +1 -1
- package/src/controller/connection.controller.ts +3 -2
- package/src/data_styles.ts +2 -1
- package/src/instrumentation.ts +5 -5
- package/src/logger.ts +46 -0
- package/src/mcp/handler_utils.ts +7 -6
- package/src/mcp/prompts/prompt_service.ts +12 -12
- package/src/mcp/resources/notebook_resource.ts +11 -10
- package/src/mcp/resources/package_resource.ts +21 -20
- package/src/mcp/resources/project_resource.ts +22 -23
- package/src/mcp/resources/query_resource.ts +12 -11
- package/src/mcp/resources/source_resource.ts +12 -11
- package/src/mcp/resources/view_resource.ts +12 -11
- package/src/mcp/server.ts +22 -21
- package/src/mcp/tools/execute_query_tool.ts +8 -11
- package/src/server.ts +42 -45
- package/src/service/db_utils.ts +17 -17
- package/src/service/model.ts +2 -1
- package/src/service/package.ts +3 -2
- package/src/service/project_store.ts +7 -7
- package/src/service/scheduler.ts +4 -3
package/src/mcp/server.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { ProjectStore } from "../service/project_store";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { logger } from "../logger";
|
|
5
|
+
import { registerPromptCapability } from "./prompts/prompt_service.js";
|
|
6
6
|
import { registerModelResource } from "./resources/model_resource";
|
|
7
|
-
import {
|
|
7
|
+
import { registerNotebookResource } from "./resources/notebook_resource";
|
|
8
|
+
import { registerPackageResource } from "./resources/package_resource";
|
|
9
|
+
import { registerProjectResource } from "./resources/project_resource";
|
|
8
10
|
import { registerQueryResource } from "./resources/query_resource";
|
|
11
|
+
import { registerSourceResource } from "./resources/source_resource";
|
|
9
12
|
import { registerViewResource } from "./resources/view_resource";
|
|
10
|
-
import { registerNotebookResource } from "./resources/notebook_resource";
|
|
11
|
-
import { registerExecuteQueryTool } from "./tools/execute_query_tool";
|
|
12
13
|
import { registerTools } from "./tools/discovery_tools";
|
|
13
|
-
import {
|
|
14
|
+
import { registerExecuteQueryTool } from "./tools/execute_query_tool";
|
|
14
15
|
|
|
15
16
|
export const testServerInfo = {
|
|
16
17
|
name: "malloy-publisher-mcp-server",
|
|
@@ -20,42 +21,42 @@ export const testServerInfo = {
|
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
export function initializeMcpServer(projectStore: ProjectStore): McpServer {
|
|
23
|
-
|
|
24
|
-
const startTime =
|
|
24
|
+
logger.info("[MCP Init] Starting initializeMcpServer...");
|
|
25
|
+
const startTime = performance.now();
|
|
25
26
|
|
|
26
27
|
const mcpServer = new McpServer(testServerInfo);
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
logger.info("[MCP Init] Registering project resource...");
|
|
29
30
|
registerProjectResource(mcpServer, projectStore);
|
|
30
|
-
|
|
31
|
+
logger.info("[MCP Init] Registering package resource...");
|
|
31
32
|
registerPackageResource(mcpServer, projectStore);
|
|
32
33
|
|
|
33
34
|
// Register more specific templates first
|
|
34
|
-
|
|
35
|
+
logger.info("[MCP Init] Registering notebook resource...");
|
|
35
36
|
registerNotebookResource(mcpServer, projectStore);
|
|
36
|
-
|
|
37
|
+
logger.info("[MCP Init] Registering source resource...");
|
|
37
38
|
registerSourceResource(mcpServer, projectStore);
|
|
38
|
-
|
|
39
|
+
logger.info("[MCP Init] Registering query resource...");
|
|
39
40
|
registerQueryResource(mcpServer, projectStore);
|
|
40
|
-
|
|
41
|
+
logger.info("[MCP Init] Registering view resource...");
|
|
41
42
|
registerViewResource(mcpServer, projectStore);
|
|
42
43
|
|
|
43
44
|
// Register the general model template last among resource types
|
|
44
|
-
|
|
45
|
+
logger.info("[MCP Init] Registering model resource...");
|
|
45
46
|
registerModelResource(mcpServer, projectStore);
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
logger.info("[MCP Init] Registering executeQuery tool...");
|
|
48
49
|
registerExecuteQueryTool(mcpServer, projectStore);
|
|
49
50
|
|
|
50
51
|
registerTools(mcpServer, projectStore);
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
logger.info("[MCP Init] Registering prompt capability...");
|
|
53
54
|
registerPromptCapability(mcpServer, projectStore);
|
|
54
55
|
|
|
55
|
-
const endTime =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
);
|
|
56
|
+
const endTime = performance.now();
|
|
57
|
+
logger.info(`[MCP Init] Finished initializeMcpServer`, {
|
|
58
|
+
duration: endTime - startTime,
|
|
59
|
+
});
|
|
59
60
|
|
|
60
61
|
return mcpServer;
|
|
61
62
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { logger } from "../../logger";
|
|
4
5
|
import { ProjectStore } from "../../service/project_store";
|
|
5
|
-
import { getModelForQuery } from "../handler_utils";
|
|
6
6
|
import { getMalloyErrorDetails, type ErrorDetails } from "../error_messages";
|
|
7
|
+
import { buildMalloyUri, getModelForQuery } from "../handler_utils";
|
|
7
8
|
import { MCP_ERROR_MESSAGES } from "../mcp_constants";
|
|
8
|
-
import { buildMalloyUri } from "../handler_utils";
|
|
9
9
|
|
|
10
10
|
// Zod shape defining required/optional params for executeQuery
|
|
11
11
|
const executeQueryShape = {
|
|
@@ -51,10 +51,7 @@ export function registerExecuteQueryTool(
|
|
|
51
51
|
queryName,
|
|
52
52
|
} = params;
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
"[MCP Tool executeQuery] Received params:",
|
|
56
|
-
JSON.stringify(params),
|
|
57
|
-
);
|
|
54
|
+
logger.info("[MCP Tool executeQuery] Received params:", { params });
|
|
58
55
|
|
|
59
56
|
const hasAdhocQuery = !!query;
|
|
60
57
|
const hasNamedQuery = !!queryName;
|
|
@@ -74,7 +71,7 @@ export function registerExecuteQueryTool(
|
|
|
74
71
|
// Zod/SDK handles missing required fields (packageName, modelPath) based on the shape
|
|
75
72
|
|
|
76
73
|
// --- Get Package and Model ---
|
|
77
|
-
|
|
74
|
+
logger.info(
|
|
78
75
|
`[MCP Tool executeQuery] Calling getModelForQuery for ${projectName}/${packageName}/${modelPath}`,
|
|
79
76
|
);
|
|
80
77
|
const modelResult = await getModelForQuery(
|
|
@@ -113,7 +110,7 @@ export function registerExecuteQueryTool(
|
|
|
113
110
|
|
|
114
111
|
// --- Execute Query ---
|
|
115
112
|
const { model } = modelResult;
|
|
116
|
-
|
|
113
|
+
logger.info(
|
|
117
114
|
`[MCP Tool executeQuery] Model found. Proceeding to execute query.`,
|
|
118
115
|
);
|
|
119
116
|
try {
|
|
@@ -192,9 +189,9 @@ export function registerExecuteQueryTool(
|
|
|
192
189
|
);
|
|
193
190
|
} catch (queryError) {
|
|
194
191
|
// Handle query execution errors (syntax errors, invalid queries, etc.)
|
|
195
|
-
|
|
192
|
+
logger.error(
|
|
196
193
|
`[MCP Server Error] Error executing query in ${projectName}/${packageName}/${modelPath}:`,
|
|
197
|
-
queryError,
|
|
194
|
+
{ error: queryError },
|
|
198
195
|
);
|
|
199
196
|
const errorDetails: ErrorDetails = getMalloyErrorDetails(
|
|
200
197
|
"executeQuery",
|
package/src/server.ts
CHANGED
|
@@ -4,7 +4,6 @@ import cors from "cors";
|
|
|
4
4
|
import express from "express";
|
|
5
5
|
import * as http from "http";
|
|
6
6
|
import { createProxyMiddleware } from "http-proxy-middleware";
|
|
7
|
-
import morgan from "morgan";
|
|
8
7
|
import { AddressInfo } from "net";
|
|
9
8
|
import * as path from "path";
|
|
10
9
|
import { ConnectionController } from "./controller/connection.controller";
|
|
@@ -14,6 +13,7 @@ import { PackageController } from "./controller/package.controller";
|
|
|
14
13
|
import { QueryController } from "./controller/query.controller";
|
|
15
14
|
import { ScheduleController } from "./controller/schedule.controller";
|
|
16
15
|
import { internalErrorToHttpError, NotImplementedError } from "./errors";
|
|
16
|
+
import { logger, loggerMiddleware } from "./logger";
|
|
17
17
|
import { initializeMcpServer } from "./mcp/server";
|
|
18
18
|
import { ProjectStore } from "./service/project_store";
|
|
19
19
|
|
|
@@ -79,7 +79,8 @@ const API_PREFIX = "/api/v0";
|
|
|
79
79
|
const isDevelopment = process.env["NODE_ENV"] === "development";
|
|
80
80
|
|
|
81
81
|
const app = express();
|
|
82
|
-
app.use(
|
|
82
|
+
app.use(loggerMiddleware);
|
|
83
|
+
app.use(cors());
|
|
83
84
|
|
|
84
85
|
const projectStore = new ProjectStore(SERVER_ROOT);
|
|
85
86
|
const connectionController = new ConnectionController(projectStore);
|
|
@@ -95,7 +96,7 @@ mcpApp.use(MCP_ENDPOINT, express.json());
|
|
|
95
96
|
mcpApp.use(MCP_ENDPOINT, cors());
|
|
96
97
|
|
|
97
98
|
mcpApp.all(MCP_ENDPOINT, async (req, res) => {
|
|
98
|
-
|
|
99
|
+
logger.info(`[MCP Debug] Handling ${req.method} (Stateless)`);
|
|
99
100
|
|
|
100
101
|
try {
|
|
101
102
|
if (req.method === "POST") {
|
|
@@ -104,35 +105,34 @@ mcpApp.all(MCP_ENDPOINT, async (req, res) => {
|
|
|
104
105
|
});
|
|
105
106
|
|
|
106
107
|
transport.onclose = () => {
|
|
107
|
-
|
|
108
|
+
logger.info(
|
|
108
109
|
`[MCP Transport Info] Stateless transport closed for a request.`,
|
|
109
110
|
);
|
|
110
111
|
};
|
|
111
112
|
transport.onerror = (err: Error) => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
);
|
|
113
|
+
logger.error(`[MCP Transport Error] Stateless transport error:`, {
|
|
114
|
+
error: err,
|
|
115
|
+
});
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
const requestMcpServer = initializeMcpServer(projectStore);
|
|
119
119
|
await requestMcpServer.connect(transport);
|
|
120
120
|
|
|
121
121
|
res.on("close", () => {
|
|
122
|
-
|
|
122
|
+
logger.info(
|
|
123
123
|
"[MCP Transport Info] Response closed, cleaning up stateless transport.",
|
|
124
124
|
);
|
|
125
125
|
transport.close().catch((err) => {
|
|
126
|
-
|
|
126
|
+
logger.error(
|
|
127
127
|
"[MCP Transport Error] Error closing stateless transport on response close:",
|
|
128
|
-
err,
|
|
128
|
+
{ error: err },
|
|
129
129
|
);
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
await transport.handleRequest(req, res, req.body);
|
|
134
134
|
} else if (req.method === "GET" || req.method === "DELETE") {
|
|
135
|
-
|
|
135
|
+
logger.warn(
|
|
136
136
|
`[MCP Transport Warn] Method Not Allowed in Stateless Mode: ${req.method}`,
|
|
137
137
|
);
|
|
138
138
|
res.setHeader("Allow", "POST");
|
|
@@ -146,7 +146,7 @@ mcpApp.all(MCP_ENDPOINT, async (req, res) => {
|
|
|
146
146
|
});
|
|
147
147
|
return;
|
|
148
148
|
} else {
|
|
149
|
-
|
|
149
|
+
logger.warn(`[MCP Transport Warn] Method Not Allowed: ${req.method}`);
|
|
150
150
|
res.setHeader("Allow", "POST");
|
|
151
151
|
res.status(405).json({
|
|
152
152
|
jsonrpc: "2.0",
|
|
@@ -156,9 +156,9 @@ mcpApp.all(MCP_ENDPOINT, async (req, res) => {
|
|
|
156
156
|
return;
|
|
157
157
|
}
|
|
158
158
|
} catch (error) {
|
|
159
|
-
|
|
159
|
+
logger.error(
|
|
160
160
|
`[MCP Transport Error] Unhandled error in ${req.method} handler (Stateless):`,
|
|
161
|
-
error,
|
|
161
|
+
{ error },
|
|
162
162
|
);
|
|
163
163
|
if (!res.headersSent) {
|
|
164
164
|
res.status(500).json({
|
|
@@ -183,10 +183,7 @@ if (!isDevelopment) {
|
|
|
183
183
|
} else {
|
|
184
184
|
// In development mode, proxy requests to React dev server
|
|
185
185
|
// Handle API routes first
|
|
186
|
-
app.use(`${API_PREFIX}`,
|
|
187
|
-
console.log(`[Express] Handling API request: ${req.method} ${req.url}`);
|
|
188
|
-
next();
|
|
189
|
-
});
|
|
186
|
+
app.use(`${API_PREFIX}`, loggerMiddleware);
|
|
190
187
|
|
|
191
188
|
// Proxy everything else to Vite
|
|
192
189
|
app.use(
|
|
@@ -213,7 +210,7 @@ app.get(`${API_PREFIX}/projects`, async (_req, res) => {
|
|
|
213
210
|
try {
|
|
214
211
|
res.status(200).json(await projectStore.listProjects());
|
|
215
212
|
} catch (error) {
|
|
216
|
-
|
|
213
|
+
logger.error(error);
|
|
217
214
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
218
215
|
res.status(status).json(json);
|
|
219
216
|
}
|
|
@@ -227,7 +224,7 @@ app.get(`${API_PREFIX}/projects/:projectName`, async (req, res) => {
|
|
|
227
224
|
);
|
|
228
225
|
res.status(200).json(await project.getProjectMetadata());
|
|
229
226
|
} catch (error) {
|
|
230
|
-
|
|
227
|
+
logger.error(error);
|
|
231
228
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
232
229
|
res.status(status).json(json);
|
|
233
230
|
}
|
|
@@ -239,7 +236,7 @@ app.get(`${API_PREFIX}/projects/:projectName/connections`, async (req, res) => {
|
|
|
239
236
|
await connectionController.listConnections(req.params.projectName),
|
|
240
237
|
);
|
|
241
238
|
} catch (error) {
|
|
242
|
-
|
|
239
|
+
logger.error(error);
|
|
243
240
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
244
241
|
res.status(status).json(json);
|
|
245
242
|
}
|
|
@@ -256,7 +253,7 @@ app.get(
|
|
|
256
253
|
),
|
|
257
254
|
);
|
|
258
255
|
} catch (error) {
|
|
259
|
-
|
|
256
|
+
logger.error(error);
|
|
260
257
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
261
258
|
res.status(status).json(json);
|
|
262
259
|
}
|
|
@@ -274,7 +271,7 @@ app.get(
|
|
|
274
271
|
),
|
|
275
272
|
);
|
|
276
273
|
} catch (error) {
|
|
277
|
-
|
|
274
|
+
logger.error(error);
|
|
278
275
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
279
276
|
res.status(status).json(json);
|
|
280
277
|
}
|
|
@@ -292,7 +289,7 @@ app.get(
|
|
|
292
289
|
),
|
|
293
290
|
);
|
|
294
291
|
} catch (error) {
|
|
295
|
-
|
|
292
|
+
logger.error(error);
|
|
296
293
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
297
294
|
res.status(status).json(json);
|
|
298
295
|
}
|
|
@@ -302,17 +299,17 @@ app.get(
|
|
|
302
299
|
app.get(
|
|
303
300
|
`${API_PREFIX}/projects/:projectName/connections/:connectionName/schemas/:schemaName/tables`,
|
|
304
301
|
async (req, res) => {
|
|
305
|
-
|
|
302
|
+
logger.info("req.params", { params: req.params });
|
|
306
303
|
try {
|
|
307
304
|
const results = await connectionController.listTables(
|
|
308
305
|
req.params.projectName,
|
|
309
306
|
req.params.connectionName,
|
|
310
307
|
req.params.schemaName,
|
|
311
308
|
);
|
|
312
|
-
|
|
309
|
+
logger.info("results", { results });
|
|
313
310
|
res.status(200).json(results);
|
|
314
311
|
} catch (error) {
|
|
315
|
-
|
|
312
|
+
logger.error(error);
|
|
316
313
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
317
314
|
res.status(status).json(json);
|
|
318
315
|
}
|
|
@@ -331,7 +328,7 @@ app.get(
|
|
|
331
328
|
),
|
|
332
329
|
);
|
|
333
330
|
} catch (error) {
|
|
334
|
-
|
|
331
|
+
logger.error(error);
|
|
335
332
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
336
333
|
res.status(status).json(json);
|
|
337
334
|
}
|
|
@@ -351,7 +348,7 @@ app.get(
|
|
|
351
348
|
),
|
|
352
349
|
);
|
|
353
350
|
} catch (error) {
|
|
354
|
-
|
|
351
|
+
logger.error(error);
|
|
355
352
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
356
353
|
res.status(status).json(json);
|
|
357
354
|
}
|
|
@@ -371,7 +368,7 @@ app.get(
|
|
|
371
368
|
),
|
|
372
369
|
);
|
|
373
370
|
} catch (error) {
|
|
374
|
-
|
|
371
|
+
logger.error(error);
|
|
375
372
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
376
373
|
res.status(status).json(json);
|
|
377
374
|
}
|
|
@@ -390,7 +387,7 @@ app.get(
|
|
|
390
387
|
),
|
|
391
388
|
);
|
|
392
389
|
} catch (error) {
|
|
393
|
-
|
|
390
|
+
logger.error(error);
|
|
394
391
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
395
392
|
res.status(status).json(json);
|
|
396
393
|
}
|
|
@@ -408,7 +405,7 @@ app.get(`${API_PREFIX}/projects/:projectName/packages`, async (req, res) => {
|
|
|
408
405
|
await packageController.listPackages(req.params.projectName),
|
|
409
406
|
);
|
|
410
407
|
} catch (error) {
|
|
411
|
-
|
|
408
|
+
logger.error(error);
|
|
412
409
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
413
410
|
res.status(status).json(json);
|
|
414
411
|
}
|
|
@@ -431,7 +428,7 @@ app.get(
|
|
|
431
428
|
),
|
|
432
429
|
);
|
|
433
430
|
} catch (error) {
|
|
434
|
-
|
|
431
|
+
logger.error(error);
|
|
435
432
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
436
433
|
res.status(status).json(json);
|
|
437
434
|
}
|
|
@@ -454,7 +451,7 @@ app.get(
|
|
|
454
451
|
),
|
|
455
452
|
);
|
|
456
453
|
} catch (error) {
|
|
457
|
-
|
|
454
|
+
logger.error(error);
|
|
458
455
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
459
456
|
res.status(status).json(json);
|
|
460
457
|
}
|
|
@@ -479,7 +476,7 @@ app.get(
|
|
|
479
476
|
),
|
|
480
477
|
);
|
|
481
478
|
} catch (error) {
|
|
482
|
-
|
|
479
|
+
logger.error(error);
|
|
483
480
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
484
481
|
res.status(status).json(json);
|
|
485
482
|
}
|
|
@@ -502,7 +499,7 @@ app.get(
|
|
|
502
499
|
),
|
|
503
500
|
);
|
|
504
501
|
} catch (error) {
|
|
505
|
-
|
|
502
|
+
logger.error(error);
|
|
506
503
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
507
504
|
res.status(status).json(json);
|
|
508
505
|
}
|
|
@@ -527,7 +524,7 @@ app.get(
|
|
|
527
524
|
),
|
|
528
525
|
);
|
|
529
526
|
} catch (error) {
|
|
530
|
-
|
|
527
|
+
logger.error(error);
|
|
531
528
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
532
529
|
res.status(status).json(json);
|
|
533
530
|
}
|
|
@@ -555,7 +552,7 @@ app.get(
|
|
|
555
552
|
),
|
|
556
553
|
);
|
|
557
554
|
} catch (error) {
|
|
558
|
-
|
|
555
|
+
logger.error(error);
|
|
559
556
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
560
557
|
res.status(status).json(json);
|
|
561
558
|
}
|
|
@@ -578,7 +575,7 @@ app.get(
|
|
|
578
575
|
),
|
|
579
576
|
);
|
|
580
577
|
} catch (error) {
|
|
581
|
-
|
|
578
|
+
logger.error(error);
|
|
582
579
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
583
580
|
res.status(status).json(json);
|
|
584
581
|
}
|
|
@@ -601,7 +598,7 @@ app.get(
|
|
|
601
598
|
),
|
|
602
599
|
);
|
|
603
600
|
} catch (error) {
|
|
604
|
-
|
|
601
|
+
logger.error(error);
|
|
605
602
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
606
603
|
res.status(status).json(json);
|
|
607
604
|
}
|
|
@@ -614,7 +611,7 @@ if (!isDevelopment) {
|
|
|
614
611
|
}
|
|
615
612
|
|
|
616
613
|
app.use((err: Error, _req: express.Request, res: express.Response) => {
|
|
617
|
-
|
|
614
|
+
logger.error("Unhandled error:", err);
|
|
618
615
|
const { json, status } = internalErrorToHttpError(err);
|
|
619
616
|
res.status(status).json(json);
|
|
620
617
|
});
|
|
@@ -622,18 +619,18 @@ app.use((err: Error, _req: express.Request, res: express.Response) => {
|
|
|
622
619
|
const mainServer = http.createServer(app);
|
|
623
620
|
mainServer.listen(PUBLISHER_PORT, PUBLISHER_HOST, () => {
|
|
624
621
|
const address = mainServer.address() as AddressInfo;
|
|
625
|
-
|
|
622
|
+
logger.info(
|
|
626
623
|
`Publisher server listening at http://${address.address}:${address.port}`,
|
|
627
624
|
);
|
|
628
625
|
if (isDevelopment) {
|
|
629
|
-
|
|
626
|
+
logger.info(
|
|
630
627
|
"Running in development mode - proxying to React dev server at http://localhost:5173",
|
|
631
628
|
);
|
|
632
629
|
}
|
|
633
630
|
});
|
|
634
631
|
|
|
635
632
|
const mcpHttpServer = mcpApp.listen(MCP_PORT, PUBLISHER_HOST, () => {
|
|
636
|
-
|
|
633
|
+
logger.info(`MCP server listening at http://${PUBLISHER_HOST}:${MCP_PORT}`);
|
|
637
634
|
});
|
|
638
635
|
|
|
639
636
|
export { app, mainServer as httpServer, mcpApp, mcpHttpServer };
|
package/src/service/db_utils.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { BigQuery } from "@google-cloud/bigquery";
|
|
1
2
|
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
3
|
import os from "os";
|
|
4
|
-
import
|
|
4
|
+
import path from "path";
|
|
5
5
|
import { Pool } from "pg";
|
|
6
|
-
import { BigQuery } from "@google-cloud/bigquery";
|
|
7
6
|
import * as snowflake from "snowflake-sdk";
|
|
7
|
+
import { v4 as uuidv4 } from "uuid";
|
|
8
|
+
import { components } from "../api";
|
|
9
|
+
import { logger } from "../logger";
|
|
8
10
|
import {
|
|
9
11
|
ApiConnection,
|
|
12
|
+
MysqlConnection,
|
|
10
13
|
PostgresConnection,
|
|
11
14
|
SnowflakeConnection,
|
|
12
|
-
MysqlConnection,
|
|
13
15
|
} from "./model";
|
|
14
|
-
import { components } from "../api";
|
|
15
16
|
|
|
16
17
|
type ApiSchemaName = components["schemas"]["SchemaName"];
|
|
17
18
|
|
|
@@ -156,7 +157,7 @@ export async function getSchemasForConnection(
|
|
|
156
157
|
} finally {
|
|
157
158
|
snowflakeConn.destroy((error) => {
|
|
158
159
|
if (error) {
|
|
159
|
-
|
|
160
|
+
logger.error(`Error closing SnowflakeConnection: ${error}`);
|
|
160
161
|
}
|
|
161
162
|
});
|
|
162
163
|
}
|
|
@@ -188,9 +189,9 @@ export async function getTablesForSchema(
|
|
|
188
189
|
const [tables] = await dataset.getTables();
|
|
189
190
|
return tables.map((table) => table.id).filter((id) => id) as string[];
|
|
190
191
|
} catch (error) {
|
|
191
|
-
|
|
192
|
-
`Error getting tables for BigQuery schema ${schemaName} in connection ${connection.name}
|
|
193
|
-
error,
|
|
192
|
+
logger.error(
|
|
193
|
+
`Error getting tables for BigQuery schema ${schemaName} in connection ${connection.name}`,
|
|
194
|
+
{ error },
|
|
194
195
|
);
|
|
195
196
|
throw new Error(
|
|
196
197
|
`Failed to get tables for BigQuery schema ${schemaName} in connection ${connection.name}: ${(error as Error).message}`,
|
|
@@ -232,7 +233,7 @@ export async function getTablesForSchema(
|
|
|
232
233
|
} finally {
|
|
233
234
|
snowflakeConn.destroy((error) => {
|
|
234
235
|
if (error) {
|
|
235
|
-
|
|
236
|
+
logger.error(`Error closing SnowflakeConnection`, { error });
|
|
236
237
|
}
|
|
237
238
|
});
|
|
238
239
|
}
|
|
@@ -274,10 +275,9 @@ async function getSnowflakeTables(
|
|
|
274
275
|
sqlText: `USE DATABASE ${connInfo?.database} `,
|
|
275
276
|
complete: (err) => {
|
|
276
277
|
if (err) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
);
|
|
278
|
+
logger.error(`Error setting database ${connInfo.database}:`, {
|
|
279
|
+
error: err,
|
|
280
|
+
});
|
|
281
281
|
reject([]);
|
|
282
282
|
return;
|
|
283
283
|
}
|
|
@@ -293,9 +293,9 @@ async function getSnowflakeTables(
|
|
|
293
293
|
binds: [schemaName],
|
|
294
294
|
complete: (err, _, rows) => {
|
|
295
295
|
if (err) {
|
|
296
|
-
|
|
296
|
+
logger.error(
|
|
297
297
|
`Error fetching tables from ${connInfo.database}:`,
|
|
298
|
-
err,
|
|
298
|
+
{ error: err },
|
|
299
299
|
);
|
|
300
300
|
reject([]);
|
|
301
301
|
} else {
|
|
@@ -320,7 +320,7 @@ async function getSnowflakeSchemas(
|
|
|
320
320
|
} else {
|
|
321
321
|
resolve(
|
|
322
322
|
rows?.map((row) => {
|
|
323
|
-
|
|
323
|
+
logger.info("row", { row });
|
|
324
324
|
return {
|
|
325
325
|
name: row.name,
|
|
326
326
|
isDefault: row.isDefault === "Y",
|
package/src/service/model.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
ModelCompilationError,
|
|
32
32
|
ModelNotFoundError,
|
|
33
33
|
} from "../errors";
|
|
34
|
+
import { logger } from "../logger";
|
|
34
35
|
import {
|
|
35
36
|
MODEL_FILE_SUFFIX,
|
|
36
37
|
NOTEBOOK_FILE_SUFFIX,
|
|
@@ -238,7 +239,7 @@ export class Model {
|
|
|
238
239
|
if (this.compilationError) {
|
|
239
240
|
throw this.compilationError;
|
|
240
241
|
}
|
|
241
|
-
|
|
242
|
+
logger.info("queryName", { queryName, query });
|
|
242
243
|
let runnable: QueryMaterializer;
|
|
243
244
|
if (!this.modelMaterializer || !this.modelDef || !this.modelInfo)
|
|
244
245
|
throw new BadRequestError("Model has no queryable entities.");
|
package/src/service/package.ts
CHANGED
|
@@ -13,6 +13,7 @@ import recursive from "recursive-readdir";
|
|
|
13
13
|
import { components } from "../api";
|
|
14
14
|
import { API_PREFIX } from "../constants";
|
|
15
15
|
import { PackageNotFoundError } from "../errors";
|
|
16
|
+
import { logger } from "../logger";
|
|
16
17
|
import {
|
|
17
18
|
MODEL_FILE_SUFFIX,
|
|
18
19
|
NOTEBOOK_FILE_SUFFIX,
|
|
@@ -116,7 +117,7 @@ export class Package {
|
|
|
116
117
|
scheduler,
|
|
117
118
|
);
|
|
118
119
|
} catch (error) {
|
|
119
|
-
|
|
120
|
+
logger.error("Error loading package", { error });
|
|
120
121
|
const endTime = performance.now();
|
|
121
122
|
const executionTime = endTime - startTime;
|
|
122
123
|
this.packageLoadHistogram.record(executionTime, {
|
|
@@ -219,7 +220,7 @@ export class Package {
|
|
|
219
220
|
try {
|
|
220
221
|
files = await recursive(packagePath);
|
|
221
222
|
} catch (error) {
|
|
222
|
-
|
|
223
|
+
logger.error(error);
|
|
223
224
|
throw new PackageNotFoundError(
|
|
224
225
|
`Package config for ${packagePath} does not exist.`,
|
|
225
226
|
);
|
|
@@ -3,6 +3,7 @@ import * as path from "path";
|
|
|
3
3
|
import { components } from "../api";
|
|
4
4
|
import { API_PREFIX } from "../constants";
|
|
5
5
|
import { ProjectNotFoundError } from "../errors";
|
|
6
|
+
import { logger } from "../logger";
|
|
6
7
|
import { Project } from "./project";
|
|
7
8
|
type ApiProject = components["schemas"]["Project"];
|
|
8
9
|
|
|
@@ -68,9 +69,9 @@ export class ProjectStore {
|
|
|
68
69
|
return JSON.parse(projectManifestContent);
|
|
69
70
|
} catch (error) {
|
|
70
71
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
logger.error(
|
|
73
|
+
`Error reading publisher.config.json. Generating from directory`,
|
|
74
|
+
{ error },
|
|
74
75
|
);
|
|
75
76
|
return { projects: {} };
|
|
76
77
|
} else {
|
|
@@ -87,10 +88,9 @@ export class ProjectStore {
|
|
|
87
88
|
}
|
|
88
89
|
return { projects };
|
|
89
90
|
} catch (lsError) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
91
|
+
logger.error(`Error listing directories in ${serverRootPath}`, {
|
|
92
|
+
error: lsError,
|
|
93
|
+
});
|
|
94
94
|
return { projects: {} };
|
|
95
95
|
}
|
|
96
96
|
}
|
package/src/service/scheduler.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Model } from "./model";
|
|
2
|
-
import { components } from "../api";
|
|
3
1
|
import cron from "node-cron";
|
|
2
|
+
import { components } from "../api";
|
|
3
|
+
import { logger } from "../logger";
|
|
4
|
+
import { Model } from "./model";
|
|
4
5
|
|
|
5
6
|
type ApiSource = components["schemas"]["Source"];
|
|
6
7
|
type ApiView = components["schemas"]["View"];
|
|
@@ -72,7 +73,7 @@ class Schedule {
|
|
|
72
73
|
// TODO: Don't split quoted strings. Use regex instead of split.
|
|
73
74
|
const annotationSplit = annotation.split(/\s+/);
|
|
74
75
|
if (annotationSplit.length != 6) {
|
|
75
|
-
|
|
76
|
+
logger.info("Length: " + annotationSplit.length);
|
|
76
77
|
throw new Error(
|
|
77
78
|
"Invalid annotation string does not have enough parts: " +
|
|
78
79
|
annotation,
|