coalesce-transform-mcp 0.3.0 → 0.4.2
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.md +74 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6 -2
- package/dist/client.js.map +1 -1
- package/dist/coalesce/api/environments.d.ts +0 -12
- package/dist/coalesce/api/environments.d.ts.map +1 -1
- package/dist/coalesce/api/environments.js +0 -4
- package/dist/coalesce/api/environments.js.map +1 -1
- package/dist/coalesce/api/jobs.d.ts +3 -5
- package/dist/coalesce/api/jobs.d.ts.map +1 -1
- package/dist/coalesce/api/jobs.js +3 -6
- package/dist/coalesce/api/jobs.js.map +1 -1
- package/dist/coalesce/api/nodes.d.ts +3 -3
- package/dist/coalesce/api/nodes.d.ts.map +1 -1
- package/dist/coalesce/api/nodes.js +6 -4
- package/dist/coalesce/api/nodes.js.map +1 -1
- package/dist/coalesce/api/runs.d.ts.map +1 -1
- package/dist/coalesce/api/runs.js +11 -1
- package/dist/coalesce/api/runs.js.map +1 -1
- package/dist/coalesce/api/scan.d.ts +14 -0
- package/dist/coalesce/api/scan.d.ts.map +1 -0
- package/dist/coalesce/api/scan.js +64 -0
- package/dist/coalesce/api/scan.js.map +1 -0
- package/dist/coalesce/api/subgraphs.d.ts +3 -2
- package/dist/coalesce/api/subgraphs.d.ts.map +1 -1
- package/dist/coalesce/api/subgraphs.js +3 -2
- package/dist/coalesce/api/subgraphs.js.map +1 -1
- package/dist/coalesce/run-schemas.d.ts.map +1 -1
- package/dist/coalesce/run-schemas.js +26 -16
- package/dist/coalesce/run-schemas.js.map +1 -1
- package/dist/coalesce/tool-response.d.ts +1 -13
- package/dist/coalesce/tool-response.d.ts.map +1 -1
- package/dist/coalesce/tool-response.js +20 -6
- package/dist/coalesce/tool-response.js.map +1 -1
- package/dist/coalesce/tool-schemas.d.ts +1 -2
- package/dist/coalesce/tool-schemas.d.ts.map +1 -1
- package/dist/coalesce/tool-schemas.js +368 -5
- package/dist/coalesce/tool-schemas.js.map +1 -1
- package/dist/coalesce/types.d.ts +8 -0
- package/dist/coalesce/types.d.ts.map +1 -1
- package/dist/coalesce/types.js +3 -1
- package/dist/coalesce/types.js.map +1 -1
- package/dist/constants.d.ts +18 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +21 -0
- package/dist/constants.js.map +1 -0
- package/dist/mcp/cache.d.ts +2 -1
- package/dist/mcp/cache.d.ts.map +1 -1
- package/dist/mcp/cache.js +122 -138
- package/dist/mcp/cache.js.map +1 -1
- package/dist/mcp/environments.d.ts +2 -1
- package/dist/mcp/environments.d.ts.map +1 -1
- package/dist/mcp/environments.js +56 -112
- package/dist/mcp/environments.js.map +1 -1
- package/dist/mcp/git-accounts.d.ts +2 -1
- package/dist/mcp/git-accounts.d.ts.map +1 -1
- package/dist/mcp/git-accounts.js +74 -96
- package/dist/mcp/git-accounts.js.map +1 -1
- package/dist/mcp/jobs.d.ts +2 -1
- package/dist/mcp/jobs.d.ts.map +1 -1
- package/dist/mcp/jobs.js +68 -122
- package/dist/mcp/jobs.js.map +1 -1
- package/dist/mcp/lineage.d.ts +5 -0
- package/dist/mcp/lineage.d.ts.map +1 -0
- package/dist/mcp/lineage.js +410 -0
- package/dist/mcp/lineage.js.map +1 -0
- package/dist/mcp/node-type-corpus.d.ts +2 -1
- package/dist/mcp/node-type-corpus.d.ts.map +1 -1
- package/dist/mcp/node-type-corpus.js +148 -151
- package/dist/mcp/node-type-corpus.js.map +1 -1
- package/dist/mcp/nodes.d.ts +2 -1
- package/dist/mcp/nodes.d.ts.map +1 -1
- package/dist/mcp/nodes.js +358 -464
- package/dist/mcp/nodes.js.map +1 -1
- package/dist/mcp/pipelines.d.ts +2 -1
- package/dist/mcp/pipelines.d.ts.map +1 -1
- package/dist/mcp/pipelines.js +514 -314
- package/dist/mcp/pipelines.js.map +1 -1
- package/dist/mcp/projects.d.ts +2 -1
- package/dist/mcp/projects.d.ts.map +1 -1
- package/dist/mcp/projects.js +66 -100
- package/dist/mcp/projects.js.map +1 -1
- package/dist/mcp/repo-node-types.d.ts +2 -1
- package/dist/mcp/repo-node-types.d.ts.map +1 -1
- package/dist/mcp/repo-node-types.js +92 -121
- package/dist/mcp/repo-node-types.js.map +1 -1
- package/dist/mcp/runs.d.ts +3 -2
- package/dist/mcp/runs.d.ts.map +1 -1
- package/dist/mcp/runs.js +93 -148
- package/dist/mcp/runs.js.map +1 -1
- package/dist/mcp/skills.d.ts +13 -0
- package/dist/mcp/skills.d.ts.map +1 -0
- package/dist/mcp/skills.js +85 -0
- package/dist/mcp/skills.js.map +1 -0
- package/dist/mcp/subgraphs.d.ts +2 -1
- package/dist/mcp/subgraphs.d.ts.map +1 -1
- package/dist/mcp/subgraphs.js +61 -98
- package/dist/mcp/subgraphs.js.map +1 -1
- package/dist/mcp/tool-helpers.d.ts +37 -0
- package/dist/mcp/tool-helpers.d.ts.map +1 -0
- package/dist/mcp/tool-helpers.js +82 -0
- package/dist/mcp/tool-helpers.js.map +1 -0
- package/dist/mcp/users.d.ts +2 -1
- package/dist/mcp/users.d.ts.map +1 -1
- package/dist/mcp/users.js +92 -145
- package/dist/mcp/users.js.map +1 -1
- package/dist/mcp/workshop.d.ts +2 -1
- package/dist/mcp/workshop.d.ts.map +1 -1
- package/dist/mcp/workshop.js +66 -101
- package/dist/mcp/workshop.js.map +1 -1
- package/dist/mcp/workspaces.d.ts +2 -1
- package/dist/mcp/workspaces.d.ts.map +1 -1
- package/dist/mcp/workspaces.js +19 -34
- package/dist/mcp/workspaces.js.map +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +85 -0
- package/dist/prompts/index.js.map +1 -1
- package/dist/resources/context/pipeline-workshop-guide.md +1 -1
- package/dist/resources/context/tool-usage.md +7 -0
- package/dist/resources/index.d.ts +13 -0
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +105 -5
- package/dist/resources/index.js.map +1 -1
- package/dist/schemas/node-payloads.d.ts +2 -2
- package/dist/server.d.ts +2 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +158 -41
- package/dist/server.js.map +1 -1
- package/dist/services/cache/snapshots.d.ts.map +1 -1
- package/dist/services/cache/snapshots.js +9 -5
- package/dist/services/cache/snapshots.js.map +1 -1
- package/dist/services/config/schema-resolver.d.ts.map +1 -1
- package/dist/services/config/schema-resolver.js +3 -6
- package/dist/services/config/schema-resolver.js.map +1 -1
- package/dist/services/lineage/lineage-cache.d.ts +53 -0
- package/dist/services/lineage/lineage-cache.d.ts.map +1 -0
- package/dist/services/lineage/lineage-cache.js +335 -0
- package/dist/services/lineage/lineage-cache.js.map +1 -0
- package/dist/services/lineage/lineage-documentation.d.ts +29 -0
- package/dist/services/lineage/lineage-documentation.d.ts.map +1 -0
- package/dist/services/lineage/lineage-documentation.js +80 -0
- package/dist/services/lineage/lineage-documentation.js.map +1 -0
- package/dist/services/lineage/lineage-propagation.d.ts +47 -0
- package/dist/services/lineage/lineage-propagation.d.ts.map +1 -0
- package/dist/services/lineage/lineage-propagation.js +176 -0
- package/dist/services/lineage/lineage-propagation.js.map +1 -0
- package/dist/services/lineage/lineage-search.d.ts +33 -0
- package/dist/services/lineage/lineage-search.d.ts.map +1 -0
- package/dist/services/lineage/lineage-search.js +133 -0
- package/dist/services/lineage/lineage-search.js.map +1 -0
- package/dist/services/lineage/lineage-traversal.d.ts +34 -0
- package/dist/services/lineage/lineage-traversal.d.ts.map +1 -0
- package/dist/services/lineage/lineage-traversal.js +283 -0
- package/dist/services/lineage/lineage-traversal.js.map +1 -0
- package/dist/services/pipelines/clause-extraction.d.ts +3 -0
- package/dist/services/pipelines/clause-extraction.d.ts.map +1 -0
- package/dist/services/pipelines/clause-extraction.js +27 -0
- package/dist/services/pipelines/clause-extraction.js.map +1 -0
- package/dist/services/pipelines/column-helpers.d.ts +8 -0
- package/dist/services/pipelines/column-helpers.d.ts.map +1 -0
- package/dist/services/pipelines/column-helpers.js +125 -0
- package/dist/services/pipelines/column-helpers.js.map +1 -0
- package/dist/services/pipelines/cte-parsing.d.ts +29 -0
- package/dist/services/pipelines/cte-parsing.d.ts.map +1 -0
- package/dist/services/pipelines/cte-parsing.js +160 -0
- package/dist/services/pipelines/cte-parsing.js.map +1 -0
- package/dist/services/pipelines/cte-planning.d.ts +22 -0
- package/dist/services/pipelines/cte-planning.d.ts.map +1 -0
- package/dist/services/pipelines/cte-planning.js +206 -0
- package/dist/services/pipelines/cte-planning.js.map +1 -0
- package/dist/services/pipelines/execution.d.ts.map +1 -1
- package/dist/services/pipelines/execution.js +0 -1
- package/dist/services/pipelines/execution.js.map +1 -1
- package/dist/services/pipelines/intent-parsing.d.ts +24 -0
- package/dist/services/pipelines/intent-parsing.d.ts.map +1 -0
- package/dist/services/pipelines/intent-parsing.js +245 -0
- package/dist/services/pipelines/intent-parsing.js.map +1 -0
- package/dist/services/pipelines/intent-resolution.d.ts +24 -0
- package/dist/services/pipelines/intent-resolution.d.ts.map +1 -0
- package/dist/services/pipelines/intent-resolution.js +141 -0
- package/dist/services/pipelines/intent-resolution.js.map +1 -0
- package/dist/services/pipelines/intent.d.ts +4 -45
- package/dist/services/pipelines/intent.d.ts.map +1 -1
- package/dist/services/pipelines/intent.js +14 -408
- package/dist/services/pipelines/intent.js.map +1 -1
- package/dist/services/pipelines/node-type-candidates.d.ts +6 -0
- package/dist/services/pipelines/node-type-candidates.d.ts.map +1 -0
- package/dist/services/pipelines/node-type-candidates.js +165 -0
- package/dist/services/pipelines/node-type-candidates.js.map +1 -0
- package/dist/services/pipelines/node-type-intent.d.ts +1 -5
- package/dist/services/pipelines/node-type-intent.d.ts.map +1 -1
- package/dist/services/pipelines/node-type-intent.js +1 -5
- package/dist/services/pipelines/node-type-intent.js.map +1 -1
- package/dist/services/pipelines/node-type-scoring.d.ts +13 -0
- package/dist/services/pipelines/node-type-scoring.d.ts.map +1 -0
- package/dist/services/pipelines/node-type-scoring.js +322 -0
- package/dist/services/pipelines/node-type-scoring.js.map +1 -0
- package/dist/services/pipelines/node-type-selection.d.ts +22 -2
- package/dist/services/pipelines/node-type-selection.d.ts.map +1 -1
- package/dist/services/pipelines/node-type-selection.js +16 -538
- package/dist/services/pipelines/node-type-selection.js.map +1 -1
- package/dist/services/pipelines/plan-builder.d.ts +33 -0
- package/dist/services/pipelines/plan-builder.d.ts.map +1 -0
- package/dist/services/pipelines/plan-builder.js +224 -0
- package/dist/services/pipelines/plan-builder.js.map +1 -0
- package/dist/services/pipelines/planning-types.d.ts +543 -0
- package/dist/services/pipelines/planning-types.d.ts.map +1 -0
- package/dist/services/pipelines/planning-types.js +85 -0
- package/dist/services/pipelines/planning-types.js.map +1 -0
- package/dist/services/pipelines/planning.d.ts +8 -537
- package/dist/services/pipelines/planning.d.ts.map +1 -1
- package/dist/services/pipelines/planning.js +10 -1956
- package/dist/services/pipelines/planning.js.map +1 -1
- package/dist/services/pipelines/review.d.ts.map +1 -1
- package/dist/services/pipelines/review.js +3 -8
- package/dist/services/pipelines/review.js.map +1 -1
- package/dist/services/pipelines/select-parsing.d.ts +7 -0
- package/dist/services/pipelines/select-parsing.d.ts.map +1 -0
- package/dist/services/pipelines/select-parsing.js +185 -0
- package/dist/services/pipelines/select-parsing.js.map +1 -0
- package/dist/services/pipelines/source-parsing.d.ts +8 -0
- package/dist/services/pipelines/source-parsing.d.ts.map +1 -0
- package/dist/services/pipelines/source-parsing.js +151 -0
- package/dist/services/pipelines/source-parsing.js.map +1 -0
- package/dist/services/pipelines/sql-parsing.d.ts +8 -0
- package/dist/services/pipelines/sql-parsing.d.ts.map +1 -0
- package/dist/services/pipelines/sql-parsing.js +9 -0
- package/dist/services/pipelines/sql-parsing.js.map +1 -0
- package/dist/services/pipelines/sql-tokenizer.d.ts +42 -0
- package/dist/services/pipelines/sql-tokenizer.d.ts.map +1 -0
- package/dist/services/pipelines/sql-tokenizer.js +493 -0
- package/dist/services/pipelines/sql-tokenizer.js.map +1 -0
- package/dist/services/pipelines/sql-utils.d.ts +30 -0
- package/dist/services/pipelines/sql-utils.d.ts.map +1 -0
- package/dist/services/pipelines/sql-utils.js +62 -0
- package/dist/services/pipelines/sql-utils.js.map +1 -0
- package/dist/services/pipelines/workshop.d.ts.map +1 -1
- package/dist/services/pipelines/workshop.js +53 -25
- package/dist/services/pipelines/workshop.js.map +1 -1
- package/dist/services/pipelines/workspace-resolution.d.ts +18 -0
- package/dist/services/pipelines/workspace-resolution.d.ts.map +1 -0
- package/dist/services/pipelines/workspace-resolution.js +279 -0
- package/dist/services/pipelines/workspace-resolution.js.map +1 -0
- package/dist/services/runs/diagnostics.d.ts.map +1 -1
- package/dist/services/runs/diagnostics.js +3 -8
- package/dist/services/runs/diagnostics.js.map +1 -1
- package/dist/services/shared/elicitation.d.ts +14 -0
- package/dist/services/shared/elicitation.d.ts.map +1 -0
- package/dist/services/shared/elicitation.js +56 -0
- package/dist/services/shared/elicitation.js.map +1 -0
- package/dist/services/workspace/node-creation.d.ts.map +1 -1
- package/dist/services/workspace/node-creation.js +5 -1
- package/dist/services/workspace/node-creation.js.map +1 -1
- package/dist/services/workspace/node-update-helpers.d.ts.map +1 -1
- package/dist/services/workspace/node-update-helpers.js +3 -8
- package/dist/services/workspace/node-update-helpers.js.map +1 -1
- package/dist/utils.d.ts +11 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +20 -1
- package/dist/utils.js.map +1 -1
- package/dist/workflows/get-environment-health.d.ts +49 -0
- package/dist/workflows/get-environment-health.d.ts.map +1 -0
- package/dist/workflows/get-environment-health.js +310 -0
- package/dist/workflows/get-environment-health.js.map +1 -0
- package/dist/workflows/get-environment-overview.d.ts +2 -1
- package/dist/workflows/get-environment-overview.d.ts.map +1 -1
- package/dist/workflows/get-environment-overview.js +13 -19
- package/dist/workflows/get-environment-overview.js.map +1 -1
- package/dist/workflows/get-run-details.d.ts +2 -2
- package/dist/workflows/get-run-details.d.ts.map +1 -1
- package/dist/workflows/get-run-details.js +14 -19
- package/dist/workflows/get-run-details.js.map +1 -1
- package/dist/workflows/retry-and-wait.d.ts.map +1 -1
- package/dist/workflows/retry-and-wait.js +3 -2
- package/dist/workflows/retry-and-wait.js.map +1 -1
- package/dist/workflows/run-and-wait.d.ts.map +1 -1
- package/dist/workflows/run-and-wait.js +3 -2
- package/dist/workflows/run-and-wait.js.map +1 -1
- package/package.json +2 -2
package/dist/mcp/nodes.js
CHANGED
|
@@ -7,472 +7,366 @@ import { buildWorkspaceProfile } from "../services/workspace/analysis.js";
|
|
|
7
7
|
import { fetchAllWorkspaceNodes, toNodeSummaries } from "../services/cache/snapshots.js";
|
|
8
8
|
import { NodeConfigInputSchema, StorageLocationInputSchema, ExternalColumnSchema, WorkspaceNodeColumnInputSchema, WorkspaceNodeMetadataInputSchema, WorkspaceNodeWriteInputSchema, } from "../schemas/node-payloads.js";
|
|
9
9
|
import { PaginationParams, buildJsonToolResponse, getToolOutputSchema, READ_ONLY_ANNOTATIONS, WRITE_ANNOTATIONS, IDEMPOTENT_WRITE_ANNOTATIONS, DESTRUCTIVE_ANNOTATIONS, handleToolError, } from "../coalesce/types.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const result = await createWorkspaceNodeFromScratch(client, params);
|
|
128
|
-
return buildJsonToolResponse("create_workspace_node_from_scratch", result);
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
return handleToolError(error);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
server.registerTool("set_workspace_node", {
|
|
135
|
-
title: "Set Workspace Node",
|
|
136
|
-
description: "Update a workspace node's full body. Reads current state, merges changes, validates, and writes back.\n\nThis is the primary mutation tool for workspace nodes. It handles column linkage preservation, passthrough transform stripping, required API field injection, and metadata cleaning automatically.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID\n - body (object, required): Fields to update — name, description, nodeType, config, metadata.columns, etc.\n\nReturns:\n { nodeID, created, warning?, validation?, configCompletion? }\n\nDo NOT set overrideSQL or metadata.sourceMapping through this tool. Use apply_join_condition or convert_join_to_aggregation for join/aggregation changes.",
|
|
137
|
-
inputSchema: z.object({
|
|
138
|
-
workspaceID: z.string().describe("The workspace ID"),
|
|
139
|
-
nodeID: z.string().describe("The node ID"),
|
|
140
|
-
body: WorkspaceNodeWriteInputSchema.describe("Complete node data to set. Common fields include name, description, nodeType, table, database, schema, locationName, storageLocations, config, and metadata. Do not include overrideSQL — it is auto-preserved."),
|
|
141
|
-
}),
|
|
142
|
-
outputSchema: getToolOutputSchema("set_workspace_node"),
|
|
143
|
-
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
144
|
-
}, async (params) => {
|
|
145
|
-
try {
|
|
146
|
-
// Deep-check for override fields the agent may have included
|
|
147
|
-
assertNoSqlOverridePayload(params.body, "set_workspace_node body");
|
|
148
|
-
// Fetch current node to preserve API-required fields
|
|
149
|
-
const current = await getWorkspaceNode(client, {
|
|
150
|
-
workspaceID: params.workspaceID,
|
|
151
|
-
nodeID: params.nodeID,
|
|
152
|
-
});
|
|
153
|
-
// Route through the shared merge+validate+clean path that ensures
|
|
154
|
-
// all API-required fields (table, overrideSQL, dataType, columnID,
|
|
155
|
-
// nullable, description, enabledColumnTestIDs) are present.
|
|
156
|
-
const body = buildUpdatedWorkspaceNodeBody(current, params.body);
|
|
157
|
-
// Preserve database/schema from current node if not provided
|
|
158
|
-
if (typeof current === "object" && current !== null && !Array.isArray(current)) {
|
|
159
|
-
const currentObj = current;
|
|
160
|
-
for (const field of ["database", "schema"]) {
|
|
161
|
-
if (!(field in body) && field in currentObj) {
|
|
162
|
-
body[field] = currentObj[field];
|
|
10
|
+
import { defineSimpleTool, defineDestructiveTool } from "./tool-helpers.js";
|
|
11
|
+
export function defineNodeTools(server, client) {
|
|
12
|
+
return [
|
|
13
|
+
defineSimpleTool(client, "list_environment_nodes", {
|
|
14
|
+
title: "List Environment Nodes",
|
|
15
|
+
description: "List all nodes deployed in a Coalesce environment.\n\nArgs:\n - environmentID (string, required): The environment ID\n - detail (boolean, optional): Include full node details\n - limit, startingFrom, orderBy, orderByDirection: Pagination\n\nReturns:\n { data: Node[], next?: string, total?: number }",
|
|
16
|
+
inputSchema: PaginationParams.extend({
|
|
17
|
+
environmentID: z.string().describe("The environment ID"),
|
|
18
|
+
detail: z.boolean().optional().describe("Include full node details in response"),
|
|
19
|
+
}),
|
|
20
|
+
annotations: READ_ONLY_ANNOTATIONS,
|
|
21
|
+
}, listEnvironmentNodes),
|
|
22
|
+
defineSimpleTool(client, "list_workspace_nodes", {
|
|
23
|
+
title: "List Workspace Nodes",
|
|
24
|
+
description: "List all nodes in a Coalesce workspace. Use list_workspaces to find workspace IDs.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - detail (boolean, optional): Include full node details\n - limit, startingFrom, orderBy, orderByDirection: Pagination\n\nReturns:\n { data: Node[], next?: string, total?: number }",
|
|
25
|
+
inputSchema: PaginationParams.extend({
|
|
26
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
27
|
+
detail: z.boolean().optional().describe("Include full node details in response"),
|
|
28
|
+
}),
|
|
29
|
+
annotations: READ_ONLY_ANNOTATIONS,
|
|
30
|
+
}, listWorkspaceNodes),
|
|
31
|
+
defineSimpleTool(client, "get_environment_node", {
|
|
32
|
+
title: "Get Environment Node",
|
|
33
|
+
description: "Get details of a specific node deployed in an environment.\n\nArgs:\n - environmentID (string, required): The environment ID\n - nodeID (string, required): The node ID\n\nReturns:\n Full node object with columns, config, metadata, and deployment state.",
|
|
34
|
+
inputSchema: z.object({
|
|
35
|
+
environmentID: z.string().describe("The environment ID"),
|
|
36
|
+
nodeID: z.string().describe("The node ID"),
|
|
37
|
+
}),
|
|
38
|
+
annotations: READ_ONLY_ANNOTATIONS,
|
|
39
|
+
}, getEnvironmentNode),
|
|
40
|
+
defineSimpleTool(client, "get_workspace_node", {
|
|
41
|
+
title: "Get Workspace Node",
|
|
42
|
+
description: "Get details of a specific node in a workspace. Use list_workspaces to find workspace IDs.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID\n\nReturns:\n Full workspace node with columns, transforms, joins, config, and metadata.",
|
|
43
|
+
inputSchema: z.object({
|
|
44
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
45
|
+
nodeID: z.string().describe("The node ID"),
|
|
46
|
+
}),
|
|
47
|
+
annotations: READ_ONLY_ANNOTATIONS,
|
|
48
|
+
}, getWorkspaceNode),
|
|
49
|
+
defineSimpleTool(client, "create_workspace_node_from_scratch", {
|
|
50
|
+
title: "Create Workspace Node from Scratch",
|
|
51
|
+
description: "Create a workspace node from scratch with NO predecessors. Only use this when the node truly has no upstream nodes — for example, a standalone utility node. If the node has ANY upstream/source nodes, use create_workspace_node_from_predecessor instead.\n\nREQUIRED: Before calling this tool, call plan_pipeline with goal + repoPath to discover the correct nodeType. Do not guess or hardcode node types — the planner ranks all available types and returns the best match.\n\nSPECIALIZED TYPES WARNING: Do NOT use Dynamic Tables, Incremental Load, Materialized View, or other specialized types unless the user explicitly requests that pattern (e.g., 'near-real-time refresh', 'incremental processing'). For standard batch ETL, CTE decomposition, and general transforms, use Stage or Work. The response includes nodeTypeValidation.warning if a specialized pattern was detected without matching context.\n\nDefaults to completionLevel='configured', which REQUIRES both `name` and `metadata.columns` to be provided. If you don't have column definitions yet, set completionLevel to 'created' or 'named' instead.\n\nAUTOMATIC CONFIG: When repoPath is provided, this tool automatically runs intelligent config completion after creation — reading the node type definition, setting node-level config defaults, and applying column-level attributes. The configCompletion result shows what was applied.\n\nDo not use overrideSQL or override.* fields; SQL override is disallowed in this project.",
|
|
52
|
+
inputSchema: z.object({
|
|
53
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
54
|
+
nodeType: z.string().describe("The type of node to create. IMPORTANT: Call plan_pipeline first to discover and rank available node types — use the nodeType from its result. Format: 'PackageName:::ID' for package types (e.g., 'base-nodes:::Stage') or simple name ('Stage') for built-in types. Always prefer the package-prefixed format returned by plan_pipeline."),
|
|
55
|
+
completionLevel: z
|
|
56
|
+
.enum(["created", "named", "configured"])
|
|
57
|
+
.optional()
|
|
58
|
+
.describe("How complete the node should be before the tool returns. Defaults to configured."),
|
|
59
|
+
name: z
|
|
60
|
+
.string()
|
|
61
|
+
.optional()
|
|
62
|
+
.describe("Optional node name to apply after creation."),
|
|
63
|
+
description: z
|
|
64
|
+
.string()
|
|
65
|
+
.optional()
|
|
66
|
+
.describe("Optional node description to apply after creation."),
|
|
67
|
+
storageLocations: z
|
|
68
|
+
.array(StorageLocationInputSchema)
|
|
69
|
+
.optional()
|
|
70
|
+
.describe("Optional storageLocations array to apply after creation."),
|
|
71
|
+
config: NodeConfigInputSchema
|
|
72
|
+
.optional()
|
|
73
|
+
.describe("Optional config object to apply after creation."),
|
|
74
|
+
metadata: WorkspaceNodeMetadataInputSchema
|
|
75
|
+
.optional()
|
|
76
|
+
.describe("Optional metadata object to apply after creation, including metadata.columns."),
|
|
77
|
+
changes: WorkspaceNodeWriteInputSchema
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("Optional additional partial fields to merge after the node is created and fetched."),
|
|
80
|
+
repoPath: z
|
|
81
|
+
.string()
|
|
82
|
+
.optional()
|
|
83
|
+
.describe("Path to local Coalesce repository for automatic config completion after creation."),
|
|
84
|
+
goal: z
|
|
85
|
+
.string()
|
|
86
|
+
.optional()
|
|
87
|
+
.describe("The goal or intent for this node (e.g., 'deduplicate customer records', 'aggregate daily sales'). Used to validate that the chosen nodeType is appropriate for the task. Same value you would pass to plan_pipeline."),
|
|
88
|
+
}),
|
|
89
|
+
annotations: WRITE_ANNOTATIONS,
|
|
90
|
+
}, createWorkspaceNodeFromScratch),
|
|
91
|
+
// set_workspace_node has complex inline logic (fetch → merge → preserve → write)
|
|
92
|
+
// that doesn't fit the simple helper pattern.
|
|
93
|
+
[
|
|
94
|
+
"set_workspace_node",
|
|
95
|
+
{
|
|
96
|
+
title: "Set Workspace Node",
|
|
97
|
+
description: "Update a workspace node's full body. Reads current state, merges changes, validates, and writes back.\n\nThis is the primary mutation tool for workspace nodes. It handles column linkage preservation, passthrough transform stripping, required API field injection, and metadata cleaning automatically.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID\n - body (object, required): Fields to update — name, description, nodeType, config, metadata.columns, etc.\n\nReturns:\n { nodeID, created, warning?, validation?, configCompletion? }\n\nDo NOT set overrideSQL or metadata.sourceMapping through this tool. Use apply_join_condition or convert_join_to_aggregation for join/aggregation changes.",
|
|
98
|
+
inputSchema: z.object({
|
|
99
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
100
|
+
nodeID: z.string().describe("The node ID"),
|
|
101
|
+
body: WorkspaceNodeWriteInputSchema.describe("Complete node data to set. Common fields include name, description, nodeType, table, database, schema, locationName, storageLocations, config, and metadata. Do not include overrideSQL — it is auto-preserved."),
|
|
102
|
+
}),
|
|
103
|
+
outputSchema: getToolOutputSchema("set_workspace_node"),
|
|
104
|
+
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
105
|
+
},
|
|
106
|
+
async (params) => {
|
|
107
|
+
try {
|
|
108
|
+
// Deep-check for override fields the agent may have included
|
|
109
|
+
assertNoSqlOverridePayload(params.body, "set_workspace_node body");
|
|
110
|
+
// Fetch current node to preserve API-required fields
|
|
111
|
+
const current = await getWorkspaceNode(client, {
|
|
112
|
+
workspaceID: params.workspaceID,
|
|
113
|
+
nodeID: params.nodeID,
|
|
114
|
+
});
|
|
115
|
+
// Route through the shared merge+validate+clean path that ensures
|
|
116
|
+
// all API-required fields (table, overrideSQL, dataType, columnID,
|
|
117
|
+
// nullable, description, enabledColumnTestIDs) are present.
|
|
118
|
+
const body = buildUpdatedWorkspaceNodeBody(current, params.body);
|
|
119
|
+
// Preserve database/schema from current node if not provided
|
|
120
|
+
if (typeof current === "object" && current !== null && !Array.isArray(current)) {
|
|
121
|
+
const currentObj = current;
|
|
122
|
+
for (const field of ["database", "schema"]) {
|
|
123
|
+
if (!(field in body) && field in currentObj) {
|
|
124
|
+
body[field] = currentObj[field];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
163
127
|
}
|
|
128
|
+
const result = await setWorkspaceNode(client, {
|
|
129
|
+
workspaceID: params.workspaceID,
|
|
130
|
+
nodeID: params.nodeID,
|
|
131
|
+
body,
|
|
132
|
+
});
|
|
133
|
+
return buildJsonToolResponse("set_workspace_node", result);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return handleToolError(error);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
],
|
|
140
|
+
defineSimpleTool(client, "update_workspace_node", {
|
|
141
|
+
title: "Update Workspace Node",
|
|
142
|
+
description: "Safely update selected fields of a workspace node by fetching the current node, applying partial changes, then writing back the full merged body. Object fields are deep-merged; arrays replace the existing array when provided. Use list_workspaces to find workspace IDs.\n\nDo not use overrideSQL or override.* fields; SQL override is disallowed in this project.\n\nNOTE: Arrays (like metadata.columns) are replaced, not merged. For complex column transformations (e.g., converting from join to aggregation), consider using replace_workspace_node_columns instead.\n\nFor guidance on SQL platforms and tool usage patterns, see resources: coalesce://context/sql-platform-selection, coalesce://context/tool-usage",
|
|
143
|
+
inputSchema: z.object({
|
|
144
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
145
|
+
nodeID: z.string().describe("The node ID"),
|
|
146
|
+
changes: WorkspaceNodeWriteInputSchema.describe("Partial node fields to update. Common fields include name, description, database, schema, locationName, storageLocations, config, and metadata. Object fields are deep-merged; arrays replace the existing array when provided."),
|
|
147
|
+
}),
|
|
148
|
+
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
149
|
+
}, updateWorkspaceNode),
|
|
150
|
+
defineSimpleTool(client, "replace_workspace_node_columns", {
|
|
151
|
+
title: "Replace Workspace Node Columns",
|
|
152
|
+
description: "Replace all columns in a workspace node with a new set of columns, optionally applying a WHERE filter and additional changes in a single call.\n\nDo not use overrideSQL or override.* fields; SQL override is disallowed in this project.\n\nUse this when:\n- Applying column transforms (UPPER, LEFT, COALESCE, etc.) after node creation\n- Adding WHERE filters at the same time as column transforms\n- Converting from a simple join to GROUP BY aggregation\n- Completely replacing column definitions with aggregate functions\n\nPrefer this over separate update_workspace_node calls. Combine column replacement + WHERE filter in one call.\n\nExample: Apply transforms and filter in one call:\n{\n columns: [\n { name: 'CUSTOMER_ID', transform: '\"CUSTOMER_LOYALTY\".\"CUSTOMER_ID\"' },\n { name: 'CITY', transform: 'UPPER(\"CUSTOMER_LOYALTY\".\"CITY\")' },\n { name: 'CONTACT_INFO', transform: 'COALESCE(\"CUSTOMER_LOYALTY\".\"E_MAIL\", \"CUSTOMER_LOYALTY\".\"PHONE_NUMBER\")' }\n ],\n whereCondition: '\"CUSTOMER_LOYALTY\".\"CUSTOMER_ID\" IS NOT NULL AND (\"CUSTOMER_LOYALTY\".\"E_MAIL\" IS NOT NULL OR \"CUSTOMER_LOYALTY\".\"PHONE_NUMBER\" IS NOT NULL)'\n}\n\nIMPORTANT: Use whereCondition for WHERE filters — do NOT construct {{ ref() }} syntax yourself. The FROM clause is already set up from node creation. The whereCondition is appended to the existing joinCondition automatically.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID\n - columns (array, required): Complete new columns array\n - whereCondition (string, optional): WHERE filter to append\n - additionalChanges (object, optional): Additional fields to update\n\nReturns:\n { nodeID, warning?, validation? }",
|
|
153
|
+
inputSchema: z.object({
|
|
154
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
155
|
+
nodeID: z.string().describe("The node ID"),
|
|
156
|
+
columns: z.array(WorkspaceNodeColumnInputSchema).describe("Complete new columns array to replace metadata.columns. Each column should include name and may include transform, dataType, description, nullable, sources, and other hydrated metadata fields."),
|
|
157
|
+
whereCondition: z
|
|
158
|
+
.string()
|
|
159
|
+
.optional()
|
|
160
|
+
.describe("Optional WHERE filter to append to the node's existing joinCondition. Just provide the condition — do NOT include the WHERE keyword or construct {{ ref() }} syntax. The FROM clause is already set from node creation. Example: '\"LOCATION\".\"LOCATION_ID\" IS NOT NULL AND \"LOCATION\".\"LOCATION_ID\" != 0'"),
|
|
161
|
+
additionalChanges: WorkspaceNodeWriteInputSchema
|
|
162
|
+
.optional()
|
|
163
|
+
.describe("Optional additional fields to update, such as name, description, config, or metadata. Object fields are deep-merged; arrays are replaced. Do NOT include metadata.sourceMapping or customSQL — use whereCondition for WHERE filters, apply_join_condition for join setup, or convert_join_to_aggregation for GROUP BY patterns."),
|
|
164
|
+
}),
|
|
165
|
+
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
166
|
+
}, replaceWorkspaceNodeColumns),
|
|
167
|
+
defineSimpleTool(client, "convert_join_to_aggregation", {
|
|
168
|
+
title: "Convert Join to Aggregation",
|
|
169
|
+
description: "Convert an existing join node into an aggregated fact table with GROUP BY. This is the REQUIRED follow-up after creating a multi-predecessor node — it completes the join setup.\n\nThis tool automatically:\n- Generates JOIN ON clauses from common columns between predecessors\n- Writes the complete FROM/JOIN/ON/GROUP BY clause to the node's joinCondition (no separate update needed)\n- Replaces columns with GROUP BY dimensions + aggregate measures\n- Infers datatypes from transform functions (COUNT → NUMBER, SUM → NUMBER(38,4), etc.)\n- Sets column-level attributes (isBusinessKey on GROUP BY columns, isChangeTracking on aggregates)\n- Validates that all non-aggregate columns are in GROUP BY\n- Runs intelligent config completion\n\nUse this to transform a simple join (row-level) into an aggregated fact table (summary-level).\n\nExample: Convert order detail join to customer metrics:\n{\n workspaceID: \"1\",\n nodeID: \"fact-node-id\",\n groupByColumns: ['\"STG_ORDER_HEADER\".\"CUSTOMER_ID\"'],\n aggregates: [\n { name: \"TOTAL_ORDERS\", function: \"COUNT\", expression: 'DISTINCT \"STG_ORDER_HEADER\".\"ORDER_ID\"' },\n { name: \"LIFETIME_VALUE\", function: \"SUM\", expression: '\"STG_ORDER_HEADER\".\"ORDER_TOTAL\"' },\n { name: \"AVG_ORDER_VALUE\", function: \"AVG\", expression: '\"STG_ORDER_HEADER\".\"ORDER_TOTAL\"' }\n ],\n joinType: \"INNER JOIN\"\n}\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID of the join to convert\n - groupByColumns (string[], required): Columns to group by (dimensions)\n - aggregates (array, required): Aggregate columns with functions and expressions\n - joinType (string, optional): JOIN type (default: INNER JOIN)\n - maintainJoins (boolean, optional): Generate JOINs (default: true)\n - repoPath (string, optional): Local repo path for config completion\n\nReturns:\n Updated node with new columns, joinCondition, GROUP BY analysis, and config completion results.",
|
|
170
|
+
inputSchema: z.object({
|
|
171
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
172
|
+
nodeID: z.string().describe("The node ID of the join to convert"),
|
|
173
|
+
groupByColumns: z
|
|
174
|
+
.array(z.string())
|
|
175
|
+
.describe("Columns to group by (dimensions). Use fully-qualified names like '\"TABLE\".\"COLUMN\"'."),
|
|
176
|
+
aggregates: z
|
|
177
|
+
.array(z.object({
|
|
178
|
+
name: z.string().describe("Column name for the aggregate"),
|
|
179
|
+
function: z.string().describe("Aggregate function: COUNT, SUM, AVG, MIN, MAX, etc."),
|
|
180
|
+
expression: z.string().describe("Expression to aggregate (e.g., 'DISTINCT \"TABLE\".\"COLUMN\"')"),
|
|
181
|
+
description: z.string().optional().describe("Optional column description"),
|
|
182
|
+
}))
|
|
183
|
+
.describe("Aggregate columns with their functions and expressions"),
|
|
184
|
+
joinType: z
|
|
185
|
+
.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL OUTER JOIN"])
|
|
186
|
+
.optional()
|
|
187
|
+
.describe("Type of JOIN to use. Defaults to INNER JOIN."),
|
|
188
|
+
maintainJoins: z
|
|
189
|
+
.boolean()
|
|
190
|
+
.optional()
|
|
191
|
+
.describe("If true (default), analyzes predecessors, generates JOIN SQL, and writes the joinCondition to the node. If false, only replaces columns with aggregates without generating joins."),
|
|
192
|
+
repoPath: z
|
|
193
|
+
.string()
|
|
194
|
+
.optional()
|
|
195
|
+
.describe("Optional path to local Coalesce repository for intelligent config completion"),
|
|
196
|
+
}),
|
|
197
|
+
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
198
|
+
}, convertJoinToAggregation),
|
|
199
|
+
defineSimpleTool(client, "apply_join_condition", {
|
|
200
|
+
title: "Apply Join Condition",
|
|
201
|
+
description: "Write a FROM/JOIN/ON clause to a workspace node's sourceMapping.join.joinCondition by analyzing predecessor columns and generating the join automatically.\n\nUse this for multi-predecessor nodes where you need to combine data via JOIN. The tool inspects predecessor columns, finds common column names for ON conditions, and writes the full joinCondition to the node.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID of the multi-predecessor node\n - joinType (enum, optional): 'INNER JOIN' | 'LEFT JOIN' | 'RIGHT JOIN' | 'FULL OUTER JOIN'. Defaults to INNER JOIN\n - whereClause (string, optional): WHERE filter to append after the JOIN (without the WHERE keyword)\n - qualifyClause (string, optional): QUALIFY clause to append (without the QUALIFY keyword)\n - joinColumnOverrides (array, optional): Explicit column mappings when column names differ across predecessors. Each entry: { leftPredecessor, rightPredecessor, leftColumn, rightColumn }\n\nReturns:\n { nodeID, joinCondition, warning?, validation? }",
|
|
202
|
+
inputSchema: z.object({
|
|
203
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
204
|
+
nodeID: z.string().describe("The node ID of the multi-predecessor node"),
|
|
205
|
+
joinType: z
|
|
206
|
+
.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL OUTER JOIN"])
|
|
207
|
+
.optional()
|
|
208
|
+
.describe("Type of JOIN to use between predecessors. Defaults to INNER JOIN."),
|
|
209
|
+
whereClause: z
|
|
210
|
+
.string()
|
|
211
|
+
.optional()
|
|
212
|
+
.describe("Optional WHERE clause to append after the JOIN (without the WHERE keyword)."),
|
|
213
|
+
qualifyClause: z
|
|
214
|
+
.string()
|
|
215
|
+
.optional()
|
|
216
|
+
.describe("Optional QUALIFY clause to append (without the QUALIFY keyword)."),
|
|
217
|
+
joinColumnOverrides: z
|
|
218
|
+
.array(z.object({
|
|
219
|
+
leftPredecessor: z.string().describe("Name of the left predecessor node"),
|
|
220
|
+
rightPredecessor: z.string().describe("Name of the right predecessor node"),
|
|
221
|
+
leftColumn: z.string().describe("Column name in the left predecessor"),
|
|
222
|
+
rightColumn: z.string().describe("Column name in the right predecessor"),
|
|
223
|
+
}))
|
|
224
|
+
.optional()
|
|
225
|
+
.describe("Explicit column mappings for joins when column names differ across predecessors. Overrides auto-detected common columns for the specified predecessor pair."),
|
|
226
|
+
}),
|
|
227
|
+
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
228
|
+
}, applyJoinCondition),
|
|
229
|
+
defineSimpleTool(client, "create_workspace_node_from_predecessor", {
|
|
230
|
+
title: "Create Workspace Node from Predecessor",
|
|
231
|
+
description: "Create a workspace node from one or more predecessor nodes, fetch it, and verify that columns were auto-populated from those predecessors before applying any optional changes.\n\nSINGLE-CALL WORKFLOW: You can create a node AND apply column transforms, WHERE filters, or aggregation in one call:\n- columns + whereCondition: Replace auto-populated columns with specific transforms and add a WHERE filter — no separate replace_workspace_node_columns needed\n- groupByColumns + aggregates: Convert to an aggregation node with GROUP BY — no separate convert_join_to_aggregation needed\nThese are mutually exclusive: use columns OR groupByColumns+aggregates, not both.\n\nREQUIRED: Before calling this tool, call `plan_pipeline` with `goal`, `sourceNodeIDs`, and `repoPath` to discover and rank available node types. Use the `nodeType` from the plan result — do NOT guess or hardcode node types like 'Stage', 'View', or numeric IDs like '65'. The planner scans all committed node type definitions and scores them against your use case.\n\nSPECIALIZED TYPES WARNING: Do NOT use Dynamic Tables, Incremental Load, Materialized View, or other specialized types unless the user explicitly requests that pattern (e.g., 'near-real-time refresh', 'continuous refresh', 'incremental processing'). For standard batch ETL, CTE decomposition, and general transforms, use Stage or Work. The response includes nodeTypeValidation.warning if a specialized pattern was detected without matching context — always check this field.\n\nJOIN INTELLIGENCE: For multi-predecessor nodes (joins), this tool automatically:\n- Analyzes common columns between each predecessor pair\n- Returns `joinSuggestions` with normalized column names and their left/right counterparts\n- Reports which predecessors are represented in the resulting column references\n- Warns if any predecessor is missing from the auto-populated columns\n\nAUTOMATIC CONFIG: When repoPath is provided, this tool automatically runs intelligent config completion after creation — reading the node type definition, setting node-level config defaults, and applying column-level attributes (isBusinessKey, isChangeTracking, etc.). The configCompletion result shows what was applied.\n\nDo not use overrideSQL or override.* fields; SQL override is disallowed in this project.\n\nFor guidance on node types, storage locations, and SQL patterns, see resources: coalesce://context/data-engineering-principles, coalesce://context/storage-mappings, coalesce://context/sql-platform-selection\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeType (string, required): Node type from plan_pipeline\n - predecessorNodeIDs (string[], required): One or more predecessor node IDs\n - changes (object, optional): Partial fields to apply after creation\n - columns (array, optional): Replace auto-populated columns (mutually exclusive with groupByColumns)\n - whereCondition (string, optional): WHERE filter (only with columns)\n - groupByColumns (string[], optional): GROUP BY columns (with aggregates)\n - aggregates (array, optional): Aggregate columns (with groupByColumns)\n - joinType (string, optional): JOIN type for multi-predecessor aggregation\n - repoPath (string, optional): Local repo path for config completion\n - goal (string, optional): Intent for node type validation\n\nReturns:\n { nodeID, created, joinSuggestions?, nodeTypeValidation?, configCompletion? }",
|
|
232
|
+
inputSchema: z.object({
|
|
233
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
234
|
+
nodeType: z.string().describe("The type of node to create. IMPORTANT: Call plan_pipeline first to discover and rank available node types — use the nodeType from its result. Format: 'PackageName:::ID' for package types (e.g., 'base-nodes:::Stage') or simple name ('Stage') for built-in types. Always prefer the package-prefixed format returned by plan_pipeline."),
|
|
235
|
+
predecessorNodeIDs: z
|
|
236
|
+
.array(z.string())
|
|
237
|
+
.min(1)
|
|
238
|
+
.describe("One or more predecessor node IDs to link to the new node"),
|
|
239
|
+
changes: WorkspaceNodeWriteInputSchema
|
|
240
|
+
.optional()
|
|
241
|
+
.describe("Optional partial fields to apply after successful auto-population validation, such as name, description, config, metadata, database, schema, or locationName."),
|
|
242
|
+
columns: z.array(WorkspaceNodeColumnInputSchema)
|
|
243
|
+
.optional()
|
|
244
|
+
.describe("Replace auto-populated columns with these specific columns and transforms. Mutually exclusive with groupByColumns/aggregates."),
|
|
245
|
+
whereCondition: z
|
|
246
|
+
.string()
|
|
247
|
+
.optional()
|
|
248
|
+
.describe("WHERE filter to append to the joinCondition (without the WHERE keyword). Only valid with columns, not with groupByColumns/aggregates."),
|
|
249
|
+
groupByColumns: z
|
|
250
|
+
.array(z.string())
|
|
251
|
+
.optional()
|
|
252
|
+
.describe("GROUP BY columns for aggregation. Must be provided with aggregates. Mutually exclusive with columns."),
|
|
253
|
+
aggregates: z
|
|
254
|
+
.array(z.object({
|
|
255
|
+
name: z.string().describe("Output column name for the aggregate"),
|
|
256
|
+
function: z.string().describe("Aggregate function: COUNT, SUM, AVG, MIN, MAX, etc."),
|
|
257
|
+
expression: z.string().describe("Expression to aggregate"),
|
|
258
|
+
description: z.string().optional().describe("Column description"),
|
|
259
|
+
}))
|
|
260
|
+
.optional()
|
|
261
|
+
.describe("Aggregate columns. Must be provided with groupByColumns. Mutually exclusive with columns."),
|
|
262
|
+
joinType: z
|
|
263
|
+
.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL OUTER JOIN"])
|
|
264
|
+
.optional()
|
|
265
|
+
.describe("JOIN type for multi-predecessor aggregation nodes. Defaults to INNER JOIN."),
|
|
266
|
+
repoPath: z
|
|
267
|
+
.string()
|
|
268
|
+
.optional()
|
|
269
|
+
.describe("Path to local Coalesce repository for automatic config completion after creation."),
|
|
270
|
+
goal: z
|
|
271
|
+
.string()
|
|
272
|
+
.optional()
|
|
273
|
+
.describe("The goal or intent for this node (e.g., 'deduplicate customer records', 'join orders with customers'). Used to validate that the chosen nodeType is appropriate for the task. Same value you would pass to plan_pipeline."),
|
|
274
|
+
}),
|
|
275
|
+
annotations: WRITE_ANNOTATIONS,
|
|
276
|
+
}, createWorkspaceNodeFromPredecessor),
|
|
277
|
+
defineSimpleTool(client, "create_node_from_external_schema", {
|
|
278
|
+
title: "Create Node from External Schema",
|
|
279
|
+
description: "Create a workspace node whose output columns match an external table schema (e.g., from Snowflake DESCRIBE TABLE, dbt manifest, or any metadata source).\n\nThis tool automates the workflow of:\n1. Creating a node from predecessor(s) (auto-populates columns from source)\n2. Reconciling auto-populated columns against the external target schema\n3. Replacing columns to match the external schema exactly\n\nReconciliation logic:\n- Matched columns (by name): Preserves source linkage from predecessor, overrides dataType to match external schema\n- New columns (in target but not predecessor): Added without source mapping, flagged as needing a transform\n- Dropped columns (in predecessor but not target): Removed from the node\n\nThe response includes a `reconciliation` object showing exactly what was matched, added, dropped, and which types changed.\n\nREQUIRED: Call `plan_pipeline` first to discover the correct nodeType.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeType (string, required): Node type from plan_pipeline\n - predecessorNodeIDs (string[], required): One or more predecessor node IDs\n - targetColumns (array, required): External column definitions to match\n - targetName (string, optional): Name for the new node (e.g., 'STG_ORDER_HEADER')\n - locationName (string, optional): Storage location name\n - repoPath (string, optional): Local repo path for config completion\n - goal (string, optional): Intent for node type validation\n\nReturns:\n { node, reconciliation: { matched, added, dropped, typeChanges }, predecessors, validation, configCompletion?, configCompletionSkipped?, nodeTypeValidation?, nextSteps }",
|
|
280
|
+
inputSchema: z.object({
|
|
281
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
282
|
+
nodeType: z.string().describe("The type of node to create. Call plan_pipeline first to discover and rank available node types."),
|
|
283
|
+
predecessorNodeIDs: z
|
|
284
|
+
.array(z.string())
|
|
285
|
+
.min(1)
|
|
286
|
+
.describe("One or more predecessor node IDs to link to the new node"),
|
|
287
|
+
targetColumns: z
|
|
288
|
+
.array(ExternalColumnSchema)
|
|
289
|
+
.min(1)
|
|
290
|
+
.describe("External column definitions describing the target table schema. Each column specifies name, dataType, and optionally nullable, description, and transform."),
|
|
291
|
+
targetName: z
|
|
292
|
+
.string()
|
|
293
|
+
.optional()
|
|
294
|
+
.describe("Name for the new node (e.g., 'STG_ORDER_HEADER'). If omitted, uses the auto-generated name from Coalesce."),
|
|
295
|
+
locationName: z
|
|
296
|
+
.string()
|
|
297
|
+
.optional()
|
|
298
|
+
.describe("Storage location name for the new node."),
|
|
299
|
+
repoPath: z
|
|
300
|
+
.string()
|
|
301
|
+
.optional()
|
|
302
|
+
.describe("Path to local Coalesce repository for automatic config completion."),
|
|
303
|
+
goal: z
|
|
304
|
+
.string()
|
|
305
|
+
.optional()
|
|
306
|
+
.describe("The goal or intent for this node. Used to validate the chosen nodeType."),
|
|
307
|
+
}),
|
|
308
|
+
annotations: WRITE_ANNOTATIONS,
|
|
309
|
+
}, createNodeFromExternalSchema),
|
|
310
|
+
// analyze_workspace_patterns has multi-step inline logic (fetch → convert → analyze)
|
|
311
|
+
// that doesn't fit the simple helper pattern.
|
|
312
|
+
[
|
|
313
|
+
"analyze_workspace_patterns",
|
|
314
|
+
{
|
|
315
|
+
title: "Analyze Workspace Patterns",
|
|
316
|
+
description: "Analyze workspace node patterns to detect package adoption, pipeline layers, data modeling methodology, and generate recommendations.\n\nThis tool examines existing workspace nodes to understand conventions before creating new nodes. Results are returned as a workspace profile summary. If you want a reusable local snapshot instead of inline data, use cache_workspace_nodes.\n\nArgs:\n - workspaceID (string, required): The workspace ID to analyze\n\nReturns:\n Workspace profile with package adoption, pipeline layers, naming conventions, and recommendations.",
|
|
317
|
+
inputSchema: z.object({
|
|
318
|
+
workspaceID: z.string().describe("The workspace ID to analyze"),
|
|
319
|
+
}),
|
|
320
|
+
outputSchema: getToolOutputSchema("analyze_workspace_patterns"),
|
|
321
|
+
annotations: READ_ONLY_ANNOTATIONS,
|
|
322
|
+
},
|
|
323
|
+
async (params) => {
|
|
324
|
+
try {
|
|
325
|
+
const nodesResponse = await fetchAllWorkspaceNodes(client, {
|
|
326
|
+
workspaceID: params.workspaceID,
|
|
327
|
+
detail: false,
|
|
328
|
+
});
|
|
329
|
+
const nodes = toNodeSummaries(nodesResponse.items);
|
|
330
|
+
const profile = buildWorkspaceProfile(params.workspaceID, nodes);
|
|
331
|
+
return buildJsonToolResponse("analyze_workspace_patterns", profile);
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
return handleToolError(error);
|
|
164
335
|
}
|
|
165
336
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
workspaceID: z.string().describe("The workspace ID"),
|
|
201
|
-
nodeID: z.string().describe("The node ID"),
|
|
202
|
-
columns: z.array(WorkspaceNodeColumnInputSchema).describe("Complete new columns array to replace metadata.columns. Each column should include name and may include transform, dataType, description, nullable, sources, and other hydrated metadata fields."),
|
|
203
|
-
whereCondition: z
|
|
204
|
-
.string()
|
|
205
|
-
.optional()
|
|
206
|
-
.describe("Optional WHERE filter to append to the node's existing joinCondition. Just provide the condition — do NOT include the WHERE keyword or construct {{ ref() }} syntax. The FROM clause is already set from node creation. Example: '\"LOCATION\".\"LOCATION_ID\" IS NOT NULL AND \"LOCATION\".\"LOCATION_ID\" != 0'"),
|
|
207
|
-
additionalChanges: WorkspaceNodeWriteInputSchema
|
|
208
|
-
.optional()
|
|
209
|
-
.describe("Optional additional fields to update, such as name, description, config, or metadata. Object fields are deep-merged; arrays are replaced. Do NOT include metadata.sourceMapping or customSQL — use whereCondition for WHERE filters, apply_join_condition for join setup, or convert_join_to_aggregation for GROUP BY patterns."),
|
|
210
|
-
}),
|
|
211
|
-
outputSchema: getToolOutputSchema("replace_workspace_node_columns"),
|
|
212
|
-
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
213
|
-
}, async (params) => {
|
|
214
|
-
try {
|
|
215
|
-
const result = await replaceWorkspaceNodeColumns(client, params);
|
|
216
|
-
return buildJsonToolResponse("replace_workspace_node_columns", result);
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
return handleToolError(error);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
server.registerTool("convert_join_to_aggregation", {
|
|
223
|
-
title: "Convert Join to Aggregation",
|
|
224
|
-
description: "Convert an existing join node into an aggregated fact table with GROUP BY. This is the REQUIRED follow-up after creating a multi-predecessor node — it completes the join setup.\n\nThis tool automatically:\n- Generates JOIN ON clauses from common columns between predecessors\n- Writes the complete FROM/JOIN/ON/GROUP BY clause to the node's joinCondition (no separate update needed)\n- Replaces columns with GROUP BY dimensions + aggregate measures\n- Infers datatypes from transform functions (COUNT → NUMBER, SUM → NUMBER(38,4), etc.)\n- Sets column-level attributes (isBusinessKey on GROUP BY columns, isChangeTracking on aggregates)\n- Validates that all non-aggregate columns are in GROUP BY\n- Runs intelligent config completion\n\nUse this to transform a simple join (row-level) into an aggregated fact table (summary-level).\n\nExample: Convert order detail join to customer metrics:\n{\n workspaceID: \"1\",\n nodeID: \"fact-node-id\",\n groupByColumns: ['\"STG_ORDER_HEADER\".\"CUSTOMER_ID\"'],\n aggregates: [\n { name: \"TOTAL_ORDERS\", function: \"COUNT\", expression: 'DISTINCT \"STG_ORDER_HEADER\".\"ORDER_ID\"' },\n { name: \"LIFETIME_VALUE\", function: \"SUM\", expression: '\"STG_ORDER_HEADER\".\"ORDER_TOTAL\"' },\n { name: \"AVG_ORDER_VALUE\", function: \"AVG\", expression: '\"STG_ORDER_HEADER\".\"ORDER_TOTAL\"' }\n ],\n joinType: \"INNER JOIN\"\n}\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID of the join to convert\n - groupByColumns (string[], required): Columns to group by (dimensions)\n - aggregates (array, required): Aggregate columns with functions and expressions\n - joinType (string, optional): JOIN type (default: INNER JOIN)\n - maintainJoins (boolean, optional): Generate JOINs (default: true)\n - repoPath (string, optional): Local repo path for config completion\n\nReturns:\n Updated node with new columns, joinCondition, GROUP BY analysis, and config completion results.",
|
|
225
|
-
inputSchema: z.object({
|
|
226
|
-
workspaceID: z.string().describe("The workspace ID"),
|
|
227
|
-
nodeID: z.string().describe("The node ID of the join to convert"),
|
|
228
|
-
groupByColumns: z
|
|
229
|
-
.array(z.string())
|
|
230
|
-
.describe("Columns to group by (dimensions). Use fully-qualified names like '\"TABLE\".\"COLUMN\"'."),
|
|
231
|
-
aggregates: z
|
|
232
|
-
.array(z.object({
|
|
233
|
-
name: z.string().describe("Column name for the aggregate"),
|
|
234
|
-
function: z.string().describe("Aggregate function: COUNT, SUM, AVG, MIN, MAX, etc."),
|
|
235
|
-
expression: z.string().describe("Expression to aggregate (e.g., 'DISTINCT \"TABLE\".\"COLUMN\"')"),
|
|
236
|
-
description: z.string().optional().describe("Optional column description"),
|
|
237
|
-
}))
|
|
238
|
-
.describe("Aggregate columns with their functions and expressions"),
|
|
239
|
-
joinType: z
|
|
240
|
-
.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL OUTER JOIN"])
|
|
241
|
-
.optional()
|
|
242
|
-
.describe("Type of JOIN to use. Defaults to INNER JOIN."),
|
|
243
|
-
maintainJoins: z
|
|
244
|
-
.boolean()
|
|
245
|
-
.optional()
|
|
246
|
-
.describe("If true (default), analyzes predecessors, generates JOIN SQL, and writes the joinCondition to the node. If false, only replaces columns with aggregates without generating joins."),
|
|
247
|
-
repoPath: z
|
|
248
|
-
.string()
|
|
249
|
-
.optional()
|
|
250
|
-
.describe("Optional path to local Coalesce repository for intelligent config completion"),
|
|
251
|
-
}),
|
|
252
|
-
outputSchema: getToolOutputSchema("convert_join_to_aggregation"),
|
|
253
|
-
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
254
|
-
}, async (params) => {
|
|
255
|
-
try {
|
|
256
|
-
const result = await convertJoinToAggregation(client, params);
|
|
257
|
-
return buildJsonToolResponse("convert_join_to_aggregation", result);
|
|
258
|
-
}
|
|
259
|
-
catch (error) {
|
|
260
|
-
return handleToolError(error);
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
server.registerTool("apply_join_condition", {
|
|
264
|
-
title: "Apply Join Condition",
|
|
265
|
-
description: "Write a FROM/JOIN/ON clause to a workspace node's sourceMapping.join.joinCondition by analyzing predecessor columns and generating the join automatically.\n\nUse this for multi-predecessor nodes where you need to combine data via JOIN. The tool inspects predecessor columns, finds common column names for ON conditions, and writes the full joinCondition to the node.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID of the multi-predecessor node\n - joinType (enum, optional): 'INNER JOIN' | 'LEFT JOIN' | 'RIGHT JOIN' | 'FULL OUTER JOIN'. Defaults to INNER JOIN\n - whereClause (string, optional): WHERE filter to append after the JOIN (without the WHERE keyword)\n - qualifyClause (string, optional): QUALIFY clause to append (without the QUALIFY keyword)\n - joinColumnOverrides (array, optional): Explicit column mappings when column names differ across predecessors. Each entry: { leftPredecessor, rightPredecessor, leftColumn, rightColumn }\n\nReturns:\n { nodeID, joinCondition, warning?, validation? }",
|
|
266
|
-
inputSchema: z.object({
|
|
267
|
-
workspaceID: z.string().describe("The workspace ID"),
|
|
268
|
-
nodeID: z.string().describe("The node ID of the multi-predecessor node"),
|
|
269
|
-
joinType: z
|
|
270
|
-
.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL OUTER JOIN"])
|
|
271
|
-
.optional()
|
|
272
|
-
.describe("Type of JOIN to use between predecessors. Defaults to INNER JOIN."),
|
|
273
|
-
whereClause: z
|
|
274
|
-
.string()
|
|
275
|
-
.optional()
|
|
276
|
-
.describe("Optional WHERE clause to append after the JOIN (without the WHERE keyword)."),
|
|
277
|
-
qualifyClause: z
|
|
278
|
-
.string()
|
|
279
|
-
.optional()
|
|
280
|
-
.describe("Optional QUALIFY clause to append (without the QUALIFY keyword)."),
|
|
281
|
-
joinColumnOverrides: z
|
|
282
|
-
.array(z.object({
|
|
283
|
-
leftPredecessor: z.string().describe("Name of the left predecessor node"),
|
|
284
|
-
rightPredecessor: z.string().describe("Name of the right predecessor node"),
|
|
285
|
-
leftColumn: z.string().describe("Column name in the left predecessor"),
|
|
286
|
-
rightColumn: z.string().describe("Column name in the right predecessor"),
|
|
287
|
-
}))
|
|
288
|
-
.optional()
|
|
289
|
-
.describe("Explicit column mappings for joins when column names differ across predecessors. Overrides auto-detected common columns for the specified predecessor pair."),
|
|
290
|
-
}),
|
|
291
|
-
outputSchema: getToolOutputSchema("apply_join_condition"),
|
|
292
|
-
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
293
|
-
}, async (params) => {
|
|
294
|
-
try {
|
|
295
|
-
const result = await applyJoinCondition(client, params);
|
|
296
|
-
return buildJsonToolResponse("apply_join_condition", result);
|
|
297
|
-
}
|
|
298
|
-
catch (error) {
|
|
299
|
-
return handleToolError(error);
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
server.registerTool("create_workspace_node_from_predecessor", {
|
|
303
|
-
title: "Create Workspace Node from Predecessor",
|
|
304
|
-
description: "Create a workspace node from one or more predecessor nodes, fetch it, and verify that columns were auto-populated from those predecessors before applying any optional changes.\n\nSINGLE-CALL WORKFLOW: You can create a node AND apply column transforms, WHERE filters, or aggregation in one call:\n- columns + whereCondition: Replace auto-populated columns with specific transforms and add a WHERE filter — no separate replace_workspace_node_columns needed\n- groupByColumns + aggregates: Convert to an aggregation node with GROUP BY — no separate convert_join_to_aggregation needed\nThese are mutually exclusive: use columns OR groupByColumns+aggregates, not both.\n\nREQUIRED: Before calling this tool, call `plan_pipeline` with `goal`, `sourceNodeIDs`, and `repoPath` to discover and rank available node types. Use the `nodeType` from the plan result — do NOT guess or hardcode node types like 'Stage', 'View', or numeric IDs like '65'. The planner scans all committed node type definitions and scores them against your use case.\n\nSPECIALIZED TYPES WARNING: Do NOT use Dynamic Tables, Incremental Load, Materialized View, or other specialized types unless the user explicitly requests that pattern (e.g., 'near-real-time refresh', 'continuous refresh', 'incremental processing'). For standard batch ETL, CTE decomposition, and general transforms, use Stage or Work. The response includes nodeTypeValidation.warning if a specialized pattern was detected without matching context — always check this field.\n\nJOIN INTELLIGENCE: For multi-predecessor nodes (joins), this tool automatically:\n- Analyzes common columns between each predecessor pair\n- Returns `joinSuggestions` with normalized column names and their left/right counterparts\n- Reports which predecessors are represented in the resulting column references\n- Warns if any predecessor is missing from the auto-populated columns\n\nAUTOMATIC CONFIG: When repoPath is provided, this tool automatically runs intelligent config completion after creation — reading the node type definition, setting node-level config defaults, and applying column-level attributes (isBusinessKey, isChangeTracking, etc.). The configCompletion result shows what was applied.\n\nDo not use overrideSQL or override.* fields; SQL override is disallowed in this project.\n\nFor guidance on node types, storage locations, and SQL patterns, see resources: coalesce://context/data-engineering-principles, coalesce://context/storage-mappings, coalesce://context/sql-platform-selection\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeType (string, required): Node type from plan_pipeline\n - predecessorNodeIDs (string[], required): One or more predecessor node IDs\n - changes (object, optional): Partial fields to apply after creation\n - columns (array, optional): Replace auto-populated columns (mutually exclusive with groupByColumns)\n - whereCondition (string, optional): WHERE filter (only with columns)\n - groupByColumns (string[], optional): GROUP BY columns (with aggregates)\n - aggregates (array, optional): Aggregate columns (with groupByColumns)\n - joinType (string, optional): JOIN type for multi-predecessor aggregation\n - repoPath (string, optional): Local repo path for config completion\n - goal (string, optional): Intent for node type validation\n\nReturns:\n { nodeID, created, joinSuggestions?, nodeTypeValidation?, configCompletion? }",
|
|
305
|
-
inputSchema: z.object({
|
|
306
|
-
workspaceID: z.string().describe("The workspace ID"),
|
|
307
|
-
nodeType: z.string().describe("The type of node to create. IMPORTANT: Call plan_pipeline first to discover and rank available node types — use the nodeType from its result. Format: 'PackageName:::ID' for package types (e.g., 'base-nodes:::Stage') or simple name ('Stage') for built-in types. Always prefer the package-prefixed format returned by plan_pipeline."),
|
|
308
|
-
predecessorNodeIDs: z
|
|
309
|
-
.array(z.string())
|
|
310
|
-
.min(1)
|
|
311
|
-
.describe("One or more predecessor node IDs to link to the new node"),
|
|
312
|
-
changes: WorkspaceNodeWriteInputSchema
|
|
313
|
-
.optional()
|
|
314
|
-
.describe("Optional partial fields to apply after successful auto-population validation, such as name, description, config, metadata, database, schema, or locationName."),
|
|
315
|
-
columns: z.array(WorkspaceNodeColumnInputSchema)
|
|
316
|
-
.optional()
|
|
317
|
-
.describe("Replace auto-populated columns with these specific columns and transforms. Mutually exclusive with groupByColumns/aggregates."),
|
|
318
|
-
whereCondition: z
|
|
319
|
-
.string()
|
|
320
|
-
.optional()
|
|
321
|
-
.describe("WHERE filter to append to the joinCondition (without the WHERE keyword). Only valid with columns, not with groupByColumns/aggregates."),
|
|
322
|
-
groupByColumns: z
|
|
323
|
-
.array(z.string())
|
|
324
|
-
.optional()
|
|
325
|
-
.describe("GROUP BY columns for aggregation. Must be provided with aggregates. Mutually exclusive with columns."),
|
|
326
|
-
aggregates: z
|
|
327
|
-
.array(z.object({
|
|
328
|
-
name: z.string().describe("Output column name for the aggregate"),
|
|
329
|
-
function: z.string().describe("Aggregate function: COUNT, SUM, AVG, MIN, MAX, etc."),
|
|
330
|
-
expression: z.string().describe("Expression to aggregate"),
|
|
331
|
-
description: z.string().optional().describe("Column description"),
|
|
332
|
-
}))
|
|
333
|
-
.optional()
|
|
334
|
-
.describe("Aggregate columns. Must be provided with groupByColumns. Mutually exclusive with columns."),
|
|
335
|
-
joinType: z
|
|
336
|
-
.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL OUTER JOIN"])
|
|
337
|
-
.optional()
|
|
338
|
-
.describe("JOIN type for multi-predecessor aggregation nodes. Defaults to INNER JOIN."),
|
|
339
|
-
repoPath: z
|
|
340
|
-
.string()
|
|
341
|
-
.optional()
|
|
342
|
-
.describe("Path to local Coalesce repository for automatic config completion after creation."),
|
|
343
|
-
goal: z
|
|
344
|
-
.string()
|
|
345
|
-
.optional()
|
|
346
|
-
.describe("The goal or intent for this node (e.g., 'deduplicate customer records', 'join orders with customers'). Used to validate that the chosen nodeType is appropriate for the task. Same value you would pass to plan_pipeline."),
|
|
347
|
-
}),
|
|
348
|
-
outputSchema: getToolOutputSchema("create_workspace_node_from_predecessor"),
|
|
349
|
-
annotations: WRITE_ANNOTATIONS,
|
|
350
|
-
}, async (params) => {
|
|
351
|
-
try {
|
|
352
|
-
const result = await createWorkspaceNodeFromPredecessor(client, params);
|
|
353
|
-
return buildJsonToolResponse("create_workspace_node_from_predecessor", result);
|
|
354
|
-
}
|
|
355
|
-
catch (error) {
|
|
356
|
-
return handleToolError(error);
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
server.registerTool("create_node_from_external_schema", {
|
|
360
|
-
title: "Create Node from External Schema",
|
|
361
|
-
description: "Create a workspace node whose output columns match an external table schema (e.g., from Snowflake DESCRIBE TABLE, dbt manifest, or any metadata source).\n\nThis tool automates the workflow of:\n1. Creating a node from predecessor(s) (auto-populates columns from source)\n2. Reconciling auto-populated columns against the external target schema\n3. Replacing columns to match the external schema exactly\n\nReconciliation logic:\n- Matched columns (by name): Preserves source linkage from predecessor, overrides dataType to match external schema\n- New columns (in target but not predecessor): Added without source mapping, flagged as needing a transform\n- Dropped columns (in predecessor but not target): Removed from the node\n\nThe response includes a `reconciliation` object showing exactly what was matched, added, dropped, and which types changed.\n\nREQUIRED: Call `plan_pipeline` first to discover the correct nodeType.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeType (string, required): Node type from plan_pipeline\n - predecessorNodeIDs (string[], required): One or more predecessor node IDs\n - targetColumns (array, required): External column definitions to match\n - targetName (string, optional): Name for the new node (e.g., 'STG_ORDER_HEADER')\n - locationName (string, optional): Storage location name\n - repoPath (string, optional): Local repo path for config completion\n - goal (string, optional): Intent for node type validation\n\nReturns:\n { node, reconciliation: { matched, added, dropped, typeChanges }, predecessors, validation, configCompletion?, configCompletionSkipped?, nodeTypeValidation?, nextSteps }",
|
|
362
|
-
inputSchema: z.object({
|
|
363
|
-
workspaceID: z.string().describe("The workspace ID"),
|
|
364
|
-
nodeType: z.string().describe("The type of node to create. Call plan_pipeline first to discover and rank available node types."),
|
|
365
|
-
predecessorNodeIDs: z
|
|
366
|
-
.array(z.string())
|
|
367
|
-
.min(1)
|
|
368
|
-
.describe("One or more predecessor node IDs to link to the new node"),
|
|
369
|
-
targetColumns: z
|
|
370
|
-
.array(ExternalColumnSchema)
|
|
371
|
-
.min(1)
|
|
372
|
-
.describe("External column definitions describing the target table schema. Each column specifies name, dataType, and optionally nullable, description, and transform."),
|
|
373
|
-
targetName: z
|
|
374
|
-
.string()
|
|
375
|
-
.optional()
|
|
376
|
-
.describe("Name for the new node (e.g., 'STG_ORDER_HEADER'). If omitted, uses the auto-generated name from Coalesce."),
|
|
377
|
-
locationName: z
|
|
378
|
-
.string()
|
|
379
|
-
.optional()
|
|
380
|
-
.describe("Storage location name for the new node."),
|
|
381
|
-
repoPath: z
|
|
382
|
-
.string()
|
|
383
|
-
.optional()
|
|
384
|
-
.describe("Path to local Coalesce repository for automatic config completion."),
|
|
385
|
-
goal: z
|
|
386
|
-
.string()
|
|
387
|
-
.optional()
|
|
388
|
-
.describe("The goal or intent for this node. Used to validate the chosen nodeType."),
|
|
389
|
-
}),
|
|
390
|
-
outputSchema: getToolOutputSchema("create_node_from_external_schema"),
|
|
391
|
-
annotations: WRITE_ANNOTATIONS,
|
|
392
|
-
}, async (params) => {
|
|
393
|
-
try {
|
|
394
|
-
const result = await createNodeFromExternalSchema(client, params);
|
|
395
|
-
return buildJsonToolResponse("create_node_from_external_schema", result);
|
|
396
|
-
}
|
|
397
|
-
catch (error) {
|
|
398
|
-
return handleToolError(error);
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
server.registerTool("analyze_workspace_patterns", {
|
|
402
|
-
title: "Analyze Workspace Patterns",
|
|
403
|
-
description: "Analyze workspace node patterns to detect package adoption, pipeline layers, data modeling methodology, and generate recommendations.\n\nThis tool examines existing workspace nodes to understand conventions before creating new nodes. Results are returned as a workspace profile summary. If you want a reusable local snapshot instead of inline data, use cache_workspace_nodes.\n\nArgs:\n - workspaceID (string, required): The workspace ID to analyze\n\nReturns:\n Workspace profile with package adoption, pipeline layers, naming conventions, and recommendations.",
|
|
404
|
-
inputSchema: z.object({
|
|
405
|
-
workspaceID: z.string().describe("The workspace ID to analyze"),
|
|
406
|
-
}),
|
|
407
|
-
outputSchema: getToolOutputSchema("analyze_workspace_patterns"),
|
|
408
|
-
annotations: READ_ONLY_ANNOTATIONS,
|
|
409
|
-
}, async (params) => {
|
|
410
|
-
try {
|
|
411
|
-
const nodesResponse = await fetchAllWorkspaceNodes(client, {
|
|
412
|
-
workspaceID: params.workspaceID,
|
|
413
|
-
detail: false,
|
|
414
|
-
});
|
|
415
|
-
const nodes = toNodeSummaries(nodesResponse.items);
|
|
416
|
-
const profile = buildWorkspaceProfile(params.workspaceID, nodes);
|
|
417
|
-
return buildJsonToolResponse("analyze_workspace_patterns", profile);
|
|
418
|
-
}
|
|
419
|
-
catch (error) {
|
|
420
|
-
return handleToolError(error);
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
server.registerTool("list_workspace_node_types", {
|
|
424
|
-
title: "List Workspace Node Types",
|
|
425
|
-
description: "List distinct node types observed in current workspace nodes. This scans existing nodes only; it is not a true installed-type registry.\n\nWARNING: Do NOT use these values directly as the nodeType parameter for create_workspace_node_from_predecessor or create_workspace_node_from_scratch. The observed values may be bare numeric IDs (e.g. '31') that differ from the proper package-prefixed format (e.g. 'base-nodes:::Stage'). Always call plan_pipeline first to discover the correct nodeType.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n\nReturns:\n { nodeTypes: { id, name, count }[] }",
|
|
426
|
-
inputSchema: z.object({
|
|
427
|
-
workspaceID: z.string().describe("The workspace ID"),
|
|
428
|
-
}),
|
|
429
|
-
outputSchema: getToolOutputSchema("list_workspace_node_types"),
|
|
430
|
-
annotations: READ_ONLY_ANNOTATIONS,
|
|
431
|
-
}, async (params) => {
|
|
432
|
-
try {
|
|
433
|
-
const result = await listWorkspaceNodeTypes(client, params);
|
|
434
|
-
return buildJsonToolResponse("list_workspace_node_types", result);
|
|
435
|
-
}
|
|
436
|
-
catch (error) {
|
|
437
|
-
return handleToolError(error);
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
server.registerTool("delete_workspace_node", {
|
|
441
|
-
title: "Delete Workspace Node",
|
|
442
|
-
description: "Permanently delete a workspace node. Destructive — check for downstream dependencies first.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID\n\nReturns:\n Confirmation message.",
|
|
443
|
-
inputSchema: z.object({
|
|
444
|
-
workspaceID: z.string().describe("The workspace ID"),
|
|
445
|
-
nodeID: z.string().describe("The node ID to delete"),
|
|
446
|
-
}),
|
|
447
|
-
outputSchema: getToolOutputSchema("delete_workspace_node"),
|
|
448
|
-
annotations: DESTRUCTIVE_ANNOTATIONS,
|
|
449
|
-
}, async (params) => {
|
|
450
|
-
try {
|
|
451
|
-
const result = await deleteWorkspaceNode(client, params);
|
|
452
|
-
return buildJsonToolResponse("delete_workspace_node", result);
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
return handleToolError(error);
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
server.registerTool("complete_node_configuration", {
|
|
459
|
-
title: "Complete Node Configuration",
|
|
460
|
-
description: "Run intelligent configuration completion on a workspace node. Analyzes the node's type definition, existing config, and column layout to fill in required and recommended configuration fields.\n\nBoth creation tools call this internally, but you can invoke it separately after manual edits.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID\n - repoPath (string, optional): Local repo path for type definition lookup\n\nReturns:\n { nodeID, configCompletion? | configCompletionSkipped? }",
|
|
461
|
-
inputSchema: z.object({
|
|
462
|
-
workspaceID: z.string().describe("The workspace ID"),
|
|
463
|
-
nodeID: z.string().describe("The node ID to configure"),
|
|
464
|
-
repoPath: z.string().optional().describe("Optional path to local Coalesce repository for schema resolution"),
|
|
465
|
-
}),
|
|
466
|
-
outputSchema: getToolOutputSchema("complete_node_configuration"),
|
|
467
|
-
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
468
|
-
}, async (params) => {
|
|
469
|
-
try {
|
|
470
|
-
const result = await completeNodeConfiguration(client, params);
|
|
471
|
-
return buildJsonToolResponse("complete_node_configuration", result);
|
|
472
|
-
}
|
|
473
|
-
catch (error) {
|
|
474
|
-
return handleToolError(error);
|
|
475
|
-
}
|
|
476
|
-
});
|
|
337
|
+
],
|
|
338
|
+
defineSimpleTool(client, "list_workspace_node_types", {
|
|
339
|
+
title: "List Workspace Node Types",
|
|
340
|
+
description: "List distinct node types observed in current workspace nodes. This scans existing nodes only; it is not a true installed-type registry.\n\nWARNING: Do NOT use these values directly as the nodeType parameter for create_workspace_node_from_predecessor or create_workspace_node_from_scratch. The observed values may be bare numeric IDs (e.g. '31') that differ from the proper package-prefixed format (e.g. 'base-nodes:::Stage'). Always call plan_pipeline first to discover the correct nodeType.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n\nReturns:\n { nodeTypes: string[], counts: Record<string, number>, total: number } — nodeTypes is sorted by frequency descending; counts maps each type to its usage count.",
|
|
341
|
+
inputSchema: z.object({
|
|
342
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
343
|
+
}),
|
|
344
|
+
annotations: READ_ONLY_ANNOTATIONS,
|
|
345
|
+
}, listWorkspaceNodeTypes),
|
|
346
|
+
defineDestructiveTool(server, client, "delete_workspace_node", {
|
|
347
|
+
title: "Delete Workspace Node",
|
|
348
|
+
description: "Permanently delete a workspace node. Destructive — check for downstream dependencies first.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID\n - confirmed (boolean, optional): Set to true after the user explicitly confirms deletion\n\nReturns:\n Confirmation message.",
|
|
349
|
+
inputSchema: z.object({
|
|
350
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
351
|
+
nodeID: z.string().describe("The node ID to delete"),
|
|
352
|
+
confirmed: z
|
|
353
|
+
.boolean()
|
|
354
|
+
.optional()
|
|
355
|
+
.describe("Set to true after the user explicitly confirms the deletion."),
|
|
356
|
+
}),
|
|
357
|
+
annotations: DESTRUCTIVE_ANNOTATIONS,
|
|
358
|
+
confirmMessage: (params) => `This will permanently delete node "${params.nodeID}" from workspace "${params.workspaceID}". This cannot be undone.`,
|
|
359
|
+
}, deleteWorkspaceNode),
|
|
360
|
+
defineSimpleTool(client, "complete_node_configuration", {
|
|
361
|
+
title: "Complete Node Configuration",
|
|
362
|
+
description: "Run intelligent configuration completion on a workspace node. Analyzes the node's type definition, existing config, and column layout to fill in required and recommended configuration fields.\n\nBoth creation tools call this internally, but you can invoke it separately after manual edits.\n\nArgs:\n - workspaceID (string, required): The workspace ID\n - nodeID (string, required): The node ID\n - repoPath (string, optional): Local repo path for type definition lookup\n\nReturns:\n { nodeID, configCompletion? | configCompletionSkipped? }",
|
|
363
|
+
inputSchema: z.object({
|
|
364
|
+
workspaceID: z.string().describe("The workspace ID"),
|
|
365
|
+
nodeID: z.string().describe("The node ID to configure"),
|
|
366
|
+
repoPath: z.string().optional().describe("Optional path to local Coalesce repository for schema resolution"),
|
|
367
|
+
}),
|
|
368
|
+
annotations: IDEMPOTENT_WRITE_ANNOTATIONS,
|
|
369
|
+
}, completeNodeConfiguration),
|
|
370
|
+
];
|
|
477
371
|
}
|
|
478
372
|
//# sourceMappingURL=nodes.js.map
|