@malloy-publisher/server 0.0.226 → 0.0.228

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 (112) hide show
  1. package/README.docker.md +8 -8
  2. package/README.md +2 -11
  3. package/dist/app/assets/{EnvironmentPage-DvOJ7L_b.js → EnvironmentPage-EW2lbGvb.js} +1 -1
  4. package/dist/app/assets/{HomePage-CXguJsXS.js → HomePage-Bkwc9Woc.js} +1 -1
  5. package/dist/app/assets/{LightMode-ZsshUznu.js → LightMode-Bum_KBpN.js} +1 -1
  6. package/dist/app/assets/{MainPage-BIe0VwBa.js → MainPage-oiEy7TNM.js} +1 -1
  7. package/dist/app/assets/{MaterializationsPage-BuZ6UJVx.js → MaterializationsPage-C_VJsTgU.js} +1 -1
  8. package/dist/app/assets/{ModelPage-DsPf-s8B.js → ModelPage-z8REqAmk.js} +1 -1
  9. package/dist/app/assets/{PackagePage-CEVNAKZa.js → PackagePage-C2Vtt1Ln.js} +1 -1
  10. package/dist/app/assets/{RouteError-Chn7lL96.js → RouteError-DmJLpLXm.js} +1 -1
  11. package/dist/app/assets/{ThemeEditorPage-DWC_FdNU.js → ThemeEditorPage-BywFjC7A.js} +1 -1
  12. package/dist/app/assets/{WorkbookPage-CGrsFz8p.js → WorkbookPage-DCMizDMR.js} +1 -1
  13. package/dist/app/assets/{core-vVgoO8IR.es-BD_THWs_.js → core-CEDZMHV1.es-_yGzNgNH.js} +1 -1
  14. package/dist/app/assets/{index-D6YtyiJ0.js → index-CE9xhdra.js} +1 -1
  15. package/dist/app/assets/{index-BioohWQj.js → index-CdmFub34.js} +1 -1
  16. package/dist/app/assets/{index-gEWxu09x.js → index-DDMrjIT3.js} +1 -1
  17. package/dist/app/assets/{index-DNUZpnaa.js → index-EqslXZ44.js} +4 -4
  18. package/dist/app/index.html +1 -1
  19. package/dist/default-publisher.config.json +7 -7
  20. package/dist/package_load_worker.mjs +86 -24
  21. package/dist/runtime/publisher.js +5 -0
  22. package/dist/server.mjs +1415 -7876
  23. package/package.json +1 -4
  24. package/publisher.config.example.bigquery.json +7 -7
  25. package/publisher.config.example.duckdb.json +7 -7
  26. package/publisher.config.json +7 -11
  27. package/src/config.spec.ts +2 -2
  28. package/src/controller/package.controller.ts +62 -31
  29. package/src/default-publisher.config.json +7 -7
  30. package/src/health.ts +3 -8
  31. package/src/mcp/error_messages.ts +8 -37
  32. package/src/mcp/handler_utils.spec.ts +108 -0
  33. package/src/mcp/handler_utils.ts +99 -124
  34. package/src/mcp/mcp_constants.ts +0 -16
  35. package/src/mcp/server.protocol.spec.ts +138 -0
  36. package/src/mcp/server.ts +57 -37
  37. package/src/mcp/skills/build_skills_bundle.ts +1 -1
  38. package/src/mcp/skills/skills_bundle.json +1 -1
  39. package/src/mcp/tools/compile_tool.spec.ts +207 -0
  40. package/src/mcp/tools/compile_tool.ts +177 -0
  41. package/src/mcp/tools/docs_search_tool.ts +1 -1
  42. package/src/mcp/tools/execute_query_tool.spec.ts +143 -0
  43. package/src/mcp/tools/execute_query_tool.ts +39 -6
  44. package/src/mcp/tools/get_context_eval.ts +3 -3
  45. package/src/mcp/tools/get_context_tool.spec.ts +196 -1
  46. package/src/mcp/tools/get_context_tool.ts +165 -67
  47. package/src/mcp/tools/reload_package_tool.spec.ts +232 -0
  48. package/src/mcp/tools/reload_package_tool.ts +158 -0
  49. package/src/package_load/package_load_pool.ts +3 -0
  50. package/src/package_load/package_load_worker.ts +68 -24
  51. package/src/package_load/protocol.ts +16 -0
  52. package/src/package_load/rpc_wait_accountant.spec.ts +109 -0
  53. package/src/package_load/rpc_wait_accountant.ts +76 -0
  54. package/src/package_load_metrics.spec.ts +114 -0
  55. package/src/package_load_metrics.ts +127 -0
  56. package/src/pg_helpers.spec.ts +2 -206
  57. package/src/pg_helpers.ts +4 -120
  58. package/src/runtime/publisher.js +5 -0
  59. package/src/server.ts +7 -21
  60. package/src/service/environment.ts +71 -7
  61. package/src/service/model.spec.ts +92 -0
  62. package/src/service/model.ts +58 -7
  63. package/src/service/package.ts +113 -55
  64. package/src/service/package_reload_safety.spec.ts +193 -0
  65. package/src/test_helpers/metrics_harness.ts +40 -0
  66. package/tests/fixtures/query-givens/data/orders.csv +7 -0
  67. package/tests/fixtures/query-givens/model.malloy +34 -0
  68. package/tests/fixtures/query-givens/publisher.json +5 -0
  69. package/tests/harness/mcp_test_setup.ts +1 -1
  70. package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +22 -22
  71. package/tests/integration/mcp/mcp_transport.integration.spec.ts +7 -31
  72. package/tests/integration/query_givens/query_givens.integration.spec.ts +146 -0
  73. package/tests/integration/query_givens/query_givens_authorize.integration.spec.ts +121 -0
  74. package/tests/integration/sdk_givens/sdk_givens.integration.spec.ts +110 -0
  75. package/tests/integration/watch-mode/watch_mode.integration.spec.ts +0 -6
  76. package/dxt/malloy_bridge.py +0 -354
  77. package/dxt/manifest.json +0 -22
  78. package/src/dto/connection.dto.spec.ts +0 -186
  79. package/src/dto/connection.dto.ts +0 -308
  80. package/src/dto/index.ts +0 -2
  81. package/src/dto/package.dto.spec.ts +0 -42
  82. package/src/dto/package.dto.ts +0 -27
  83. package/src/dto/validate.spec.ts +0 -76
  84. package/src/dto/validate.ts +0 -31
  85. package/src/ducklake_version.spec.ts +0 -43
  86. package/src/ducklake_version.ts +0 -26
  87. package/src/mcp/agent_server.protocol.spec.ts +0 -78
  88. package/src/mcp/agent_server.spec.ts +0 -18
  89. package/src/mcp/agent_server.ts +0 -144
  90. package/src/mcp/prompts/handlers.ts +0 -84
  91. package/src/mcp/prompts/index.ts +0 -11
  92. package/src/mcp/prompts/prompt_definitions.ts +0 -160
  93. package/src/mcp/prompts/prompt_service.ts +0 -67
  94. package/src/mcp/prompts/utils.ts +0 -62
  95. package/src/mcp/resource_metadata.ts +0 -47
  96. package/src/mcp/resources/environment_resource.ts +0 -187
  97. package/src/mcp/resources/model_resource.ts +0 -155
  98. package/src/mcp/resources/notebook_resource.ts +0 -137
  99. package/src/mcp/resources/package_resource.ts +0 -373
  100. package/src/mcp/resources/query_resource.ts +0 -122
  101. package/src/mcp/resources/source_resource.ts +0 -141
  102. package/src/mcp/resources/view_resource.ts +0 -136
  103. package/src/mcp/tools/discovery_tools.ts +0 -280
  104. package/src/storage/BaseRepository.ts +0 -31
  105. package/src/storage/StorageManager.mock.ts +0 -50
  106. package/tests/harness/e2e.ts +0 -96
  107. package/tests/harness/mocks.ts +0 -39
  108. package/tests/harness/uris.ts +0 -31
  109. package/tests/integration/mcp/mcp_resource.integration.spec.ts +0 -655
  110. package/tests/integration/mcp/setup.spec.ts +0 -5
  111. package/tests/unit/mcp/prompt_definitions.test.ts +0 -102
  112. package/tests/unit/mcp/prompt_happy.test.ts +0 -51
@@ -1,62 +0,0 @@
1
- import { EnvironmentStore } from "../../service/environment_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
- environmentName: string;
11
- packageName: string;
12
- modelPath: string;
13
- }
14
-
15
- /**
16
- * Parse a canonical Malloy model URI of the form:
17
- * malloy://environment/{environment}/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:\/\/environment\/([^/]+)\/package\/([^/]+)\/models\/(.+)$/,
24
- );
25
-
26
- if (!match) {
27
- throw new McpError(ErrorCode.InvalidParams, `Invalid Malloy URI: ${uri}`);
28
- }
29
-
30
- return {
31
- environmentName: 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
- * EnvironmentStore. Results are memoised in `compiledModelCache`.
40
- */
41
- export async function getCompiledModel(
42
- uri: string,
43
- environmentStore: EnvironmentStore,
44
- ): Promise<ApiCompiledModel> {
45
- const { environmentName, packageName, modelPath } = parseMalloyUri(uri);
46
-
47
- // Look up the model via the EnvironmentStore hierarchy.
48
- const model = await environmentStore
49
- .getEnvironment(environmentName, false)
50
- .then((e) => e.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
- }
@@ -1,47 +0,0 @@
1
- import type { ResourceMetadata as SdkResourceMetadata } from "@modelcontextprotocol/sdk/server/mcp";
2
-
3
- // Define the specific resource types expected as keys
4
- type McpResourceType = "model" | "source" | "view" | "query" | "notebook";
5
-
6
- // Descriptions and usage guidance for different Malloy resource types.
7
- // Sourced from documentation and adapted for MCP client context.
8
- // Type assertion ensures compatibility with the SDK's ResourceMetadata,
9
- // which might include optional fields or an index signature.
10
- export const RESOURCE_METADATA: Record<
11
- McpResourceType | "package" | "environment",
12
- SdkResourceMetadata // Use the imported SDK type here
13
- > = {
14
- model: {
15
- description:
16
- "A blueprint organizing your data tables, their connections, and reusable calculations (like 'revenue'). Models contain Sources, Views, and saved Queries.",
17
- usage: "This is your main map. Use its `definition` to see the available Sources, Views, and Queries inside (identified by their names or URIs). You can then fetch details for those specific items using `GetResource`. Use the `malloy_executeQuery` tool with queries defined here to get results, typically as structured data tables (JSON). The results might also include styling information suggesting visualizations.",
18
- },
19
- source: {
20
- description:
21
- "Represents a table or dataset (like 'orders'), including its columns and any attached calculations (dimensions/measures) or Views.",
22
- usage: "Examine the `definition` to understand the structure of this data and see available Views. Use `GetResource` on a View's URI to inspect it, or use the `malloy_executeQuery` tool (referencing the source and potentially a view) to get data results (JSON).",
23
- },
24
- view: {
25
- description:
26
- "A saved recipe within a Source for a specific analysis (like 'Top 10 Products').",
27
- usage: "Run this predefined analysis using the `malloy_executeQuery` tool (referencing the parent source/model and this view's name) to get results, typically structured data tables (JSON), sometimes with visualization hints.",
28
- },
29
- query: {
30
- description:
31
- "Similar to a View, a saved recipe for an analysis, usually stored directly in a Model.",
32
- usage: "Run this standard report using the `malloy_executeQuery` tool (referencing the parent model and this query's name) to get results, typically structured data tables (JSON), sometimes with visualization hints.",
33
- },
34
- notebook: {
35
- description:
36
- "An interactive document combining notes (Markdown text), Malloy code, and potentially saved queries or results.",
37
- usage: "Read the `definition` to explore the analysis steps, notes (Markdown), and code. Identify queries within the notebook cells that you can run using the `malloy_executeQuery` tool to get results (JSON data, visualization hints).",
38
- },
39
- package: {
40
- description: "A folder grouping related Models and Notebooks.",
41
- usage: "Use `ListResources` on this Package's URI to discover the Models and Notebooks it contains. You can then use `GetResource` on those items' URIs to explore them further.",
42
- },
43
- environment: {
44
- description: "The main workspace folder holding Packages, Models, etc.",
45
- usage: "Use `ListResources` on this environment's URI to see the Packages inside and begin navigating your data assets.",
46
- },
47
- };
@@ -1,187 +0,0 @@
1
- import {
2
- McpServer,
3
- ResourceTemplate,
4
- } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import type { ListResourcesResult } from "@modelcontextprotocol/sdk/types.js"; // Needed for list return type
6
- import { logger } from "../../logger";
7
- import { EnvironmentStore } from "../../service/environment_store";
8
- import { getInternalError, getNotFoundError } from "../error_messages"; // Needed for error handling in list AND get
9
- import { handleResourceGet, McpGetResourceError } from "../handler_utils";
10
- import { RESOURCE_METADATA } from "../resource_metadata";
11
-
12
- // Define an interface for the package object augmented with environment name
13
- interface PackageWithEnvironment {
14
- name?: string;
15
- // Add other relevant package properties if needed
16
- environmentName: string;
17
- }
18
-
19
- /**
20
- * Registers the Malloy environment resource type with the MCP server (URI scheme malloy://environment/...).
21
- * Lists packages across environments and resolves environment metadata on read.
22
- */
23
- export function registerEnvironmentResource(
24
- mcpServer: McpServer,
25
- environmentStore: EnvironmentStore,
26
- ): void {
27
- mcpServer.resource(
28
- "environment",
29
- new ResourceTemplate("malloy://environment/{environmentName}", {
30
- /**
31
- * Handles ListResources requests.
32
- * If environmentName is specified, lists packages for that environment (only 'home' supported).
33
- * If environmentName is not specified (general ListResources call), lists packages for the default 'home' environment.
34
- */
35
- list: async (/* extra: ListEnvironmentExtra - Deleted */): Promise<ListResourcesResult> => {
36
- logger.info(
37
- "[MCP LOG] Entering ListResources (environment) handler (listing ALL packages)...",
38
- );
39
- // Ignore parameters from 'extra' as URI path params aren't passed to list handlers.
40
-
41
- try {
42
- const allEnvironments =
43
- await environmentStore.listEnvironments();
44
- logger.info(
45
- `[MCP LOG] Found ${allEnvironments.length} environments defined.`,
46
- );
47
-
48
- const packagePromises = allEnvironments.map(async (env) => {
49
- try {
50
- logger.info(
51
- `[MCP LOG] Getting environment '${env.name}' to list its packages...`,
52
- );
53
- const environmentInstance =
54
- await environmentStore.getEnvironment(env.name!, false);
55
- const packages = await environmentInstance.listPackages();
56
- logger.info(
57
- `[MCP LOG] Found ${packages.length} packages in environment '${env.name}'.`,
58
- );
59
- // Return packages along with their environment name for URI construction
60
- return packages.map((pkg) => ({
61
- ...pkg,
62
- environmentName: env.name,
63
- }));
64
- } catch (environmentError) {
65
- logger.error(
66
- `[MCP Server Error] Error getting/listing packages for environment ${env.name}:`,
67
- { error: environmentError },
68
- );
69
- return []; // Return empty array for this environment on error
70
- }
71
- });
72
-
73
- const results = await Promise.allSettled(packagePromises);
74
- const allPackagesWithEnvironmentName = results
75
- .filter((result) => result.status === "fulfilled")
76
- .flatMap(
77
- (result) =>
78
- (
79
- result as PromiseFulfilledResult<
80
- PackageWithEnvironment[]
81
- >
82
- ).value,
83
- );
84
-
85
- logger.info(
86
- `[MCP LOG] Total packages found across all environments: ${allPackagesWithEnvironmentName.length}`,
87
- );
88
-
89
- const packageMetadata = RESOURCE_METADATA.package;
90
- const mappedResources = allPackagesWithEnvironmentName.map(
91
- (pkg) => {
92
- const name = pkg.name || "unknown";
93
- // Construct URI using the package's specific environmentName
94
- const uri = `malloy://environment/${pkg.environmentName}/package/${name}`;
95
- return {
96
- uri: uri,
97
- name: name,
98
- type: "package",
99
- description: packageMetadata?.description as
100
- | string
101
- | undefined,
102
- metadata: packageMetadata,
103
- };
104
- },
105
- );
106
-
107
- logger.info(
108
- `[MCP LOG] ListResources (environment): Returning ${mappedResources.length} package resources.`,
109
- );
110
- return {
111
- resources: mappedResources,
112
- };
113
- } catch (error) {
114
- // Catch errors from environmentStore.listEnvironments() itself
115
- logger.error(`[MCP Server Error] Error listing environments:`, {
116
- error,
117
- });
118
- const errorDetails = getInternalError(
119
- `ListResources (environment - initial list)`,
120
- error,
121
- );
122
- logger.error("MCP ListResources (environment) error:", {
123
- error: errorDetails.message,
124
- });
125
- logger.info(
126
- "[MCP LOG] ListResources (environment): Returning empty on error listing environments.",
127
- );
128
- return { resources: [] };
129
- }
130
- },
131
- }),
132
- /** Handles GetResource requests for Malloy environments */
133
- (uri, params) =>
134
- handleResourceGet(
135
- uri,
136
- params,
137
- "environment",
138
- async ({ environmentName }: { environmentName?: unknown }) => {
139
- logger.info(
140
- `[MCP LOG] Entering GetResource (environment) handler for environmentName: ${environmentName}`,
141
- );
142
- // Validate environment name parameter
143
- if (typeof environmentName !== "string") {
144
- logger.error(
145
- "[MCP LOG] GetResource (environment): Invalid environment name param.",
146
- );
147
- throw new Error("Invalid environment name parameter.");
148
- }
149
-
150
- try {
151
- logger.info(
152
- `[MCP LOG] GetResource: Getting environment '${environmentName}'...`,
153
- );
154
- // Get the environment instance, but we might not need its metadata directly
155
- await environmentStore.getEnvironment(environmentName, false);
156
- // Construct the definition object expected by the test
157
- const definition = { name: environmentName };
158
- logger.info(
159
- `[MCP LOG] GetResource (environment): Returning definition for '${environmentName}'.`,
160
- );
161
- // Return the explicit definition structure
162
- return definition;
163
- } catch (error) {
164
- logger.error(
165
- `[MCP LOG] GetResource (environment): Error caught for '${environmentName}':`,
166
- { error },
167
- );
168
- // Catch expected errors from this specific resource logic
169
- if (error instanceof Error) {
170
- // Use getNotFoundError for the specific environment not found case
171
- // or a generic message for the invalid param case.
172
- const errorDetails = getNotFoundError(
173
- error.message.includes("not found")
174
- ? `Environment '${environmentName}'` // More specific context
175
- : `Invalid environment identifier provided for URI ${uri.href}`, // Generic but informative
176
- );
177
- // Re-throw structured error for handleResourceGet to catch
178
- throw new McpGetResourceError(errorDetails);
179
- }
180
- // Re-throw unexpected errors to be caught by handleResourceGet's generic handler
181
- throw error;
182
- }
183
- },
184
- RESOURCE_METADATA.environment,
185
- ),
186
- );
187
- }
@@ -1,155 +0,0 @@
1
- import {
2
- McpServer,
3
- ResourceTemplate,
4
- } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import { EnvironmentStore } from "../../service/environment_store";
6
- import {
7
- ModelNotFoundError,
8
- PackageNotFoundError,
9
- ModelCompilationError,
10
- } from "../../errors";
11
- import { handleResourceGet, McpGetResourceError } from "../handler_utils";
12
- import { RESOURCE_METADATA } from "../resource_metadata";
13
- import {
14
- getNotFoundError,
15
- getMalloyErrorDetails,
16
- getInternalError,
17
- } from "../error_messages";
18
- import type { components } from "../../api"; // Import the components type
19
-
20
- /**
21
- * Registers the Malloy Model resource type with the MCP server.
22
- * Handles getting details for a specific model.
23
- */
24
- export function registerModelResource(
25
- mcpServer: McpServer,
26
- environmentStore: EnvironmentStore,
27
- ): void {
28
- mcpServer.resource(
29
- "model",
30
- new ResourceTemplate(
31
- "malloy://environment/{environmentName}/package/{packageName}/models/{modelPath}",
32
- { list: undefined }, // No list handler for individual models
33
- ),
34
- /** Handles GetResource requests for specific Malloy Models */
35
- (uri, params) =>
36
- handleResourceGet(
37
- uri,
38
- params,
39
- "model",
40
- async ({
41
- environmentName,
42
- packageName,
43
- modelPath,
44
- }: {
45
- environmentName?: unknown;
46
- packageName?: unknown;
47
- modelPath?: unknown;
48
- }) => {
49
- try {
50
- // Validate all parameters
51
- if (typeof environmentName !== "string") {
52
- throw new Error("Invalid environment name parameter.");
53
- }
54
- if (typeof packageName !== "string") {
55
- throw new Error("Invalid package name parameter.");
56
- }
57
- if (typeof modelPath !== "string") {
58
- throw new Error("Invalid model path parameter.");
59
- }
60
-
61
- // *** UPDATED LOGIC using EnvironmentStore ***
62
- // getEnvironment can throw EnvironmentNotFoundError (though unlikely if name is 'home')
63
- const environment = await environmentStore.getEnvironment(
64
- environmentName,
65
- false,
66
- );
67
- // getPackage can throw PackageNotFoundError
68
- const pkg = await environment.getPackage(packageName, false);
69
- // getModel is SYNCHRONOUS
70
- const modelInstance = pkg.getModel(modelPath);
71
- // *** END UPDATED LOGIC ***
72
-
73
- if (
74
- !modelInstance ||
75
- modelInstance.getModelType() === "notebook"
76
- ) {
77
- // Explicitly throw the expected error if not found or wrong type
78
- throw new ModelNotFoundError(modelPath);
79
- }
80
-
81
- // modelInstance.getModel() is ASYNC and can throw ModelCompilationError
82
- const compiledModelDefinition: components["schemas"]["CompiledModel"] =
83
- await modelInstance.getModel();
84
-
85
- // Strip implicit filters from agent-facing responses
86
- if (compiledModelDefinition.sources) {
87
- for (const source of compiledModelDefinition.sources) {
88
- if (source.filters) {
89
- source.filters = source.filters.filter(
90
- (f) => !f.implicit,
91
- );
92
- }
93
- }
94
- }
95
-
96
- return compiledModelDefinition;
97
- } catch (error) {
98
- let errorDetails;
99
- // Provide specific context for error messages
100
- // Use validated string parameters here
101
- const safeEnvironmentName =
102
- typeof environmentName === "string"
103
- ? environmentName
104
- : "unknown";
105
- const safePackageName =
106
- typeof packageName === "string" ? packageName : "unknown";
107
- const safeModelPath =
108
- typeof modelPath === "string" ? modelPath : "unknown";
109
-
110
- const notFoundContext = `Model '${safeModelPath}' in package '${safePackageName}' for environment '${safeEnvironmentName}'`;
111
- const malloyErrorContext = `${safeEnvironmentName}/${safePackageName}/${safeModelPath}`;
112
-
113
- if (error instanceof PackageNotFoundError) {
114
- // Package not found during getPackage call
115
- errorDetails = getNotFoundError(
116
- `Package '${safePackageName}' in environment '${safeEnvironmentName}'`,
117
- );
118
- } else if (error instanceof ModelNotFoundError) {
119
- // Model not found (either from getModel or type check)
120
- errorDetails = getNotFoundError(notFoundContext);
121
- } else if (error instanceof ModelCompilationError) {
122
- // Compilation error when fetching model definition
123
- errorDetails = getMalloyErrorDetails(
124
- "GetResource (model)",
125
- malloyErrorContext,
126
- error,
127
- );
128
- } else if (error instanceof Error) {
129
- // Catch invalid param errors or other generic errors
130
- // Provide a clearer message differentiating param errors
131
- if (error.message.includes("parameter")) {
132
- errorDetails = getNotFoundError(
133
- `Invalid identifier parameter provided for URI ${uri.href}: ${error.message}`,
134
- );
135
- } else {
136
- errorDetails = getInternalError(
137
- "GetResource (model) - Unexpected Error",
138
- error,
139
- );
140
- }
141
- } else {
142
- // Fallback for truly unexpected non-Error throws
143
- errorDetails = getInternalError(
144
- "GetResource (model)",
145
- error,
146
- );
147
- }
148
- // Wrap and re-throw for handleResourceGet
149
- throw new McpGetResourceError(errorDetails);
150
- }
151
- },
152
- RESOURCE_METADATA.model,
153
- ),
154
- );
155
- }
@@ -1,137 +0,0 @@
1
- import {
2
- McpServer,
3
- ResourceTemplate,
4
- } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import { URL } from "url";
6
- import type { components } from "../../api"; // Need this for CompiledModel type
7
- import {
8
- ModelCompilationError,
9
- ModelNotFoundError,
10
- PackageNotFoundError,
11
- } from "../../errors";
12
- import { logger } from "../../logger";
13
- import { EnvironmentStore } from "../../service/environment_store";
14
- import {
15
- getInternalError,
16
- getMalloyErrorDetails,
17
- getNotFoundError,
18
- } from "../error_messages";
19
- import { handleResourceGet, McpGetResourceError } from "../handler_utils";
20
- import { RESOURCE_METADATA } from "../resource_metadata";
21
-
22
- // Define the expected parameter types
23
- type NotebookParams = {
24
- environmentName?: unknown;
25
- packageName?: unknown;
26
- notebookName?: unknown;
27
- };
28
-
29
- /**
30
- * Registers the Malloy Notebook resource type.
31
- * Handles getting details for a specific notebook.
32
- */
33
- export function registerNotebookResource(
34
- mcpServer: McpServer,
35
- environmentStore: EnvironmentStore,
36
- ): void {
37
- mcpServer.resource(
38
- "notebook",
39
- new ResourceTemplate(
40
- "malloy://environment/{environmentName}/package/{packageName}/notebooks/{notebookName}",
41
- { list: undefined }, // Listing notebooks is not supported via this template
42
- ),
43
- (uri, params) =>
44
- handleResourceGet(
45
- uri,
46
- params as NotebookParams,
47
- "notebook",
48
- async (
49
- { environmentName, packageName, notebookName }: NotebookParams,
50
- uri: URL,
51
- ) => {
52
- if (
53
- typeof environmentName !== "string" ||
54
- typeof packageName !== "string" ||
55
- typeof notebookName !== "string"
56
- ) {
57
- throw new Error("Invalid parameters for notebook resource.");
58
- }
59
-
60
- let modelInstance;
61
- try {
62
- const environment = await environmentStore.getEnvironment(
63
- environmentName,
64
- false,
65
- );
66
- const pkg = await environment.getPackage(packageName, false);
67
- // Get the model instance using the notebookName as the path
68
- modelInstance = pkg.getModel(notebookName);
69
-
70
- // Check if it exists and is actually a notebook
71
- if (
72
- !modelInstance ||
73
- modelInstance.getModelType() !== "notebook"
74
- ) {
75
- const isNotebookError =
76
- modelInstance?.getModelType() !== "notebook";
77
- const errorDetails = getNotFoundError(
78
- `Notebook '${notebookName}' in package '${packageName}' environment '${environmentName}'${
79
- isNotebookError ? " (not a .malloynb file)" : ""
80
- }`,
81
- );
82
- throw new McpGetResourceError(errorDetails);
83
- }
84
-
85
- // Now try to get the actual notebook content
86
- const notebookContent: components["schemas"]["RawNotebook"] =
87
- await modelInstance.getNotebook();
88
- return notebookContent;
89
- } catch (error) {
90
- if (error instanceof McpGetResourceError) {
91
- throw error; // Re-throw if already formatted by checks above
92
- }
93
-
94
- // Handle specific errors like PackageNotFoundError or ModelCompilationError
95
- if (error instanceof PackageNotFoundError) {
96
- throw new McpGetResourceError(
97
- getNotFoundError(
98
- `Package '${packageName}' in environment '${environmentName}'`,
99
- ),
100
- );
101
- }
102
- // Handle ModelCompilationError from modelInstance.getModel()
103
- if (error instanceof ModelCompilationError) {
104
- const malloyErrorContext = `${environmentName}/${packageName}/${notebookName}`;
105
- throw new McpGetResourceError(
106
- getMalloyErrorDetails(
107
- "GetResource (notebook compilation)",
108
- malloyErrorContext,
109
- error,
110
- ),
111
- );
112
- }
113
- // Handle ModelNotFoundError specifically from pkg.getModel()
114
- if (error instanceof ModelNotFoundError) {
115
- throw new McpGetResourceError(
116
- getNotFoundError(
117
- `Notebook '${notebookName}' not found in package '${packageName}' environment '${environmentName}'`,
118
- ),
119
- );
120
- }
121
-
122
- // Handle other unexpected errors
123
- logger.error(
124
- `[MCP Server Error] Error fetching notebook '${notebookName}' from ${uri.href}:`,
125
- { error },
126
- );
127
- const fallbackErrorDetails = getInternalError(
128
- `GetResource (notebook: ${uri.href})`,
129
- error,
130
- );
131
- throw new McpGetResourceError(fallbackErrorDetails);
132
- }
133
- },
134
- RESOURCE_METADATA.notebook,
135
- ),
136
- );
137
- }