coalesce-transform-mcp 0.1.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.
Files changed (134) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +304 -0
  3. package/dist/cache-dir.d.ts +26 -0
  4. package/dist/cache-dir.js +106 -0
  5. package/dist/client.d.ts +25 -0
  6. package/dist/client.js +212 -0
  7. package/dist/coalesce/api/environments.d.ts +20 -0
  8. package/dist/coalesce/api/environments.js +15 -0
  9. package/dist/coalesce/api/git-accounts.d.ts +21 -0
  10. package/dist/coalesce/api/git-accounts.js +21 -0
  11. package/dist/coalesce/api/jobs.d.ts +25 -0
  12. package/dist/coalesce/api/jobs.js +21 -0
  13. package/dist/coalesce/api/nodes.d.ts +29 -0
  14. package/dist/coalesce/api/nodes.js +33 -0
  15. package/dist/coalesce/api/projects.d.ts +22 -0
  16. package/dist/coalesce/api/projects.js +25 -0
  17. package/dist/coalesce/api/runs.d.ts +19 -0
  18. package/dist/coalesce/api/runs.js +34 -0
  19. package/dist/coalesce/api/subgraphs.d.ts +20 -0
  20. package/dist/coalesce/api/subgraphs.js +17 -0
  21. package/dist/coalesce/api/users.d.ts +30 -0
  22. package/dist/coalesce/api/users.js +31 -0
  23. package/dist/coalesce/types.d.ts +298 -0
  24. package/dist/coalesce/types.js +746 -0
  25. package/dist/generated/.gitkeep +0 -0
  26. package/dist/generated/node-type-corpus.json +42656 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +10 -0
  29. package/dist/mcp/cache.d.ts +3 -0
  30. package/dist/mcp/cache.js +137 -0
  31. package/dist/mcp/environments.d.ts +3 -0
  32. package/dist/mcp/environments.js +61 -0
  33. package/dist/mcp/git-accounts.d.ts +3 -0
  34. package/dist/mcp/git-accounts.js +70 -0
  35. package/dist/mcp/jobs.d.ts +3 -0
  36. package/dist/mcp/jobs.js +77 -0
  37. package/dist/mcp/node-type-corpus.d.ts +3 -0
  38. package/dist/mcp/node-type-corpus.js +173 -0
  39. package/dist/mcp/nodes.d.ts +3 -0
  40. package/dist/mcp/nodes.js +341 -0
  41. package/dist/mcp/pipelines.d.ts +3 -0
  42. package/dist/mcp/pipelines.js +342 -0
  43. package/dist/mcp/projects.d.ts +3 -0
  44. package/dist/mcp/projects.js +70 -0
  45. package/dist/mcp/repo-node-types.d.ts +135 -0
  46. package/dist/mcp/repo-node-types.js +387 -0
  47. package/dist/mcp/runs.d.ts +3 -0
  48. package/dist/mcp/runs.js +92 -0
  49. package/dist/mcp/subgraphs.d.ts +3 -0
  50. package/dist/mcp/subgraphs.js +60 -0
  51. package/dist/mcp/users.d.ts +3 -0
  52. package/dist/mcp/users.js +107 -0
  53. package/dist/prompts/index.d.ts +2 -0
  54. package/dist/prompts/index.js +58 -0
  55. package/dist/resources/context/aggregation-patterns.md +145 -0
  56. package/dist/resources/context/data-engineering-principles.md +183 -0
  57. package/dist/resources/context/hydrated-metadata.md +92 -0
  58. package/dist/resources/context/id-discovery.md +64 -0
  59. package/dist/resources/context/intelligent-node-configuration.md +162 -0
  60. package/dist/resources/context/node-creation-decision-tree.md +156 -0
  61. package/dist/resources/context/node-operations.md +316 -0
  62. package/dist/resources/context/node-payloads.md +114 -0
  63. package/dist/resources/context/node-type-corpus.md +166 -0
  64. package/dist/resources/context/node-type-selection-guide.md +96 -0
  65. package/dist/resources/context/overview.md +135 -0
  66. package/dist/resources/context/pipeline-workflows.md +355 -0
  67. package/dist/resources/context/run-operations.md +55 -0
  68. package/dist/resources/context/sql-bigquery.md +41 -0
  69. package/dist/resources/context/sql-databricks.md +40 -0
  70. package/dist/resources/context/sql-platform-selection.md +70 -0
  71. package/dist/resources/context/sql-snowflake.md +43 -0
  72. package/dist/resources/context/storage-mappings.md +49 -0
  73. package/dist/resources/context/tool-usage.md +98 -0
  74. package/dist/resources/index.d.ts +5 -0
  75. package/dist/resources/index.js +254 -0
  76. package/dist/schemas/node-payloads.d.ts +5019 -0
  77. package/dist/schemas/node-payloads.js +147 -0
  78. package/dist/server.d.ts +7 -0
  79. package/dist/server.js +63 -0
  80. package/dist/services/cache/snapshots.d.ts +108 -0
  81. package/dist/services/cache/snapshots.js +275 -0
  82. package/dist/services/config/context-analyzer.d.ts +14 -0
  83. package/dist/services/config/context-analyzer.js +76 -0
  84. package/dist/services/config/field-classifier.d.ts +23 -0
  85. package/dist/services/config/field-classifier.js +47 -0
  86. package/dist/services/config/intelligent.d.ts +55 -0
  87. package/dist/services/config/intelligent.js +306 -0
  88. package/dist/services/config/rules.d.ts +6 -0
  89. package/dist/services/config/rules.js +44 -0
  90. package/dist/services/config/schema-resolver.d.ts +18 -0
  91. package/dist/services/config/schema-resolver.js +80 -0
  92. package/dist/services/corpus/loader.d.ts +56 -0
  93. package/dist/services/corpus/loader.js +25 -0
  94. package/dist/services/corpus/search.d.ts +49 -0
  95. package/dist/services/corpus/search.js +69 -0
  96. package/dist/services/corpus/templates.d.ts +4 -0
  97. package/dist/services/corpus/templates.js +11 -0
  98. package/dist/services/pipelines/execution.d.ts +20 -0
  99. package/dist/services/pipelines/execution.js +290 -0
  100. package/dist/services/pipelines/node-type-intent.d.ts +96 -0
  101. package/dist/services/pipelines/node-type-intent.js +356 -0
  102. package/dist/services/pipelines/node-type-selection.d.ts +66 -0
  103. package/dist/services/pipelines/node-type-selection.js +758 -0
  104. package/dist/services/pipelines/planning.d.ts +543 -0
  105. package/dist/services/pipelines/planning.js +1839 -0
  106. package/dist/services/policies/sql-override.d.ts +7 -0
  107. package/dist/services/policies/sql-override.js +109 -0
  108. package/dist/services/repo/operations.d.ts +6 -0
  109. package/dist/services/repo/operations.js +10 -0
  110. package/dist/services/repo/parser.d.ts +70 -0
  111. package/dist/services/repo/parser.js +365 -0
  112. package/dist/services/repo/path.d.ts +2 -0
  113. package/dist/services/repo/path.js +58 -0
  114. package/dist/services/templates/nodes.d.ts +50 -0
  115. package/dist/services/templates/nodes.js +336 -0
  116. package/dist/services/workspace/analysis.d.ts +56 -0
  117. package/dist/services/workspace/analysis.js +151 -0
  118. package/dist/services/workspace/mutations.d.ts +150 -0
  119. package/dist/services/workspace/mutations.js +1718 -0
  120. package/dist/utils.d.ts +5 -0
  121. package/dist/utils.js +7 -0
  122. package/dist/workflows/get-environment-overview.d.ts +9 -0
  123. package/dist/workflows/get-environment-overview.js +23 -0
  124. package/dist/workflows/get-run-details.d.ts +10 -0
  125. package/dist/workflows/get-run-details.js +28 -0
  126. package/dist/workflows/progress.d.ts +20 -0
  127. package/dist/workflows/progress.js +54 -0
  128. package/dist/workflows/retry-and-wait.d.ts +13 -0
  129. package/dist/workflows/retry-and-wait.js +139 -0
  130. package/dist/workflows/run-and-wait.d.ts +13 -0
  131. package/dist/workflows/run-and-wait.js +141 -0
  132. package/dist/workflows/run-status.d.ts +10 -0
  133. package/dist/workflows/run-status.js +27 -0
  134. package/package.json +34 -0
@@ -0,0 +1,387 @@
1
+ import { z } from "zod";
2
+ import { buildSetWorkspaceNodeTemplateFromDefinition, compareGeneratedTemplateToWorkspaceNode, renderYaml, } from "../services/templates/nodes.js";
3
+ import { sanitizeNodeDefinitionSqlOverridePolicy } from "../services/policies/sql-override.js";
4
+ import { getWorkspaceNode } from "../coalesce/api/nodes.js";
5
+ import { buildJsonToolResponse, handleToolError, READ_ONLY_ANNOTATIONS, } from "../coalesce/types.js";
6
+ import { parseRepo, resolveRepoNodeType, } from "../services/repo/parser.js";
7
+ import { resolveRepoPathInput } from "../services/repo/path.js";
8
+ import { isPlainObject } from "../utils.js";
9
+ function compareStrings(left, right) {
10
+ return left.localeCompare(right, undefined, {
11
+ numeric: true,
12
+ sensitivity: "case",
13
+ });
14
+ }
15
+ function requireComparisonPair(params) {
16
+ if ((params.workspaceID && !params.nodeID) || (!params.workspaceID && params.nodeID)) {
17
+ throw new Error("workspaceID and nodeID must be provided together when requesting a live node comparison.");
18
+ }
19
+ }
20
+ function summarizePackageRecord(record) {
21
+ return {
22
+ alias: record.alias,
23
+ aliasSource: record.aliasSource,
24
+ packageFilePath: record.packageFilePath,
25
+ packageID: record.packageID,
26
+ releaseID: record.releaseID,
27
+ packageVariables: record.packageVariables,
28
+ enabledNodeTypeIDs: record.enabledNodeTypeIDs,
29
+ resolvedDefinitionIDs: record.resolvedDefinitionIDs,
30
+ missingDefinitionIDs: record.missingDefinitionIDs,
31
+ ambiguousDefinitionIDs: record.ambiguousDefinitionIDs,
32
+ usageByNodeTypeID: record.usageByNodeTypeID,
33
+ usageCount: record.usageCount,
34
+ warnings: record.warnings,
35
+ };
36
+ }
37
+ function buildRepoSummary(parsedRepo) {
38
+ return {
39
+ repoPath: parsedRepo.summary.repoPath,
40
+ resolvedRepoPath: parsedRepo.summary.resolvedRepoPath,
41
+ packageCount: parsedRepo.summary.packageCount,
42
+ uniquePackageAliasCount: parsedRepo.summary.uniquePackageAliasCount,
43
+ nodeTypeDefinitionCount: parsedRepo.summary.nodeTypeDefinitionCount,
44
+ uniqueNodeTypeIDCount: parsedRepo.summary.uniqueNodeTypeIDCount,
45
+ nodeCount: parsedRepo.summary.nodeCount,
46
+ warnings: parsedRepo.summary.warnings,
47
+ };
48
+ }
49
+ function buildNodeTypeRow(resolution) {
50
+ return {
51
+ nodeType: resolution.resolvedNodeType,
52
+ outerID: resolution.nodeTypeRecord.outerDefinition.id,
53
+ displayName: resolution.nodeTypeRecord.outerDefinition.name,
54
+ resolutionKind: resolution.resolutionKind,
55
+ ...(resolution.resolutionKind === "package"
56
+ ? { packageAlias: resolution.packageAlias }
57
+ : {}),
58
+ definitionPath: resolution.nodeTypeRecord.definitionPath,
59
+ createPath: resolution.nodeTypeRecord.createPath,
60
+ runPath: resolution.nodeTypeRecord.runPath,
61
+ usageCount: resolution.usageCount,
62
+ parseError: resolution.nodeTypeRecord.parseError,
63
+ warnings: resolution.nodeTypeRecord.warnings,
64
+ };
65
+ }
66
+ function buildResolutionMetadata(resolution) {
67
+ if (resolution.resolutionKind === "package") {
68
+ return {
69
+ resolutionKind: resolution.resolutionKind,
70
+ requestedNodeType: resolution.requestedNodeType,
71
+ resolvedNodeType: resolution.resolvedNodeType,
72
+ packageAlias: resolution.packageAlias,
73
+ packageFilePath: resolution.packageRecord.packageFilePath,
74
+ packageID: resolution.packageRecord.packageID,
75
+ releaseID: resolution.packageRecord.releaseID,
76
+ enabledNodeTypeID: resolution.nodeTypeRecord.outerDefinition.id,
77
+ usageCount: resolution.usageCount,
78
+ };
79
+ }
80
+ return {
81
+ resolutionKind: resolution.resolutionKind,
82
+ requestedNodeType: resolution.requestedNodeType,
83
+ resolvedNodeType: resolution.resolvedNodeType,
84
+ usageCount: resolution.usageCount,
85
+ };
86
+ }
87
+ function sanitizeRepoNodeDefinition(nodeDefinition, nodeMetadataSpec, warnings) {
88
+ if (!isPlainObject(nodeDefinition)) {
89
+ return {
90
+ nodeDefinition,
91
+ nodeMetadataSpecYaml: nodeMetadataSpec,
92
+ warnings,
93
+ };
94
+ }
95
+ const sanitized = sanitizeNodeDefinitionSqlOverridePolicy(nodeDefinition);
96
+ return {
97
+ nodeDefinition: sanitized.nodeDefinition,
98
+ nodeMetadataSpecYaml: renderYaml(sanitized.nodeDefinition),
99
+ warnings: Array.from(new Set([...warnings, ...sanitized.warnings])),
100
+ };
101
+ }
102
+ export function listRepoPackages(params) {
103
+ const repoPath = resolveRepoPathInput(params.repoPath);
104
+ const parsedRepo = parseRepo(repoPath);
105
+ return {
106
+ summary: buildRepoSummary(parsedRepo),
107
+ packages: parsedRepo.packages
108
+ .slice()
109
+ .sort((left, right) => compareStrings(left.alias, right.alias))
110
+ .map(summarizePackageRecord),
111
+ };
112
+ }
113
+ export function listRepoNodeTypes(params) {
114
+ const repoPath = resolveRepoPathInput(params.repoPath);
115
+ const parsedRepo = parseRepo(repoPath);
116
+ const rows = [];
117
+ const warnings = [...parsedRepo.summary.warnings];
118
+ if (params.packageAlias) {
119
+ const packageMatches = parsedRepo.packagesByAlias.get(params.packageAlias) ?? [];
120
+ if (packageMatches.length === 0) {
121
+ warnings.push(`No package alias ${params.packageAlias} was found under packages/.`);
122
+ }
123
+ else if (packageMatches.length > 1) {
124
+ warnings.push(`Package alias ${params.packageAlias} is ambiguous across ${packageMatches
125
+ .map((match) => match.packageFilePath)
126
+ .join(", ")}.`);
127
+ }
128
+ else {
129
+ for (const definitionID of packageMatches[0].resolvedDefinitionIDs) {
130
+ const resolution = resolveRepoNodeType(parsedRepo, `${params.packageAlias}:::${definitionID}`);
131
+ rows.push(buildNodeTypeRow(resolution));
132
+ }
133
+ }
134
+ }
135
+ else {
136
+ for (const [id, matches] of parsedRepo.nodeTypesByID.entries()) {
137
+ if (matches.length !== 1) {
138
+ continue;
139
+ }
140
+ rows.push(buildNodeTypeRow(resolveRepoNodeType(parsedRepo, id)));
141
+ }
142
+ for (const packageRecord of parsedRepo.packages) {
143
+ const packageMatches = parsedRepo.packagesByAlias.get(packageRecord.alias) ?? [];
144
+ if (packageMatches.length !== 1) {
145
+ continue;
146
+ }
147
+ for (const definitionID of packageRecord.resolvedDefinitionIDs) {
148
+ rows.push(buildNodeTypeRow(resolveRepoNodeType(parsedRepo, `${packageRecord.alias}:::${definitionID}`)));
149
+ }
150
+ }
151
+ }
152
+ const filteredRows = (params.inUseOnly ? rows.filter((row) => row.usageCount > 0) : rows)
153
+ .sort((left, right) => right.usageCount === left.usageCount
154
+ ? compareStrings(left.nodeType, right.nodeType)
155
+ : right.usageCount - left.usageCount);
156
+ return {
157
+ summary: {
158
+ ...buildRepoSummary(parsedRepo),
159
+ warnings: Array.from(new Set(warnings)).sort(compareStrings),
160
+ packageAlias: params.packageAlias ?? null,
161
+ inUseOnly: params.inUseOnly ?? false,
162
+ matchedCount: filteredRows.length,
163
+ },
164
+ nodeTypes: filteredRows,
165
+ };
166
+ }
167
+ export function getRepoNodeTypeDefinition(params) {
168
+ const repoPath = resolveRepoPathInput(params.repoPath);
169
+ const parsedRepo = parseRepo(repoPath);
170
+ const resolution = resolveRepoNodeType(parsedRepo, params.nodeType);
171
+ const sanitized = sanitizeRepoNodeDefinition(resolution.nodeTypeRecord.nodeDefinition, resolution.nodeTypeRecord.nodeMetadataSpec, resolution.nodeTypeRecord.warnings);
172
+ return {
173
+ repoPath,
174
+ resolvedRepoPath: parsedRepo.summary.resolvedRepoPath,
175
+ repoWarnings: parsedRepo.summary.warnings,
176
+ requestedNodeType: params.nodeType,
177
+ resolvedNodeType: resolution.resolvedNodeType,
178
+ resolution: buildResolutionMetadata(resolution),
179
+ ...(resolution.resolutionKind === "package"
180
+ ? {
181
+ package: summarizePackageRecord(resolution.packageRecord),
182
+ }
183
+ : {}),
184
+ outerDefinition: resolution.nodeTypeRecord.outerDefinition,
185
+ nodeMetadataSpecYaml: sanitized.nodeMetadataSpecYaml,
186
+ nodeDefinition: sanitized.nodeDefinition,
187
+ parseError: resolution.nodeTypeRecord.parseError,
188
+ filePaths: {
189
+ definitionPath: resolution.nodeTypeRecord.definitionPath,
190
+ createPath: resolution.nodeTypeRecord.createPath,
191
+ runPath: resolution.nodeTypeRecord.runPath,
192
+ },
193
+ usageSummary: {
194
+ exactNodeType: resolution.resolvedNodeType,
195
+ usageCount: resolution.usageCount,
196
+ },
197
+ warnings: sanitized.warnings,
198
+ };
199
+ }
200
+ async function maybeBuildComparison(client, generated, params) {
201
+ requireComparisonPair(params);
202
+ if (!params.workspaceID || !params.nodeID) {
203
+ return undefined;
204
+ }
205
+ const workspaceNode = await getWorkspaceNode(client, {
206
+ workspaceID: params.workspaceID,
207
+ nodeID: params.nodeID,
208
+ });
209
+ if (!isPlainObject(workspaceNode)) {
210
+ throw new Error("Workspace node comparison target was not an object");
211
+ }
212
+ return {
213
+ workspaceID: params.workspaceID,
214
+ nodeID: params.nodeID,
215
+ result: compareGeneratedTemplateToWorkspaceNode(generated, workspaceNode),
216
+ };
217
+ }
218
+ export async function generateSetWorkspaceNodeTemplate(client, params) {
219
+ const usingDefinition = params.definition !== undefined;
220
+ if (usingDefinition && params.repoPath !== undefined) {
221
+ throw new Error("Provide exactly one input mode: either definition for raw mode or repoPath with nodeType for repo mode.");
222
+ }
223
+ if (usingDefinition && !isPlainObject(params.definition)) {
224
+ throw new Error("definition must be an object when using raw mode.");
225
+ }
226
+ if (!usingDefinition && !params.nodeType) {
227
+ throw new Error("nodeType is required when using repo mode.");
228
+ }
229
+ let generated;
230
+ let result;
231
+ if (usingDefinition) {
232
+ const sanitizedDefinition = sanitizeNodeDefinitionSqlOverridePolicy(params.definition);
233
+ generated = buildSetWorkspaceNodeTemplateFromDefinition(params.definition, {
234
+ nodeName: params.nodeName,
235
+ nodeType: params.nodeType,
236
+ locationName: params.locationName,
237
+ database: params.database,
238
+ schema: params.schema,
239
+ });
240
+ result = {
241
+ definitionSource: {
242
+ mode: "raw",
243
+ },
244
+ nodeDefinition: sanitizedDefinition.nodeDefinition,
245
+ ...generated,
246
+ warnings: Array.from(new Set([...generated.warnings, ...sanitizedDefinition.warnings])),
247
+ setWorkspaceNodeBodyTemplateYaml: renderYaml(generated.setWorkspaceNodeBodyTemplate),
248
+ };
249
+ }
250
+ else {
251
+ const repoPath = resolveRepoPathInput(params.repoPath);
252
+ const definition = getRepoNodeTypeDefinition({
253
+ repoPath,
254
+ nodeType: params.nodeType,
255
+ });
256
+ if (!isPlainObject(definition.nodeDefinition)) {
257
+ const parseError = typeof definition.parseError === "string" ? definition.parseError : "unknown";
258
+ throw new Error(`Repo-backed definition could not be resolved for template generation because metadata.nodeMetadataSpec could not be parsed. Parse error: ${parseError} Use the corpus tools (search-node-type-variants, get-node-type-variant, or generate-set-workspace-node-template-from-variant) as the next step.`);
259
+ }
260
+ generated = buildSetWorkspaceNodeTemplateFromDefinition(definition.nodeDefinition, {
261
+ nodeName: params.nodeName,
262
+ nodeType: definition.resolvedNodeType,
263
+ locationName: params.locationName,
264
+ database: params.database,
265
+ schema: params.schema,
266
+ });
267
+ result = {
268
+ definitionSource: {
269
+ mode: "repo",
270
+ },
271
+ repoPath,
272
+ resolvedRepoPath: definition.resolvedRepoPath,
273
+ requestedNodeType: definition.requestedNodeType,
274
+ resolvedNodeType: definition.resolvedNodeType,
275
+ resolution: definition.resolution,
276
+ ...(definition.package ? { package: definition.package } : {}),
277
+ outerDefinition: definition.outerDefinition,
278
+ nodeMetadataSpecYaml: definition.nodeMetadataSpecYaml,
279
+ nodeDefinition: definition.nodeDefinition,
280
+ filePaths: definition.filePaths,
281
+ usageSummary: definition.usageSummary,
282
+ repoWarnings: definition.repoWarnings,
283
+ ...generated,
284
+ warnings: Array.from(new Set([...definition.warnings, ...generated.warnings])),
285
+ setWorkspaceNodeBodyTemplateYaml: renderYaml(generated.setWorkspaceNodeBodyTemplate),
286
+ };
287
+ }
288
+ const comparison = await maybeBuildComparison(client, generated, params);
289
+ return comparison ? { ...result, comparison } : result;
290
+ }
291
+ export function registerRepoNodeTypeTools(server, client) {
292
+ server.tool("list-repo-packages", "Inspect a committed local Coalesce repo and list package aliases from packages/*.yml. Use this when a local repo is available and you want repo-backed node-type discovery before falling back to the corpus.", {
293
+ repoPath: z
294
+ .string()
295
+ .optional()
296
+ .describe("Optional absolute or relative path to the local committed Coalesce repo. Falls back to COALESCE_REPO_PATH when omitted."),
297
+ }, READ_ONLY_ANNOTATIONS, async (params) => {
298
+ try {
299
+ return buildJsonToolResponse("list-repo-packages", listRepoPackages(params));
300
+ }
301
+ catch (error) {
302
+ return handleToolError(error);
303
+ }
304
+ });
305
+ server.tool("list-repo-node-types", "Inspect a committed local Coalesce repo and list exact resolvable node-type identifiers from nodeTypes/, optionally filtered to one package alias. Repo-backed discovery is preferred when the repo contains the committed definition; otherwise use the corpus tools.", {
306
+ repoPath: z
307
+ .string()
308
+ .optional()
309
+ .describe("Optional absolute or relative path to the local committed Coalesce repo. Falls back to COALESCE_REPO_PATH when omitted."),
310
+ packageAlias: z
311
+ .string()
312
+ .optional()
313
+ .describe("Optional exact package alias filter sourced from package YAML name."),
314
+ inUseOnly: z
315
+ .boolean()
316
+ .optional()
317
+ .describe("When true, return only identifiers currently referenced by committed nodes/*.yml operation.sqlType values."),
318
+ }, READ_ONLY_ANNOTATIONS, async (params) => {
319
+ try {
320
+ return buildJsonToolResponse("list-repo-node-types", listRepoNodeTypes(params));
321
+ }
322
+ catch (error) {
323
+ return handleToolError(error);
324
+ }
325
+ });
326
+ server.tool("get-repo-node-type-definition", "Resolve one exact node type from a committed local Coalesce repo. Supports direct identifiers like Stage or 65 and package-backed identifiers like alias:::id. If the repo cannot resolve the definition exactly, use the corpus tools as the fallback path.", {
327
+ repoPath: z
328
+ .string()
329
+ .optional()
330
+ .describe("Optional absolute or relative path to the local committed Coalesce repo. Falls back to COALESCE_REPO_PATH when omitted."),
331
+ nodeType: z
332
+ .string()
333
+ .describe("Exact direct node type identifier or exact package-backed alias:::id value."),
334
+ }, READ_ONLY_ANNOTATIONS, async (params) => {
335
+ try {
336
+ return buildJsonToolResponse("get-repo-node-type-definition", getRepoNodeTypeDefinition(params));
337
+ }
338
+ catch (error) {
339
+ return handleToolError(error);
340
+ }
341
+ });
342
+ server.tool("generate-set-workspace-node-template", "Generate a YAML-friendly set-workspace-node body template either from a raw node definition object or by resolving a committed node type from a local repo. Prefer repo mode when a local committed repo contains the definition; use the corpus tools when repo-backed resolution is unavailable. SQL override controls are removed from returned templates because they are disallowed in this project.", {
343
+ definition: z
344
+ .record(z.unknown())
345
+ .optional()
346
+ .describe("Raw parsed node definition object for template generation in raw mode."),
347
+ repoPath: z
348
+ .string()
349
+ .optional()
350
+ .describe("Optional local committed repo path for repo mode. Falls back to COALESCE_REPO_PATH when omitted."),
351
+ nodeType: z
352
+ .string()
353
+ .optional()
354
+ .describe("Exact repo node type identifier in repo mode, or optional nodeType override in raw mode."),
355
+ nodeName: z
356
+ .string()
357
+ .optional()
358
+ .describe("Optional node name to inject into the generated template."),
359
+ locationName: z
360
+ .string()
361
+ .optional()
362
+ .describe("Optional storage location name to include in the template."),
363
+ database: z
364
+ .string()
365
+ .optional()
366
+ .describe("Optional database value to include in the template."),
367
+ schema: z
368
+ .string()
369
+ .optional()
370
+ .describe("Optional schema value to include in the template."),
371
+ workspaceID: z
372
+ .string()
373
+ .optional()
374
+ .describe("Optional workspace ID for comparing inferred mappings to a live workspace node."),
375
+ nodeID: z
376
+ .string()
377
+ .optional()
378
+ .describe("Optional node ID for comparing inferred mappings to a live workspace node."),
379
+ }, READ_ONLY_ANNOTATIONS, async (params) => {
380
+ try {
381
+ return buildJsonToolResponse("generate-set-workspace-node-template", await generateSetWorkspaceNodeTemplate(client, params));
382
+ }
383
+ catch (error) {
384
+ return handleToolError(error);
385
+ }
386
+ });
387
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { CoalesceClient } from "../client.js";
3
+ export declare function registerRunTools(server: McpServer, client: CoalesceClient): void;
@@ -0,0 +1,92 @@
1
+ import { z } from "zod";
2
+ import { listRuns, getRun, getRunResults, startRun, runStatus, retryRun, cancelRun, } from "../coalesce/api/runs.js";
3
+ import { PaginationParams, StartRunParams, RerunParams, buildJsonToolResponse, handleToolError, sanitizeResponse, READ_ONLY_ANNOTATIONS, WRITE_ANNOTATIONS, DESTRUCTIVE_ANNOTATIONS, } from "../coalesce/types.js";
4
+ export function registerRunTools(server, client) {
5
+ server.tool("list-runs", "List all Coalesce runs. Supports filtering by runType, runStatus, and environmentID.", PaginationParams.extend({
6
+ runType: z.enum(["deploy", "refresh"]).optional().describe("Filter by run type"),
7
+ runStatus: z.enum(["completed", "failed", "canceled", "running", "waitingToRun"]).optional().describe("Filter by run status"),
8
+ environmentID: z.string().optional().describe("Filter by environment ID"),
9
+ detail: z.boolean().optional().describe("Include full run details in response"),
10
+ }).shape, READ_ONLY_ANNOTATIONS, async (params) => {
11
+ try {
12
+ const result = await listRuns(client, params);
13
+ return buildJsonToolResponse("list-runs", sanitizeResponse(result));
14
+ }
15
+ catch (error) {
16
+ return handleToolError(error);
17
+ }
18
+ });
19
+ server.tool("get-run", "Get details of a specific Coalesce run", {
20
+ runID: z.string().describe("The numeric run ID (integer, e.g. '401'). Use the runCounter value from start-run or run-status responses — not the UUID from run URLs."),
21
+ }, READ_ONLY_ANNOTATIONS, async (params) => {
22
+ try {
23
+ const result = await getRun(client, params);
24
+ return buildJsonToolResponse("get-run", sanitizeResponse(result));
25
+ }
26
+ catch (error) {
27
+ return handleToolError(error);
28
+ }
29
+ });
30
+ server.tool("get-run-results", "Get the results of a specific Coalesce run", {
31
+ runID: z.string().describe("The numeric run ID (integer, e.g. '401'). Use the runCounter value from start-run or run-status responses — not the UUID from run URLs."),
32
+ }, READ_ONLY_ANNOTATIONS, async (params) => {
33
+ try {
34
+ const result = await getRunResults(client, params);
35
+ return buildJsonToolResponse("get-run-results", sanitizeResponse(result));
36
+ }
37
+ catch (error) {
38
+ return handleToolError(error);
39
+ }
40
+ });
41
+ server.tool("start-run", "Start a new Coalesce run. Requires a numeric environmentID and optionally a jobID (not job name). " +
42
+ "If the user provides a job name instead of an ID, ask them for the job ID. " +
43
+ "If the user doesn't know the environment ID, use list-environments to look it up by name. " +
44
+ "Requires Snowflake Key Pair auth; credentials are read from environment variables.", StartRunParams.shape, WRITE_ANNOTATIONS, async (params) => {
45
+ try {
46
+ const result = await startRun(client, params);
47
+ return buildJsonToolResponse("start-run", sanitizeResponse(result));
48
+ }
49
+ catch (error) {
50
+ return handleToolError(error);
51
+ }
52
+ });
53
+ server.tool("run-status", "Get the status of a Coalesce run by run counter", {
54
+ runCounter: z.number().describe("The run counter number"),
55
+ }, READ_ONLY_ANNOTATIONS, async (params) => {
56
+ try {
57
+ const result = await runStatus(client, params);
58
+ return buildJsonToolResponse("run-status", sanitizeResponse(result));
59
+ }
60
+ catch (error) {
61
+ return handleToolError(error);
62
+ }
63
+ });
64
+ server.tool("retry-run", "Retry a failed Coalesce run. Requires the runID from the original run. " +
65
+ "Requires Snowflake Key Pair auth; credentials are read from environment variables.", RerunParams.shape, WRITE_ANNOTATIONS, async (params) => {
66
+ try {
67
+ const result = await retryRun(client, params);
68
+ return buildJsonToolResponse("retry-run", sanitizeResponse(result));
69
+ }
70
+ catch (error) {
71
+ return handleToolError(error);
72
+ }
73
+ });
74
+ server.tool("cancel-run", "Cancel an in-progress Coalesce run. Requires the runID and environmentID. " +
75
+ "Provide orgID explicitly, or omit it to use COALESCE_ORG_ID from the environment. " +
76
+ "If the user does not know the orgID, they can find it in Coalesce Support Information.", {
77
+ runID: z.string().describe("The numeric run ID (integer) of the run to cancel"),
78
+ orgID: z
79
+ .string()
80
+ .optional()
81
+ .describe("The organization ID. Optional if COALESCE_ORG_ID is set."),
82
+ environmentID: z.string().describe("The environment ID the run belongs to"),
83
+ }, DESTRUCTIVE_ANNOTATIONS, async (params) => {
84
+ try {
85
+ const result = await cancelRun(client, params);
86
+ return buildJsonToolResponse("cancel-run", sanitizeResponse(result));
87
+ }
88
+ catch (error) {
89
+ return handleToolError(error);
90
+ }
91
+ });
92
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { CoalesceClient } from "../client.js";
3
+ export declare function registerSubgraphTools(server: McpServer, client: CoalesceClient): void;
@@ -0,0 +1,60 @@
1
+ import { z } from "zod";
2
+ import { getWorkspaceSubgraph, createWorkspaceSubgraph, updateWorkspaceSubgraph, deleteWorkspaceSubgraph, } from "../coalesce/api/subgraphs.js";
3
+ import { buildJsonToolResponse, handleToolError, READ_ONLY_ANNOTATIONS, WRITE_ANNOTATIONS, DESTRUCTIVE_ANNOTATIONS, } from "../coalesce/types.js";
4
+ export function registerSubgraphTools(server, client) {
5
+ server.tool("get-workspace-subgraph", "Get details of a specific subgraph in a Coalesce workspace.", {
6
+ workspaceID: z.string().describe("The workspace ID"),
7
+ subgraphID: z.string().describe("The subgraph ID"),
8
+ }, READ_ONLY_ANNOTATIONS, async (params) => {
9
+ try {
10
+ const result = await getWorkspaceSubgraph(client, params);
11
+ return buildJsonToolResponse("get-workspace-subgraph", result);
12
+ }
13
+ catch (error) {
14
+ return handleToolError(error);
15
+ }
16
+ });
17
+ server.tool("create-workspace-subgraph", "Create a subgraph in a Coalesce workspace. A subgraph groups nodes together visually. The steps array contains node IDs to include in the subgraph.", {
18
+ workspaceID: z.string().describe("The workspace ID"),
19
+ name: z.string().describe("Name for the subgraph"),
20
+ steps: z
21
+ .array(z.string())
22
+ .describe("Array of node IDs to include in the subgraph"),
23
+ }, WRITE_ANNOTATIONS, async (params) => {
24
+ try {
25
+ const result = await createWorkspaceSubgraph(client, params);
26
+ return buildJsonToolResponse("create-workspace-subgraph", result);
27
+ }
28
+ catch (error) {
29
+ return handleToolError(error);
30
+ }
31
+ });
32
+ server.tool("update-workspace-subgraph", "Update a subgraph in a Coalesce workspace. Replaces the subgraph's name and steps (node IDs).", {
33
+ workspaceID: z.string().describe("The workspace ID"),
34
+ subgraphID: z.string().describe("The subgraph ID to update"),
35
+ name: z.string().describe("Updated name for the subgraph"),
36
+ steps: z
37
+ .array(z.string())
38
+ .describe("Updated array of node IDs to include in the subgraph"),
39
+ }, WRITE_ANNOTATIONS, async (params) => {
40
+ try {
41
+ const result = await updateWorkspaceSubgraph(client, params);
42
+ return buildJsonToolResponse("update-workspace-subgraph", result);
43
+ }
44
+ catch (error) {
45
+ return handleToolError(error);
46
+ }
47
+ });
48
+ server.tool("delete-workspace-subgraph", "Delete a subgraph from a Coalesce workspace. This is a destructive operation — the subgraph will be permanently removed. The nodes within it are NOT deleted.", {
49
+ workspaceID: z.string().describe("The workspace ID"),
50
+ subgraphID: z.string().describe("The subgraph ID to delete"),
51
+ }, DESTRUCTIVE_ANNOTATIONS, async (params) => {
52
+ try {
53
+ const result = await deleteWorkspaceSubgraph(client, params);
54
+ return buildJsonToolResponse("delete-workspace-subgraph", result);
55
+ }
56
+ catch (error) {
57
+ return handleToolError(error);
58
+ }
59
+ });
60
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { CoalesceClient } from "../client.js";
3
+ export declare function registerUserTools(server: McpServer, client: CoalesceClient): void;
@@ -0,0 +1,107 @@
1
+ import { z } from "zod";
2
+ import { listOrgUsers, getUserRoles, listUserRoles, setOrgRole, setProjectRole, deleteProjectRole, setEnvRole, deleteEnvRole, } from "../coalesce/api/users.js";
3
+ import { PaginationParams, buildJsonToolResponse, handleToolError, READ_ONLY_ANNOTATIONS, IDEMPOTENT_WRITE_ANNOTATIONS, DESTRUCTIVE_ANNOTATIONS, } from "../coalesce/types.js";
4
+ export function registerUserTools(server, client) {
5
+ server.tool("list-org-users", "List all users in the Coalesce organization", PaginationParams.shape, READ_ONLY_ANNOTATIONS, async (params) => {
6
+ try {
7
+ const result = await listOrgUsers(client, params);
8
+ return buildJsonToolResponse("list-org-users", result);
9
+ }
10
+ catch (error) {
11
+ return handleToolError(error);
12
+ }
13
+ });
14
+ server.tool("get-user-roles", "Get roles assigned to a specific user", {
15
+ userID: z.string().describe("The user ID"),
16
+ projectID: z.string().optional().describe("Optional project scope filter"),
17
+ environmentID: z.string().optional().describe("Optional environment scope filter"),
18
+ }, READ_ONLY_ANNOTATIONS, async (params) => {
19
+ try {
20
+ const result = await getUserRoles(client, params);
21
+ return buildJsonToolResponse("get-user-roles", result);
22
+ }
23
+ catch (error) {
24
+ return handleToolError(error);
25
+ }
26
+ });
27
+ server.tool("list-user-roles", "List roles for all users in the organization", {
28
+ projectID: z.string().optional().describe("Optional project scope filter"),
29
+ environmentID: z.string().optional().describe("Optional environment scope filter"),
30
+ }, READ_ONLY_ANNOTATIONS, async (params) => {
31
+ try {
32
+ const result = await listUserRoles(client, params);
33
+ return buildJsonToolResponse("list-user-roles", result);
34
+ }
35
+ catch (error) {
36
+ return handleToolError(error);
37
+ }
38
+ });
39
+ server.tool("set-org-role", "Set the organization-level role for a user", {
40
+ userID: z.string().describe("The user ID"),
41
+ body: z
42
+ .record(z.unknown())
43
+ .describe("The organization role assignment body"),
44
+ }, IDEMPOTENT_WRITE_ANNOTATIONS, async (params) => {
45
+ try {
46
+ const result = await setOrgRole(client, params);
47
+ return buildJsonToolResponse("set-org-role", result);
48
+ }
49
+ catch (error) {
50
+ return handleToolError(error);
51
+ }
52
+ });
53
+ server.tool("set-project-role", "Set a user's role for a specific project", {
54
+ userID: z.string().describe("The user ID"),
55
+ projectID: z.string().describe("The project ID"),
56
+ body: z
57
+ .record(z.unknown())
58
+ .describe("The project role assignment body"),
59
+ }, IDEMPOTENT_WRITE_ANNOTATIONS, async (params) => {
60
+ try {
61
+ const result = await setProjectRole(client, params);
62
+ return buildJsonToolResponse("set-project-role", result);
63
+ }
64
+ catch (error) {
65
+ return handleToolError(error);
66
+ }
67
+ });
68
+ server.tool("delete-project-role", "Remove a user's role from a specific project", {
69
+ userID: z.string().describe("The user ID"),
70
+ projectID: z.string().describe("The project ID"),
71
+ }, DESTRUCTIVE_ANNOTATIONS, async (params) => {
72
+ try {
73
+ const result = await deleteProjectRole(client, params);
74
+ return buildJsonToolResponse("delete-project-role", result);
75
+ }
76
+ catch (error) {
77
+ return handleToolError(error);
78
+ }
79
+ });
80
+ server.tool("set-env-role", "Set a user's role for a specific environment", {
81
+ userID: z.string().describe("The user ID"),
82
+ environmentID: z.string().describe("The environment ID"),
83
+ body: z
84
+ .record(z.unknown())
85
+ .describe("The environment role assignment body"),
86
+ }, IDEMPOTENT_WRITE_ANNOTATIONS, async (params) => {
87
+ try {
88
+ const result = await setEnvRole(client, params);
89
+ return buildJsonToolResponse("set-env-role", result);
90
+ }
91
+ catch (error) {
92
+ return handleToolError(error);
93
+ }
94
+ });
95
+ server.tool("delete-env-role", "Remove a user's role from a specific environment", {
96
+ userID: z.string().describe("The user ID"),
97
+ environmentID: z.string().describe("The environment ID"),
98
+ }, DESTRUCTIVE_ANNOTATIONS, async (params) => {
99
+ try {
100
+ const result = await deleteEnvRole(client, params);
101
+ return buildJsonToolResponse("delete-env-role", result);
102
+ }
103
+ catch (error) {
104
+ return handleToolError(error);
105
+ }
106
+ });
107
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerPrompts(server: McpServer): void;