@malloy-publisher/server 0.0.382-dev → 0.2.0
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/README.docker.md +145 -20
- package/README.md +65 -12
- package/dist/app/api-doc.html +5 -0
- package/dist/app/api-doc.yaml +3903 -583
- package/dist/app/assets/EnvironmentPage-DiUgLuCH.js +1 -0
- package/dist/app/assets/HomePage-MhhjhnFj.js +1 -0
- package/dist/app/assets/LightMode-BEyvNUzT.js +1 -0
- package/dist/app/assets/MainPage-D4Cfee9o.js +2 -0
- package/dist/app/assets/MaterializationsPage-nGgGqwSN.js +1 -0
- package/dist/app/assets/ModelPage-DLAcEw-F.js +1 -0
- package/dist/app/assets/PackagePage-Df0ikH7w.js +1 -0
- package/dist/app/assets/RouteError-BFQwtroH.js +1 -0
- package/dist/app/assets/ThemeEditorPage-OoskoQYN.js +1 -0
- package/dist/app/assets/WorkbookPage-CpFgvUDC.js +1 -0
- package/dist/app/assets/{core-CjeTkq8O.es-BqRc6yhC.js → core-lb28vQkR.es-BoVSOc75.js} +1 -1
- package/dist/app/assets/github-dark-DenFmJkN.es-DHJKELXO.js +1 -0
- package/dist/app/assets/index-5eLCcNmP.css +1 -0
- package/dist/app/assets/index-6ypuFH6g.js +1774 -0
- package/dist/app/assets/index-B8pnU_V_.js +615 -0
- package/dist/app/assets/index-BImcHmVP.js +1 -0
- package/dist/app/assets/index-DSoCUF-f.js +2725 -0
- package/dist/app/assets/index-DfqzSItA.js +2 -0
- package/dist/app/assets/index-w_0OQJgZ.js +23 -0
- package/dist/app/index.html +8 -4
- package/dist/cpufeatures-1yrn0vtw.node +0 -0
- package/dist/default-publisher.config.json +23 -0
- package/dist/{instrumentation.js → instrumentation.mjs} +10631 -10573
- package/dist/package_load_worker.mjs +15219 -0
- package/dist/runtime/publisher.js +331 -0
- package/dist/{server.js → server.mjs} +115548 -49590
- package/dist/sshcrypto-vd2k5hq9.node +0 -0
- package/package.json +41 -28
- package/publisher.config.example.bigquery.json +33 -0
- package/publisher.config.example.duckdb.json +23 -0
- package/.eslintrc.json +0 -44
- package/.prettierignore +0 -1
- package/.prettierrc +0 -4
- package/build.ts +0 -38
- package/dist/app/assets/HomePage-Dn3E4CuB.js +0 -1
- package/dist/app/assets/MainPage-BzB3yoqi.js +0 -2
- package/dist/app/assets/ModelPage-C9O_sAXT.js +0 -1
- package/dist/app/assets/PackagePage-DcxKEjBX.js +0 -1
- package/dist/app/assets/ProjectPage-BDj307rF.js +0 -1
- package/dist/app/assets/RouteError-DAShbVCG.js +0 -1
- package/dist/app/assets/WorkbookPage-Cs_XYEaB.js +0 -1
- package/dist/app/assets/index-15BOvhp0.js +0 -456
- package/dist/app/assets/index-Bb2jqquW.js +0 -676
- package/dist/app/assets/index-CMlGQMcl.css +0 -1
- package/dist/app/assets/index-D68X76-7.js +0 -1276
- package/dist/app/assets/index.umd-DGBekgSu.js +0 -1145
- package/dxt/malloy_bridge.py +0 -354
- package/dxt/manifest.json +0 -22
- package/eslint.config.mjs +0 -76
- package/k6-tests/README.md +0 -144
- package/k6-tests/bun.lock +0 -797
- package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
- package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
- package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
- package/k6-tests/load-test/load-test-crud.ts +0 -142
- package/k6-tests/load-test/load-test.ts +0 -784
- package/k6-tests/package.json +0 -27
- package/k6-tests/scripts/run-with-otel.sh +0 -50
- package/k6-tests/smoke-test/smoke-test.ts +0 -158
- package/k6-tests/tsconfig.json +0 -29
- package/k6-tests/utils/client_factory.ts +0 -128
- package/k6-tests/utils/common.ts +0 -489
- package/k6-tests/utils/logger.ts +0 -62
- package/k6-tests/utils/types.d.ts +0 -35
- package/malloy_mcp.dxt +0 -0
- package/publisher.config.json +0 -33
- package/src/config.spec.ts +0 -850
- package/src/config.ts +0 -305
- package/src/constants.ts +0 -10
- package/src/controller/compile.controller.ts +0 -35
- package/src/controller/connection.controller.ts +0 -515
- package/src/controller/database.controller.ts +0 -21
- package/src/controller/manifest.controller.ts +0 -29
- package/src/controller/materialization.controller.ts +0 -125
- package/src/controller/model.controller.ts +0 -107
- package/src/controller/package.controller.ts +0 -182
- package/src/controller/query.controller.ts +0 -60
- package/src/controller/watch-mode.controller.ts +0 -98
- package/src/data_styles.ts +0 -71
- package/src/dto/connection.dto.spec.ts +0 -131
- package/src/dto/connection.dto.ts +0 -174
- package/src/dto/index.ts +0 -2
- package/src/dto/package.dto.spec.ts +0 -42
- package/src/dto/package.dto.ts +0 -12
- package/src/dto/validate.spec.ts +0 -76
- package/src/dto/validate.ts +0 -31
- package/src/errors.ts +0 -116
- package/src/health.ts +0 -189
- package/src/instrumentation.ts +0 -148
- package/src/logger.ts +0 -154
- package/src/mcp/error_messages.ts +0 -199
- package/src/mcp/handler_utils.ts +0 -239
- package/src/mcp/mcp_constants.ts +0 -22
- package/src/mcp/prompts/handlers.ts +0 -84
- package/src/mcp/prompts/index.ts +0 -11
- package/src/mcp/prompts/prompt_definitions.ts +0 -160
- package/src/mcp/prompts/prompt_service.ts +0 -67
- package/src/mcp/prompts/utils.ts +0 -62
- package/src/mcp/resource_metadata.ts +0 -47
- package/src/mcp/resources/model_resource.ts +0 -153
- package/src/mcp/resources/notebook_resource.ts +0 -137
- package/src/mcp/resources/package_resource.ts +0 -370
- package/src/mcp/resources/project_resource.ts +0 -184
- package/src/mcp/resources/query_resource.ts +0 -117
- package/src/mcp/resources/source_resource.ts +0 -141
- package/src/mcp/resources/view_resource.ts +0 -136
- package/src/mcp/server.ts +0 -62
- package/src/mcp/tools/discovery_tools.ts +0 -258
- package/src/mcp/tools/execute_query_tool.ts +0 -265
- package/src/server.ts +0 -1195
- package/src/service/connection.spec.ts +0 -1331
- package/src/service/connection.ts +0 -1511
- package/src/service/connection_service.spec.ts +0 -641
- package/src/service/connection_service.ts +0 -181
- package/src/service/db_utils.spec.ts +0 -714
- package/src/service/db_utils.ts +0 -1227
- package/src/service/filter.spec.ts +0 -447
- package/src/service/filter.ts +0 -337
- package/src/service/filter_integration.spec.ts +0 -825
- package/src/service/gcs_s3_utils.ts +0 -427
- package/src/service/manifest_service.spec.ts +0 -201
- package/src/service/manifest_service.ts +0 -106
- package/src/service/materialization_service.spec.ts +0 -648
- package/src/service/materialization_service.ts +0 -929
- package/src/service/materialized_table_gc.spec.ts +0 -383
- package/src/service/materialized_table_gc.ts +0 -279
- package/src/service/model.spec.ts +0 -257
- package/src/service/model.ts +0 -1069
- package/src/service/package.spec.ts +0 -236
- package/src/service/package.ts +0 -463
- package/src/service/project.ts +0 -681
- package/src/service/project_compile.spec.ts +0 -197
- package/src/service/project_store.spec.ts +0 -859
- package/src/service/project_store.ts +0 -1531
- package/src/service/quoting.ts +0 -41
- package/src/service/resolve_project.ts +0 -13
- package/src/storage/BaseRepository.ts +0 -31
- package/src/storage/DatabaseInterface.ts +0 -180
- package/src/storage/StorageManager.mock.ts +0 -59
- package/src/storage/StorageManager.ts +0 -210
- package/src/storage/duckdb/ConnectionRepository.ts +0 -141
- package/src/storage/duckdb/DuckDBConnection.ts +0 -177
- package/src/storage/duckdb/DuckDBManifestStore.ts +0 -70
- package/src/storage/duckdb/DuckDBRepository.ts +0 -224
- package/src/storage/duckdb/ManifestRepository.ts +0 -119
- package/src/storage/duckdb/MaterializationRepository.ts +0 -249
- package/src/storage/duckdb/PackageRepository.ts +0 -135
- package/src/storage/duckdb/ProjectRepository.ts +0 -148
- package/src/storage/duckdb/manifest_store.spec.ts +0 -133
- package/src/storage/duckdb/schema.ts +0 -147
- package/src/storage/ducklake/DuckLakeManifestStore.ts +0 -146
- package/src/utils.ts +0 -13
- package/tests/fixtures/persist-test/data/orders.csv +0 -5
- package/tests/fixtures/persist-test/persist_test.malloy +0 -11
- package/tests/fixtures/persist-test/publisher.json +0 -5
- package/tests/fixtures/publisher.config.json +0 -15
- package/tests/harness/e2e.ts +0 -96
- package/tests/harness/mcp_test_setup.ts +0 -344
- package/tests/harness/mocks.ts +0 -37
- package/tests/harness/rest_e2e.ts +0 -68
- package/tests/harness/uris.ts +0 -31
- package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -470
- package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -434
- package/tests/integration/mcp/mcp_resource.integration.spec.ts +0 -663
- package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -122
- package/tests/integration/mcp/setup.spec.ts +0 -5
- package/tests/unit/duckdb/attached_databases.test.ts +0 -1132
- package/tests/unit/ducklake/ducklake.test.ts +0 -950
- package/tests/unit/mcp/prompt_definitions.test.ts +0 -102
- package/tests/unit/mcp/prompt_happy.test.ts +0 -51
- package/tsconfig.json +0 -32
package/src/mcp/handler_utils.ts
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import { URL } from "url";
|
|
2
|
-
import type { ResourceMetadata } from "@modelcontextprotocol/sdk/server/mcp";
|
|
3
|
-
import type { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
import { ProjectStore } from "../service/project_store";
|
|
5
|
-
import {
|
|
6
|
-
PackageNotFoundError,
|
|
7
|
-
ModelNotFoundError,
|
|
8
|
-
ModelCompilationError,
|
|
9
|
-
ProjectNotFoundError,
|
|
10
|
-
} from "../errors";
|
|
11
|
-
import {
|
|
12
|
-
getNotFoundError,
|
|
13
|
-
getInternalError,
|
|
14
|
-
getMalloyErrorDetails,
|
|
15
|
-
type ErrorDetails,
|
|
16
|
-
} from "./error_messages";
|
|
17
|
-
import type { Model } from "../service/model";
|
|
18
|
-
import { logger } from "../logger";
|
|
19
|
-
|
|
20
|
-
// Custom error to wrap specific GetResource application errors
|
|
21
|
-
export class McpGetResourceError extends Error {
|
|
22
|
-
details: ErrorDetails;
|
|
23
|
-
|
|
24
|
-
constructor(details: ErrorDetails) {
|
|
25
|
-
super(details.message); // Pass message to the base Error constructor
|
|
26
|
-
this.name = "McpGetResourceError"; // Custom error name
|
|
27
|
-
this.details = details; // Store the structured details
|
|
28
|
-
|
|
29
|
-
// Maintain stack trace (important for V8)
|
|
30
|
-
if (Error.captureStackTrace) {
|
|
31
|
-
Error.captureStackTrace(this, McpGetResourceError);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Helper type for the data fetching logic within a resource handler
|
|
37
|
-
type GetDataLogic<TParams, TDefinition> = (
|
|
38
|
-
params: TParams,
|
|
39
|
-
uri: URL,
|
|
40
|
-
) => Promise<TDefinition>;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Handles the common logic for GetResource handlers, fetching data and formatting the response or error.
|
|
44
|
-
*/
|
|
45
|
-
export async function handleResourceGet<
|
|
46
|
-
TParams extends Record<string, unknown>,
|
|
47
|
-
TDefinition,
|
|
48
|
-
>(
|
|
49
|
-
uri: URL,
|
|
50
|
-
params: TParams,
|
|
51
|
-
resourceType: string, // e.g., 'project', 'package' for logging/errors
|
|
52
|
-
getData: GetDataLogic<TParams, TDefinition>,
|
|
53
|
-
resourceMetadata: ResourceMetadata | undefined,
|
|
54
|
-
): Promise<ReadResourceResult> {
|
|
55
|
-
try {
|
|
56
|
-
const definition = await getData(params, uri);
|
|
57
|
-
|
|
58
|
-
// Combine definition and metadata
|
|
59
|
-
const responsePayload = JSON.stringify(
|
|
60
|
-
{ definition: definition, metadata: resourceMetadata },
|
|
61
|
-
null,
|
|
62
|
-
2,
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
contents: [
|
|
67
|
-
{
|
|
68
|
-
type: "application/json",
|
|
69
|
-
uri: uri.href,
|
|
70
|
-
text: responsePayload,
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
};
|
|
74
|
-
} catch (error) {
|
|
75
|
-
logger.error(
|
|
76
|
-
`[MCP Server Error] Error reading ${resourceType} ${uri.href}:`,
|
|
77
|
-
{ error },
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
let errorDetails: ErrorDetails;
|
|
81
|
-
|
|
82
|
-
// Determine the correct error details based on the error type
|
|
83
|
-
if (error instanceof McpGetResourceError) {
|
|
84
|
-
// The getData function already caught, formatted, and wrapped the error
|
|
85
|
-
errorDetails = error.details;
|
|
86
|
-
} else {
|
|
87
|
-
// Catch-all for truly unexpected errors not handled by the specific getData logic
|
|
88
|
-
logger.error(
|
|
89
|
-
"[MCP Server Error] Unexpected error type caught in handleResourceGet:",
|
|
90
|
-
{ error },
|
|
91
|
-
);
|
|
92
|
-
errorDetails = getInternalError(
|
|
93
|
-
`GetResource (${resourceType})`,
|
|
94
|
-
error,
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Format the error response consistently
|
|
99
|
-
return {
|
|
100
|
-
isError: true,
|
|
101
|
-
contents: [
|
|
102
|
-
{
|
|
103
|
-
type: "application/json", // Keep JSON for structured error
|
|
104
|
-
uri: uri.href,
|
|
105
|
-
text: JSON.stringify(
|
|
106
|
-
{
|
|
107
|
-
error: errorDetails.message,
|
|
108
|
-
suggestions: errorDetails.suggestions,
|
|
109
|
-
},
|
|
110
|
-
null,
|
|
111
|
-
2,
|
|
112
|
-
),
|
|
113
|
-
},
|
|
114
|
-
],
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Fetches and validates the Package and Model instances needed for query execution.
|
|
121
|
-
* Handles errors related to package/model access and initial compilation.
|
|
122
|
-
* @returns An object containing the Model instance or a pre-formatted ErrorDetails object.
|
|
123
|
-
*/
|
|
124
|
-
export async function getModelForQuery(
|
|
125
|
-
projectStore: ProjectStore,
|
|
126
|
-
projectName: string,
|
|
127
|
-
packageName: string,
|
|
128
|
-
modelPath: string,
|
|
129
|
-
): Promise<{ model: Model } | { error: ErrorDetails }> {
|
|
130
|
-
try {
|
|
131
|
-
const project = await projectStore.getProject(projectName, false);
|
|
132
|
-
const pkg = await project.getPackage(packageName, false);
|
|
133
|
-
const model = pkg.getModel(modelPath);
|
|
134
|
-
if (!model || model.getModelType() === "notebook") {
|
|
135
|
-
// Ensure it's actually a model
|
|
136
|
-
const details = getNotFoundError(
|
|
137
|
-
`model '${modelPath}' in package '${packageName}' for project '${projectName}'`,
|
|
138
|
-
);
|
|
139
|
-
return { error: details };
|
|
140
|
-
}
|
|
141
|
-
// Attempt to get the model definition early to catch initial compilation errors
|
|
142
|
-
await model.getModel(); // This might throw ModelCompilationError
|
|
143
|
-
return { model };
|
|
144
|
-
} catch (error) {
|
|
145
|
-
// Handle errors during package/model access or initial compilation
|
|
146
|
-
let errorDetails: ErrorDetails;
|
|
147
|
-
if (error instanceof ProjectNotFoundError) {
|
|
148
|
-
errorDetails = getNotFoundError(`project '${projectName}'`);
|
|
149
|
-
} else if (error instanceof PackageNotFoundError) {
|
|
150
|
-
errorDetails = getNotFoundError(
|
|
151
|
-
`package '${packageName}' in project '${projectName}'`,
|
|
152
|
-
);
|
|
153
|
-
} else if (error instanceof ModelNotFoundError) {
|
|
154
|
-
errorDetails = getNotFoundError(
|
|
155
|
-
`model '${modelPath}' in package '${packageName}' for project '${projectName}'`,
|
|
156
|
-
);
|
|
157
|
-
} else if (error instanceof ModelCompilationError) {
|
|
158
|
-
errorDetails = getMalloyErrorDetails(
|
|
159
|
-
"executeQuery (load model)",
|
|
160
|
-
`${projectName}/${packageName}/${modelPath}`,
|
|
161
|
-
error,
|
|
162
|
-
);
|
|
163
|
-
} else {
|
|
164
|
-
// Unexpected error during setup
|
|
165
|
-
errorDetails = getInternalError("executeQuery (Setup)", error);
|
|
166
|
-
}
|
|
167
|
-
logger.error(
|
|
168
|
-
`[MCP Server Error] Error accessing package/model for query: ${projectName}/${packageName}/${modelPath}`,
|
|
169
|
-
{ error },
|
|
170
|
-
);
|
|
171
|
-
return { error: errorDetails };
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Constructs a valid malloy:// URI string from its components.
|
|
177
|
-
* Handles encoding of path segments.
|
|
178
|
-
*
|
|
179
|
-
* @param components An object containing the URI parts (project, package, model, etc.)
|
|
180
|
-
* @param fragment Optional fragment identifier (e.g., #queryResult)
|
|
181
|
-
* @returns A valid malloy:// URI string.
|
|
182
|
-
*/
|
|
183
|
-
export function buildMalloyUri(
|
|
184
|
-
components: {
|
|
185
|
-
project?: string;
|
|
186
|
-
package?: string;
|
|
187
|
-
model?: string;
|
|
188
|
-
resourceType?:
|
|
189
|
-
| "models"
|
|
190
|
-
| "packages"
|
|
191
|
-
| "notebooks"
|
|
192
|
-
| "sources"
|
|
193
|
-
| "queries"
|
|
194
|
-
| "views"; // Type of resource list or specific resource
|
|
195
|
-
resourceName?: string; // Specific name for model, query, etc.
|
|
196
|
-
subResourceType?: "views"; // For views within sources
|
|
197
|
-
subResourceName?: string; // Name of the view
|
|
198
|
-
},
|
|
199
|
-
fragment?: string,
|
|
200
|
-
): string {
|
|
201
|
-
let path = "/project/";
|
|
202
|
-
|
|
203
|
-
if (components.project) {
|
|
204
|
-
path += encodeURIComponent(components.project);
|
|
205
|
-
} else {
|
|
206
|
-
// Default to 'home' if not provided, consistent with current behavior
|
|
207
|
-
path += "home";
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (components.package) {
|
|
211
|
-
path += "/package/" + encodeURIComponent(components.package);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (components.resourceType) {
|
|
215
|
-
path += "/" + components.resourceType;
|
|
216
|
-
if (components.resourceName) {
|
|
217
|
-
path += "/" + encodeURIComponent(components.resourceName);
|
|
218
|
-
|
|
219
|
-
if (components.subResourceType && components.subResourceName) {
|
|
220
|
-
path +=
|
|
221
|
-
"/" +
|
|
222
|
-
components.subResourceType +
|
|
223
|
-
"/" +
|
|
224
|
-
encodeURIComponent(components.subResourceName);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// The URL constructor seems to normalize malloy://path to malloy:///path
|
|
230
|
-
// which breaks the tests expecting malloy://project/...
|
|
231
|
-
// We manually construct the string instead.
|
|
232
|
-
let uriString = "malloy:/" + path; // Start with one slash after scheme
|
|
233
|
-
|
|
234
|
-
if (fragment) {
|
|
235
|
-
uriString += "#" + fragment;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return uriString;
|
|
239
|
-
}
|
package/src/mcp/mcp_constants.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export const MCP_ERROR_MESSAGES = {
|
|
2
|
-
// Protocol-level errors (validation)
|
|
3
|
-
MISSING_REQUIRED_PARAMS:
|
|
4
|
-
"Either 'query' or both 'sourceName' and 'queryName' must be provided",
|
|
5
|
-
MUTUALLY_EXCLUSIVE_PARAMS: "Cannot provide both 'query' and 'queryName'",
|
|
6
|
-
INVALID_ARGUMENTS: (toolName: string, errors: string[]) =>
|
|
7
|
-
`Invalid arguments for tool ${toolName}: ${errors.join(", ")}`,
|
|
8
|
-
|
|
9
|
-
// Application-level errors (runtime)
|
|
10
|
-
PROJECT_NOT_FOUND: (projectName: string) =>
|
|
11
|
-
`Project '${projectName}' is not available or does not exist.`,
|
|
12
|
-
PACKAGE_NOT_FOUND: (packageName: string) =>
|
|
13
|
-
`Package manifest for ${packageName} does not exist.`,
|
|
14
|
-
MODEL_NOT_FOUND: (packageName: string, modelPath: string) =>
|
|
15
|
-
`Model '${modelPath}' not found in package '${packageName}'`,
|
|
16
|
-
QUERY_NOT_FOUND: (queryName: string) => `'${queryName}' is not defined`,
|
|
17
|
-
COMPILATION_ERROR: (error: string) => `Error(s) compiling model:\n${error}`,
|
|
18
|
-
|
|
19
|
-
// Error message prefixes
|
|
20
|
-
ERROR_EXECUTING_QUERY: (packagePath: string) =>
|
|
21
|
-
`Error executing query on '${packagePath}':`,
|
|
22
|
-
} as const;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import * as Handlebars from "handlebars";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import {
|
|
4
|
-
PromptMessageSchema,
|
|
5
|
-
GetPromptResultSchema,
|
|
6
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
-
import { PROMPTS } from "./prompt_definitions";
|
|
8
|
-
import { getCompiledModel } from "./utils";
|
|
9
|
-
import { ProjectStore } from "../../service/project_store";
|
|
10
|
-
|
|
11
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
12
|
-
|
|
13
|
-
/* --------------------------------------------------------------------------
|
|
14
|
-
* Helper – create a simple text prompt message
|
|
15
|
-
* ----------------------------------------------------------------------- */
|
|
16
|
-
|
|
17
|
-
const textMsg = (text: string): z.infer<typeof PromptMessageSchema> => ({
|
|
18
|
-
role: "user",
|
|
19
|
-
content: { type: "text", text },
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
/* --------------------------------------------------------------------------
|
|
23
|
-
* makePromptHandler – higher-order factory
|
|
24
|
-
* ----------------------------------------------------------------------- */
|
|
25
|
-
|
|
26
|
-
export function makePromptHandler(id: string) {
|
|
27
|
-
const def = PROMPTS[id];
|
|
28
|
-
if (!def) throw new Error(`Unknown prompt id: ${id}`);
|
|
29
|
-
|
|
30
|
-
// pre-compile Handlebars template
|
|
31
|
-
const templateFn = Handlebars.compile(def.template, { noEscape: true });
|
|
32
|
-
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
-
return async (params: any, ps: ProjectStore) => {
|
|
35
|
-
const data: Record<string, unknown> = { ...params };
|
|
36
|
-
|
|
37
|
-
// Dynamically add model content / schema context if the args include URIs
|
|
38
|
-
if (ps) {
|
|
39
|
-
if (typeof params?.model_uri === "string") {
|
|
40
|
-
const compiled = await getCompiledModel(params.model_uri, ps);
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
-
(data as any).model_def_content = (compiled as any).modelDef;
|
|
43
|
-
}
|
|
44
|
-
if (typeof params?.target_model_uri === "string") {
|
|
45
|
-
const compiled = await getCompiledModel(
|
|
46
|
-
params.target_model_uri,
|
|
47
|
-
ps,
|
|
48
|
-
);
|
|
49
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
-
(data as any).schema_context = (compiled as any).modelInfo;
|
|
51
|
-
}
|
|
52
|
-
if (typeof params?.model_context_uri === "string") {
|
|
53
|
-
const compiled = await getCompiledModel(
|
|
54
|
-
params.model_context_uri,
|
|
55
|
-
ps,
|
|
56
|
-
);
|
|
57
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
-
(data as any).schema_context = (compiled as any).modelInfo;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const rendered = templateFn(data);
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
messages: [textMsg(rendered)],
|
|
66
|
-
// Ensure shape matches SDK schema
|
|
67
|
-
} as z.infer<typeof GetPromptResultSchema>;
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/* --------------------------------------------------------------------------
|
|
72
|
-
* Registry – id -> handler
|
|
73
|
-
* ----------------------------------------------------------------------- */
|
|
74
|
-
|
|
75
|
-
export const promptHandlerMap = Object.fromEntries(
|
|
76
|
-
Object.keys(PROMPTS).map((id) => [id, makePromptHandler(id)]),
|
|
77
|
-
) as Record<
|
|
78
|
-
string,
|
|
79
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
-
(
|
|
81
|
-
params: any,
|
|
82
|
-
ps: ProjectStore,
|
|
83
|
-
) => Promise<z.infer<typeof GetPromptResultSchema>>
|
|
84
|
-
>;
|
package/src/mcp/prompts/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
withVersion,
|
|
3
|
-
PROMPTS,
|
|
4
|
-
MALLOY_PROMPTS,
|
|
5
|
-
ExplainArgsSchema,
|
|
6
|
-
GenerateQueryArgsSchema,
|
|
7
|
-
TranslateSqlArgsSchema,
|
|
8
|
-
SummarizeModelArgsSchema,
|
|
9
|
-
} from "./prompt_definitions";
|
|
10
|
-
export { promptHandlerMap, makePromptHandler } from "./handlers";
|
|
11
|
-
export { registerPromptCapability } from "./prompt_service";
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Helper to build fully-qualified prompt IDs combining name + semver.
|
|
5
|
-
*/
|
|
6
|
-
export const withVersion = (name: string, v: string) => `${name}@${v}`;
|
|
7
|
-
|
|
8
|
-
/* --------------------------------------------------------------------------
|
|
9
|
-
* Argument Schemas (Zod) – one per prompt
|
|
10
|
-
* ----------------------------------------------------------------------- */
|
|
11
|
-
|
|
12
|
-
export const ExplainArgsSchema = z
|
|
13
|
-
.object({
|
|
14
|
-
query_code: z.string(),
|
|
15
|
-
model_context_uri: z.string().url().optional(),
|
|
16
|
-
})
|
|
17
|
-
.describe(
|
|
18
|
-
"Explain a Malloy query: purpose, transformations, output structure.",
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
export const GenerateQueryArgsSchema = z
|
|
22
|
-
.object({
|
|
23
|
-
natural_language_goal: z.string(),
|
|
24
|
-
target_model_uri: z.string().url(),
|
|
25
|
-
data_source_hints: z.string().optional(),
|
|
26
|
-
})
|
|
27
|
-
.describe(
|
|
28
|
-
"Generate a Malloy query from a natural-language goal and model context.",
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
export const TranslateSqlArgsSchema = z
|
|
32
|
-
.object({
|
|
33
|
-
sql_query: z.string(),
|
|
34
|
-
target_model_uri: z.string().url(),
|
|
35
|
-
sql_dialect: z.string().default("standard_sql"),
|
|
36
|
-
})
|
|
37
|
-
.describe("Translate a SQL query to Malloy using model schema context.");
|
|
38
|
-
|
|
39
|
-
export const SummarizeModelArgsSchema = z
|
|
40
|
-
.object({
|
|
41
|
-
model_uri: z.string().url(),
|
|
42
|
-
})
|
|
43
|
-
.describe("Summarize a Malloy model's purpose, sources, and joins.");
|
|
44
|
-
|
|
45
|
-
/* --------------------------------------------------------------------------
|
|
46
|
-
* PromptDefinition
|
|
47
|
-
* ----------------------------------------------------------------------- */
|
|
48
|
-
|
|
49
|
-
export interface PromptDefinition {
|
|
50
|
-
id: string;
|
|
51
|
-
argsSchema: z.ZodTypeAny;
|
|
52
|
-
template: string; // Handlebars syntax
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/* --------------------------------------------------------------------------
|
|
56
|
-
* Prompt Templates (Handlebars strings)
|
|
57
|
-
* ----------------------------------------------------------------------- */
|
|
58
|
-
|
|
59
|
-
const explainTemplate = `
|
|
60
|
-
<instructions>
|
|
61
|
-
You are an expert Malloy data analyst.
|
|
62
|
-
Explain the following Malloy query. Provide details on:
|
|
63
|
-
• Purpose and intended insight
|
|
64
|
-
• Key sources involved
|
|
65
|
-
• Main transformations / calculations
|
|
66
|
-
• Expected result structure (dimensions/measures)
|
|
67
|
-
</instructions>
|
|
68
|
-
|
|
69
|
-
<malloy_query>
|
|
70
|
-
{{query_code}}
|
|
71
|
-
</malloy_query>
|
|
72
|
-
|
|
73
|
-
{{#if schema_context}}
|
|
74
|
-
<model_schema_context uri="{{model_context_uri}}">
|
|
75
|
-
{{schema_context}}
|
|
76
|
-
</model_schema_context>
|
|
77
|
-
{{/if}}
|
|
78
|
-
`;
|
|
79
|
-
|
|
80
|
-
const generateTemplate = `
|
|
81
|
-
<instructions>
|
|
82
|
-
You are an expert Malloy modeler and query writer.
|
|
83
|
-
Generate a Malloy query that satisfies the goal below using the model at {{target_model_uri}}.
|
|
84
|
-
|
|
85
|
-
Goal: {{natural_language_goal}}
|
|
86
|
-
|
|
87
|
-
{{#if data_source_hints}}
|
|
88
|
-
Hints to consider: {{data_source_hints}}
|
|
89
|
-
{{/if}}
|
|
90
|
-
|
|
91
|
-
Return ONLY the Malloy query code.
|
|
92
|
-
</instructions>
|
|
93
|
-
|
|
94
|
-
<malloy_model_context uri="{{target_model_uri}}" />
|
|
95
|
-
{{#if schema_context}}
|
|
96
|
-
<fetched_model_schema_context>
|
|
97
|
-
{{schema_context}}
|
|
98
|
-
</fetched_model_schema_context>
|
|
99
|
-
{{/if}}
|
|
100
|
-
`;
|
|
101
|
-
|
|
102
|
-
const translateTemplate = `
|
|
103
|
-
<instructions>
|
|
104
|
-
You are fluent in both {{sql_dialect}} SQL and Malloy.
|
|
105
|
-
Translate the SQL below into Malloy using the model at {{target_model_uri}} for schema context. Return ONLY Malloy code.
|
|
106
|
-
</instructions>
|
|
107
|
-
|
|
108
|
-
<sql_query dialect="{{sql_dialect}}">{{sql_query}}</sql_query>
|
|
109
|
-
|
|
110
|
-
<malloy_model_context uri="{{target_model_uri}}" />
|
|
111
|
-
{{#if schema_context}}
|
|
112
|
-
<fetched_model_schema_context>
|
|
113
|
-
{{schema_context}}
|
|
114
|
-
</fetched_model_schema_context>
|
|
115
|
-
{{/if}}
|
|
116
|
-
`;
|
|
117
|
-
|
|
118
|
-
const summarizeTemplate = `
|
|
119
|
-
<instructions>
|
|
120
|
-
You are an expert Malloy modeler.
|
|
121
|
-
Provide a concise summary of the model at {{model_uri}} covering purpose, key sources, dimensions, measures & joins.
|
|
122
|
-
</instructions>
|
|
123
|
-
|
|
124
|
-
<malloy_model_content uri="{{model_uri}}" />
|
|
125
|
-
{{#if model_def_content}}
|
|
126
|
-
<fetched_model_definition>
|
|
127
|
-
{{model_def_content}}
|
|
128
|
-
</fetched_model_definition>
|
|
129
|
-
{{/if}}
|
|
130
|
-
`;
|
|
131
|
-
|
|
132
|
-
/* --------------------------------------------------------------------------
|
|
133
|
-
* PROMPTS registry
|
|
134
|
-
* ----------------------------------------------------------------------- */
|
|
135
|
-
|
|
136
|
-
export const PROMPTS: Record<string, PromptDefinition> = {
|
|
137
|
-
[withVersion("explain-malloy-query", "1.0.0")]: {
|
|
138
|
-
id: withVersion("explain-malloy-query", "1.0.0"),
|
|
139
|
-
argsSchema: ExplainArgsSchema,
|
|
140
|
-
template: explainTemplate,
|
|
141
|
-
},
|
|
142
|
-
[withVersion("generate-malloy-query-from-description", "1.0.0")]: {
|
|
143
|
-
id: withVersion("generate-malloy-query-from-description", "1.0.0"),
|
|
144
|
-
argsSchema: GenerateQueryArgsSchema,
|
|
145
|
-
template: generateTemplate,
|
|
146
|
-
},
|
|
147
|
-
[withVersion("translate-sql-to-malloy", "1.0.0")]: {
|
|
148
|
-
id: withVersion("translate-sql-to-malloy", "1.0.0"),
|
|
149
|
-
argsSchema: TranslateSqlArgsSchema,
|
|
150
|
-
template: translateTemplate,
|
|
151
|
-
},
|
|
152
|
-
[withVersion("summarize-malloy-model", "1.0.0")]: {
|
|
153
|
-
id: withVersion("summarize-malloy-model", "1.0.0"),
|
|
154
|
-
argsSchema: SummarizeModelArgsSchema,
|
|
155
|
-
template: summarizeTemplate,
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
// Backwards-compat alias until the old handlers are removed (step 4).
|
|
160
|
-
export const MALLOY_PROMPTS = PROMPTS;
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { logger } from "../../logger";
|
|
4
|
-
import { ProjectStore } from "../../service/project_store";
|
|
5
|
-
import { promptHandlerMap } from "./handlers";
|
|
6
|
-
import { MALLOY_PROMPTS } from "./prompt_definitions";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Registers all defined Malloy prompts with the MCP server.
|
|
10
|
-
*
|
|
11
|
-
* @param mcpServer The McpServer instance.
|
|
12
|
-
* @param projectStore The ProjectStore instance for handlers to access project data.
|
|
13
|
-
*/
|
|
14
|
-
export function registerPromptCapability(
|
|
15
|
-
mcpServer: McpServer,
|
|
16
|
-
projectStore: ProjectStore,
|
|
17
|
-
): void {
|
|
18
|
-
logger.info("[MCP Init] Registering prompt capability...");
|
|
19
|
-
const startTime = performance.now();
|
|
20
|
-
let registeredCount = 0;
|
|
21
|
-
|
|
22
|
-
for (const promptKey in MALLOY_PROMPTS) {
|
|
23
|
-
const promptDefinition = MALLOY_PROMPTS[promptKey];
|
|
24
|
-
const handler = promptHandlerMap[promptDefinition.id];
|
|
25
|
-
|
|
26
|
-
if (handler && promptDefinition.argsSchema instanceof z.ZodObject) {
|
|
27
|
-
// Prepare a handler that injects the projectStore
|
|
28
|
-
const preparedHandler = (
|
|
29
|
-
args: z.infer<typeof promptDefinition.argsSchema>,
|
|
30
|
-
) => handler(args, projectStore);
|
|
31
|
-
|
|
32
|
-
// Register using prompt ID, the Zod schema shape, and the prepared handler.
|
|
33
|
-
mcpServer.prompt(
|
|
34
|
-
promptDefinition.id,
|
|
35
|
-
(
|
|
36
|
-
promptDefinition.argsSchema as z.ZodObject<
|
|
37
|
-
z.ZodRawShape,
|
|
38
|
-
z.UnknownKeysParam,
|
|
39
|
-
z.ZodTypeAny,
|
|
40
|
-
object,
|
|
41
|
-
object
|
|
42
|
-
>
|
|
43
|
-
).shape,
|
|
44
|
-
preparedHandler,
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
registeredCount++;
|
|
48
|
-
logger.info(
|
|
49
|
-
`[MCP Prompt Reg] Registered prompt: ${promptDefinition.id}`,
|
|
50
|
-
);
|
|
51
|
-
} else if (!handler) {
|
|
52
|
-
logger.warn(
|
|
53
|
-
`Handler not found for prompt: ${promptDefinition.id}. It will not be registered.`,
|
|
54
|
-
);
|
|
55
|
-
} else if (!(promptDefinition.argsSchema instanceof z.ZodObject)) {
|
|
56
|
-
logger.warn(
|
|
57
|
-
`Prompt definition for ${promptDefinition.id} does not have a ZodObject schema. It will not be registered.`,
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const endTime = performance.now();
|
|
63
|
-
logger.info(
|
|
64
|
-
`[MCP Init] Finished registering prompts. Registered: ${registeredCount}`,
|
|
65
|
-
{ duration: endTime - startTime },
|
|
66
|
-
);
|
|
67
|
-
}
|
package/src/mcp/prompts/utils.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { ProjectStore } from "../../service/project_store";
|
|
2
|
-
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
-
|
|
4
|
-
// Type alias for compiled model pulled from OpenAPI-generated types.
|
|
5
|
-
// Using `any` fallback in case the exact type path changes.
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
-
export type ApiCompiledModel = any;
|
|
8
|
-
|
|
9
|
-
export interface ParsedMalloyUri {
|
|
10
|
-
projectName: string;
|
|
11
|
-
packageName: string;
|
|
12
|
-
modelPath: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Parse a canonical Malloy model URI of the form:
|
|
17
|
-
* malloy://project/{project}/package/{package}/models/{modelPath}
|
|
18
|
-
*
|
|
19
|
-
* Throws an McpError(InvalidParams) if the URI is malformed.
|
|
20
|
-
*/
|
|
21
|
-
export function parseMalloyUri(uri: string): ParsedMalloyUri {
|
|
22
|
-
const match = uri.match(
|
|
23
|
-
/^malloy:\/\/project\/([^/]+)\/package\/([^/]+)\/models\/(.+)$/,
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
if (!match) {
|
|
27
|
-
throw new McpError(ErrorCode.InvalidParams, `Invalid Malloy URI: ${uri}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
projectName: match[1],
|
|
32
|
-
packageName: match[2],
|
|
33
|
-
modelPath: match[3],
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Retrieve the compiled model for the given Malloy URI, using the provided
|
|
39
|
-
* ProjectStore. Results are memoised in `compiledModelCache`.
|
|
40
|
-
*/
|
|
41
|
-
export async function getCompiledModel(
|
|
42
|
-
uri: string,
|
|
43
|
-
projectStore: ProjectStore,
|
|
44
|
-
): Promise<ApiCompiledModel> {
|
|
45
|
-
const { projectName, packageName, modelPath } = parseMalloyUri(uri);
|
|
46
|
-
|
|
47
|
-
// Look up the model via the ProjectStore hierarchy.
|
|
48
|
-
const model = await projectStore
|
|
49
|
-
.getProject(projectName, false)
|
|
50
|
-
.then((p) => p.getPackage(packageName, false))
|
|
51
|
-
.then((pkg) => pkg.getModel(modelPath));
|
|
52
|
-
|
|
53
|
-
if (!model || model.getModelType() === "notebook") {
|
|
54
|
-
throw new McpError(
|
|
55
|
-
ErrorCode.InvalidParams,
|
|
56
|
-
`Model ${uri} not found or refers to a notebook`,
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const compiled = await model.getModel();
|
|
61
|
-
return compiled;
|
|
62
|
-
}
|