@databrainhq/mcp-server 0.3.0-rc.10 → 0.3.0-rc.12
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 +15 -6
- package/dist/apps/server.d.ts +113 -47
- package/dist/apps/server.js +5 -4
- package/dist/apps/stdio.js +6 -5
- package/dist/apps/stdio.js.map +1 -1
- package/dist/bundle-TDQXWIPM.js +24 -0
- package/dist/bundle-TDQXWIPM.js.map +1 -0
- package/dist/chunk-347U6GXR.js +884 -0
- package/dist/chunk-347U6GXR.js.map +1 -0
- package/dist/{chunk-N7X762VU.js → chunk-LACJHQYH.js} +4 -4
- package/dist/{chunk-ERTAKB2H.js → chunk-OLOC2MTA.js} +960 -683
- package/dist/chunk-OLOC2MTA.js.map +1 -0
- package/dist/{chunk-OCBTNAAO.js → chunk-Q5EYDKSA.js} +550 -1272
- package/dist/chunk-Q5EYDKSA.js.map +1 -0
- package/dist/{chunk-CUPL7CAN.js → chunk-V3UW3B6U.js} +2 -2
- package/dist/{chunk-5IGP6A6T.js → chunk-ZTHGF27R.js} +225 -34
- package/dist/chunk-ZTHGF27R.js.map +1 -0
- package/dist/cli.js +6 -4
- package/dist/cli.js.map +1 -1
- package/dist/custom-chart/custom-runtime.js +63 -0
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/{main-KZRHWE3K.js → main-IUV24DMP.js} +47 -24
- package/dist/main-IUV24DMP.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-5IGP6A6T.js.map +0 -1
- package/dist/chunk-ERTAKB2H.js.map +0 -1
- package/dist/chunk-OCBTNAAO.js.map +0 -1
- package/dist/main-KZRHWE3K.js.map +0 -1
- /package/dist/{chunk-N7X762VU.js.map → chunk-LACJHQYH.js.map} +0 -0
- /package/dist/{chunk-CUPL7CAN.js.map → chunk-V3UW3B6U.js.map} +0 -0
|
@@ -3,22 +3,31 @@ import {
|
|
|
3
3
|
CustomChartsApi,
|
|
4
4
|
DatabrainApiError,
|
|
5
5
|
apiProblems,
|
|
6
|
-
buildProject,
|
|
7
|
-
bundleSizeBytes,
|
|
8
|
-
contentHash,
|
|
9
6
|
createCustomChartsApi,
|
|
7
|
+
editWithBryn,
|
|
10
8
|
logger,
|
|
11
|
-
optionSchema,
|
|
12
9
|
planUpload,
|
|
13
|
-
readDist,
|
|
14
10
|
scaffoldProject,
|
|
15
|
-
slotSchema,
|
|
16
11
|
stringify,
|
|
12
|
+
studioDraftSchema,
|
|
17
13
|
toolContext,
|
|
18
14
|
toolError,
|
|
19
|
-
toolSuccess
|
|
15
|
+
toolSuccess
|
|
16
|
+
} from "./chunk-347U6GXR.js";
|
|
17
|
+
import {
|
|
18
|
+
MAX_BUNDLE_CSS_BYTES,
|
|
19
|
+
MAX_BUNDLE_JS_BYTES,
|
|
20
|
+
MAX_ICON_BYTES,
|
|
21
|
+
MAX_MANIFEST_BYTES,
|
|
22
|
+
buildProject,
|
|
23
|
+
buildSource,
|
|
24
|
+
bundleSizeBytes,
|
|
25
|
+
contentHash,
|
|
26
|
+
optionSchema,
|
|
27
|
+
readDist,
|
|
28
|
+
slotSchema,
|
|
20
29
|
validateBundle
|
|
21
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-Q5EYDKSA.js";
|
|
22
31
|
|
|
23
32
|
// src/apps/contract.ts
|
|
24
33
|
import { z } from "zod";
|
|
@@ -28,10 +37,15 @@ var APP_MIME = "text/html;profile=mcp-app";
|
|
|
28
37
|
var RESOURCE_URI_META_KEY = "ui/resourceUri";
|
|
29
38
|
var PAGE_SIZE = 50;
|
|
30
39
|
var MAX_RESULT_CHARS = 124e3;
|
|
40
|
+
var MAX_CUSTOM_BUNDLE_CHARS = 6 * (MAX_BUNDLE_JS_BYTES + MAX_BUNDLE_CSS_BYTES + MAX_ICON_BYTES) + MAX_MANIFEST_BYTES + 128;
|
|
41
|
+
var CUSTOM_BUNDLE_CHUNK_CHARS = 32e3;
|
|
31
42
|
var id = z.string().trim().min(1).max(256);
|
|
32
43
|
var cell = z.union([z.string().max(2e3), z.number().finite(), z.boolean(), z.null()]);
|
|
33
44
|
var rowsSchema = z.array(z.record(cell)).max(PAGE_SIZE);
|
|
34
|
-
var referenceSchema = z.
|
|
45
|
+
var referenceSchema = z.union([
|
|
46
|
+
z.object({ embedId: id, clientId: id, widgetId: id, dataAppName: id.optional() }).strict(),
|
|
47
|
+
z.object({ dashboardId: id, widgetId: id, clientId: id.optional(), display: z.enum(["chart", "kpi"]).optional() }).strict()
|
|
48
|
+
]);
|
|
35
49
|
var point = z.object({
|
|
36
50
|
key: id,
|
|
37
51
|
label: z.string().max(2e3),
|
|
@@ -52,22 +66,33 @@ var viewSchema = z.object({
|
|
|
52
66
|
drillId: id.optional()
|
|
53
67
|
}).strict();
|
|
54
68
|
var pageSchema = z.object({
|
|
55
|
-
kind: z.enum(["chart", "kpi"]),
|
|
69
|
+
kind: z.enum(["chart", "kpi", "table"]),
|
|
56
70
|
title: z.string().max(500),
|
|
71
|
+
description: z.string().max(2e3).optional(),
|
|
72
|
+
rowsTotal: z.number().int().nonnegative().optional(),
|
|
57
73
|
chartSpec: z.object({ type: z.string().max(80) }).strict(),
|
|
58
74
|
rows: rowsSchema,
|
|
59
75
|
columns: z.array(z.string().max(128)).max(20),
|
|
60
76
|
svg: z.string().max(1e5).optional(),
|
|
77
|
+
customChart: z.object({
|
|
78
|
+
key: z.string().regex(/^[a-z][a-z0-9_-]{0,63}$/),
|
|
79
|
+
bindings: z.record(z.array(z.string().max(128)).max(20)),
|
|
80
|
+
options: z.record(z.union([z.string().max(2e3), z.number().finite(), z.boolean()]))
|
|
81
|
+
}).strict().optional(),
|
|
82
|
+
fallback: z.string().max(1e3).optional(),
|
|
61
83
|
width: z.number().int().min(240).max(1200),
|
|
62
84
|
height: z.number().int().min(120).max(800),
|
|
63
85
|
points: z.array(point).max(PAGE_SIZE),
|
|
64
86
|
value: cell.optional(),
|
|
65
87
|
nextCursor: z.string().min(1).max(2048).optional()
|
|
66
88
|
}).strict().superRefine((page, ctx) => {
|
|
67
|
-
if (page.kind === "chart" && !page.svg) ctx.addIssue({ code: "custom", message: "SVG required" });
|
|
89
|
+
if (page.kind === "chart" && !page.svg && !page.customChart) ctx.addIssue({ code: "custom", message: "SVG required" });
|
|
68
90
|
if (page.kind === "kpi" && (page.svg || page.points.length || page.value === void 0)) {
|
|
69
91
|
ctx.addIssue({ code: "custom", message: "KPI requires a value and no SVG/points" });
|
|
70
92
|
}
|
|
93
|
+
if (page.kind === "table" && (page.svg || page.points.length || page.value !== void 0)) {
|
|
94
|
+
ctx.addIssue({ code: "custom", message: "Table requires rows without SVG/points/value" });
|
|
95
|
+
}
|
|
71
96
|
if (new Set(page.points.map((p) => p.key)).size !== page.points.length || page.points.some((p) => p.x + p.width > page.width || p.y + p.height > page.height)) {
|
|
72
97
|
ctx.addIssue({ code: "custom", message: "Invalid point geometry" });
|
|
73
98
|
}
|
|
@@ -88,8 +113,8 @@ function firstRows(value) {
|
|
|
88
113
|
}
|
|
89
114
|
|
|
90
115
|
// src/apps/feature.ts
|
|
91
|
-
function appMeta(visibility) {
|
|
92
|
-
return { ui: { resourceUri:
|
|
116
|
+
function appMeta(visibility, uri = CHART_URI) {
|
|
117
|
+
return { ui: { resourceUri: uri, visibility }, [RESOURCE_URI_META_KEY]: uri };
|
|
93
118
|
}
|
|
94
119
|
var enabled = /* @__PURE__ */ new WeakSet();
|
|
95
120
|
function enableChartApps(server) {
|
|
@@ -105,9 +130,196 @@ function chartResult(summary, payload) {
|
|
|
105
130
|
return bounded({
|
|
106
131
|
content: [{ type: "text", text: summary }],
|
|
107
132
|
structuredContent: payload,
|
|
108
|
-
_meta: appMeta(["model", "app"])
|
|
133
|
+
_meta: appMeta(["model", "app"], payload.kind === "kpi" ? KPI_URI : CHART_URI)
|
|
109
134
|
});
|
|
110
135
|
}
|
|
136
|
+
function chartIndex(dashboardId, charts) {
|
|
137
|
+
return `To draw one, call render_metric_tile with dashboardId ${JSON.stringify(dashboardId)} and metricId set to a widgetId below.
|
|
138
|
+
` + JSON.stringify(charts.slice(0, 50).map((c) => ({ widgetId: c.widgetId, name: (c.name || "Untitled widget").slice(0, 200), chartType: c.chartType || null }))) + (charts.length > 50 ? `
|
|
139
|
+
Showing the first 50 of ${charts.length}.` : "");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/tools/custom-chart/author.ts
|
|
143
|
+
import { z as z3 } from "zod";
|
|
144
|
+
|
|
145
|
+
// src/tools/annotations.ts
|
|
146
|
+
var READ_ONLY = {
|
|
147
|
+
readOnlyHint: true,
|
|
148
|
+
destructiveHint: false,
|
|
149
|
+
idempotentHint: true,
|
|
150
|
+
openWorldHint: false
|
|
151
|
+
};
|
|
152
|
+
var WRITE_IDEMPOTENT = {
|
|
153
|
+
readOnlyHint: false,
|
|
154
|
+
destructiveHint: false,
|
|
155
|
+
idempotentHint: true,
|
|
156
|
+
openWorldHint: false
|
|
157
|
+
};
|
|
158
|
+
var WRITE_CREATE = {
|
|
159
|
+
readOnlyHint: false,
|
|
160
|
+
destructiveHint: false,
|
|
161
|
+
idempotentHint: false,
|
|
162
|
+
openWorldHint: false
|
|
163
|
+
};
|
|
164
|
+
var DESTRUCTIVE = {
|
|
165
|
+
readOnlyHint: false,
|
|
166
|
+
destructiveHint: true,
|
|
167
|
+
idempotentHint: false,
|
|
168
|
+
openWorldHint: false
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/tools/custom-chart/shared.ts
|
|
172
|
+
import { z as z2 } from "zod";
|
|
173
|
+
var slotInputSchema = slotSchema;
|
|
174
|
+
var optionInputSchema = optionSchema;
|
|
175
|
+
var manifestInputSchema = z2.record(z2.unknown()).describe("manifest.json as an object (schemaVersion 2). Validated with the same rules as the server.");
|
|
176
|
+
var bundleInputShape = {
|
|
177
|
+
manifest: manifestInputSchema.optional(),
|
|
178
|
+
chartTs: z2.string().max(64e3).optional().describe("Hosted source: export render(ctx) in TypeScript or JavaScript. Compiled in memory without filesystem or imports. Pass instead of bundleJs/projectDir."),
|
|
179
|
+
bundleJs: z2.string().optional().describe("dist/index.js from `databrain custom-chart build` (the shim plus the chart module)."),
|
|
180
|
+
bundleCss: z2.string().optional().describe("The raw chart.css, kept for provenance."),
|
|
181
|
+
iconSvg: z2.string().optional().describe("icon.svg, at most 16 KiB, no script, no foreignObject, no external href."),
|
|
182
|
+
projectDir: z2.string().optional().describe("Local only: a chart project folder. The tool reads it instead of manifest/bundleJs/iconSvg.")
|
|
183
|
+
};
|
|
184
|
+
function formatProblems(problems) {
|
|
185
|
+
if (problems.length === 0) return "No problems.";
|
|
186
|
+
return problems.map((p) => `${p.path || "/"} ${p.code} ${p.message}`).join("\n");
|
|
187
|
+
}
|
|
188
|
+
function projectDirRefusal() {
|
|
189
|
+
if (process.env.DATABRAIN_HOSTED_PUBLIC_URL) {
|
|
190
|
+
return "projectDir is not available on the hosted server. Pass chartTs, manifest, bundleCss and iconSvg to compile in memory, or pass an already built bundleJs.";
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
async function resolveBundleInput(params, options) {
|
|
195
|
+
if (params.chartTs !== void 0) {
|
|
196
|
+
if (params.projectDir || params.bundleJs || !params.manifest || !params.iconSvg) {
|
|
197
|
+
return { problems: [], error: "chartTs needs manifest and iconSvg, and cannot be combined with bundleJs or projectDir.", source: "arguments" };
|
|
198
|
+
}
|
|
199
|
+
const result = await buildSource({ manifest: params.manifest, chartTs: params.chartTs, bundleCss: params.bundleCss, iconSvg: params.iconSvg });
|
|
200
|
+
return { bundle: result.outputs, problems: result.problems, source: "arguments" };
|
|
201
|
+
}
|
|
202
|
+
if (params.projectDir) {
|
|
203
|
+
const refusal = projectDirRefusal();
|
|
204
|
+
if (refusal) return { problems: [], error: refusal, source: "projectDir" };
|
|
205
|
+
if (params.manifest || params.bundleJs || params.iconSvg) {
|
|
206
|
+
return { problems: [], error: "Pass either projectDir or manifest/bundleJs/iconSvg, not both.", source: "projectDir" };
|
|
207
|
+
}
|
|
208
|
+
if (options.build) {
|
|
209
|
+
const result = await buildProject(params.projectDir, { write: false });
|
|
210
|
+
if (!result.ok || !result.outputs) return { problems: result.problems, source: "projectDir" };
|
|
211
|
+
const { manifest, bundleJs, bundleCss, iconSvg } = result.outputs;
|
|
212
|
+
return { bundle: { manifest, bundleJs, bundleCss, iconSvg }, problems: [], source: "projectDir" };
|
|
213
|
+
}
|
|
214
|
+
const dist = readDist(params.projectDir);
|
|
215
|
+
if (!dist.bundle) return { problems: dist.problems, source: "projectDir" };
|
|
216
|
+
return { bundle: dist.bundle, problems: [], source: "projectDir" };
|
|
217
|
+
}
|
|
218
|
+
if (!params.manifest) {
|
|
219
|
+
return { problems: [], error: "manifest is required (or pass projectDir when running locally).", source: "arguments" };
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
bundle: {
|
|
223
|
+
manifest: params.manifest,
|
|
224
|
+
bundleJs: params.bundleJs,
|
|
225
|
+
bundleCss: params.bundleCss,
|
|
226
|
+
iconSvg: params.iconSvg
|
|
227
|
+
},
|
|
228
|
+
problems: [],
|
|
229
|
+
source: "arguments"
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
async function remoteValidate(client, bundle) {
|
|
233
|
+
try {
|
|
234
|
+
const result = await createCustomChartsApi(client).validate(bundle);
|
|
235
|
+
return { status: "ok", result };
|
|
236
|
+
} catch (err) {
|
|
237
|
+
if (err instanceof DatabrainApiError) {
|
|
238
|
+
if (err.code === "MISSING_SERVICE_TOKEN") {
|
|
239
|
+
return { status: "skipped", reason: "No service token is configured, so only the local validator ran." };
|
|
240
|
+
}
|
|
241
|
+
return { status: "failed", reason: err.message, problems: apiProblems(err) };
|
|
242
|
+
}
|
|
243
|
+
return { status: "failed", reason: err.message, problems: [] };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function isCompleteBundle(bundle) {
|
|
247
|
+
return typeof bundle.bundleJs === "string" && typeof bundle.iconSvg === "string";
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/tools/custom-chart/author.ts
|
|
251
|
+
function registerCustomChartAuthor(server, client) {
|
|
252
|
+
server.tool(
|
|
253
|
+
"custom_chart_edit",
|
|
254
|
+
"Iterate a custom chart draft with Chart studio Bryn in a granted workspace. Never saves a version or publishes. Feature-detects the backend route. If absent, returns the draft for the calling agent to edit and validate with chartTs, without a local build.",
|
|
255
|
+
{
|
|
256
|
+
key: z3.string().regex(/^[a-z][a-z0-9_-]{0,63}$/),
|
|
257
|
+
workspaceId: z3.string().uuid(),
|
|
258
|
+
message: z3.string().trim().min(1).max(8e3),
|
|
259
|
+
draft: studioDraftSchema,
|
|
260
|
+
sessionId: z3.string().uuid().optional()
|
|
261
|
+
},
|
|
262
|
+
WRITE_CREATE,
|
|
263
|
+
async (input) => {
|
|
264
|
+
try {
|
|
265
|
+
if (input.draft.manifest.id !== input.key) return toolError("The draft manifest id must match key.");
|
|
266
|
+
const result = await editWithBryn(client, input);
|
|
267
|
+
return toolSuccess(JSON.stringify({
|
|
268
|
+
...result,
|
|
269
|
+
nextStep: result.available ? "Check the returned draft. Validate and preview before saving. Publishing still requires the author to confirm." : "Studio Bryn is not installed. Edit draft.code as plain JavaScript defining render(ctx), then use custom_chart_validate / custom_chart_preview / custom_chart_upload with chartTs=draft.code, manifest, bundleCss=draft.css and iconSvg. Nothing was saved."
|
|
270
|
+
}));
|
|
271
|
+
} catch (error) {
|
|
272
|
+
return error instanceof DatabrainApiError ? error.toToolResponse() : toolError("Bryn could not edit the draft. No version was saved.");
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
const preview = server.tool(
|
|
277
|
+
"custom_chart_preview",
|
|
278
|
+
"Validate and preview a draft custom chart as an MCP App tile using bounded sample query rows. No version is saved. Pass chartTs to compile on the server without local files; bindings map manifest slot ids to row column names. Hosts without MCP Apps get the rows and validation result.",
|
|
279
|
+
{
|
|
280
|
+
...bundleInputShape,
|
|
281
|
+
rows: rowsSchema,
|
|
282
|
+
bindings: z3.record(z3.array(z3.string().max(128)).max(20)),
|
|
283
|
+
options: z3.record(z3.union([z3.string().max(2e3), z3.number().finite(), z3.boolean()])).default({})
|
|
284
|
+
},
|
|
285
|
+
READ_ONLY,
|
|
286
|
+
async (input) => {
|
|
287
|
+
try {
|
|
288
|
+
const resolved = await resolveBundleInput(input, { build: true });
|
|
289
|
+
if (resolved.error) return toolError(resolved.error);
|
|
290
|
+
if (!resolved.bundle || !isCompleteBundle(resolved.bundle)) return toolError(formatProblems(resolved.problems));
|
|
291
|
+
const { validateBundle: validateBundle2 } = await import("./bundle-TDQXWIPM.js");
|
|
292
|
+
const problems = validateBundle2(resolved.bundle);
|
|
293
|
+
if (problems.length) return toolError(formatProblems(problems));
|
|
294
|
+
const manifest = resolved.bundle.manifest;
|
|
295
|
+
const columns = Object.keys(input.rows[0] ?? {});
|
|
296
|
+
const missing = manifest.slots.filter((slot) => slot.required && !(input.bindings[slot.id] ?? []).some((column) => columns.includes(column)));
|
|
297
|
+
if (input.rows.length && missing.length) return toolError(`Bind required slots ${missing.map((slot) => slot.id).join(", ")} to sample columns: ${columns.join(", ") || "(none)"}.`);
|
|
298
|
+
const customChart = { key: manifest.id, bindings: input.bindings, options: input.options };
|
|
299
|
+
const payload = {
|
|
300
|
+
kind: "chart",
|
|
301
|
+
title: manifest.name,
|
|
302
|
+
chartSpec: { type: `custom:${manifest.id}` },
|
|
303
|
+
rows: input.rows,
|
|
304
|
+
columns: Object.keys(input.rows[0] ?? {}),
|
|
305
|
+
width: 600,
|
|
306
|
+
height: 320,
|
|
307
|
+
points: [],
|
|
308
|
+
customChart,
|
|
309
|
+
fallback: "Draft validated. An MCP Apps host can preview it. These are sample rows, not a saved metric."
|
|
310
|
+
};
|
|
311
|
+
if (!hasChartApps(server)) return toolSuccess(JSON.stringify({ ...payload, ok: true }));
|
|
312
|
+
const result = chartResult("Custom chart draft validated. Preview uses sample rows. Nothing was saved or published.", payload);
|
|
313
|
+
result._meta = { ...result._meta, "databrain/custom-chart-bundle": resolved.bundle };
|
|
314
|
+
if (JSON.stringify(result).length > MAX_RESULT_CHARS) return toolError("Draft preview exceeds the MCP host result budget. Reduce the source, CSS or sample rows before previewing.");
|
|
315
|
+
return result;
|
|
316
|
+
} catch (error) {
|
|
317
|
+
return error instanceof DatabrainApiError ? error.toToolResponse() : toolError(error instanceof Error && error.message === "Chart page exceeds payload budget" ? "Draft preview exceeds the MCP host result budget. Reduce the source, CSS or sample rows before previewing." : "Custom chart preview could not be prepared. Check the manifest, source and bindings.");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
tagChartTool(server, preview);
|
|
322
|
+
}
|
|
111
323
|
|
|
112
324
|
// src/tools/tool-metadata.ts
|
|
113
325
|
var DEFAULT_TOOL_META = {
|
|
@@ -182,6 +394,7 @@ var TOOL_METADATA = {
|
|
|
182
394
|
preview_chart: { category: "internal_analytics", workflowIntent: "query_data", effect: "view" },
|
|
183
395
|
save_chart_as_dashboard: { category: "internal_analytics", workflowIntent: "author_dashboard", effect: "commit" },
|
|
184
396
|
// Read-only to the user, but each interaction mints an embed-scoped guest credential.
|
|
397
|
+
render_metric_tile: { category: "internal_analytics", workflowIntent: "chart_app", effect: "read" },
|
|
185
398
|
render_chart: { category: "embedding", workflowIntent: "chart_app", effect: "commit" },
|
|
186
399
|
refresh_widget: { category: "embedding", workflowIntent: "chart_app", effect: "commit" },
|
|
187
400
|
drill_down: { category: "embedding", workflowIntent: "chart_app", effect: "commit" },
|
|
@@ -204,6 +417,10 @@ var TOOL_METADATA = {
|
|
|
204
417
|
list_scheduled_reports: { category: "reports", workflowIntent: "reports", effect: "read" },
|
|
205
418
|
// Custom chart types (DAT-925)
|
|
206
419
|
// Returns template files; nothing is written or persisted.
|
|
420
|
+
custom_chart_edit: { category: "custom_charts", workflowIntent: "custom_chart", effect: "commit" },
|
|
421
|
+
custom_chart_preview: { category: "custom_charts", workflowIntent: "custom_chart", effect: "read" },
|
|
422
|
+
load_custom_chart_bundle: { category: "custom_charts", workflowIntent: "custom_chart", effect: "read" },
|
|
423
|
+
load_custom_chart_runtime: { category: "custom_charts", workflowIntent: "custom_chart", effect: "read" },
|
|
207
424
|
custom_chart_scaffold: { category: "custom_charts", workflowIntent: "custom_chart", effect: "read" },
|
|
208
425
|
// Local rules plus POST /validate, which never persists.
|
|
209
426
|
custom_chart_validate: { category: "custom_charts", workflowIntent: "custom_chart", effect: "read" },
|
|
@@ -214,7 +431,7 @@ var TOOL_METADATA = {
|
|
|
214
431
|
};
|
|
215
432
|
|
|
216
433
|
// src/tools/data-app/list-data-apps.ts
|
|
217
|
-
import { z as
|
|
434
|
+
import { z as z4 } from "zod";
|
|
218
435
|
|
|
219
436
|
// src/api/dashboards-api.ts
|
|
220
437
|
var DashboardsApi = class {
|
|
@@ -399,10 +616,12 @@ var EmbedsApi = class {
|
|
|
399
616
|
this.client = client;
|
|
400
617
|
}
|
|
401
618
|
async createEmbed(body) {
|
|
619
|
+
const { dataAppName, ...payload } = body;
|
|
402
620
|
return this.client.request({
|
|
403
621
|
method: "POST",
|
|
404
622
|
path: "/api/v2/data-app/embeds",
|
|
405
|
-
|
|
623
|
+
query: typeof dataAppName === "string" ? { dataAppName } : void 0,
|
|
624
|
+
body: payload,
|
|
406
625
|
auth: "api",
|
|
407
626
|
invalidateCache: ["list_embeds"]
|
|
408
627
|
});
|
|
@@ -424,12 +643,12 @@ var EmbedsApi = class {
|
|
|
424
643
|
cacheTtlMs: 5 * 60 * 1e3
|
|
425
644
|
});
|
|
426
645
|
}
|
|
427
|
-
async getByEmbedId(embedId) {
|
|
646
|
+
async getByEmbedId(embedId, dataAppName) {
|
|
428
647
|
return this.client.request({
|
|
429
648
|
method: "GET",
|
|
430
649
|
path: "/api/v2/data-app/embeds",
|
|
431
650
|
auth: "api",
|
|
432
|
-
query: { embedId }
|
|
651
|
+
query: { embedId, ...dataAppName ? { dataAppName } : {} }
|
|
433
652
|
});
|
|
434
653
|
}
|
|
435
654
|
async updateEmbed(body) {
|
|
@@ -509,7 +728,7 @@ var QueryApi = class {
|
|
|
509
728
|
async startSemanticLayerGeneration(datamartId2) {
|
|
510
729
|
return this.client.request({
|
|
511
730
|
method: "POST",
|
|
512
|
-
path: `/
|
|
731
|
+
path: `/schema/datamart/${encodeURIComponent(datamartId2)}/semantic-layer/auto-generate`,
|
|
513
732
|
auth: "service",
|
|
514
733
|
invalidateCache: ["semantic_layer_"]
|
|
515
734
|
});
|
|
@@ -517,7 +736,7 @@ var QueryApi = class {
|
|
|
517
736
|
async getSemanticLayerGenerationStatus(datamartId2) {
|
|
518
737
|
return this.client.request({
|
|
519
738
|
method: "GET",
|
|
520
|
-
path: `/
|
|
739
|
+
path: `/schema/datamart/${encodeURIComponent(datamartId2)}/semantic-layer/generation-status`,
|
|
521
740
|
auth: "service"
|
|
522
741
|
});
|
|
523
742
|
}
|
|
@@ -556,6 +775,7 @@ var ScheduledReportsApi = class {
|
|
|
556
775
|
embedId: params.embedId,
|
|
557
776
|
clientId: params.clientId
|
|
558
777
|
};
|
|
778
|
+
if (params.dataAppName) query.dataAppName = params.dataAppName;
|
|
559
779
|
if (params.userIdentifier) query.userIdentifier = params.userIdentifier;
|
|
560
780
|
if (params.pageNumber !== void 0) query.pageNumber = String(params.pageNumber);
|
|
561
781
|
if (params.isPagination !== void 0) {
|
|
@@ -579,15 +799,15 @@ var SemanticModelApi = class {
|
|
|
579
799
|
path(id2, verb) {
|
|
580
800
|
return `/api/v2/semantic/${encodeURIComponent(id2)}/${verb}`;
|
|
581
801
|
}
|
|
582
|
-
pull(id2) {
|
|
583
|
-
return this.client.request({ method: "GET", path: this.path(id2, "pull"), auth: "service" });
|
|
802
|
+
pull(id2, mode) {
|
|
803
|
+
return this.client.request({ method: "GET", path: this.path(id2, "pull"), auth: "service", ...mode ? { query: { mode } } : {} });
|
|
584
804
|
}
|
|
585
805
|
retrieve(id2, q, k) {
|
|
586
806
|
return this.client.request({
|
|
587
|
-
method: "
|
|
807
|
+
method: "POST",
|
|
588
808
|
path: this.path(id2, "retrieve"),
|
|
589
809
|
auth: "service",
|
|
590
|
-
|
|
810
|
+
body: { q, k }
|
|
591
811
|
});
|
|
592
812
|
}
|
|
593
813
|
validate(id2, source2) {
|
|
@@ -663,12 +883,12 @@ var WidgetsApi = class {
|
|
|
663
883
|
constructor(client) {
|
|
664
884
|
this.client = client;
|
|
665
885
|
}
|
|
666
|
-
async listWidgets(embedId, clientId) {
|
|
886
|
+
async listWidgets(embedId, clientId, dataAppName) {
|
|
667
887
|
return this.client.request({
|
|
668
888
|
method: "GET",
|
|
669
889
|
path: "/api/v2/data-app/metrics",
|
|
670
890
|
auth: "api",
|
|
671
|
-
query: { embedId, clientId }
|
|
891
|
+
query: { embedId, clientId, ...dataAppName ? { dataAppName } : {} }
|
|
672
892
|
});
|
|
673
893
|
}
|
|
674
894
|
async createMetric(guestToken, body) {
|
|
@@ -677,7 +897,7 @@ var WidgetsApi = class {
|
|
|
677
897
|
path: "/api/v2/dashboard/createMetric",
|
|
678
898
|
auth: "none",
|
|
679
899
|
token: guestToken,
|
|
680
|
-
body
|
|
900
|
+
body: { ...body, isLive: body.isLive ?? false }
|
|
681
901
|
});
|
|
682
902
|
}
|
|
683
903
|
async updateMetric(guestToken, body) {
|
|
@@ -692,7 +912,7 @@ var WidgetsApi = class {
|
|
|
692
912
|
async markArchived(guestToken, body) {
|
|
693
913
|
return this.client.request({
|
|
694
914
|
method: "POST",
|
|
695
|
-
path: "/api/
|
|
915
|
+
path: "/api/v3/metric/markArchived",
|
|
696
916
|
auth: "none",
|
|
697
917
|
token: guestToken,
|
|
698
918
|
body
|
|
@@ -758,7 +978,7 @@ var DiscoveryService = class {
|
|
|
758
978
|
normalizeDashboard(dashboard) {
|
|
759
979
|
return {
|
|
760
980
|
name: isNonEmptyString(dashboard.name) && dashboard.name || isNonEmptyString(dashboard.dashboardName) && dashboard.dashboardName || "Unnamed Dashboard",
|
|
761
|
-
dashboardId: isNonEmptyString(dashboard.dashboardId) && dashboard.dashboardId || isNonEmptyString(dashboard.
|
|
981
|
+
dashboardId: isNonEmptyString(dashboard.dashboardId) && dashboard.dashboardId || isNonEmptyString(dashboard._id) && dashboard._id || isNonEmptyString(dashboard.id) && dashboard.id || isNonEmptyString(dashboard.uid) && dashboard.uid || isNonEmptyString(dashboard.externalDashboardId) && dashboard.externalDashboardId || null,
|
|
762
982
|
externalDashboardId: isNonEmptyString(dashboard.externalDashboardId) && dashboard.externalDashboardId || null,
|
|
763
983
|
workspaceName: isNonEmptyString(dashboard.workspaceName) && dashboard.workspaceName || isNonEmptyString(dashboard.workspace) && dashboard.workspace || isNonEmptyString(dashboard.workspaceId) && dashboard.workspaceId || null,
|
|
764
984
|
metricCount: typeof dashboard.metricCount === "number" ? dashboard.metricCount : null
|
|
@@ -874,8 +1094,8 @@ var EmbedService = class {
|
|
|
874
1094
|
};
|
|
875
1095
|
return this.embedsApi.createEmbed(body);
|
|
876
1096
|
}
|
|
877
|
-
async resolveEmbedStrict(embedId) {
|
|
878
|
-
const response = await this.embedsApi.getByEmbedId(embedId);
|
|
1097
|
+
async resolveEmbedStrict(embedId, dataAppName) {
|
|
1098
|
+
const response = await this.embedsApi.getByEmbedId(embedId, dataAppName);
|
|
879
1099
|
const records = Array.isArray(response) ? response.filter((r) => !!r && typeof r === "object") : [response];
|
|
880
1100
|
const exact = records.filter((r) => r.embedId === embedId);
|
|
881
1101
|
if (exact.length === 1) return exact[0];
|
|
@@ -890,8 +1110,8 @@ var EmbedService = class {
|
|
|
890
1110
|
Received records (sample): ${sample || "none"}`
|
|
891
1111
|
);
|
|
892
1112
|
}
|
|
893
|
-
async buildUpdateBase(embedId) {
|
|
894
|
-
const embed = await this.resolveEmbedStrict(embedId);
|
|
1113
|
+
async buildUpdateBase(embedId, dataAppName) {
|
|
1114
|
+
const embed = await this.resolveEmbedStrict(embedId, dataAppName);
|
|
895
1115
|
const existingAccess = extractAccessSettings(embed);
|
|
896
1116
|
if (existingAccess) {
|
|
897
1117
|
return { embedId, accessSettings: existingAccess };
|
|
@@ -939,8 +1159,8 @@ Run \`update_embed\` with explicit \`accessSettings.datamartName\`, or recreate
|
|
|
939
1159
|
}
|
|
940
1160
|
};
|
|
941
1161
|
}
|
|
942
|
-
async updateEmbed(embedId, updatePatch) {
|
|
943
|
-
const body = await this.buildUpdateBase(embedId);
|
|
1162
|
+
async updateEmbed(embedId, updatePatch, dataAppName) {
|
|
1163
|
+
const body = await this.buildUpdateBase(embedId, dataAppName);
|
|
944
1164
|
const baseAccessSettings = asRecord2(body.accessSettings);
|
|
945
1165
|
const patchAccessSettings = asRecord2(updatePatch.accessSettings);
|
|
946
1166
|
for (const [key, value] of Object.entries(updatePatch)) {
|
|
@@ -955,7 +1175,7 @@ Run \`update_embed\` with explicit \`accessSettings.datamartName\`, or recreate
|
|
|
955
1175
|
} else if (patchAccessSettings) {
|
|
956
1176
|
body.accessSettings = patchAccessSettings;
|
|
957
1177
|
}
|
|
958
|
-
return this.embedsApi.updateEmbed(body);
|
|
1178
|
+
return this.embedsApi.updateEmbed({ ...body, ...dataAppName ? { dataAppName } : {} });
|
|
959
1179
|
}
|
|
960
1180
|
};
|
|
961
1181
|
|
|
@@ -1023,7 +1243,7 @@ var WidgetService = class {
|
|
|
1023
1243
|
* where the user only knows the dashboard + workspace, not the embedId or
|
|
1024
1244
|
* data app name. Returns a business-user-friendly error on ambiguity.
|
|
1025
1245
|
*/
|
|
1026
|
-
async resolveEmbedFromDashboard(dashboardName, workspaceName) {
|
|
1246
|
+
async resolveEmbedFromDashboard(dashboardName, workspaceName, dataAppName) {
|
|
1027
1247
|
const wsProvided = workspaceName && workspaceName.trim().length > 0 ? workspaceName.trim() : void 0;
|
|
1028
1248
|
let dashboards;
|
|
1029
1249
|
try {
|
|
@@ -1065,8 +1285,23 @@ var WidgetService = class {
|
|
|
1065
1285
|
};
|
|
1066
1286
|
}
|
|
1067
1287
|
let embeds;
|
|
1288
|
+
let dataAppSelectionAvailable = false;
|
|
1068
1289
|
try {
|
|
1069
|
-
|
|
1290
|
+
try {
|
|
1291
|
+
embeds = await this.embedsApi.listEmbeds(dataAppName);
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
if (!(error instanceof DatabrainApiError) || error.code !== "DATA_APP_REQUIRED") throw error;
|
|
1294
|
+
dataAppSelectionAvailable = true;
|
|
1295
|
+
const apps = await this.dataAppsApi.listDataApps(dashboard.workspaceName || wsProvided);
|
|
1296
|
+
embeds = [];
|
|
1297
|
+
for (const app of apps) {
|
|
1298
|
+
const name = app.name || app.dataAppName;
|
|
1299
|
+
if (typeof name === "string" && name.trim()) {
|
|
1300
|
+
const scoped = await this.embedsApi.listEmbeds(name);
|
|
1301
|
+
embeds.push(...scoped.map((e) => ({ ...e, embedMetadata: { ...e.embedMetadata, dataAppName: name } })));
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1070
1305
|
} catch (err) {
|
|
1071
1306
|
return {
|
|
1072
1307
|
ok: false,
|
|
@@ -1092,10 +1327,11 @@ var WidgetService = class {
|
|
|
1092
1327
|
};
|
|
1093
1328
|
}
|
|
1094
1329
|
if (embedMatches.length > 1) {
|
|
1095
|
-
const names = embedMatches.map((x) => `"${x.n.name}"`).join(", ");
|
|
1330
|
+
const names = embedMatches.map((x) => `"${x.n.name}" (embedId: ${x.n.embedId})`).join(", ");
|
|
1331
|
+
const apps = [...new Set(embedMatches.map((x) => x.n.dataAppName).filter(Boolean))];
|
|
1096
1332
|
return {
|
|
1097
1333
|
ok: false,
|
|
1098
|
-
error: `Dashboard "${dashboard.name}" has multiple embeds (${names}).
|
|
1334
|
+
error: `Dashboard "${dashboard.name}" has multiple embeds (${names}). ` + (dataAppSelectionAvailable && apps.length > 1 ? `Pass dataAppName to choose one of: ${apps.join(", ")}, or select an embedId. (DATA_APP_REQUIRED)` : "Select an embedId and retry.")
|
|
1099
1335
|
};
|
|
1100
1336
|
}
|
|
1101
1337
|
return {
|
|
@@ -1121,7 +1357,8 @@ var WidgetService = class {
|
|
|
1121
1357
|
if (params.dashboardName && params.dashboardName.trim().length > 0) {
|
|
1122
1358
|
const res = await this.resolveEmbedFromDashboard(
|
|
1123
1359
|
params.dashboardName,
|
|
1124
|
-
params.workspaceName
|
|
1360
|
+
params.workspaceName,
|
|
1361
|
+
params.dataAppName
|
|
1125
1362
|
);
|
|
1126
1363
|
if (!res.ok) return res;
|
|
1127
1364
|
return {
|
|
@@ -1171,7 +1408,7 @@ var WidgetService = class {
|
|
|
1171
1408
|
error: "`clientId` cannot be empty. Pick a tenant/client first."
|
|
1172
1409
|
};
|
|
1173
1410
|
}
|
|
1174
|
-
const ctx = await this.resolveEmbedContext(params.embedId);
|
|
1411
|
+
const ctx = await this.resolveEmbedContext(params.embedId, params.dataAppName);
|
|
1175
1412
|
if (ctx.externalDashboardCreatedBy === clientId) {
|
|
1176
1413
|
return {
|
|
1177
1414
|
ok: true,
|
|
@@ -1246,7 +1483,7 @@ var WidgetService = class {
|
|
|
1246
1483
|
}
|
|
1247
1484
|
let ctx;
|
|
1248
1485
|
try {
|
|
1249
|
-
ctx = await this.resolveEmbedContext(params.embedId);
|
|
1486
|
+
ctx = await this.resolveEmbedContext(params.embedId, params.dataAppName);
|
|
1250
1487
|
} catch (err) {
|
|
1251
1488
|
return { ok: false, error: err.message };
|
|
1252
1489
|
}
|
|
@@ -1354,7 +1591,17 @@ Do not guess between the two \u2014 wait for the user to choose.`
|
|
|
1354
1591
|
}
|
|
1355
1592
|
return false;
|
|
1356
1593
|
};
|
|
1357
|
-
|
|
1594
|
+
let target = datasources.find(matchPredicate);
|
|
1595
|
+
if (!target && provided.integrationId) {
|
|
1596
|
+
for (const datasource of datasources) {
|
|
1597
|
+
if (datasource._id || datasource.id || typeof datasource.name !== "string") continue;
|
|
1598
|
+
const id3 = await this.datasourcesApi.resolveIdByName(datasource.name);
|
|
1599
|
+
if (id3 === provided.integrationId) {
|
|
1600
|
+
target = { ...datasource, id: id3 };
|
|
1601
|
+
break;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1358
1605
|
if (!target) {
|
|
1359
1606
|
const available = datasources.filter((ds) => typeof ds.name === "string" && ds.name.trim().length > 0).map((ds) => `"${ds.name}"`).join(", ");
|
|
1360
1607
|
const inactiveMatch = allDatasources.find(
|
|
@@ -1424,8 +1671,8 @@ Do not guess between the two \u2014 wait for the user to choose.`
|
|
|
1424
1671
|
}
|
|
1425
1672
|
return { kind: "no_metric_fields", widgetsTried: toProbe.length, fetchErrors };
|
|
1426
1673
|
}
|
|
1427
|
-
async resolveEmbedContext(embedId) {
|
|
1428
|
-
const response = await this.embedsApi.getByEmbedId(embedId);
|
|
1674
|
+
async resolveEmbedContext(embedId, dataAppNameHint) {
|
|
1675
|
+
const response = await this.embedsApi.getByEmbedId(embedId, dataAppNameHint);
|
|
1429
1676
|
const records = Array.isArray(response) ? response : response ? [response] : [];
|
|
1430
1677
|
const exact = records.find((r) => r && r.embedId === embedId) || records[0];
|
|
1431
1678
|
if (!exact) {
|
|
@@ -1577,32 +1824,6 @@ function createServiceBundle(client) {
|
|
|
1577
1824
|
};
|
|
1578
1825
|
}
|
|
1579
1826
|
|
|
1580
|
-
// src/tools/annotations.ts
|
|
1581
|
-
var READ_ONLY = {
|
|
1582
|
-
readOnlyHint: true,
|
|
1583
|
-
destructiveHint: false,
|
|
1584
|
-
idempotentHint: true,
|
|
1585
|
-
openWorldHint: false
|
|
1586
|
-
};
|
|
1587
|
-
var WRITE_IDEMPOTENT = {
|
|
1588
|
-
readOnlyHint: false,
|
|
1589
|
-
destructiveHint: false,
|
|
1590
|
-
idempotentHint: true,
|
|
1591
|
-
openWorldHint: false
|
|
1592
|
-
};
|
|
1593
|
-
var WRITE_CREATE = {
|
|
1594
|
-
readOnlyHint: false,
|
|
1595
|
-
destructiveHint: false,
|
|
1596
|
-
idempotentHint: false,
|
|
1597
|
-
openWorldHint: false
|
|
1598
|
-
};
|
|
1599
|
-
var DESTRUCTIVE = {
|
|
1600
|
-
readOnlyHint: false,
|
|
1601
|
-
destructiveHint: true,
|
|
1602
|
-
idempotentHint: false,
|
|
1603
|
-
openWorldHint: false
|
|
1604
|
-
};
|
|
1605
|
-
|
|
1606
1827
|
// src/tools/data-app/list-data-apps.ts
|
|
1607
1828
|
function registerListDataApps(server, client) {
|
|
1608
1829
|
const services = createServiceBundle(client);
|
|
@@ -1610,7 +1831,7 @@ function registerListDataApps(server, client) {
|
|
|
1610
1831
|
"list_data_apps",
|
|
1611
1832
|
"List data apps in your organization. Start here for embed setup.",
|
|
1612
1833
|
{
|
|
1613
|
-
workspaceName:
|
|
1834
|
+
workspaceName: z4.string().optional().describe("Filter by workspace name")
|
|
1614
1835
|
},
|
|
1615
1836
|
READ_ONLY,
|
|
1616
1837
|
async (params) => {
|
|
@@ -1639,7 +1860,7 @@ function registerListDataApps(server, client) {
|
|
|
1639
1860
|
}
|
|
1640
1861
|
|
|
1641
1862
|
// src/tools/data-app/list-tenants.ts
|
|
1642
|
-
import { z as
|
|
1863
|
+
import { z as z5 } from "zod";
|
|
1643
1864
|
var isNonEmptyString4 = (value) => typeof value === "string" && value.trim().length > 0;
|
|
1644
1865
|
function workspaceIdFrom(workspace) {
|
|
1645
1866
|
return isNonEmptyString4(workspace.id) && workspace.id || isNonEmptyString4(workspace._id) && workspace._id || isNonEmptyString4(workspace.workspaceId) && workspace.workspaceId || null;
|
|
@@ -1677,12 +1898,12 @@ function registerListTenants(server, client) {
|
|
|
1677
1898
|
"list_tenants",
|
|
1678
1899
|
"List available tenants/clients from the workspace tenancy database. Use this to pick a valid clientId before creating guest tokens or editing customer dashboards.",
|
|
1679
1900
|
{
|
|
1680
|
-
workspaceName:
|
|
1681
|
-
workspaceId:
|
|
1682
|
-
searchValue:
|
|
1683
|
-
limit:
|
|
1684
|
-
datasourceId:
|
|
1685
|
-
datamartId:
|
|
1901
|
+
workspaceName: z5.string().optional().describe("Workspace name. Preferred for non-technical callers; the tool resolves it to a workspaceId."),
|
|
1902
|
+
workspaceId: z5.string().optional().describe("Technical workspace UUID. Provide this instead of workspaceName if already known."),
|
|
1903
|
+
searchValue: z5.string().optional().describe("Optional case-insensitive search string for tenant/client labels."),
|
|
1904
|
+
limit: z5.number().int().positive().max(500).optional().describe("Maximum number of tenants to return. Backend may apply connector-specific limits."),
|
|
1905
|
+
datasourceId: z5.string().optional().describe("Optional datasource UUID override when the workspace has multiple datasource contexts."),
|
|
1906
|
+
datamartId: z5.string().optional().describe("Optional datamart UUID override when tenant lookup should use a specific datamart.")
|
|
1686
1907
|
},
|
|
1687
1908
|
READ_ONLY,
|
|
1688
1909
|
async (params) => {
|
|
@@ -1776,15 +1997,15 @@ function registerListTenants(server, client) {
|
|
|
1776
1997
|
}
|
|
1777
1998
|
|
|
1778
1999
|
// src/tools/data-app/create-data-app.ts
|
|
1779
|
-
import { z as
|
|
2000
|
+
import { z as z6 } from "zod";
|
|
1780
2001
|
function registerCreateDataApp(server, client) {
|
|
1781
2002
|
server.tool(
|
|
1782
2003
|
"create_data_app",
|
|
1783
2004
|
"Create a new data app.",
|
|
1784
2005
|
{
|
|
1785
|
-
name:
|
|
1786
|
-
workspaceName:
|
|
1787
|
-
body:
|
|
2006
|
+
name: z6.string().describe("Data app name"),
|
|
2007
|
+
workspaceName: z6.string().optional().describe("Optional workspace name if required by backend"),
|
|
2008
|
+
body: z6.record(z6.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/")
|
|
1788
2009
|
},
|
|
1789
2010
|
WRITE_CREATE,
|
|
1790
2011
|
async (params) => {
|
|
@@ -1818,7 +2039,7 @@ function registerCreateDataApp(server, client) {
|
|
|
1818
2039
|
}
|
|
1819
2040
|
|
|
1820
2041
|
// src/tools/data-app/list-api-tokens.ts
|
|
1821
|
-
import { z as
|
|
2042
|
+
import { z as z7 } from "zod";
|
|
1822
2043
|
|
|
1823
2044
|
// src/utils/resolve-resource.ts
|
|
1824
2045
|
function resolveResource(options) {
|
|
@@ -1887,7 +2108,7 @@ function registerListApiTokens(server, client) {
|
|
|
1887
2108
|
"list_api_tokens",
|
|
1888
2109
|
"List API tokens for a data app. Omit dataAppName to auto-resolve.",
|
|
1889
2110
|
{
|
|
1890
|
-
dataAppName:
|
|
2111
|
+
dataAppName: z7.string().optional().describe("Data app name. Omit to list available data apps.")
|
|
1891
2112
|
},
|
|
1892
2113
|
READ_ONLY,
|
|
1893
2114
|
async (params) => {
|
|
@@ -1932,16 +2153,16 @@ function registerListApiTokens(server, client) {
|
|
|
1932
2153
|
}
|
|
1933
2154
|
|
|
1934
2155
|
// src/tools/data-app/create-api-token.ts
|
|
1935
|
-
import { z as
|
|
2156
|
+
import { z as z8 } from "zod";
|
|
1936
2157
|
function registerCreateApiToken(server, client) {
|
|
1937
2158
|
const services = createServiceBundle(client);
|
|
1938
2159
|
server.tool(
|
|
1939
2160
|
"create_api_token",
|
|
1940
2161
|
"Create an API token for a data app and activate it for this session. Omit dataAppName to auto-resolve.",
|
|
1941
2162
|
{
|
|
1942
|
-
dataAppName:
|
|
1943
|
-
name:
|
|
1944
|
-
body:
|
|
2163
|
+
dataAppName: z8.string().optional().describe("Data app name. Omit to list available data apps."),
|
|
2164
|
+
name: z8.string().optional().describe("Optional token name"),
|
|
2165
|
+
body: z8.record(z8.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/api-tokens")
|
|
1945
2166
|
},
|
|
1946
2167
|
WRITE_CREATE,
|
|
1947
2168
|
async (params) => {
|
|
@@ -1994,16 +2215,17 @@ Save this in backend secrets immediately. This value may not be returned again.`
|
|
|
1994
2215
|
}
|
|
1995
2216
|
|
|
1996
2217
|
// src/tools/data-app/rotate-api-token.ts
|
|
1997
|
-
import {
|
|
2218
|
+
import { randomUUID } from "crypto";
|
|
2219
|
+
import { z as z9 } from "zod";
|
|
1998
2220
|
function registerRotateApiToken(server, client) {
|
|
1999
2221
|
const services = createServiceBundle(client);
|
|
2000
2222
|
server.tool(
|
|
2001
2223
|
"rotate_api_token",
|
|
2002
|
-
"Rotate
|
|
2224
|
+
"Rotate through the raw token-authenticated endpoint, or issue a replacement token for a data app. The replacement fallback does not revoke existing tokens.",
|
|
2003
2225
|
{
|
|
2004
|
-
dataAppName:
|
|
2005
|
-
tokenId:
|
|
2006
|
-
body:
|
|
2226
|
+
dataAppName: z9.string().optional().describe("Data app name. Omit to list available data apps."),
|
|
2227
|
+
tokenId: z9.string().optional().describe("Optional token ID to rotate"),
|
|
2228
|
+
body: z9.record(z9.unknown()).optional().describe("Raw API body passthrough for rotate endpoint")
|
|
2007
2229
|
},
|
|
2008
2230
|
DESTRUCTIVE,
|
|
2009
2231
|
async (params) => {
|
|
@@ -2039,13 +2261,14 @@ function registerRotateApiToken(server, client) {
|
|
|
2039
2261
|
` : "") + "Update backend secrets immediately."
|
|
2040
2262
|
);
|
|
2041
2263
|
}
|
|
2264
|
+
if (params.tokenId) return toolError("Rotating a named token requires the token-authenticated raw body endpoint. No replacement was issued. Use the API token settings to retire a specific token.");
|
|
2042
2265
|
const createRes = await client.request({
|
|
2043
2266
|
method: "POST",
|
|
2044
2267
|
path: "/api/v2/data-app/api-tokens",
|
|
2045
2268
|
auth: "service",
|
|
2046
2269
|
body: {
|
|
2047
2270
|
dataAppName,
|
|
2048
|
-
name: `${dataAppName}-
|
|
2271
|
+
name: `${dataAppName}-rotated-${randomUUID()}`
|
|
2049
2272
|
}
|
|
2050
2273
|
});
|
|
2051
2274
|
if (createRes.error) {
|
|
@@ -2058,7 +2281,7 @@ function registerRotateApiToken(server, client) {
|
|
|
2058
2281
|
|
|
2059
2282
|
` + (key ? `**New Token:** \`${key}\`
|
|
2060
2283
|
|
|
2061
|
-
` : "") + "
|
|
2284
|
+
` : "") + "Existing tokens remain active. Update backend secrets, then retire the old token in API token settings."
|
|
2062
2285
|
);
|
|
2063
2286
|
} catch (err) {
|
|
2064
2287
|
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
@@ -2122,14 +2345,14 @@ function registerListDatasources(server, client) {
|
|
|
2122
2345
|
}
|
|
2123
2346
|
|
|
2124
2347
|
// src/tools/infrastructure/discover-datasource-schema.ts
|
|
2125
|
-
import { z as
|
|
2348
|
+
import { z as z10 } from "zod";
|
|
2126
2349
|
function registerDiscoverDatasourceSchema(server, client) {
|
|
2127
2350
|
const services = createServiceBundle(client);
|
|
2128
2351
|
server.tool(
|
|
2129
2352
|
"discover_datasource_schema",
|
|
2130
2353
|
"Inspect tables and columns in a datasource. Omit datasourceName to list available datasources.",
|
|
2131
2354
|
{
|
|
2132
|
-
datasourceName:
|
|
2355
|
+
datasourceName: z10.string().optional().describe(
|
|
2133
2356
|
"Name of the datasource to inspect. Omit to list available datasources."
|
|
2134
2357
|
)
|
|
2135
2358
|
},
|
|
@@ -2212,13 +2435,13 @@ Fix the connection or add a new database in the **Databrain UI**.`
|
|
|
2212
2435
|
}
|
|
2213
2436
|
|
|
2214
2437
|
// src/tools/infrastructure/list-datamarts.ts
|
|
2215
|
-
import { z as
|
|
2438
|
+
import { z as z11 } from "zod";
|
|
2216
2439
|
function registerListDatamarts(server, client) {
|
|
2217
2440
|
server.tool(
|
|
2218
2441
|
"list_datamarts",
|
|
2219
2442
|
"List datamarts with table counts and semantic layer scores.",
|
|
2220
2443
|
{
|
|
2221
|
-
dataAppName:
|
|
2444
|
+
dataAppName: z11.string().optional().describe("Filter by data app name")
|
|
2222
2445
|
},
|
|
2223
2446
|
READ_ONLY,
|
|
2224
2447
|
async (params) => {
|
|
@@ -2307,7 +2530,7 @@ function registerListWorkspaces(server, client) {
|
|
|
2307
2530
|
}
|
|
2308
2531
|
|
|
2309
2532
|
// src/tools/infrastructure/create-datamart.ts
|
|
2310
|
-
import { z as
|
|
2533
|
+
import { z as z12 } from "zod";
|
|
2311
2534
|
|
|
2312
2535
|
// src/tools/widgets/production-guards.ts
|
|
2313
2536
|
var APPLY_TO_PRODUCTION_CONFIRMATION = "APPLY_TO_PRODUCTION";
|
|
@@ -2325,29 +2548,29 @@ function genericProductionSafetyNote() {
|
|
|
2325
2548
|
}
|
|
2326
2549
|
|
|
2327
2550
|
// src/tools/infrastructure/create-datamart.ts
|
|
2328
|
-
var columnSchema =
|
|
2329
|
-
name:
|
|
2330
|
-
alias:
|
|
2331
|
-
label:
|
|
2332
|
-
isHide:
|
|
2333
|
-
isCustomColumn:
|
|
2334
|
-
sql:
|
|
2551
|
+
var columnSchema = z12.object({
|
|
2552
|
+
name: z12.string(),
|
|
2553
|
+
alias: z12.string().optional(),
|
|
2554
|
+
label: z12.string().optional(),
|
|
2555
|
+
isHide: z12.boolean().optional(),
|
|
2556
|
+
isCustomColumn: z12.boolean().optional(),
|
|
2557
|
+
sql: z12.string().optional()
|
|
2335
2558
|
});
|
|
2336
|
-
var tableSchema =
|
|
2337
|
-
name:
|
|
2338
|
-
schemaName:
|
|
2339
|
-
clientColumn:
|
|
2340
|
-
label:
|
|
2341
|
-
columns:
|
|
2559
|
+
var tableSchema = z12.object({
|
|
2560
|
+
name: z12.string(),
|
|
2561
|
+
schemaName: z12.string().optional(),
|
|
2562
|
+
clientColumn: z12.string().optional(),
|
|
2563
|
+
label: z12.string().optional(),
|
|
2564
|
+
columns: z12.array(columnSchema)
|
|
2342
2565
|
});
|
|
2343
|
-
var relationshipSchema =
|
|
2344
|
-
parentTableName:
|
|
2345
|
-
parentColumnName:
|
|
2346
|
-
childTableName:
|
|
2347
|
-
childColumnName:
|
|
2348
|
-
cardinality:
|
|
2349
|
-
join:
|
|
2350
|
-
relationshipName:
|
|
2566
|
+
var relationshipSchema = z12.object({
|
|
2567
|
+
parentTableName: z12.string(),
|
|
2568
|
+
parentColumnName: z12.string(),
|
|
2569
|
+
childTableName: z12.string(),
|
|
2570
|
+
childColumnName: z12.string(),
|
|
2571
|
+
cardinality: z12.enum(["OneToOne", "OneToMany", "ManyToOne", "ManyToMany"]),
|
|
2572
|
+
join: z12.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN"]),
|
|
2573
|
+
relationshipName: z12.string()
|
|
2351
2574
|
});
|
|
2352
2575
|
function datamartSuccessMessage(name, result) {
|
|
2353
2576
|
return `Datamart "${name}" created successfully (ID: ${result.id || result._id || "UNKNOWN"}).
|
|
@@ -2368,21 +2591,21 @@ function registerCreateDatamart(server, client) {
|
|
|
2368
2591
|
"create_datamart",
|
|
2369
2592
|
"Create a production datamart from selected tables and relationships. Defaults to dry-run; apply requires explicit confirmation.",
|
|
2370
2593
|
{
|
|
2371
|
-
mode:
|
|
2372
|
-
confirm:
|
|
2373
|
-
name:
|
|
2374
|
-
datasourceName:
|
|
2375
|
-
tableList:
|
|
2376
|
-
relationships:
|
|
2377
|
-
tenancySettings:
|
|
2378
|
-
tenancyLevel:
|
|
2379
|
-
clientColumnType:
|
|
2380
|
-
schemaName:
|
|
2381
|
-
tableName:
|
|
2382
|
-
tableClientNameColumn:
|
|
2383
|
-
tablePrimaryKeyColumn:
|
|
2594
|
+
mode: z12.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run returns the plan only; apply creates the datamart"),
|
|
2595
|
+
confirm: z12.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
|
|
2596
|
+
name: z12.string().describe("Unique datamart name"),
|
|
2597
|
+
datasourceName: z12.string().optional().describe("Name of the datasource to use. Omit to list available datasources."),
|
|
2598
|
+
tableList: z12.array(tableSchema).optional().describe("Array of tables with their columns"),
|
|
2599
|
+
relationships: z12.array(relationshipSchema).optional().describe("Join definitions between tables"),
|
|
2600
|
+
tenancySettings: z12.object({
|
|
2601
|
+
tenancyLevel: z12.enum(["TABLE", "DATABASE"]).optional(),
|
|
2602
|
+
clientColumnType: z12.string().optional(),
|
|
2603
|
+
schemaName: z12.string().optional(),
|
|
2604
|
+
tableName: z12.string().optional(),
|
|
2605
|
+
tableClientNameColumn: z12.string().optional(),
|
|
2606
|
+
tablePrimaryKeyColumn: z12.string().optional()
|
|
2384
2607
|
}).optional().describe("Multi-tenant configuration"),
|
|
2385
|
-
body:
|
|
2608
|
+
body: z12.record(z12.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/datamarts")
|
|
2386
2609
|
},
|
|
2387
2610
|
WRITE_CREATE,
|
|
2388
2611
|
async (params) => {
|
|
@@ -2477,14 +2700,14 @@ function registerCreateDatamart(server, client) {
|
|
|
2477
2700
|
}
|
|
2478
2701
|
|
|
2479
2702
|
// src/tools/infrastructure/sync-datasource.ts
|
|
2480
|
-
import { z as
|
|
2703
|
+
import { z as z13 } from "zod";
|
|
2481
2704
|
function registerSyncDatasource(server, client) {
|
|
2482
2705
|
const datasourcesApi = new DatasourcesApi(client);
|
|
2483
2706
|
server.tool(
|
|
2484
2707
|
"sync_datasource",
|
|
2485
2708
|
"Trigger a datasource schema sync to refresh table and column metadata. If datasourceName is omitted and there are multiple datasources, returns the list so the user can choose.",
|
|
2486
2709
|
{
|
|
2487
|
-
datasourceName:
|
|
2710
|
+
datasourceName: z13.string().optional().describe("Name of the datasource to sync. Omit to list available datasources.")
|
|
2488
2711
|
},
|
|
2489
2712
|
WRITE_IDEMPOTENT,
|
|
2490
2713
|
async (params) => {
|
|
@@ -2533,7 +2756,7 @@ The schema metadata will be refreshed. You can now use \`discover_datasource_sch
|
|
|
2533
2756
|
}
|
|
2534
2757
|
|
|
2535
2758
|
// src/tools/embed/create-embed.ts
|
|
2536
|
-
import { z as
|
|
2759
|
+
import { z as z14 } from "zod";
|
|
2537
2760
|
var DEFAULT_ACCESS_SETTINGS2 = {
|
|
2538
2761
|
isAllowMetricCreation: false,
|
|
2539
2762
|
isAllowMetricDeletion: false,
|
|
@@ -2546,18 +2769,18 @@ var DEFAULT_ACCESS_SETTINGS2 = {
|
|
|
2546
2769
|
isAllowCreateDashboardView: false,
|
|
2547
2770
|
metricCreationMode: "DRAG_DROP"
|
|
2548
2771
|
};
|
|
2549
|
-
var accessSettingsSchema =
|
|
2550
|
-
datamartName:
|
|
2551
|
-
isAllowMetricCreation:
|
|
2552
|
-
metricCreationMode:
|
|
2553
|
-
isAllowMetricDeletion:
|
|
2554
|
-
isAllowMetricUpdate:
|
|
2555
|
-
isAllowMetricLayoutChange:
|
|
2556
|
-
isAllowManageMetrics:
|
|
2557
|
-
isAllowUnderlyingData:
|
|
2558
|
-
isAllowAiPilot:
|
|
2559
|
-
isAllowEmailReports:
|
|
2560
|
-
isAllowCreateDashboardView:
|
|
2772
|
+
var accessSettingsSchema = z14.object({
|
|
2773
|
+
datamartName: z14.string().describe("Name of the datamart (required)"),
|
|
2774
|
+
isAllowMetricCreation: z14.boolean().optional().describe("Allow end-users to create metrics"),
|
|
2775
|
+
metricCreationMode: z14.enum(["DRAG_DROP", "CHAT"]).optional().describe("Metric creation mode"),
|
|
2776
|
+
isAllowMetricDeletion: z14.boolean().optional().describe("Allow metric deletion"),
|
|
2777
|
+
isAllowMetricUpdate: z14.boolean().optional().describe("Allow metric editing"),
|
|
2778
|
+
isAllowMetricLayoutChange: z14.boolean().optional().describe("Allow layout changes"),
|
|
2779
|
+
isAllowManageMetrics: z14.boolean().optional().describe("Show manage metrics UI"),
|
|
2780
|
+
isAllowUnderlyingData: z14.boolean().optional().describe("Allow viewing underlying data"),
|
|
2781
|
+
isAllowAiPilot: z14.boolean().optional().describe("Enable AI pilot features"),
|
|
2782
|
+
isAllowEmailReports: z14.boolean().optional().describe("Allow email report scheduling"),
|
|
2783
|
+
isAllowCreateDashboardView: z14.boolean().optional().describe("Allow creating dashboard views")
|
|
2561
2784
|
}).describe("Embed access settings. Only datamartName is required \u2014 all flags default to false.");
|
|
2562
2785
|
function registerCreateEmbed(server, client) {
|
|
2563
2786
|
const services = createServiceBundle(client);
|
|
@@ -2565,28 +2788,37 @@ function registerCreateEmbed(server, client) {
|
|
|
2565
2788
|
"create_embed",
|
|
2566
2789
|
"Create an embed. Provide dashboardId, workspaceName, accessSettings.datamartName. Add clientId for multi-tenant per-client dashboards. All isAllow* flags default to false.",
|
|
2567
2790
|
{
|
|
2568
|
-
body:
|
|
2791
|
+
body: z14.record(z14.unknown()).optional().describe(
|
|
2569
2792
|
"Raw API body passthrough. If provided, all other fields are ignored."
|
|
2570
2793
|
),
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2794
|
+
dataAppName: z14.string().optional().describe("Data app name; required when the workspace has more than one"),
|
|
2795
|
+
dashboardId: z14.string().optional().describe("Existing dashboard UUID or externalDashboardId slug for a standard embed; a new unique slug for a client dashboard copy"),
|
|
2796
|
+
workspaceName: z14.string().optional().describe("Workspace name. Omit to auto-resolve if only one exists."),
|
|
2797
|
+
embedType: z14.enum(["dashboard", "metric"]).optional().describe("Embed type"),
|
|
2798
|
+
metricId: z14.string().optional().describe("Required for metric embed type"),
|
|
2799
|
+
name: z14.string().optional().describe("Human-readable embed config name"),
|
|
2576
2800
|
accessSettings: accessSettingsSchema.optional(),
|
|
2577
2801
|
// Multi-tenant (dashboard embed) params
|
|
2578
|
-
clientId:
|
|
2802
|
+
clientId: z14.string().optional().describe(
|
|
2579
2803
|
"Client identifier for multi-tenant isolation. When provided, creates a dashboard embed (each client gets their own dashboard copy)."
|
|
2580
2804
|
),
|
|
2581
|
-
templateDashboardId:
|
|
2582
|
-
isImportMetrics:
|
|
2583
|
-
metricImportMode:
|
|
2584
|
-
isRenameDashboard:
|
|
2805
|
+
templateDashboardId: z14.string().optional().describe("Template dashboard UUID or externalDashboardId slug to clone metrics from"),
|
|
2806
|
+
isImportMetrics: z14.boolean().optional().describe("Import metrics from template dashboard"),
|
|
2807
|
+
metricImportMode: z14.enum(["GALLERY", "METRIC"]).optional().describe("GALLERY imports dashboard gallery metrics; METRIC imports selected metrics"),
|
|
2808
|
+
isRenameDashboard: z14.boolean().optional().describe("Rename the dashboard for this client")
|
|
2585
2809
|
},
|
|
2586
2810
|
WRITE_CREATE,
|
|
2587
2811
|
async (params) => {
|
|
2588
2812
|
try {
|
|
2589
2813
|
const isMultiTenant = !!params.clientId;
|
|
2814
|
+
const dashboardSlug = async (value, workspace) => {
|
|
2815
|
+
if (typeof value !== "string" || !z14.string().uuid().safeParse(value).success) return value;
|
|
2816
|
+
if (typeof workspace !== "string" || !workspace) throw new Error("workspaceName is required to resolve a dashboard UUID.");
|
|
2817
|
+
const boards = await services.dashboardsApi.listDashboardsForWorkspace(workspace);
|
|
2818
|
+
const board = boards.find((item) => item.id === value || item.dashboardId === value);
|
|
2819
|
+
if (!board?.externalDashboardId) throw new Error("Dashboard not found in this workspace. Use list_dashboards to choose one.");
|
|
2820
|
+
return board.externalDashboardId;
|
|
2821
|
+
};
|
|
2590
2822
|
let resolvedWorkspaceName = params.workspaceName;
|
|
2591
2823
|
if (!params.body && isMultiTenant && !resolvedWorkspaceName) {
|
|
2592
2824
|
const workspaces = await services.workspacesApi.listWorkspaces();
|
|
@@ -2604,6 +2836,7 @@ function registerCreateEmbed(server, client) {
|
|
|
2604
2836
|
if (isMultiTenant) {
|
|
2605
2837
|
const body2 = params.body ? { ...params.body } : {
|
|
2606
2838
|
dashboardId: params.dashboardId,
|
|
2839
|
+
dataAppName: params.dataAppName,
|
|
2607
2840
|
workspaceName: resolvedWorkspaceName,
|
|
2608
2841
|
clientId: params.clientId,
|
|
2609
2842
|
name: params.name,
|
|
@@ -2615,10 +2848,13 @@ function registerCreateEmbed(server, client) {
|
|
|
2615
2848
|
};
|
|
2616
2849
|
const callerAccess = body2.accessSettings && typeof body2.accessSettings === "object" ? body2.accessSettings : {};
|
|
2617
2850
|
body2.accessSettings = { ...DEFAULT_ACCESS_SETTINGS2, ...callerAccess };
|
|
2851
|
+
if (body2.templateDashboardId) body2.templateDashboardId = await dashboardSlug(body2.templateDashboardId, body2.workspaceName);
|
|
2852
|
+
const { dataAppName, ...payload } = body2;
|
|
2618
2853
|
const result2 = await client.request({
|
|
2619
2854
|
method: "POST",
|
|
2620
2855
|
path: "/api/v2/data-app/dashboard-embeds",
|
|
2621
|
-
|
|
2856
|
+
query: typeof dataAppName === "string" ? { dataAppName } : void 0,
|
|
2857
|
+
body: payload,
|
|
2622
2858
|
auth: "api",
|
|
2623
2859
|
invalidateCache: ["list_embeds"]
|
|
2624
2860
|
});
|
|
@@ -2639,6 +2875,7 @@ Next: use \`generate_guest_token\` with the clientId and \`generate_embed_code\`
|
|
|
2639
2875
|
}
|
|
2640
2876
|
const body = params.body ? { ...params.body } : {
|
|
2641
2877
|
dashboardId: params.dashboardId,
|
|
2878
|
+
dataAppName: params.dataAppName,
|
|
2642
2879
|
workspaceName: params.workspaceName,
|
|
2643
2880
|
embedType: params.embedType || "dashboard",
|
|
2644
2881
|
metricId: params.metricId,
|
|
@@ -2659,6 +2896,7 @@ Next: use \`generate_guest_token\` with the clientId and \`generate_embed_code\`
|
|
|
2659
2896
|
if (!datamartName || typeof datamartName !== "string") {
|
|
2660
2897
|
return toolError("create_embed requires `accessSettings.datamartName`.");
|
|
2661
2898
|
}
|
|
2899
|
+
body.dashboardId = await dashboardSlug(body.dashboardId, body.workspaceName);
|
|
2662
2900
|
const result = await services.embedService.createEmbedStrict(body);
|
|
2663
2901
|
if (result.error) {
|
|
2664
2902
|
return toolError(`Embed creation failed: ${stringify(result.error)}`);
|
|
@@ -2683,14 +2921,14 @@ Next: run \`get_embed_details\` to verify the embed configuration.`
|
|
|
2683
2921
|
}
|
|
2684
2922
|
|
|
2685
2923
|
// src/tools/embed/list-embeds.ts
|
|
2686
|
-
import { z as
|
|
2924
|
+
import { z as z15 } from "zod";
|
|
2687
2925
|
function registerListEmbeds(server, client) {
|
|
2688
2926
|
const services = createServiceBundle(client);
|
|
2689
2927
|
server.tool(
|
|
2690
2928
|
"list_embeds",
|
|
2691
2929
|
"List embed configurations. Requires an API token (use create_api_token first). Use `list_tenants` for authoritative client discovery from the tenancy database.",
|
|
2692
2930
|
{
|
|
2693
|
-
dataAppName:
|
|
2931
|
+
dataAppName: z15.string().optional().describe("Filter by data app name")
|
|
2694
2932
|
},
|
|
2695
2933
|
READ_ONLY,
|
|
2696
2934
|
async (params) => {
|
|
@@ -2741,19 +2979,20 @@ function registerListEmbeds(server, client) {
|
|
|
2741
2979
|
}
|
|
2742
2980
|
|
|
2743
2981
|
// src/tools/embed/get-embed-details.ts
|
|
2744
|
-
import { z as
|
|
2982
|
+
import { z as z16 } from "zod";
|
|
2745
2983
|
function registerGetEmbedDetails(server, client) {
|
|
2746
2984
|
const services = createServiceBundle(client);
|
|
2747
2985
|
server.tool(
|
|
2748
2986
|
"get_embed_details",
|
|
2749
2987
|
"Get full embed configuration including access settings and theme.",
|
|
2750
2988
|
{
|
|
2751
|
-
embedId:
|
|
2989
|
+
embedId: z16.string().describe("ID of the embed"),
|
|
2990
|
+
dataAppName: z16.string().trim().min(1).optional().describe("Data app when this embed ID belongs to several apps")
|
|
2752
2991
|
},
|
|
2753
2992
|
READ_ONLY,
|
|
2754
2993
|
async (params) => {
|
|
2755
2994
|
try {
|
|
2756
|
-
const embed = await services.embedService.resolveEmbedStrict(params.embedId);
|
|
2995
|
+
const embed = await services.embedService.resolveEmbedStrict(params.embedId, params.dataAppName);
|
|
2757
2996
|
return toolSuccess(JSON.stringify({ embedId: params.embedId, ...embed }));
|
|
2758
2997
|
} catch (err) {
|
|
2759
2998
|
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
@@ -2764,7 +3003,7 @@ function registerGetEmbedDetails(server, client) {
|
|
|
2764
3003
|
}
|
|
2765
3004
|
|
|
2766
3005
|
// src/tools/embed/update-embed.ts
|
|
2767
|
-
import { z as
|
|
3006
|
+
import { z as z17 } from "zod";
|
|
2768
3007
|
|
|
2769
3008
|
// src/tools/embed/helpers.ts
|
|
2770
3009
|
var GUEST_TOKEN_REMINDER = "\n\n\u26A0\uFE0F **Guest tokens must be regenerated** for these changes to take effect. Existing guest tokens still carry the old settings. Call `generate_guest_token` to create a new token with the updated configuration.";
|
|
@@ -2802,15 +3041,16 @@ function registerUpdateEmbed(server, client) {
|
|
|
2802
3041
|
"update_embed",
|
|
2803
3042
|
"Update embed access settings, permissions, theme, or name.",
|
|
2804
3043
|
{
|
|
2805
|
-
embedId:
|
|
2806
|
-
|
|
3044
|
+
embedId: z17.string().describe("ID of the embed to update"),
|
|
3045
|
+
dataAppName: z17.string().trim().min(1).optional().describe("Data app when this embed ID belongs to several apps"),
|
|
3046
|
+
body: z17.record(z17.unknown()).optional().describe(
|
|
2807
3047
|
"Raw API body for PUT /api/v2/data-app/embeds. If provided, it is sent directly (passthrough mode)."
|
|
2808
3048
|
),
|
|
2809
|
-
name:
|
|
2810
|
-
accessSettings:
|
|
3049
|
+
name: z17.string().optional().describe("New name for the embed"),
|
|
3050
|
+
accessSettings: z17.record(z17.unknown()).optional().describe(
|
|
2811
3051
|
"Partial update of access settings. See `get_embed_details` for current settings."
|
|
2812
3052
|
),
|
|
2813
|
-
theme:
|
|
3053
|
+
theme: z17.record(z17.unknown()).optional().describe(
|
|
2814
3054
|
"Theme configuration object. See databrain://theme-reference for the full schema."
|
|
2815
3055
|
)
|
|
2816
3056
|
},
|
|
@@ -2820,6 +3060,7 @@ function registerUpdateEmbed(server, client) {
|
|
|
2820
3060
|
if (params.body) {
|
|
2821
3061
|
const rawBody = { ...params.body };
|
|
2822
3062
|
if (!rawBody.embedId) rawBody.embedId = params.embedId;
|
|
3063
|
+
if (params.dataAppName) rawBody.dataAppName = params.dataAppName;
|
|
2823
3064
|
const result2 = await services.embedsApi.updateEmbed(rawBody);
|
|
2824
3065
|
if (result2.error) {
|
|
2825
3066
|
return toolError(`Embed update failed: ${stringify(result2.error)}`);
|
|
@@ -2834,7 +3075,8 @@ function registerUpdateEmbed(server, client) {
|
|
|
2834
3075
|
if (params.theme) patch.theme = params.theme;
|
|
2835
3076
|
const result = await services.embedService.updateEmbed(
|
|
2836
3077
|
params.embedId,
|
|
2837
|
-
patch
|
|
3078
|
+
patch,
|
|
3079
|
+
params.dataAppName
|
|
2838
3080
|
);
|
|
2839
3081
|
if (result.error) {
|
|
2840
3082
|
return toolError(`Embed update failed: ${stringify(result.error)}`);
|
|
@@ -2856,7 +3098,7 @@ function registerUpdateEmbed(server, client) {
|
|
|
2856
3098
|
}
|
|
2857
3099
|
|
|
2858
3100
|
// src/tools/embed/customize-theme.ts
|
|
2859
|
-
import { z as
|
|
3101
|
+
import { z as z18 } from "zod";
|
|
2860
3102
|
var THEME_PRESETS = {
|
|
2861
3103
|
light: {
|
|
2862
3104
|
general: {
|
|
@@ -2990,7 +3232,7 @@ var ACCESS_PRESETS = {
|
|
|
2990
3232
|
metricCreationMode: "CHAT"
|
|
2991
3233
|
}
|
|
2992
3234
|
};
|
|
2993
|
-
var fontFamilyEnum =
|
|
3235
|
+
var fontFamilyEnum = z18.enum([
|
|
2994
3236
|
"Inter",
|
|
2995
3237
|
"Roboto",
|
|
2996
3238
|
"Open Sans",
|
|
@@ -3002,17 +3244,17 @@ var fontFamilyEnum = z16.enum([
|
|
|
3002
3244
|
"Nunito",
|
|
3003
3245
|
"system-ui"
|
|
3004
3246
|
]).describe("Font family");
|
|
3005
|
-
var tooltipFontSchema =
|
|
3006
|
-
fontFamily:
|
|
3007
|
-
fontSize:
|
|
3008
|
-
fontWeight:
|
|
3009
|
-
fontColor:
|
|
3247
|
+
var tooltipFontSchema = z18.object({
|
|
3248
|
+
fontFamily: z18.string().optional(),
|
|
3249
|
+
fontSize: z18.number().optional(),
|
|
3250
|
+
fontWeight: z18.number().optional(),
|
|
3251
|
+
fontColor: z18.string().optional()
|
|
3010
3252
|
}).optional();
|
|
3011
|
-
var fontConfigInputSchema =
|
|
3012
|
-
fontSize:
|
|
3013
|
-
fontFamily:
|
|
3014
|
-
fontWeight:
|
|
3015
|
-
color:
|
|
3253
|
+
var fontConfigInputSchema = z18.object({
|
|
3254
|
+
fontSize: z18.number().optional(),
|
|
3255
|
+
fontFamily: z18.string().optional(),
|
|
3256
|
+
fontWeight: z18.number().optional(),
|
|
3257
|
+
color: z18.string().optional()
|
|
3016
3258
|
}).optional();
|
|
3017
3259
|
function mapToFrontendFontConfig(input) {
|
|
3018
3260
|
if (!input) return void 0;
|
|
@@ -3023,23 +3265,23 @@ function mapToFrontendFontConfig(input) {
|
|
|
3023
3265
|
...input.color && { color: input.color }
|
|
3024
3266
|
};
|
|
3025
3267
|
}
|
|
3026
|
-
var axisSchema =
|
|
3027
|
-
hideAxisLines:
|
|
3028
|
-
hideSplitLines:
|
|
3029
|
-
hideAxisLabels:
|
|
3030
|
-
hideAxisTicks:
|
|
3031
|
-
axisName:
|
|
3032
|
-
axisNameOffset:
|
|
3033
|
-
axisLabelMargin:
|
|
3034
|
-
fontSize:
|
|
3035
|
-
fontFamily:
|
|
3036
|
-
fontWeight:
|
|
3037
|
-
color:
|
|
3038
|
-
axisColor:
|
|
3039
|
-
axisNameFontConfig:
|
|
3040
|
-
fontFamily:
|
|
3041
|
-
fontSize:
|
|
3042
|
-
fontWeight:
|
|
3268
|
+
var axisSchema = z18.object({
|
|
3269
|
+
hideAxisLines: z18.boolean().optional().describe("Hide axis lines"),
|
|
3270
|
+
hideSplitLines: z18.boolean().optional().describe("Hide grid/split lines"),
|
|
3271
|
+
hideAxisLabels: z18.boolean().optional().describe("Hide axis labels"),
|
|
3272
|
+
hideAxisTicks: z18.boolean().optional().describe("Hide axis tick marks"),
|
|
3273
|
+
axisName: z18.string().optional().describe("Axis title text"),
|
|
3274
|
+
axisNameOffset: z18.number().optional().describe("Axis title offset from axis"),
|
|
3275
|
+
axisLabelMargin: z18.number().optional().describe("Margin between axis labels and chart area in pixels"),
|
|
3276
|
+
fontSize: z18.number().optional().describe("Axis label font size"),
|
|
3277
|
+
fontFamily: z18.string().optional().describe("Axis label font family"),
|
|
3278
|
+
fontWeight: z18.number().optional().describe("Axis label font weight"),
|
|
3279
|
+
color: z18.string().optional().describe("Axis label color (hex)"),
|
|
3280
|
+
axisColor: z18.string().optional().describe("Axis line color (hex)"),
|
|
3281
|
+
axisNameFontConfig: z18.object({
|
|
3282
|
+
fontFamily: z18.string().optional(),
|
|
3283
|
+
fontSize: z18.number().optional(),
|
|
3284
|
+
fontWeight: z18.number().optional()
|
|
3043
3285
|
}).optional().describe("Axis title font configuration")
|
|
3044
3286
|
}).optional();
|
|
3045
3287
|
function buildAdminTheme(p, presetBase) {
|
|
@@ -3195,94 +3437,95 @@ function registerCustomizeTheme(server, client) {
|
|
|
3195
3437
|
"customize_embed_theme",
|
|
3196
3438
|
`Theme an embed: presets (${Object.keys(THEME_PRESETS).join("/")}), colors, fonts, cards, chart appearance, responsive layout, or access presets (${Object.keys(ACCESS_PRESETS).join("/")}).`,
|
|
3197
3439
|
{
|
|
3198
|
-
embedId:
|
|
3199
|
-
|
|
3440
|
+
embedId: z18.string().describe("ID of the embed to theme"),
|
|
3441
|
+
dataAppName: z18.string().trim().min(1).optional().describe("Data app when this embed ID belongs to several apps"),
|
|
3442
|
+
body: z18.record(z18.unknown()).optional().describe(
|
|
3200
3443
|
"Raw API body for PUT /api/v2/data-app/embeds. If provided, sent directly and all convenience fields are ignored."
|
|
3201
3444
|
),
|
|
3202
3445
|
// --- Presets ---
|
|
3203
|
-
themePreset:
|
|
3204
|
-
accessPreset:
|
|
3446
|
+
themePreset: z18.enum(["light", "dark", "corporate", "minimal"]).optional().describe("Apply a theme preset as the base. Granular params below override preset values."),
|
|
3447
|
+
accessPreset: z18.enum(["view-only", "power-user", "export-only", "ai-enabled"]).optional().describe("Apply an access settings preset"),
|
|
3205
3448
|
// --- General ---
|
|
3206
3449
|
fontFamily: fontFamilyEnum.optional().describe("Global font family"),
|
|
3207
|
-
datePickerFormat:
|
|
3450
|
+
datePickerFormat: z18.string().optional().describe("Date picker display format"),
|
|
3208
3451
|
// --- Colors ---
|
|
3209
|
-
primaryColor:
|
|
3210
|
-
backgroundColor:
|
|
3211
|
-
textColor:
|
|
3212
|
-
ctaColor:
|
|
3213
|
-
ctaTextColor:
|
|
3452
|
+
primaryColor: z18.string().optional().describe("Primary accent color (hex)"),
|
|
3453
|
+
backgroundColor: z18.string().optional().describe("Overall background color (hex)"),
|
|
3454
|
+
textColor: z18.string().optional().describe("Default text color (hex)"),
|
|
3455
|
+
ctaColor: z18.string().optional().describe("Call-to-action button color (hex)"),
|
|
3456
|
+
ctaTextColor: z18.string().optional().describe("CTA button text color (hex)"),
|
|
3214
3457
|
// --- Dashboard ---
|
|
3215
|
-
dashboardBackgroundColor:
|
|
3216
|
-
selectBoxSize:
|
|
3217
|
-
selectBoxVariant:
|
|
3218
|
-
selectBoxBorderRadius:
|
|
3219
|
-
selectBoxTextColor:
|
|
3220
|
-
metricCardColor:
|
|
3458
|
+
dashboardBackgroundColor: z18.string().optional().describe("Dashboard container background (hex)"),
|
|
3459
|
+
selectBoxSize: z18.enum(["small", "medium", "large"]).optional().describe("Filter select box size"),
|
|
3460
|
+
selectBoxVariant: z18.enum(["floating", "outlined", "standard"]).optional().describe("Filter select box style"),
|
|
3461
|
+
selectBoxBorderRadius: z18.string().optional().describe('Select box border radius (e.g. "20px")'),
|
|
3462
|
+
selectBoxTextColor: z18.string().optional().describe("Select box text color (hex)"),
|
|
3463
|
+
metricCardColor: z18.string().optional().describe("Metric card background color (hex)"),
|
|
3221
3464
|
// --- Card Title ---
|
|
3222
|
-
cardTitleFontSize:
|
|
3223
|
-
cardTitleFontWeight:
|
|
3224
|
-
cardTitleColor:
|
|
3465
|
+
cardTitleFontSize: z18.string().optional().describe('Card title font size (e.g. "16px")'),
|
|
3466
|
+
cardTitleFontWeight: z18.string().optional().describe('Card title font weight (e.g. "600")'),
|
|
3467
|
+
cardTitleColor: z18.string().optional().describe("Card title text color (hex)"),
|
|
3225
3468
|
// --- Card Description ---
|
|
3226
|
-
cardDescriptionFontSize:
|
|
3227
|
-
cardDescriptionFontWeight:
|
|
3228
|
-
cardDescriptionColor:
|
|
3469
|
+
cardDescriptionFontSize: z18.string().optional().describe("Card description font size"),
|
|
3470
|
+
cardDescriptionFontWeight: z18.string().optional().describe("Card description font weight"),
|
|
3471
|
+
cardDescriptionColor: z18.string().optional().describe("Card description text color (hex)"),
|
|
3229
3472
|
// --- Card Customization ---
|
|
3230
|
-
cardPadding:
|
|
3231
|
-
cardBorderRadius:
|
|
3232
|
-
cardShadow:
|
|
3233
|
-
disableShadowOnHover:
|
|
3234
|
-
disableCardStroke:
|
|
3235
|
-
cardStrokeColor:
|
|
3473
|
+
cardPadding: z18.string().optional().describe("Card internal padding"),
|
|
3474
|
+
cardBorderRadius: z18.string().optional().describe("Card border radius"),
|
|
3475
|
+
cardShadow: z18.string().optional().describe("Card box-shadow CSS value"),
|
|
3476
|
+
disableShadowOnHover: z18.boolean().optional().describe("Disable shadow on card hover"),
|
|
3477
|
+
disableCardStroke: z18.boolean().optional().describe("Remove card border stroke"),
|
|
3478
|
+
cardStrokeColor: z18.string().optional().describe("Card border stroke color (hex)"),
|
|
3236
3479
|
// --- Chart Colors (admin theme level) ---
|
|
3237
|
-
chartFontFamily:
|
|
3238
|
-
chartFontColor:
|
|
3239
|
-
chartColors:
|
|
3240
|
-
chartPaletteName:
|
|
3241
|
-
chartPaletteColors:
|
|
3480
|
+
chartFontFamily: z18.string().optional().describe("Font family for chart text"),
|
|
3481
|
+
chartFontColor: z18.string().optional().describe("Font color for chart text (hex)"),
|
|
3482
|
+
chartColors: z18.array(z18.string()).optional().describe("Array of chart series colors (hex)"),
|
|
3483
|
+
chartPaletteName: z18.string().optional().describe("Name for a custom palette"),
|
|
3484
|
+
chartPaletteColors: z18.array(z18.string()).optional().describe("9 colors for a custom palette"),
|
|
3242
3485
|
// --- Chart Tooltip (admin theme level) ---
|
|
3243
3486
|
tooltipHeader: tooltipFontSchema.describe("Tooltip header font config"),
|
|
3244
3487
|
tooltipLabel: tooltipFontSchema.describe("Tooltip label font config"),
|
|
3245
3488
|
tooltipValue: tooltipFontSchema.describe("Tooltip value font config"),
|
|
3246
3489
|
// --- Component Theme ---
|
|
3247
|
-
buttonPrimaryColor:
|
|
3248
|
-
buttonPrimaryTextColor:
|
|
3249
|
-
buttonSecondaryColor:
|
|
3250
|
-
buttonSecondaryTextColor:
|
|
3251
|
-
checkboxCheckedColor:
|
|
3252
|
-
checkboxUncheckedColor:
|
|
3253
|
-
switchEnabledColor:
|
|
3254
|
-
switchDisabledColor:
|
|
3255
|
-
breadcrumbFontFamily:
|
|
3256
|
-
breadcrumbFontColor:
|
|
3257
|
-
breadcrumbActiveColor:
|
|
3258
|
-
filterBadgeColor:
|
|
3259
|
-
filterBadgeTextColor:
|
|
3260
|
-
datePickerColor:
|
|
3490
|
+
buttonPrimaryColor: z18.string().optional().describe("Primary button background (hex)"),
|
|
3491
|
+
buttonPrimaryTextColor: z18.string().optional().describe("Primary button text (hex)"),
|
|
3492
|
+
buttonSecondaryColor: z18.string().optional().describe("Secondary button background (hex)"),
|
|
3493
|
+
buttonSecondaryTextColor: z18.string().optional().describe("Secondary button text (hex)"),
|
|
3494
|
+
checkboxCheckedColor: z18.string().optional().describe("Checked checkbox color (hex)"),
|
|
3495
|
+
checkboxUncheckedColor: z18.string().optional().describe("Unchecked checkbox color (hex)"),
|
|
3496
|
+
switchEnabledColor: z18.string().optional().describe("Enabled switch color (hex)"),
|
|
3497
|
+
switchDisabledColor: z18.string().optional().describe("Disabled switch color (hex)"),
|
|
3498
|
+
breadcrumbFontFamily: z18.string().optional().describe("Drill breadcrumb font family"),
|
|
3499
|
+
breadcrumbFontColor: z18.string().optional().describe("Drill breadcrumb font color (hex)"),
|
|
3500
|
+
breadcrumbActiveColor: z18.string().optional().describe("Active breadcrumb color (hex)"),
|
|
3501
|
+
filterBadgeColor: z18.string().optional().describe("Multi-select filter badge color (hex)"),
|
|
3502
|
+
filterBadgeTextColor: z18.string().optional().describe("Filter badge text color (hex)"),
|
|
3503
|
+
datePickerColor: z18.string().optional().describe("Date picker accent color (hex)"),
|
|
3261
3504
|
// --- Responsive Breakpoints ---
|
|
3262
|
-
breakpointXl:
|
|
3263
|
-
breakpointLg:
|
|
3264
|
-
breakpointMd:
|
|
3265
|
-
breakpointSm:
|
|
3266
|
-
breakpointXs:
|
|
3505
|
+
breakpointXl: z18.number().optional().describe("XL breakpoint width in px"),
|
|
3506
|
+
breakpointLg: z18.number().optional().describe("LG breakpoint width in px"),
|
|
3507
|
+
breakpointMd: z18.number().optional().describe("MD breakpoint width in px"),
|
|
3508
|
+
breakpointSm: z18.number().optional().describe("SM breakpoint width in px"),
|
|
3509
|
+
breakpointXs: z18.number().optional().describe("XS breakpoint width in px"),
|
|
3267
3510
|
// --- Metric Layout Columns ---
|
|
3268
|
-
layoutColsXl:
|
|
3269
|
-
layoutColsLg:
|
|
3270
|
-
layoutColsMd:
|
|
3271
|
-
layoutColsSm:
|
|
3272
|
-
layoutColsXs:
|
|
3273
|
-
layoutColsXxs:
|
|
3511
|
+
layoutColsXl: z18.number().optional().describe("Grid columns at XL breakpoint"),
|
|
3512
|
+
layoutColsLg: z18.number().optional().describe("Grid columns at LG breakpoint"),
|
|
3513
|
+
layoutColsMd: z18.number().optional().describe("Grid columns at MD breakpoint"),
|
|
3514
|
+
layoutColsSm: z18.number().optional().describe("Grid columns at SM breakpoint"),
|
|
3515
|
+
layoutColsXs: z18.number().optional().describe("Grid columns at XS breakpoint"),
|
|
3516
|
+
layoutColsXxs: z18.number().optional().describe("Grid columns at XXS breakpoint"),
|
|
3274
3517
|
// --- Chart Appearance (detailed) ---
|
|
3275
3518
|
tooltipHeaderStyle: fontConfigInputSchema.describe("Tooltip header: fontSize, fontFamily, fontWeight, color"),
|
|
3276
3519
|
tooltipLabelStyle: fontConfigInputSchema.describe("Tooltip label: fontSize, fontFamily, fontWeight, color"),
|
|
3277
3520
|
tooltipValueStyle: fontConfigInputSchema.describe("Tooltip value: fontSize, fontFamily, fontWeight, color"),
|
|
3278
|
-
labelPosition:
|
|
3279
|
-
radialLabelPosition:
|
|
3280
|
-
marginTop:
|
|
3281
|
-
marginLeft:
|
|
3282
|
-
marginRight:
|
|
3283
|
-
marginBottom:
|
|
3284
|
-
legendShow:
|
|
3285
|
-
legendPosition:
|
|
3521
|
+
labelPosition: z18.enum(["hidden", "top", "left", "right", "bottom", "inside"]).optional().describe("Data label position on charts"),
|
|
3522
|
+
radialLabelPosition: z18.enum(["outside", "inside"]).optional().describe("Label position for radial/pie charts"),
|
|
3523
|
+
marginTop: z18.number().optional().describe("Chart top margin in pixels"),
|
|
3524
|
+
marginLeft: z18.number().optional().describe("Chart left margin in pixels"),
|
|
3525
|
+
marginRight: z18.number().optional().describe("Chart right margin in pixels"),
|
|
3526
|
+
marginBottom: z18.number().optional().describe("Chart bottom margin in pixels"),
|
|
3527
|
+
legendShow: z18.boolean().optional().describe("Show or hide chart legend"),
|
|
3528
|
+
legendPosition: z18.enum([
|
|
3286
3529
|
"top-left",
|
|
3287
3530
|
"top-center",
|
|
3288
3531
|
"top-right",
|
|
@@ -3292,27 +3535,28 @@ function registerCustomizeTheme(server, client) {
|
|
|
3292
3535
|
"bottom-center",
|
|
3293
3536
|
"bottom-right"
|
|
3294
3537
|
]).optional().describe("Legend position"),
|
|
3295
|
-
legendLayout:
|
|
3296
|
-
legendShape:
|
|
3297
|
-
legendFontSize:
|
|
3298
|
-
legendFontWeight:
|
|
3299
|
-
legendFontFamily:
|
|
3300
|
-
legendColor:
|
|
3301
|
-
legendTruncateLength:
|
|
3302
|
-
disableLegendScrolling:
|
|
3303
|
-
enableVariableLegendPosition:
|
|
3304
|
-
legendTop:
|
|
3305
|
-
legendLeft:
|
|
3538
|
+
legendLayout: z18.enum(["horizontal", "vertical"]).optional().describe("Legend layout direction"),
|
|
3539
|
+
legendShape: z18.enum(["circle", "rect", "roundRect", "triangle", "diamond", "arrow", "none"]).optional().describe("Legend icon shape"),
|
|
3540
|
+
legendFontSize: z18.number().optional().describe("Legend font size"),
|
|
3541
|
+
legendFontWeight: z18.number().optional().describe("Legend font weight"),
|
|
3542
|
+
legendFontFamily: z18.string().optional().describe("Legend font family"),
|
|
3543
|
+
legendColor: z18.string().optional().describe("Legend text color (hex)"),
|
|
3544
|
+
legendTruncateLength: z18.number().optional().describe("Max characters before truncating legend"),
|
|
3545
|
+
disableLegendScrolling: z18.boolean().optional().describe("Disable scrolling in legend"),
|
|
3546
|
+
enableVariableLegendPosition: z18.boolean().optional().describe("Allow user to drag legend"),
|
|
3547
|
+
legendTop: z18.number().optional().describe("Custom legend top offset"),
|
|
3548
|
+
legendLeft: z18.number().optional().describe("Custom legend left offset"),
|
|
3306
3549
|
verticalAxis: axisSchema.describe("Vertical (Y) axis configuration"),
|
|
3307
3550
|
horizontalAxis: axisSchema.describe("Horizontal (X) axis configuration")
|
|
3308
3551
|
},
|
|
3309
3552
|
WRITE_IDEMPOTENT,
|
|
3310
3553
|
async (params) => {
|
|
3311
|
-
const { embedId, body, themePreset, accessPreset, ...themeParams } = params;
|
|
3554
|
+
const { embedId, dataAppName, body, themePreset, accessPreset, ...themeParams } = params;
|
|
3312
3555
|
if (body) {
|
|
3313
3556
|
try {
|
|
3314
3557
|
const rawBody = { ...body };
|
|
3315
3558
|
if (!rawBody.embedId) rawBody.embedId = embedId;
|
|
3559
|
+
if (dataAppName) rawBody.dataAppName = dataAppName;
|
|
3316
3560
|
const result = await services.embedsApi.updateEmbed(rawBody);
|
|
3317
3561
|
if (result.error) {
|
|
3318
3562
|
return toolError(`Theme update failed: ${stringify(result.error)}`);
|
|
@@ -3345,7 +3589,7 @@ function registerCustomizeTheme(server, client) {
|
|
|
3345
3589
|
return toolError("No theme properties provided. Specify at least one property, or use a themePreset.");
|
|
3346
3590
|
}
|
|
3347
3591
|
try {
|
|
3348
|
-
const result = await services.embedService.updateEmbed(embedId, patch);
|
|
3592
|
+
const result = await services.embedService.updateEmbed(embedId, patch, dataAppName);
|
|
3349
3593
|
if (result.error) {
|
|
3350
3594
|
return toolError(`Theme update failed: ${stringify(result.error)}`);
|
|
3351
3595
|
}
|
|
@@ -3377,7 +3621,7 @@ Use \`get_embed_details\` to view the full current theme configuration.` + GUEST
|
|
|
3377
3621
|
}
|
|
3378
3622
|
|
|
3379
3623
|
// src/tools/embed/get-demo-link.ts
|
|
3380
|
-
import { z as
|
|
3624
|
+
import { z as z19 } from "zod";
|
|
3381
3625
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
3382
3626
|
var DEFAULT_EXPIRY_HOURS = 24;
|
|
3383
3627
|
var MAX_EXPIRY_HOURS = 24 * 30;
|
|
@@ -3387,11 +3631,12 @@ function registerGetDemoLink(server, client) {
|
|
|
3387
3631
|
"get_demo_link",
|
|
3388
3632
|
`Get a shareable demo URL for a dashboard embed scoped to a specific clientId \u2014 same link the UI's "Share Demo Link" generates. Loads the dashboard as that end-customer sees it. Use to verify ops-built widgets without a test app.`,
|
|
3389
3633
|
{
|
|
3390
|
-
clientId:
|
|
3391
|
-
dashboardName:
|
|
3392
|
-
workspaceName:
|
|
3393
|
-
|
|
3394
|
-
|
|
3634
|
+
clientId: z19.string().describe("End-customer identifier to scope the preview to."),
|
|
3635
|
+
dashboardName: z19.string().optional().describe("Dashboard name (non-technical). Preferred \u2014 pass with optional `workspaceName`."),
|
|
3636
|
+
workspaceName: z19.string().optional().describe("Workspace name. Only needed if `dashboardName` is ambiguous."),
|
|
3637
|
+
dataAppName: z19.string().trim().min(1).optional().describe("Data app name when dashboard embeds span several apps"),
|
|
3638
|
+
embedId: z19.string().optional().describe("Technical alternative to `dashboardName`."),
|
|
3639
|
+
expiryHours: z19.number().min(1).max(MAX_EXPIRY_HOURS).optional().describe(`Token TTL in hours. Defaults to ${DEFAULT_EXPIRY_HOURS}. Max ${MAX_EXPIRY_HOURS} (30 days).`)
|
|
3395
3640
|
},
|
|
3396
3641
|
WRITE_CREATE,
|
|
3397
3642
|
async (params) => {
|
|
@@ -3400,12 +3645,13 @@ function registerGetDemoLink(server, client) {
|
|
|
3400
3645
|
embedId: params.embedId,
|
|
3401
3646
|
dashboardName: params.dashboardName,
|
|
3402
3647
|
workspaceName: params.workspaceName,
|
|
3648
|
+
dataAppName: params.dataAppName,
|
|
3403
3649
|
required: true
|
|
3404
3650
|
});
|
|
3405
3651
|
if (!embed.ok) return toolError(embed.error);
|
|
3406
3652
|
const embedId = embed.value.embedId;
|
|
3407
3653
|
const dashboardName = embed.value.resolvedFrom?.dashboardName || null;
|
|
3408
|
-
const ctx = await services.widgetService.resolveEmbedContext(embedId);
|
|
3654
|
+
const ctx = await services.widgetService.resolveEmbedContext(embedId, params.dataAppName);
|
|
3409
3655
|
if (!ctx.dataAppName) {
|
|
3410
3656
|
return toolError(
|
|
3411
3657
|
`Could not resolve the data app for embed "${embedId}". The demo link needs a data app to mint a guest token against. Run \`list_data_apps\` to confirm one exists, or pass \`embedId\` for an embed with metadata.dataAppName populated.`
|
|
@@ -3447,70 +3693,70 @@ function registerGetDemoLink(server, client) {
|
|
|
3447
3693
|
}
|
|
3448
3694
|
|
|
3449
3695
|
// src/tools/frontend/generate-guest-token.ts
|
|
3450
|
-
import { z as
|
|
3696
|
+
import { z as z20 } from "zod";
|
|
3451
3697
|
function registerGenerateGuestToken(server, client) {
|
|
3452
3698
|
const services = createServiceBundle(client);
|
|
3453
3699
|
server.tool(
|
|
3454
3700
|
"generate_guest_token",
|
|
3455
3701
|
"Generate a guest token for frontend embedding. Requires clientId. Omit dataAppName to auto-resolve.",
|
|
3456
3702
|
{
|
|
3457
|
-
clientId:
|
|
3458
|
-
dataAppName:
|
|
3459
|
-
body:
|
|
3703
|
+
clientId: z20.string().describe('Unique end-user identifier for multi-tenant filtering. Use "None" if no tenancy.'),
|
|
3704
|
+
dataAppName: z20.string().optional().describe("Name of the data app. Omit to list available data apps."),
|
|
3705
|
+
body: z20.record(z20.unknown()).optional().describe(
|
|
3460
3706
|
"Raw API body for POST /api/v2/guest-token/create. If provided, sent directly and convenience fields are ignored."
|
|
3461
3707
|
),
|
|
3462
|
-
params:
|
|
3463
|
-
allowedEmbeds:
|
|
3464
|
-
rlsSettings:
|
|
3465
|
-
metricId:
|
|
3466
|
-
values:
|
|
3708
|
+
params: z20.object({
|
|
3709
|
+
allowedEmbeds: z20.array(z20.string()).optional().describe("Restrict token to specific embed IDs"),
|
|
3710
|
+
rlsSettings: z20.array(z20.object({
|
|
3711
|
+
metricId: z20.string().describe("Metric ID"),
|
|
3712
|
+
values: z20.record(z20.unknown()).describe("RLS values by column name")
|
|
3467
3713
|
})).optional().describe("Legacy alias used in docs/examples; forwarded as params.rlsSettings"),
|
|
3468
|
-
appFilters:
|
|
3469
|
-
metricId:
|
|
3470
|
-
values:
|
|
3714
|
+
appFilters: z20.array(z20.object({
|
|
3715
|
+
metricId: z20.string().describe("Metric ID"),
|
|
3716
|
+
values: z20.record(z20.unknown()).describe("Filter values by column name")
|
|
3471
3717
|
})).optional().describe("Metric-level app filters"),
|
|
3472
|
-
dashboardAppFilters:
|
|
3473
|
-
dashboardId:
|
|
3474
|
-
values:
|
|
3475
|
-
isShowOnUrl:
|
|
3718
|
+
dashboardAppFilters: z20.array(z20.object({
|
|
3719
|
+
dashboardId: z20.string().describe("Dashboard ID"),
|
|
3720
|
+
values: z20.record(z20.unknown()).describe("Filter values by dashboard filter name"),
|
|
3721
|
+
isShowOnUrl: z20.boolean().optional().describe("Whether filters are shown in URL params")
|
|
3476
3722
|
})).optional().describe("Dashboard-level app filters"),
|
|
3477
|
-
hideDashboardFilters:
|
|
3478
|
-
userIdentifier:
|
|
3479
|
-
timezone:
|
|
3723
|
+
hideDashboardFilters: z20.array(z20.string()).optional().describe("Dashboard filter names to hide"),
|
|
3724
|
+
userIdentifier: z20.string().optional().describe("End-user identifier for private metrics support"),
|
|
3725
|
+
timezone: z20.string().optional().describe('IANA timezone, e.g. "America/New_York"')
|
|
3480
3726
|
}).optional().describe("Advanced token params"),
|
|
3481
3727
|
// Legacy convenience fields (auto-mapped into params for backward compatibility)
|
|
3482
|
-
timezone:
|
|
3483
|
-
allowedEmbeds:
|
|
3484
|
-
rlsSettings:
|
|
3485
|
-
metricId:
|
|
3486
|
-
values:
|
|
3728
|
+
timezone: z20.string().optional().describe("Legacy: moved under params.timezone"),
|
|
3729
|
+
allowedEmbeds: z20.array(z20.string()).optional().describe("Legacy: moved under params.allowedEmbeds"),
|
|
3730
|
+
rlsSettings: z20.array(z20.object({
|
|
3731
|
+
metricId: z20.string().describe("Metric ID"),
|
|
3732
|
+
values: z20.record(z20.unknown()).describe("RLS values by column name")
|
|
3487
3733
|
})).optional().describe("Legacy: moved under params.rlsSettings"),
|
|
3488
|
-
appFilters:
|
|
3489
|
-
metricId:
|
|
3490
|
-
values:
|
|
3734
|
+
appFilters: z20.array(z20.object({
|
|
3735
|
+
metricId: z20.string().describe("Metric ID"),
|
|
3736
|
+
values: z20.record(z20.unknown()).describe("Filter values by column name")
|
|
3491
3737
|
})).optional().describe("Legacy: moved under params.appFilters"),
|
|
3492
|
-
dashboardAppFilters:
|
|
3493
|
-
dashboardId:
|
|
3494
|
-
values:
|
|
3495
|
-
isShowOnUrl:
|
|
3738
|
+
dashboardAppFilters: z20.array(z20.object({
|
|
3739
|
+
dashboardId: z20.string().describe("Dashboard ID to apply filters to"),
|
|
3740
|
+
values: z20.record(z20.unknown()).describe("Filter values for this dashboard"),
|
|
3741
|
+
isShowOnUrl: z20.boolean().optional().describe("Whether filters are shown in URL params")
|
|
3496
3742
|
})).optional().describe("Legacy: moved under params.dashboardAppFilters"),
|
|
3497
|
-
permissions:
|
|
3498
|
-
isEnableArchiveMetrics:
|
|
3499
|
-
isEnableManageMetrics:
|
|
3500
|
-
isEnableCreateDashboardView:
|
|
3501
|
-
isEnableMetricUpdation:
|
|
3502
|
-
isEnableCustomizeLayout:
|
|
3503
|
-
isEnableUnderlyingData:
|
|
3504
|
-
isEnableDownloadMetrics:
|
|
3505
|
-
isShowSideBar:
|
|
3506
|
-
isShowDashboardName:
|
|
3507
|
-
isDisableMetricCreation:
|
|
3743
|
+
permissions: z20.object({
|
|
3744
|
+
isEnableArchiveMetrics: z20.boolean().optional(),
|
|
3745
|
+
isEnableManageMetrics: z20.boolean().optional(),
|
|
3746
|
+
isEnableCreateDashboardView: z20.boolean().optional(),
|
|
3747
|
+
isEnableMetricUpdation: z20.boolean().optional(),
|
|
3748
|
+
isEnableCustomizeLayout: z20.boolean().optional(),
|
|
3749
|
+
isEnableUnderlyingData: z20.boolean().optional(),
|
|
3750
|
+
isEnableDownloadMetrics: z20.boolean().optional(),
|
|
3751
|
+
isShowSideBar: z20.boolean().optional(),
|
|
3752
|
+
isShowDashboardName: z20.boolean().optional(),
|
|
3753
|
+
isDisableMetricCreation: z20.boolean().optional()
|
|
3508
3754
|
}).optional().describe("Runtime permission overrides (merge with and override embed-level settings)"),
|
|
3509
|
-
theme:
|
|
3510
|
-
hideDashboardFilters:
|
|
3511
|
-
userIdentifier:
|
|
3512
|
-
expiryTime:
|
|
3513
|
-
datasourceName:
|
|
3755
|
+
theme: z20.record(z20.unknown()).optional().describe("Runtime theme overrides (overrides embed-level theme)"),
|
|
3756
|
+
hideDashboardFilters: z20.array(z20.string()).optional().describe("Legacy: moved under params.hideDashboardFilters"),
|
|
3757
|
+
userIdentifier: z20.string().optional().describe("Legacy: moved under params.userIdentifier"),
|
|
3758
|
+
expiryTime: z20.number().optional().describe("Token TTL in milliseconds"),
|
|
3759
|
+
datasourceName: z20.string().optional().describe("Datasource name for multi-datasource routing")
|
|
3514
3760
|
},
|
|
3515
3761
|
WRITE_CREATE,
|
|
3516
3762
|
async (params) => {
|
|
@@ -3563,7 +3809,7 @@ Next step: Use \`generate_embed_code\` to get copy-paste-ready frontend code.`
|
|
|
3563
3809
|
}
|
|
3564
3810
|
|
|
3565
3811
|
// src/tools/frontend/generate-embed-code.ts
|
|
3566
|
-
import { z as
|
|
3812
|
+
import { z as z21 } from "zod";
|
|
3567
3813
|
|
|
3568
3814
|
// src/templates/react.ts
|
|
3569
3815
|
function kebabCase(str) {
|
|
@@ -4559,22 +4805,22 @@ function registerGenerateEmbedCode(server, _client) {
|
|
|
4559
4805
|
"generate_embed_code",
|
|
4560
4806
|
"Generate framework-specific embed code (React, Next.js, Vue, Angular, Svelte, SolidJS, vanilla HTML).",
|
|
4561
4807
|
{
|
|
4562
|
-
embedId:
|
|
4563
|
-
framework:
|
|
4564
|
-
embedType:
|
|
4565
|
-
options:
|
|
4566
|
-
enableDownloadCsv:
|
|
4567
|
-
enableEmailCsv:
|
|
4568
|
-
enableDownloadAllMetrics:
|
|
4569
|
-
enableDownloadAllPdf:
|
|
4570
|
-
disableFullscreen:
|
|
4571
|
-
isHideTablePreview:
|
|
4572
|
-
isHideChartSettings:
|
|
4573
|
-
isStickyDashboardFilters:
|
|
4574
|
-
enableMultiMetricFilters:
|
|
4575
|
-
themeName:
|
|
4576
|
-
customTheme:
|
|
4577
|
-
optionsIcon:
|
|
4808
|
+
embedId: z21.string().describe("The embed ID to generate code for"),
|
|
4809
|
+
framework: z21.enum(FRAMEWORKS).describe("Frontend framework"),
|
|
4810
|
+
embedType: z21.enum(["dashboard", "metric"]).optional().default("dashboard"),
|
|
4811
|
+
options: z21.object({
|
|
4812
|
+
enableDownloadCsv: z21.boolean().optional(),
|
|
4813
|
+
enableEmailCsv: z21.boolean().optional(),
|
|
4814
|
+
enableDownloadAllMetrics: z21.boolean().optional(),
|
|
4815
|
+
enableDownloadAllPdf: z21.boolean().optional(),
|
|
4816
|
+
disableFullscreen: z21.boolean().optional(),
|
|
4817
|
+
isHideTablePreview: z21.boolean().optional(),
|
|
4818
|
+
isHideChartSettings: z21.boolean().optional(),
|
|
4819
|
+
isStickyDashboardFilters: z21.boolean().optional(),
|
|
4820
|
+
enableMultiMetricFilters: z21.boolean().optional(),
|
|
4821
|
+
themeName: z21.string().optional(),
|
|
4822
|
+
customTheme: z21.record(z21.unknown()).optional(),
|
|
4823
|
+
optionsIcon: z21.enum(["kebab-menu-vertical", "download"]).optional()
|
|
4578
4824
|
}).optional()
|
|
4579
4825
|
},
|
|
4580
4826
|
READ_ONLY,
|
|
@@ -4614,17 +4860,17 @@ ${generated.instructions}`;
|
|
|
4614
4860
|
}
|
|
4615
4861
|
|
|
4616
4862
|
// src/tools/analytics/list-dashboards.ts
|
|
4617
|
-
import { z as
|
|
4863
|
+
import { z as z22 } from "zod";
|
|
4618
4864
|
function registerListDashboards(server, client) {
|
|
4619
4865
|
const services = createServiceBundle(client);
|
|
4620
4866
|
server.tool(
|
|
4621
4867
|
"list_dashboards",
|
|
4622
4868
|
"List dashboards globally or for a specific data app.",
|
|
4623
4869
|
{
|
|
4624
|
-
scope:
|
|
4870
|
+
scope: z22.enum(["auto", "global", "data_app"]).optional().describe(
|
|
4625
4871
|
"Dashboard listing scope. auto: use data_app when dataAppName + API token are available, otherwise global."
|
|
4626
4872
|
),
|
|
4627
|
-
dataAppName:
|
|
4873
|
+
dataAppName: z22.string().optional().describe("Data app name for data_app scope. Ignored for global scope.")
|
|
4628
4874
|
},
|
|
4629
4875
|
READ_ONLY,
|
|
4630
4876
|
async (params) => {
|
|
@@ -4641,9 +4887,11 @@ function registerListDashboards(server, client) {
|
|
|
4641
4887
|
count: result.length,
|
|
4642
4888
|
scope: useDataAppScope ? "data_app" : "global",
|
|
4643
4889
|
dashboards: result.map((d) => {
|
|
4644
|
-
const { name, dashboardId, workspaceName, metricCount } = services.discoveryService.normalizeDashboard(d);
|
|
4890
|
+
const { name, dashboardId, externalDashboardId, workspaceName, metricCount } = services.discoveryService.normalizeDashboard(d);
|
|
4645
4891
|
return {
|
|
4646
4892
|
id: dashboardId || null,
|
|
4893
|
+
dashboardId,
|
|
4894
|
+
externalDashboardId,
|
|
4647
4895
|
name,
|
|
4648
4896
|
workspace: workspaceName || null,
|
|
4649
4897
|
metricCount: metricCount ?? null
|
|
@@ -4659,36 +4907,36 @@ function registerListDashboards(server, client) {
|
|
|
4659
4907
|
}
|
|
4660
4908
|
|
|
4661
4909
|
// src/tools/dashboard/schema.ts
|
|
4662
|
-
import { z as
|
|
4663
|
-
var dashboardPositionSchema =
|
|
4664
|
-
x:
|
|
4665
|
-
y:
|
|
4666
|
-
w:
|
|
4667
|
-
h:
|
|
4910
|
+
import { z as z23 } from "zod";
|
|
4911
|
+
var dashboardPositionSchema = z23.object({
|
|
4912
|
+
x: z23.number().int().min(0).max(23),
|
|
4913
|
+
y: z23.number().int().min(0),
|
|
4914
|
+
w: z23.number().int().min(1).max(24),
|
|
4915
|
+
h: z23.number().int().min(1).max(40)
|
|
4668
4916
|
}).strict();
|
|
4669
|
-
var dashboardChartSchema =
|
|
4670
|
-
type:
|
|
4671
|
-
metricRef:
|
|
4672
|
-
query:
|
|
4673
|
-
companyIntegrationId:
|
|
4674
|
-
chartOptions:
|
|
4917
|
+
var dashboardChartSchema = z23.object({
|
|
4918
|
+
type: z23.string(),
|
|
4919
|
+
metricRef: z23.string().optional(),
|
|
4920
|
+
query: z23.string().optional(),
|
|
4921
|
+
companyIntegrationId: z23.string().optional(),
|
|
4922
|
+
chartOptions: z23.record(z23.unknown()).optional()
|
|
4675
4923
|
}).passthrough();
|
|
4676
|
-
var dashboardTileSchema =
|
|
4677
|
-
id:
|
|
4678
|
-
kind:
|
|
4924
|
+
var dashboardTileSchema = z23.object({
|
|
4925
|
+
id: z23.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/),
|
|
4926
|
+
kind: z23.literal("databrain-chart"),
|
|
4679
4927
|
position: dashboardPositionSchema,
|
|
4680
|
-
title:
|
|
4928
|
+
title: z23.string().min(1).max(200),
|
|
4681
4929
|
chart: dashboardChartSchema.optional()
|
|
4682
4930
|
}).strict();
|
|
4683
4931
|
var dashboardDocumentShape = {
|
|
4684
|
-
schemaVersion:
|
|
4685
|
-
title:
|
|
4686
|
-
description:
|
|
4687
|
-
layout:
|
|
4688
|
-
tiles:
|
|
4932
|
+
schemaVersion: z23.literal(1),
|
|
4933
|
+
title: z23.string().min(1).max(200),
|
|
4934
|
+
description: z23.string().max(2e3).optional(),
|
|
4935
|
+
layout: z23.object({ columns: z23.union([z23.literal(12), z23.literal(24)]).default(24) }).strict().optional(),
|
|
4936
|
+
tiles: z23.array(dashboardTileSchema).min(1).max(50)
|
|
4689
4937
|
};
|
|
4690
|
-
var workspaceNameSchema =
|
|
4691
|
-
var idempotencyKeySchema =
|
|
4938
|
+
var workspaceNameSchema = z23.string().min(1).describe("Target workspace name");
|
|
4939
|
+
var idempotencyKeySchema = z23.string().regex(
|
|
4692
4940
|
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
|
4693
4941
|
"idempotencyKey must be a UUID v4"
|
|
4694
4942
|
).describe("UUID v4 unique to this workspace dashboard create request");
|
|
@@ -4741,7 +4989,7 @@ ${stringify(validation)}`
|
|
|
4741
4989
|
}
|
|
4742
4990
|
|
|
4743
4991
|
// src/tools/dashboard/generate-dashboard-tiles.ts
|
|
4744
|
-
import { z as
|
|
4992
|
+
import { z as z24 } from "zod";
|
|
4745
4993
|
function registerGenerateDashboardTiles(server, client) {
|
|
4746
4994
|
const services = createServiceBundle(client);
|
|
4747
4995
|
server.tool(
|
|
@@ -4749,15 +4997,16 @@ function registerGenerateDashboardTiles(server, client) {
|
|
|
4749
4997
|
"Generate multiple workspace dashboard tile drafts without persisting them.",
|
|
4750
4998
|
{
|
|
4751
4999
|
workspaceName: workspaceNameSchema,
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
5000
|
+
clientId: z24.string().trim().min(1).describe("Tenant client identifier in this data app; use list_tenants to choose it"),
|
|
5001
|
+
dataAppName: z24.string().optional().describe("Data app in the workspace; auto-resolved when unique"),
|
|
5002
|
+
integrationId: z24.string().optional().describe("Datasource UUID override"),
|
|
5003
|
+
integrationName: z24.string().optional().describe("Datasource name override"),
|
|
5004
|
+
tiles: z24.array(
|
|
5005
|
+
z24.object({
|
|
5006
|
+
id: z24.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/),
|
|
5007
|
+
title: z24.string().min(1).max(200),
|
|
4759
5008
|
position: dashboardPositionSchema,
|
|
4760
|
-
userInputs:
|
|
5009
|
+
userInputs: z24.record(z24.unknown())
|
|
4761
5010
|
}).strict()
|
|
4762
5011
|
).min(1).max(50)
|
|
4763
5012
|
},
|
|
@@ -4778,10 +5027,10 @@ function registerGenerateDashboardTiles(server, client) {
|
|
|
4778
5027
|
method: "POST",
|
|
4779
5028
|
path: "/api/v2/guest-token/create",
|
|
4780
5029
|
auth: "service",
|
|
4781
|
-
body: { clientId:
|
|
5030
|
+
body: { clientId: params.clientId, dataAppName: resolution.name }
|
|
4782
5031
|
});
|
|
4783
5032
|
const datasource = await services.widgetService.resolveDatasource(guest.token, {
|
|
4784
|
-
clientId:
|
|
5033
|
+
clientId: params.clientId,
|
|
4785
5034
|
integrationId: params.integrationId,
|
|
4786
5035
|
integrationName: params.integrationName
|
|
4787
5036
|
});
|
|
@@ -4802,11 +5051,6 @@ function registerGenerateDashboardTiles(server, client) {
|
|
|
4802
5051
|
`Tile "${item.input.id}" generation failed: ${stringify(item.result.error)}`
|
|
4803
5052
|
);
|
|
4804
5053
|
}
|
|
4805
|
-
if (typeof item.result.chartType !== "string" || !item.result.chartType) {
|
|
4806
|
-
return toolError(
|
|
4807
|
-
`Tile "${item.input.id}" returned no chartType. Refine its userInputs and retry.`
|
|
4808
|
-
);
|
|
4809
|
-
}
|
|
4810
5054
|
}
|
|
4811
5055
|
return toolSuccess(
|
|
4812
5056
|
stringify({
|
|
@@ -4818,7 +5062,7 @@ function registerGenerateDashboardTiles(server, client) {
|
|
|
4818
5062
|
position: input.position,
|
|
4819
5063
|
title: input.title,
|
|
4820
5064
|
chart: {
|
|
4821
|
-
type: result.chartType,
|
|
5065
|
+
type: result.chartType || "table",
|
|
4822
5066
|
query: result.query,
|
|
4823
5067
|
companyIntegrationId: datasource.value.integrationId
|
|
4824
5068
|
}
|
|
@@ -4841,14 +5085,15 @@ function registerGenerateDashboardTiles(server, client) {
|
|
|
4841
5085
|
}
|
|
4842
5086
|
|
|
4843
5087
|
// src/tools/dashboard/get-dashboard.ts
|
|
4844
|
-
import { z as
|
|
5088
|
+
import { z as z25 } from "zod";
|
|
4845
5089
|
function registerGetDashboard(server, client) {
|
|
4846
|
-
server.tool(
|
|
5090
|
+
const tool = server.tool(
|
|
4847
5091
|
"get_dashboard",
|
|
4848
5092
|
"Retrieve a workspace dashboard projected back into the versioned tile-list contract.",
|
|
4849
5093
|
{
|
|
4850
|
-
workspaceName: workspaceNameSchema,
|
|
4851
|
-
|
|
5094
|
+
workspaceName: workspaceNameSchema.optional(),
|
|
5095
|
+
workspaceId: z25.string().min(1).optional().describe("Workspace UUID from a dashboard link wid parameter"),
|
|
5096
|
+
dashboardId: z25.string().min(1).describe("Dashboard UUID or externalDashboardId slug returned by list_dashboards or create_dashboard")
|
|
4852
5097
|
},
|
|
4853
5098
|
READ_ONLY,
|
|
4854
5099
|
async (params) => {
|
|
@@ -4858,10 +5103,33 @@ function registerGetDashboard(server, client) {
|
|
|
4858
5103
|
path: "/api/v2/workspace/dashboard",
|
|
4859
5104
|
auth: "service",
|
|
4860
5105
|
query: {
|
|
4861
|
-
workspaceName: params.workspaceName,
|
|
5106
|
+
...params.workspaceName ? { workspaceName: params.workspaceName } : {},
|
|
5107
|
+
...params.workspaceId ? { workspaceId: params.workspaceId } : {},
|
|
4862
5108
|
dashboardId: params.dashboardId
|
|
4863
5109
|
}
|
|
4864
5110
|
});
|
|
5111
|
+
if (hasChartApps(server) && client.isHosted()) {
|
|
5112
|
+
const tiles = Array.isArray(result.tiles) ? result.tiles : [];
|
|
5113
|
+
const charts = tiles.filter((t) => !t.unavailable && t.chart?.metricRef);
|
|
5114
|
+
const dashboardId = String(result.dashboardId || params.dashboardId);
|
|
5115
|
+
const picker = {
|
|
5116
|
+
kind: "dashboard",
|
|
5117
|
+
title: result.title || "Dashboard",
|
|
5118
|
+
rows: [],
|
|
5119
|
+
widgets: charts.map((t) => ({
|
|
5120
|
+
name: t.title || "Widget",
|
|
5121
|
+
chartType: t.chart.type || null,
|
|
5122
|
+
reference: { dashboardId, widgetId: t.chart.metricRef }
|
|
5123
|
+
})),
|
|
5124
|
+
dashboardId: result.dashboardId
|
|
5125
|
+
};
|
|
5126
|
+
try {
|
|
5127
|
+
return chartResult(stringify(result), picker);
|
|
5128
|
+
} catch (error) {
|
|
5129
|
+
if (!(error instanceof Error) || error.message !== "Chart page exceeds payload budget") throw error;
|
|
5130
|
+
return chartResult(`Dashboard ready. ${picker.widgets.length} saved charts. The tile shows a compact picker because the full dashboard document exceeds the App payload budget. ` + chartIndex(dashboardId, picker.widgets.map((w) => ({ widgetId: w.reference.widgetId, name: w.name, chartType: w.chartType }))), picker);
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
4865
5133
|
return toolSuccess(stringify(result));
|
|
4866
5134
|
} catch (err) {
|
|
4867
5135
|
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
@@ -4869,10 +5137,11 @@ function registerGetDashboard(server, client) {
|
|
|
4869
5137
|
}
|
|
4870
5138
|
}
|
|
4871
5139
|
);
|
|
5140
|
+
tagChartTool(server, tool);
|
|
4872
5141
|
}
|
|
4873
5142
|
|
|
4874
5143
|
// src/tools/dashboard/dashboard-snapshot.ts
|
|
4875
|
-
import { z as
|
|
5144
|
+
import { z as z26 } from "zod";
|
|
4876
5145
|
var SNAPSHOT_REQUEST_MS = 45e3;
|
|
4877
5146
|
function registerDashboardSnapshot(server, client) {
|
|
4878
5147
|
server.tool(
|
|
@@ -4880,10 +5149,10 @@ function registerDashboardSnapshot(server, client) {
|
|
|
4880
5149
|
"Get a PNG picture of a dashboard, or of one chart on it (metricId), as the signed-in user sees it in Databrain. Light or dark, 480 to 1600 px wide. Needs a Databrain sign-in (OAuth), not a service token.",
|
|
4881
5150
|
{
|
|
4882
5151
|
workspaceName: workspaceNameSchema.optional(),
|
|
4883
|
-
dashboardId:
|
|
4884
|
-
metricId:
|
|
4885
|
-
theme:
|
|
4886
|
-
width:
|
|
5152
|
+
dashboardId: z26.string().min(1).max(200).optional().describe("Dashboard id from list_dashboards. Needed unless metricId is given."),
|
|
5153
|
+
metricId: z26.string().min(1).max(200).optional().describe("One chart (metric) id: draw only that chart, from a dashboard it is on."),
|
|
5154
|
+
theme: z26.enum(["light", "dark"]).default("light").describe("Picture colours"),
|
|
5155
|
+
width: z26.number().int().min(480).max(1600).default(1200).describe("Picture width in pixels")
|
|
4887
5156
|
},
|
|
4888
5157
|
READ_ONLY,
|
|
4889
5158
|
async (params) => {
|
|
@@ -4928,18 +5197,18 @@ function registerDashboardSnapshot(server, client) {
|
|
|
4928
5197
|
|
|
4929
5198
|
// src/apps/preview.ts
|
|
4930
5199
|
import { createHash } from "crypto";
|
|
4931
|
-
import { z as
|
|
4932
|
-
var draftSchema =
|
|
4933
|
-
question:
|
|
4934
|
-
sql:
|
|
4935
|
-
datamartName:
|
|
4936
|
-
dimensions:
|
|
4937
|
-
measures:
|
|
4938
|
-
chartType:
|
|
4939
|
-
title:
|
|
4940
|
-
sessionId:
|
|
5200
|
+
import { z as z27 } from "zod";
|
|
5201
|
+
var draftSchema = z27.object({
|
|
5202
|
+
question: z27.string().min(1).max(2e3),
|
|
5203
|
+
sql: z27.string().min(1).max(2e4),
|
|
5204
|
+
datamartName: z27.string().min(1).max(256),
|
|
5205
|
+
dimensions: z27.array(z27.string().max(128)).max(20).default([]),
|
|
5206
|
+
measures: z27.array(z27.string().max(128)).max(20).default([]),
|
|
5207
|
+
chartType: z27.string().min(1).max(80),
|
|
5208
|
+
title: z27.string().min(1).max(200),
|
|
5209
|
+
sessionId: z27.string().max(256).optional(),
|
|
4941
5210
|
/** Workspaces built on this datamart. One means Save needs no choice. */
|
|
4942
|
-
workspaces:
|
|
5211
|
+
workspaces: z27.array(z27.string().max(256)).max(50).default([])
|
|
4943
5212
|
}).strict();
|
|
4944
5213
|
var PREVIEW_PATH = "/api/v2/data-app/chart-preview";
|
|
4945
5214
|
var METRIC_PATH = "/api/mcp/metrics/create";
|
|
@@ -5066,7 +5335,7 @@ async function saveDraftAsDashboard(client, draft, input) {
|
|
|
5066
5335
|
}
|
|
5067
5336
|
|
|
5068
5337
|
// src/tools/query/ask-question.ts
|
|
5069
|
-
import { z as
|
|
5338
|
+
import { z as z28 } from "zod";
|
|
5070
5339
|
|
|
5071
5340
|
// src/tools/query/answer-format.ts
|
|
5072
5341
|
function formatConfidence(value) {
|
|
@@ -5123,29 +5392,29 @@ function registerAskQuestion(server, client) {
|
|
|
5123
5392
|
"ask_question",
|
|
5124
5393
|
"Ask a natural language question \u2014 converts to SQL, returns data + chart suggestion. Requires datamartName with a populated semantic layer.",
|
|
5125
5394
|
{
|
|
5126
|
-
question:
|
|
5395
|
+
question: z28.string().describe(
|
|
5127
5396
|
'The natural language question to ask (e.g. "What was total revenue last month?")'
|
|
5128
5397
|
),
|
|
5129
|
-
datamartName:
|
|
5398
|
+
datamartName: z28.string().optional().describe(
|
|
5130
5399
|
"Name of the datamart to query. Use `list_datamarts` to find available names. REQUIRED unless exactly one datamart exists (auto-resolved). Do NOT pass a workspace name here."
|
|
5131
5400
|
),
|
|
5132
|
-
sessionId:
|
|
5401
|
+
sessionId: z28.string().optional().describe(
|
|
5133
5402
|
"Session ID from a previous ask_question response, for follow-up questions."
|
|
5134
5403
|
),
|
|
5135
|
-
timezone:
|
|
5404
|
+
timezone: z28.string().optional().describe('IANA timezone (e.g. "America/New_York"). Defaults to UTC.')
|
|
5136
5405
|
},
|
|
5137
5406
|
WRITE_CREATE,
|
|
5138
5407
|
async (params) => {
|
|
5139
5408
|
try {
|
|
5140
5409
|
let datamartName = params.datamartName;
|
|
5141
|
-
|
|
5410
|
+
{
|
|
5142
5411
|
const datamarts = await services.datamartsApi.listDatamarts();
|
|
5143
5412
|
if (!Array.isArray(datamarts) || datamarts.length === 0) {
|
|
5144
5413
|
return toolError(
|
|
5145
|
-
"No datamarts
|
|
5414
|
+
"No datamarts are available in this workspace. A datamart is **required** before you can query data.\n\nTell the user they need to create a datamart in the **Databrain UI** first:\n1. Go to the Databrain platform and connect a datasource\n2. Create a datamart by selecting tables from the datasource\n3. Come back here and use `ask_question` with the `datamartName`\n\nYou can use `list_datasources` and `discover_datasource_schema` to explore what's available."
|
|
5146
5415
|
);
|
|
5147
5416
|
}
|
|
5148
|
-
if (datamarts.length === 1) {
|
|
5417
|
+
if (!datamartName && datamarts.length === 1) {
|
|
5149
5418
|
const resolvedName = datamarts[0].name || datamarts[0].datamartName;
|
|
5150
5419
|
if (!resolvedName) {
|
|
5151
5420
|
return toolError(
|
|
@@ -5153,7 +5422,7 @@ function registerAskQuestion(server, client) {
|
|
|
5153
5422
|
);
|
|
5154
5423
|
}
|
|
5155
5424
|
datamartName = resolvedName;
|
|
5156
|
-
} else {
|
|
5425
|
+
} else if (!datamartName) {
|
|
5157
5426
|
const list = datamarts.map(formatDatamart).join("\n");
|
|
5158
5427
|
return toolSuccess(
|
|
5159
5428
|
`Found ${datamarts.length} datamarts. Please specify which one to query:
|
|
@@ -5332,14 +5601,14 @@ To fix this:
|
|
|
5332
5601
|
}
|
|
5333
5602
|
|
|
5334
5603
|
// src/tools/query/get-semantic-layer.ts
|
|
5335
|
-
import { z as
|
|
5604
|
+
import { z as z29 } from "zod";
|
|
5336
5605
|
function registerGetSemanticLayer(server, client) {
|
|
5337
5606
|
const services = createServiceBundle(client);
|
|
5338
5607
|
server.tool(
|
|
5339
5608
|
"get_semantic_layer",
|
|
5340
5609
|
"Get semantic layer metadata for a datamart: table/column descriptions, synonyms, completion score. Omit datamartName to auto-resolve.",
|
|
5341
5610
|
{
|
|
5342
|
-
datamartName:
|
|
5611
|
+
datamartName: z29.string().optional().describe(
|
|
5343
5612
|
"Name of the datamart to inspect. Omit to list available datamarts."
|
|
5344
5613
|
)
|
|
5345
5614
|
},
|
|
@@ -5449,7 +5718,7 @@ ${result.feedback}`);
|
|
|
5449
5718
|
}
|
|
5450
5719
|
|
|
5451
5720
|
// src/tools/query/semantic-model.ts
|
|
5452
|
-
import { z as
|
|
5721
|
+
import { z as z30 } from "zod";
|
|
5453
5722
|
|
|
5454
5723
|
// src/services/semantic-source.ts
|
|
5455
5724
|
import { parseDocument, stringify as stringify2 } from "yaml";
|
|
@@ -5649,21 +5918,21 @@ function retrieveSemanticSource(source2, question, k) {
|
|
|
5649
5918
|
}
|
|
5650
5919
|
|
|
5651
5920
|
// src/tools/query/semantic-model.ts
|
|
5652
|
-
var datamartId =
|
|
5653
|
-
var source =
|
|
5654
|
-
var entityKind =
|
|
5921
|
+
var datamartId = z30.string().trim().min(1).max(256).describe("Datamart ID from list_datamarts.");
|
|
5922
|
+
var source = z30.string().min(1).max(2e6).describe("Complete proposed semantic model as YAML.");
|
|
5923
|
+
var entityKind = z30.enum(["table", "column", "measure", "metric", "join", "entity", "glossary", "saved_query", "instruction", "access_rule"]);
|
|
5655
5924
|
var changeInputs = {
|
|
5656
5925
|
source: source.optional(),
|
|
5657
|
-
yaml_patch:
|
|
5658
|
-
change:
|
|
5659
|
-
operation:
|
|
5926
|
+
yaml_patch: z30.string().min(1).max(2e6).optional().describe("YAML mapping merged into the pulled model. Named lists merge by name. Other lists replace. Null removes a field. An empty list clears it."),
|
|
5927
|
+
change: z30.object({
|
|
5928
|
+
operation: z30.enum(["upsert", "remove"]),
|
|
5660
5929
|
kind: entityKind,
|
|
5661
|
-
name:
|
|
5662
|
-
table:
|
|
5663
|
-
value:
|
|
5930
|
+
name: z30.string().trim().min(1),
|
|
5931
|
+
table: z30.string().trim().min(1).optional().describe("Required for columns. Use the table name and an unqualified column name."),
|
|
5932
|
+
value: z30.record(z30.unknown()).optional().describe("Fields to merge for upsert. Omit for remove.")
|
|
5664
5933
|
}).strict().optional().describe("One named entity change. Supply exactly one of source, yaml_patch or change.")
|
|
5665
5934
|
};
|
|
5666
|
-
var expectedHash =
|
|
5935
|
+
var expectedHash = z30.string().trim().min(1).nullable().optional().describe("Stored content_hash from the reviewed proposal. Null explicitly means the first apply. Required when dry_run is false.");
|
|
5667
5936
|
function errorCode(error) {
|
|
5668
5937
|
const nested = isObject(error.details) && isObject(error.details.error) ? error.details.error : void 0;
|
|
5669
5938
|
return typeof nested?.code === "string" ? nested.code : error.code;
|
|
@@ -5722,7 +5991,7 @@ function registerSearchSemanticModel(server, client) {
|
|
|
5722
5991
|
server.tool(
|
|
5723
5992
|
"search_semantic_model",
|
|
5724
5993
|
"Find semantic model entities relevant to a question. Ranks names, aliases, synonyms and descriptions, then includes related entities. Uses the stored model and never writes.",
|
|
5725
|
-
{ datamartId, question:
|
|
5994
|
+
{ datamartId, question: z30.string().trim().min(1).max(1e4), k: z30.number().int().min(1).max(100).default(10) },
|
|
5726
5995
|
READ_ONLY,
|
|
5727
5996
|
async ({ datamartId: datamartId2, question, k }) => {
|
|
5728
5997
|
try {
|
|
@@ -5745,7 +6014,7 @@ function registerGetSemanticEntity(server, client) {
|
|
|
5745
6014
|
server.tool(
|
|
5746
6015
|
"get_semantic_entity",
|
|
5747
6016
|
"Read one entity by kind and exact name from the stored semantic model. For a column, use table.column. Returns the stored content hash for change review.",
|
|
5748
|
-
{ datamartId, kind: entityKind, name:
|
|
6017
|
+
{ datamartId, kind: entityKind, name: z30.string().trim().min(1) },
|
|
5749
6018
|
READ_ONLY,
|
|
5750
6019
|
async ({ datamartId: datamartId2, kind, name }) => {
|
|
5751
6020
|
try {
|
|
@@ -5783,9 +6052,9 @@ function registerApplySemanticChange(server, client) {
|
|
|
5783
6052
|
{
|
|
5784
6053
|
datamartId,
|
|
5785
6054
|
...changeInputs,
|
|
5786
|
-
dry_run:
|
|
6055
|
+
dry_run: z30.boolean().default(true),
|
|
5787
6056
|
expected_content_hash: expectedHash,
|
|
5788
|
-
confirm:
|
|
6057
|
+
confirm: z30.string().optional().describe("User supplied APPLY_TO_PRODUCTION after reviewing the plan. Never invent confirmation.")
|
|
5789
6058
|
},
|
|
5790
6059
|
DESTRUCTIVE,
|
|
5791
6060
|
async (params) => {
|
|
@@ -5808,19 +6077,17 @@ function registerEvalSemanticModel(server, client) {
|
|
|
5808
6077
|
const api = createServiceBundle(client).semanticModelApi;
|
|
5809
6078
|
server.tool(
|
|
5810
6079
|
"eval_semantic_model",
|
|
5811
|
-
"Evaluate
|
|
5812
|
-
{ datamartId, source: source.optional(), questions:
|
|
5813
|
-
|
|
5814
|
-
question:
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
expected_empty: z28.boolean().optional()
|
|
5818
|
-
}).strict()])).max(100).optional() },
|
|
6080
|
+
"Evaluate closed world semantic assertions against proposed or stored YAML. Each question must name an id and the model entities it exercises. Does not execute warehouse SQL or write the model.",
|
|
6081
|
+
{ datamartId, source: source.optional(), questions: z30.array(z30.object({
|
|
6082
|
+
id: z30.string().trim().min(1),
|
|
6083
|
+
question: z30.string().trim().min(1),
|
|
6084
|
+
exercises: z30.array(z30.string().trim().min(1)).describe("Exact model entity names asserted by this question; an empty list is reported as unscoreable.")
|
|
6085
|
+
}).strict()).max(100).default([]) },
|
|
5819
6086
|
READ_ONLY,
|
|
5820
6087
|
async ({ datamartId: datamartId2, source: source2, questions }) => {
|
|
5821
|
-
const shaped = questions?.map((entry) => typeof entry === "string" ? { question: entry } : entry);
|
|
5822
6088
|
try {
|
|
5823
|
-
|
|
6089
|
+
const yaml = source2 ?? (await api.pull(datamartId2, "combined")).source;
|
|
6090
|
+
return toolSuccess(stringify(await api.evaluate(datamartId2, { source: yaml, questions })));
|
|
5824
6091
|
} catch (error) {
|
|
5825
6092
|
return failure(error);
|
|
5826
6093
|
}
|
|
@@ -5829,15 +6096,15 @@ function registerEvalSemanticModel(server, client) {
|
|
|
5829
6096
|
}
|
|
5830
6097
|
|
|
5831
6098
|
// src/tools/query/update-semantic-layer.ts
|
|
5832
|
-
import { z as
|
|
5833
|
-
var columnSchema2 =
|
|
5834
|
-
name:
|
|
5835
|
-
description:
|
|
5836
|
-
synonyms:
|
|
6099
|
+
import { z as z31 } from "zod";
|
|
6100
|
+
var columnSchema2 = z31.object({
|
|
6101
|
+
name: z31.string().describe("Column name (must exist in the datamart)"),
|
|
6102
|
+
description: z31.string().optional().describe("Business-friendly description of what this column represents"),
|
|
6103
|
+
synonyms: z31.array(z31.string()).optional().describe(
|
|
5837
6104
|
'Alternative names users might use for this column (e.g. ["revenue", "sales amount"])'
|
|
5838
6105
|
),
|
|
5839
|
-
miscellaneousInfo:
|
|
5840
|
-
columnType:
|
|
6106
|
+
miscellaneousInfo: z31.string().optional().describe("Additional context such as valid values, units, or caveats"),
|
|
6107
|
+
columnType: z31.enum([
|
|
5841
6108
|
"String",
|
|
5842
6109
|
"Long String",
|
|
5843
6110
|
"String (Custom)",
|
|
@@ -5852,21 +6119,21 @@ var columnSchema2 = z29.object({
|
|
|
5852
6119
|
// The public PUT accepts these three as well (docs: Update Semantic Layer).
|
|
5853
6120
|
// They were missing here, and zod strips unknown keys, so a caller who set
|
|
5854
6121
|
// `isIdentifier: true` got a success message and a row with `false`.
|
|
5855
|
-
columnTypeConfig:
|
|
5856
|
-
isIdentifier:
|
|
5857
|
-
isNotIndexed:
|
|
6122
|
+
columnTypeConfig: z31.union([z31.record(z31.string(), z31.string()), z31.string(), z31.object({ lowerLimit: z31.number(), upperLimit: z31.number() }), z31.null()]).optional().describe("Shape depends on columnType: value-to-description object for String / String (Custom) / ENUM / Mapper, { lowerLimit, upperLimit } for Range, a string for Expression and JSON. Omit for Identifier, Number and Long String."),
|
|
6123
|
+
isIdentifier: z31.boolean().optional().describe("Mark the column as an identifier (primary or foreign key)."),
|
|
6124
|
+
isNotIndexed: z31.boolean().optional().describe("Exclude the column from AI indexing.")
|
|
5858
6125
|
});
|
|
5859
|
-
var tableSchema2 =
|
|
5860
|
-
name:
|
|
5861
|
-
schemaName:
|
|
5862
|
-
description:
|
|
6126
|
+
var tableSchema2 = z31.object({
|
|
6127
|
+
name: z31.string().describe("Table name (must exist in the datamart)"),
|
|
6128
|
+
schemaName: z31.string().optional().describe("Schema the table lives in, when the datamart has the same table name in more than one schema."),
|
|
6129
|
+
description: z31.string().optional().describe(
|
|
5863
6130
|
"Business-friendly description of what this table represents"
|
|
5864
6131
|
),
|
|
5865
|
-
synonyms:
|
|
5866
|
-
miscellaneousInfo:
|
|
6132
|
+
synonyms: z31.array(z31.string()).optional().describe("Alternative names for this table"),
|
|
6133
|
+
miscellaneousInfo: z31.string().optional().describe(
|
|
5867
6134
|
"Additional context such as how the table relates to other tables"
|
|
5868
6135
|
),
|
|
5869
|
-
columns:
|
|
6136
|
+
columns: z31.array(columnSchema2).optional().describe("Column-level semantic definitions to update")
|
|
5870
6137
|
});
|
|
5871
6138
|
function columnKey(tableName, columnName) {
|
|
5872
6139
|
return `${tableName.toLowerCase()}\0${columnName.toLowerCase()}`;
|
|
@@ -5943,13 +6210,13 @@ function registerUpdateSemanticLayer(server, client) {
|
|
|
5943
6210
|
"update_semantic_layer",
|
|
5944
6211
|
"Populate or update the semantic layer for a datamart. Add table/column descriptions, synonyms, context. Required before ask_question works. Auto-generate descriptions from column names when empty.",
|
|
5945
6212
|
{
|
|
5946
|
-
datamartName:
|
|
6213
|
+
datamartName: z31.string().optional().describe(
|
|
5947
6214
|
"Name of the datamart to update. Omit to list available datamarts."
|
|
5948
6215
|
),
|
|
5949
|
-
tables:
|
|
6216
|
+
tables: z31.array(tableSchema2).optional().describe(
|
|
5950
6217
|
"Table and column definitions to update. Only include tables/columns you want to change."
|
|
5951
6218
|
),
|
|
5952
|
-
feedback:
|
|
6219
|
+
feedback: z31.string().optional().describe(
|
|
5953
6220
|
'General feedback or context about the datamart (e.g. "This datamart contains e-commerce data for the US market").'
|
|
5954
6221
|
)
|
|
5955
6222
|
},
|
|
@@ -6011,7 +6278,7 @@ You can now:
|
|
|
6011
6278
|
}
|
|
6012
6279
|
|
|
6013
6280
|
// src/tools/query/semantic-generation.ts
|
|
6014
|
-
import { z as
|
|
6281
|
+
import { z as z32 } from "zod";
|
|
6015
6282
|
function nonEmptyString(value) {
|
|
6016
6283
|
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
6017
6284
|
}
|
|
@@ -6115,11 +6382,11 @@ function registerStartSemanticLayerGeneration(server, client) {
|
|
|
6115
6382
|
const services = createServiceBundle(client);
|
|
6116
6383
|
server.tool(
|
|
6117
6384
|
"start_semantic_layer_generation",
|
|
6118
|
-
"Start backend semantic-layer auto-generation for a datamart. Returns an async job id to poll.",
|
|
6385
|
+
"Start backend semantic-layer auto-generation for a datamart through a hosted OAuth sign-in. Static service-token authentication is not supported by this backend route. Returns an async job id to poll.",
|
|
6119
6386
|
{
|
|
6120
|
-
datamartName:
|
|
6121
|
-
workspaceName:
|
|
6122
|
-
datamartId:
|
|
6387
|
+
datamartName: z32.string().optional().describe("Datamart name. Omit to list available datamarts"),
|
|
6388
|
+
workspaceName: z32.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
|
|
6389
|
+
datamartId: z32.string().optional().describe("Datamart UUID. Use only when already known.")
|
|
6123
6390
|
},
|
|
6124
6391
|
WRITE_CREATE,
|
|
6125
6392
|
async (params) => {
|
|
@@ -6159,11 +6426,11 @@ function registerGetSemanticLayerGenerationStatus(server, client) {
|
|
|
6159
6426
|
const services = createServiceBundle(client);
|
|
6160
6427
|
server.tool(
|
|
6161
6428
|
"get_semantic_layer_generation_status",
|
|
6162
|
-
"Get semantic-layer auto-generation job status for a datamart.",
|
|
6429
|
+
"Get semantic-layer auto-generation job status for a datamart through a hosted OAuth sign-in. Static service-token authentication is not supported by this backend route.",
|
|
6163
6430
|
{
|
|
6164
|
-
datamartName:
|
|
6165
|
-
workspaceName:
|
|
6166
|
-
datamartId:
|
|
6431
|
+
datamartName: z32.string().optional().describe("Datamart name. Omit to list available datamarts"),
|
|
6432
|
+
workspaceName: z32.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
|
|
6433
|
+
datamartId: z32.string().optional().describe("Datamart UUID. Use only when already known.")
|
|
6167
6434
|
},
|
|
6168
6435
|
READ_ONLY,
|
|
6169
6436
|
async (params) => {
|
|
@@ -6200,7 +6467,7 @@ function registerGetSemanticLayerGenerationStatus(server, client) {
|
|
|
6200
6467
|
}
|
|
6201
6468
|
|
|
6202
6469
|
// src/tools/widgets/list-widgets.ts
|
|
6203
|
-
import { z as
|
|
6470
|
+
import { z as z33 } from "zod";
|
|
6204
6471
|
function widgetReferenceId(widget) {
|
|
6205
6472
|
for (const candidate2 of [widget.externalMetricId, widget.id, widget._id, widget.metricId]) {
|
|
6206
6473
|
if (typeof candidate2 === "string" && candidate2.trim().length > 0) return candidate2.trim();
|
|
@@ -6211,27 +6478,75 @@ function registerListWidgets(server, client) {
|
|
|
6211
6478
|
const services = createServiceBundle(client);
|
|
6212
6479
|
const tool = server.tool(
|
|
6213
6480
|
"list_widgets",
|
|
6214
|
-
"List widgets
|
|
6481
|
+
"List saved widgets. Hosted sign-ins read an authorized workspace dashboard without an embed; guest/API flows use clientId and an embed. Non-technical flow: pass `clientId` + `dashboardName` (+ optional `workspaceName` if ambiguous). Returns each widget's externalMetricId (uuid, used for update/remove), metricId (slug), name, published state, and creator identifier.",
|
|
6215
6482
|
{
|
|
6216
|
-
clientId:
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6483
|
+
clientId: z33.string().optional().describe("Client/tenant identifier for this dashboard"),
|
|
6484
|
+
dataAppName: z33.string().trim().min(1).optional().describe("Data app for a tenant/embed read. Omit to resolve it from the embed; hosted workspace reads need none."),
|
|
6485
|
+
workspaceId: z33.string().min(1).optional().describe("Workspace UUID from the wid query parameter in a dashboard link."),
|
|
6486
|
+
dashboardId: z33.string().min(1).optional().describe("Dashboard UUID or slug returned by list_dashboards. Hosted sign-ins only."),
|
|
6487
|
+
dashboardName: z33.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
|
|
6488
|
+
workspaceName: z33.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
|
|
6489
|
+
embedId: z33.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier.")
|
|
6220
6490
|
},
|
|
6221
6491
|
READ_ONLY,
|
|
6222
6492
|
async (params) => {
|
|
6223
6493
|
try {
|
|
6494
|
+
if (params.dashboardId && (params.clientId !== void 0 || params.embedId)) {
|
|
6495
|
+
return toolError("dashboardId is for hosted workspace reads. For a tenant, use clientId with dashboardName or embedId.");
|
|
6496
|
+
}
|
|
6497
|
+
if (client.isHosted() && params.clientId === void 0 && !params.embedId) {
|
|
6498
|
+
const result = await client.request({
|
|
6499
|
+
method: "POST",
|
|
6500
|
+
path: "/api/v2/workspace/dashboard/widgets",
|
|
6501
|
+
auth: "service",
|
|
6502
|
+
body: {
|
|
6503
|
+
...params.dashboardId ? { dashboardId: params.dashboardId } : {},
|
|
6504
|
+
...params.dashboardName ? { dashboardName: params.dashboardName } : {},
|
|
6505
|
+
workspaceName: params.workspaceName,
|
|
6506
|
+
workspaceId: params.workspaceId
|
|
6507
|
+
}
|
|
6508
|
+
});
|
|
6509
|
+
const widgets2 = result.widgets;
|
|
6510
|
+
if (hasChartApps(server)) {
|
|
6511
|
+
const references = widgets2.slice(0, 50).flatMap((widget) => {
|
|
6512
|
+
const widgetId = widgetReferenceId(widget);
|
|
6513
|
+
return widgetId ? [{
|
|
6514
|
+
name: widget.name || "Untitled widget",
|
|
6515
|
+
chartType: typeof widget.chartType === "string" ? widget.chartType : null,
|
|
6516
|
+
reference: { dashboardId: result.dashboardId, widgetId }
|
|
6517
|
+
}] : [];
|
|
6518
|
+
});
|
|
6519
|
+
return chartResult(`${widgets2.length} saved widgets available. ` + chartIndex(
|
|
6520
|
+
result.dashboardId,
|
|
6521
|
+
references.map((r) => ({ widgetId: r.reference.widgetId, name: r.name, chartType: r.chartType }))
|
|
6522
|
+
), {
|
|
6523
|
+
kind: "dashboard",
|
|
6524
|
+
title: params.dashboardName || "Dashboard",
|
|
6525
|
+
rows: [],
|
|
6526
|
+
widgets: references,
|
|
6527
|
+
dashboardId: result.dashboardId,
|
|
6528
|
+
externalDashboardId: result.externalDashboardId,
|
|
6529
|
+
description: widgets2.length > 50 ? "Showing the first 50 widgets." : ""
|
|
6530
|
+
});
|
|
6531
|
+
}
|
|
6532
|
+
return toolSuccess(JSON.stringify(result));
|
|
6533
|
+
}
|
|
6534
|
+
if (!params.clientId) return toolError("clientId is required for an embed/tenant dashboard. Hosted sign-ins may omit it for a workspace dashboard.");
|
|
6224
6535
|
const embed = await services.widgetService.resolveEmbedId({
|
|
6225
6536
|
embedId: params.embedId,
|
|
6226
6537
|
dashboardName: params.dashboardName,
|
|
6227
6538
|
workspaceName: params.workspaceName,
|
|
6539
|
+
dataAppName: params.dataAppName,
|
|
6228
6540
|
required: true
|
|
6229
6541
|
});
|
|
6230
6542
|
if (!embed.ok) return toolError(embed.error);
|
|
6231
6543
|
const embedId = embed.value.embedId;
|
|
6544
|
+
const app = client.isHosted() ? await services.widgetService.resolveDataAppName(params.dataAppName, embedId) : { ok: true, value: params.dataAppName };
|
|
6545
|
+
if (!app.ok) return toolError(app.error);
|
|
6232
6546
|
const widgets = await services.widgetsApi.listWidgets(
|
|
6233
6547
|
embedId,
|
|
6234
|
-
params.clientId
|
|
6548
|
+
params.clientId,
|
|
6549
|
+
app.value
|
|
6235
6550
|
);
|
|
6236
6551
|
if (hasChartApps(server)) {
|
|
6237
6552
|
const visible = (Array.isArray(widgets) ? widgets : []).slice(0, 50);
|
|
@@ -6239,7 +6554,12 @@ function registerListWidgets(server, client) {
|
|
|
6239
6554
|
const widgetId = widgetReferenceId(w);
|
|
6240
6555
|
return widgetId ? [{
|
|
6241
6556
|
name: typeof w.name === "string" ? w.name.slice(0, 500) : "Untitled widget",
|
|
6242
|
-
reference: {
|
|
6557
|
+
reference: {
|
|
6558
|
+
widgetId,
|
|
6559
|
+
embedId,
|
|
6560
|
+
clientId: params.clientId,
|
|
6561
|
+
...app.value ? { dataAppName: app.value } : {}
|
|
6562
|
+
}
|
|
6243
6563
|
}] : [];
|
|
6244
6564
|
});
|
|
6245
6565
|
return chartResult(`${references.length} widgets available in the first dashboard page.`, {
|
|
@@ -6280,29 +6600,29 @@ function registerListWidgets(server, client) {
|
|
|
6280
6600
|
}
|
|
6281
6601
|
|
|
6282
6602
|
// src/tools/widgets/add-widget.ts
|
|
6283
|
-
import { z as
|
|
6603
|
+
import { z as z34 } from "zod";
|
|
6284
6604
|
function registerAddWidget(server, client) {
|
|
6285
6605
|
const services = createServiceBundle(client);
|
|
6286
6606
|
server.tool(
|
|
6287
6607
|
"add_widget",
|
|
6288
6608
|
"Add a widget to a customer dashboard. Non-technical flow: pass `clientId` + `dashboardName` (+ optional `workspaceName` if ambiguous). Cloning via `templateMetricId` is the recommended path \u2014 query, chart config, and datasource are all inherited. Data app and datasource auto-resolve; never ask the user for them.",
|
|
6289
6609
|
{
|
|
6290
|
-
clientId:
|
|
6291
|
-
dashboardName:
|
|
6292
|
-
workspaceName:
|
|
6293
|
-
embedId:
|
|
6294
|
-
dataAppName:
|
|
6295
|
-
templateMetricId:
|
|
6296
|
-
name:
|
|
6297
|
-
metricId:
|
|
6298
|
-
query:
|
|
6299
|
-
chartOptions:
|
|
6300
|
-
description:
|
|
6301
|
-
companyIntegrationId:
|
|
6302
|
-
integrationName:
|
|
6303
|
-
isPublished:
|
|
6304
|
-
body:
|
|
6305
|
-
widgets:
|
|
6610
|
+
clientId: z34.string().describe("Client/tenant identifier (widget is created in their scope)"),
|
|
6611
|
+
dashboardName: z34.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
|
|
6612
|
+
workspaceName: z34.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
|
|
6613
|
+
embedId: z34.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
|
|
6614
|
+
dataAppName: z34.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
|
|
6615
|
+
templateMetricId: z34.string().optional().describe("Source widget metricId to clone from. Copies query, chartOptions, and datasource fields; caller can override via other params."),
|
|
6616
|
+
name: z34.string().optional().describe("Widget name (overrides template)"),
|
|
6617
|
+
metricId: z34.string().optional().describe("Unique slug for the new widget (auto-generated if omitted)"),
|
|
6618
|
+
query: z34.string().optional().describe("SQL query body (overrides template)"),
|
|
6619
|
+
chartOptions: z34.record(z34.unknown()).optional().describe("Chart config JSON (chart type, colors, legend, axes). Overrides template."),
|
|
6620
|
+
description: z34.string().optional(),
|
|
6621
|
+
companyIntegrationId: z34.string().optional().describe("Datasource UUID override. Auto-resolved from `embedId` or sole active datasource; leave blank unless the user explicitly picked one by ID."),
|
|
6622
|
+
integrationName: z34.string().optional().describe('Datasource display name (e.g. "Snowflake"). Use this when the user picks a datasource by name \u2014 the id is looked up automatically.'),
|
|
6623
|
+
isPublished: z34.boolean().optional().describe("Publish immediately (default true)"),
|
|
6624
|
+
body: z34.record(z34.unknown()).optional().describe("Raw createMetric body passthrough. If provided, used as-is (after defaults). Overrides all other params."),
|
|
6625
|
+
widgets: z34.array(z34.record(z34.unknown())).min(1).max(50).optional().describe("Batch of raw createMetric bodies. Dashboard attachment is added to each body.")
|
|
6306
6626
|
},
|
|
6307
6627
|
WRITE_CREATE,
|
|
6308
6628
|
async (params) => {
|
|
@@ -6311,11 +6631,12 @@ function registerAddWidget(server, client) {
|
|
|
6311
6631
|
embedId: params.embedId,
|
|
6312
6632
|
dashboardName: params.dashboardName,
|
|
6313
6633
|
workspaceName: params.workspaceName,
|
|
6634
|
+
dataAppName: params.dataAppName,
|
|
6314
6635
|
required: true
|
|
6315
6636
|
});
|
|
6316
6637
|
if (!embed.ok) return toolError(embed.error);
|
|
6317
6638
|
const embedId = embed.value.embedId;
|
|
6318
|
-
const ctx = await services.widgetService.resolveEmbedContext(embedId);
|
|
6639
|
+
const ctx = await services.widgetService.resolveEmbedContext(embedId, params.dataAppName);
|
|
6319
6640
|
if (!ctx.externalDashboardId) {
|
|
6320
6641
|
return toolError(
|
|
6321
6642
|
`Dashboard "${params.dashboardName || embedId}" has no externalDashboardId \u2014 cannot attach a widget. Verify via \`get_embed_details\`.`
|
|
@@ -6450,20 +6771,20 @@ Use \`list_widgets\` to verify, or \`update_widget\`/\`remove_widget\` to adjust
|
|
|
6450
6771
|
}
|
|
6451
6772
|
|
|
6452
6773
|
// src/tools/widgets/update-widget.ts
|
|
6453
|
-
import { z as
|
|
6774
|
+
import { z as z35 } from "zod";
|
|
6454
6775
|
function registerUpdateWidget(server, client) {
|
|
6455
6776
|
const services = createServiceBundle(client);
|
|
6456
6777
|
server.tool(
|
|
6457
6778
|
"update_widget",
|
|
6458
6779
|
"Update a widget on a customer dashboard. Non-technical flow: pass `clientId` + `dashboardName` (+ optional `workspaceName` if ambiguous) + `externalMetricId` + `set`. Data app auto-resolves from the dashboard.",
|
|
6459
6780
|
{
|
|
6460
|
-
externalMetricId:
|
|
6461
|
-
clientId:
|
|
6462
|
-
dashboardName:
|
|
6463
|
-
workspaceName:
|
|
6464
|
-
embedId:
|
|
6465
|
-
dataAppName:
|
|
6466
|
-
set:
|
|
6781
|
+
externalMetricId: z35.string().describe("Internal uuid of the widget (externalMetrics.id). Not the user-facing metricId slug."),
|
|
6782
|
+
clientId: z35.string().describe("Client/tenant identifier"),
|
|
6783
|
+
dashboardName: z35.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
|
|
6784
|
+
workspaceName: z35.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
|
|
6785
|
+
embedId: z35.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
|
|
6786
|
+
dataAppName: z35.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank."),
|
|
6787
|
+
set: z35.record(z35.unknown()).describe("Partial patch. Keys can include: name, query, chartOptions, description, isPublished, isLive, selectedGroupBy, drillDownSettings, limit, etc.")
|
|
6467
6788
|
},
|
|
6468
6789
|
WRITE_IDEMPOTENT,
|
|
6469
6790
|
async (params) => {
|
|
@@ -6475,6 +6796,7 @@ function registerUpdateWidget(server, client) {
|
|
|
6475
6796
|
embedId: params.embedId,
|
|
6476
6797
|
dashboardName: params.dashboardName,
|
|
6477
6798
|
workspaceName: params.workspaceName,
|
|
6799
|
+
dataAppName: params.dataAppName,
|
|
6478
6800
|
required: true
|
|
6479
6801
|
});
|
|
6480
6802
|
if (!embed.ok) return toolError(embed.error);
|
|
@@ -6526,19 +6848,19 @@ Use \`list_widgets\` to verify.`
|
|
|
6526
6848
|
}
|
|
6527
6849
|
|
|
6528
6850
|
// src/tools/widgets/remove-widget.ts
|
|
6529
|
-
import { z as
|
|
6851
|
+
import { z as z36 } from "zod";
|
|
6530
6852
|
function registerRemoveWidget(server, client) {
|
|
6531
6853
|
const services = createServiceBundle(client);
|
|
6532
6854
|
server.tool(
|
|
6533
6855
|
"remove_widget",
|
|
6534
6856
|
"Remove a widget from a customer dashboard. Non-technical flow: pass `clientId` + `dashboardName` (+ optional `workspaceName` if ambiguous) + `externalMetricId`. Per-client soft delete \u2014 the widget stays on the template dashboard and on other customers' dashboards.",
|
|
6535
6857
|
{
|
|
6536
|
-
externalMetricId:
|
|
6537
|
-
clientId:
|
|
6538
|
-
dashboardName:
|
|
6539
|
-
workspaceName:
|
|
6540
|
-
embedId:
|
|
6541
|
-
dataAppName:
|
|
6858
|
+
externalMetricId: z36.string().describe("Internal uuid of the widget (externalMetrics.id)"),
|
|
6859
|
+
clientId: z36.string().describe("Client/tenant identifier \u2014 the widget will be hidden for this client only"),
|
|
6860
|
+
dashboardName: z36.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
|
|
6861
|
+
workspaceName: z36.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
|
|
6862
|
+
embedId: z36.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
|
|
6863
|
+
dataAppName: z36.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank.")
|
|
6542
6864
|
},
|
|
6543
6865
|
DESTRUCTIVE,
|
|
6544
6866
|
async (params) => {
|
|
@@ -6547,6 +6869,7 @@ function registerRemoveWidget(server, client) {
|
|
|
6547
6869
|
embedId: params.embedId,
|
|
6548
6870
|
dashboardName: params.dashboardName,
|
|
6549
6871
|
workspaceName: params.workspaceName,
|
|
6872
|
+
dataAppName: params.dataAppName,
|
|
6550
6873
|
required: true
|
|
6551
6874
|
});
|
|
6552
6875
|
if (!embed.ok) return toolError(embed.error);
|
|
@@ -6594,21 +6917,21 @@ Note: this is a per-client soft delete. The widget remains available for other c
|
|
|
6594
6917
|
}
|
|
6595
6918
|
|
|
6596
6919
|
// src/tools/widgets/generate-widget.ts
|
|
6597
|
-
import { z as
|
|
6920
|
+
import { z as z37 } from "zod";
|
|
6598
6921
|
function registerGenerateWidget(server, client) {
|
|
6599
6922
|
const services = createServiceBundle(client);
|
|
6600
6923
|
const tool = server.tool(
|
|
6601
6924
|
"generate_widget",
|
|
6602
6925
|
"AI-generate a widget draft from a natural-language prompt. Does NOT persist \u2014 returns a draft to pass to `add_widget`. Non-technical flow: `clientId` + `dashboardName` (+ `workspaceName` if ambiguous); data app and datasource auto-resolve. Never ask the user for data app, datasource, or integration.",
|
|
6603
6926
|
{
|
|
6604
|
-
clientId:
|
|
6605
|
-
userInputs:
|
|
6606
|
-
dashboardName:
|
|
6607
|
-
workspaceName:
|
|
6608
|
-
embedId:
|
|
6609
|
-
dataAppName:
|
|
6610
|
-
integrationId:
|
|
6611
|
-
integrationName:
|
|
6927
|
+
clientId: z37.string().describe("Client/tenant identifier"),
|
|
6928
|
+
userInputs: z37.record(z37.unknown()).describe('Prompt + context object. Typical shape: { prompt: "calls per day over last 30 days", schema: {...}, examples: [...] }. Backend decides which keys it honors.'),
|
|
6929
|
+
dashboardName: z37.string().optional().describe("Dashboard name (non-technical). Preferred input for CS/CX flows \u2014 pass this with optional `workspaceName` instead of `embedId`."),
|
|
6930
|
+
workspaceName: z37.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
|
|
6931
|
+
embedId: z37.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
|
|
6932
|
+
dataAppName: z37.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
|
|
6933
|
+
integrationId: z37.string().optional().describe("Technical override \u2014 the datasource UUID. Non-technical callers must leave this blank; the datasource is inherited from the dashboard's existing widgets."),
|
|
6934
|
+
integrationName: z37.string().optional().describe("Technical override \u2014 datasource display name. Non-technical callers must leave this blank; the datasource is inherited from the dashboard's existing widgets.")
|
|
6612
6935
|
},
|
|
6613
6936
|
READ_ONLY,
|
|
6614
6937
|
async (params) => {
|
|
@@ -6617,6 +6940,7 @@ function registerGenerateWidget(server, client) {
|
|
|
6617
6940
|
embedId: params.embedId,
|
|
6618
6941
|
dashboardName: params.dashboardName,
|
|
6619
6942
|
workspaceName: params.workspaceName,
|
|
6943
|
+
dataAppName: params.dataAppName,
|
|
6620
6944
|
required: true
|
|
6621
6945
|
});
|
|
6622
6946
|
if (!embed.ok) return toolError(embed.error);
|
|
@@ -6693,9 +7017,9 @@ function registerGenerateWidget(server, client) {
|
|
|
6693
7017
|
}
|
|
6694
7018
|
|
|
6695
7019
|
// src/tools/widgets/create-or-update-metric-from-query.ts
|
|
6696
|
-
import { z as
|
|
6697
|
-
var modeSchema =
|
|
6698
|
-
var conflictStrategySchema =
|
|
7020
|
+
import { z as z38 } from "zod";
|
|
7021
|
+
var modeSchema = z38.enum(["dry_run", "apply"]).default("dry_run");
|
|
7022
|
+
var conflictStrategySchema = z38.enum(["fail", "update", "create_new"]).default("fail");
|
|
6699
7023
|
function makePlanText(plan) {
|
|
6700
7024
|
return JSON.stringify(plan, null, 2);
|
|
6701
7025
|
}
|
|
@@ -6712,22 +7036,22 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
|
|
|
6712
7036
|
"[EMBEDDING] Create or update a production metric/widget on a customer dashboard from validated SQL. Requires clientId (multi-tenant embed). Defaults to dry-run; apply requires explicit confirmation.",
|
|
6713
7037
|
{
|
|
6714
7038
|
mode: modeSchema.describe("dry_run returns the plan only; apply executes after confirmation"),
|
|
6715
|
-
confirm:
|
|
7039
|
+
confirm: z38.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
|
|
6716
7040
|
conflictStrategy: conflictStrategySchema.describe("What to do if metricId already exists on the target dashboard"),
|
|
6717
|
-
clientId:
|
|
6718
|
-
workspaceName:
|
|
6719
|
-
dashboardName:
|
|
6720
|
-
embedId:
|
|
6721
|
-
dataAppName:
|
|
6722
|
-
name:
|
|
6723
|
-
metricId:
|
|
6724
|
-
query:
|
|
6725
|
-
description:
|
|
6726
|
-
chartOptions:
|
|
6727
|
-
companyIntegrationId:
|
|
6728
|
-
integrationName:
|
|
6729
|
-
isPublished:
|
|
6730
|
-
requestedBy:
|
|
7041
|
+
clientId: z38.string().describe("Client/tenant identifier for the dashboard widget scope"),
|
|
7042
|
+
workspaceName: z38.string().describe("Target workspace name"),
|
|
7043
|
+
dashboardName: z38.string().optional().describe("Dashboard name to attach to"),
|
|
7044
|
+
embedId: z38.string().optional().describe("Technical embed id alternative to dashboardName"),
|
|
7045
|
+
dataAppName: z38.string().optional().describe("Optional data app override"),
|
|
7046
|
+
name: z38.string().describe("Metric/widget display name"),
|
|
7047
|
+
metricId: z38.string().describe("Stable metric slug to create or update"),
|
|
7048
|
+
query: z38.string().describe("SQL query already validated against the warehouse"),
|
|
7049
|
+
description: z38.string().optional().describe("Metric description"),
|
|
7050
|
+
chartOptions: z38.record(z38.unknown()).optional().describe("Chart configuration JSON"),
|
|
7051
|
+
companyIntegrationId: z38.string().optional().describe("Datasource UUID override"),
|
|
7052
|
+
integrationName: z38.string().optional().describe("Datasource display name override"),
|
|
7053
|
+
isPublished: z38.boolean().optional().describe("Publish after apply. Defaults to false for production safety"),
|
|
7054
|
+
requestedBy: z38.string().optional().describe("Human operator/request context to store on the metric when supported")
|
|
6731
7055
|
},
|
|
6732
7056
|
WRITE_IDEMPOTENT,
|
|
6733
7057
|
async (params) => {
|
|
@@ -6741,11 +7065,12 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
|
|
|
6741
7065
|
embedId: params.embedId,
|
|
6742
7066
|
dashboardName: params.dashboardName,
|
|
6743
7067
|
workspaceName: params.workspaceName,
|
|
7068
|
+
dataAppName: params.dataAppName,
|
|
6744
7069
|
required: true
|
|
6745
7070
|
});
|
|
6746
7071
|
if (!embed.ok) return toolError(embed.error);
|
|
6747
7072
|
const embedId = embed.value.embedId;
|
|
6748
|
-
const ctx = await services.widgetService.resolveEmbedContext(embedId);
|
|
7073
|
+
const ctx = await services.widgetService.resolveEmbedContext(embedId, params.dataAppName);
|
|
6749
7074
|
if (!ctx.externalDashboardId) {
|
|
6750
7075
|
return toolError(
|
|
6751
7076
|
`Dashboard "${params.dashboardName || embedId}" has no externalDashboardId. Verify with get_embed_details before creating metrics.`
|
|
@@ -6887,8 +7212,8 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
|
|
|
6887
7212
|
}
|
|
6888
7213
|
|
|
6889
7214
|
// src/tools/widgets/create-workspace-metric.ts
|
|
6890
|
-
import { z as
|
|
6891
|
-
var modeSchema2 =
|
|
7215
|
+
import { z as z39 } from "zod";
|
|
7216
|
+
var modeSchema2 = z39.enum(["dry_run", "apply"]).default("dry_run");
|
|
6892
7217
|
function registerCreateWorkspaceMetric(server, client) {
|
|
6893
7218
|
server.tool(
|
|
6894
7219
|
"create_workspace_metric",
|
|
@@ -6897,18 +7222,18 @@ function registerCreateWorkspaceMetric(server, client) {
|
|
|
6897
7222
|
mode: modeSchema2.describe(
|
|
6898
7223
|
"dry_run validates the SQL only (no write); apply creates or updates the metric after confirmation"
|
|
6899
7224
|
),
|
|
6900
|
-
confirm:
|
|
6901
|
-
workspaceName:
|
|
6902
|
-
name:
|
|
6903
|
-
query:
|
|
7225
|
+
confirm: z39.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
|
|
7226
|
+
workspaceName: z39.string().describe("Target workspace name"),
|
|
7227
|
+
name: z39.string().describe("Metric display name"),
|
|
7228
|
+
query: z39.string().describe(
|
|
6904
7229
|
"SQL query to validate and store. In a TABLE-tenancy workspace never compare the client column to a fixed client value; use `client_id_variable` so each client sees only its own rows (the backend refuses it with TENANT_KEY_LITERAL)."
|
|
6905
7230
|
),
|
|
6906
|
-
metricId:
|
|
7231
|
+
metricId: z39.string().optional().describe(
|
|
6907
7232
|
"Stable metric slug. Pass an existing slug to UPDATE that metric in place. Omit to create \u2014 the slug is derived deterministically from the name (no random/uniqueness suffix), so re-running with the same name upserts the same metric rather than creating a duplicate."
|
|
6908
7233
|
),
|
|
6909
|
-
description:
|
|
6910
|
-
datasourceId:
|
|
6911
|
-
isPublished:
|
|
7234
|
+
description: z39.string().optional().describe("Metric description"),
|
|
7235
|
+
datasourceId: z39.string().optional().describe("Datasource UUID override; defaults to the workspace datasource"),
|
|
7236
|
+
isPublished: z39.boolean().optional().describe(
|
|
6912
7237
|
"Publish after apply. Defaults to false. Applies to creates; on updates the existing publish state is preserved."
|
|
6913
7238
|
)
|
|
6914
7239
|
},
|
|
@@ -6964,25 +7289,25 @@ function registerCreateWorkspaceMetric(server, client) {
|
|
|
6964
7289
|
}
|
|
6965
7290
|
|
|
6966
7291
|
// src/tools/widgets/validate-metric-spec.ts
|
|
6967
|
-
import { z as
|
|
7292
|
+
import { z as z40 } from "zod";
|
|
6968
7293
|
function registerValidateMetricSpec(server, client) {
|
|
6969
7294
|
const services = createServiceBundle(client);
|
|
6970
7295
|
server.tool(
|
|
6971
7296
|
"validate_metric_spec",
|
|
6972
7297
|
"Validate a production metric spec from SQL without saving it. Resolves dashboard, data app, datasource, conflicts, and required fields.",
|
|
6973
7298
|
{
|
|
6974
|
-
clientId:
|
|
6975
|
-
workspaceName:
|
|
6976
|
-
dashboardName:
|
|
6977
|
-
embedId:
|
|
6978
|
-
dataAppName:
|
|
6979
|
-
name:
|
|
6980
|
-
metricId:
|
|
6981
|
-
query:
|
|
6982
|
-
description:
|
|
6983
|
-
chartOptions:
|
|
6984
|
-
companyIntegrationId:
|
|
6985
|
-
integrationName:
|
|
7299
|
+
clientId: z40.string().describe("Client/tenant identifier used to validate dashboard widget scope"),
|
|
7300
|
+
workspaceName: z40.string().describe("Target production-backed workspace name"),
|
|
7301
|
+
dashboardName: z40.string().optional().describe("Dashboard name to attach to"),
|
|
7302
|
+
embedId: z40.string().optional().describe("Technical embed id alternative to dashboardName"),
|
|
7303
|
+
dataAppName: z40.string().optional().describe("Optional data app override"),
|
|
7304
|
+
name: z40.string().describe("Metric/widget display name"),
|
|
7305
|
+
metricId: z40.string().describe("Stable metric slug to create or update"),
|
|
7306
|
+
query: z40.string().describe("SQL query already validated against the warehouse"),
|
|
7307
|
+
description: z40.string().optional().describe("Metric description"),
|
|
7308
|
+
chartOptions: z40.record(z40.unknown()).optional().describe("Chart configuration JSON"),
|
|
7309
|
+
companyIntegrationId: z40.string().optional().describe("Datasource UUID override"),
|
|
7310
|
+
integrationName: z40.string().optional().describe("Datasource display name override")
|
|
6986
7311
|
},
|
|
6987
7312
|
READ_ONLY,
|
|
6988
7313
|
async (params) => {
|
|
@@ -6994,11 +7319,12 @@ function registerValidateMetricSpec(server, client) {
|
|
|
6994
7319
|
embedId: params.embedId,
|
|
6995
7320
|
dashboardName: params.dashboardName,
|
|
6996
7321
|
workspaceName: params.workspaceName,
|
|
7322
|
+
dataAppName: params.dataAppName,
|
|
6997
7323
|
required: true
|
|
6998
7324
|
});
|
|
6999
7325
|
if (!embed.ok) return toolError(embed.error);
|
|
7000
7326
|
const embedId = embed.value.embedId;
|
|
7001
|
-
const ctx = await services.widgetService.resolveEmbedContext(embedId);
|
|
7327
|
+
const ctx = await services.widgetService.resolveEmbedContext(embedId, params.dataAppName);
|
|
7002
7328
|
if (!ctx.externalDashboardId) {
|
|
7003
7329
|
return toolError(
|
|
7004
7330
|
`Dashboard "${params.dashboardName || embedId}" has no externalDashboardId. Verify with get_embed_details before creating metrics.`
|
|
@@ -7073,20 +7399,21 @@ function registerValidateMetricSpec(server, client) {
|
|
|
7073
7399
|
}
|
|
7074
7400
|
|
|
7075
7401
|
// src/tools/reports/list-scheduled-reports.ts
|
|
7076
|
-
import { z as
|
|
7402
|
+
import { z as z41 } from "zod";
|
|
7077
7403
|
function registerListScheduledReports(server, client) {
|
|
7078
7404
|
const services = createServiceBundle(client);
|
|
7079
7405
|
server.tool(
|
|
7080
7406
|
"list_scheduled_reports",
|
|
7081
7407
|
"List configured scheduled reports for a specific client/dashboard embed. Shows schedule metadata, not delivery success history.",
|
|
7082
7408
|
{
|
|
7083
|
-
clientId:
|
|
7084
|
-
dashboardName:
|
|
7085
|
-
workspaceName:
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7409
|
+
clientId: z41.string().describe("Client/tenant identifier. Required by the current backend API for non-empty results"),
|
|
7410
|
+
dashboardName: z41.string().optional().describe("Dashboard name to inspect"),
|
|
7411
|
+
workspaceName: z41.string().optional().describe("Workspace name if dashboardName is ambiguous"),
|
|
7412
|
+
dataAppName: z41.string().trim().min(1).optional().describe("Data app name when dashboard embeds span several apps"),
|
|
7413
|
+
embedId: z41.string().optional().describe("Technical embed id alternative to dashboardName"),
|
|
7414
|
+
userIdentifier: z41.string().optional().describe("Optional creator/user filter"),
|
|
7415
|
+
pageNumber: z41.number().int().positive().optional().describe("Page number when pagination is enabled"),
|
|
7416
|
+
isPagination: z41.boolean().optional().describe("Enable backend pagination")
|
|
7090
7417
|
},
|
|
7091
7418
|
READ_ONLY,
|
|
7092
7419
|
async (params) => {
|
|
@@ -7095,12 +7422,16 @@ function registerListScheduledReports(server, client) {
|
|
|
7095
7422
|
embedId: params.embedId,
|
|
7096
7423
|
dashboardName: params.dashboardName,
|
|
7097
7424
|
workspaceName: params.workspaceName,
|
|
7425
|
+
dataAppName: params.dataAppName,
|
|
7098
7426
|
required: true
|
|
7099
7427
|
});
|
|
7100
7428
|
if (!embed.ok) return toolError(embed.error);
|
|
7101
7429
|
const embedId = embed.value.embedId;
|
|
7430
|
+
const app = client.isHosted() ? await services.widgetService.resolveDataAppName(params.dataAppName, embedId) : { ok: true, value: params.dataAppName };
|
|
7431
|
+
if (!app.ok) return toolError(app.error);
|
|
7102
7432
|
const reports = await services.scheduledReportsApi.listByEmbed({
|
|
7103
7433
|
embedId,
|
|
7434
|
+
dataAppName: app.value,
|
|
7104
7435
|
clientId: params.clientId,
|
|
7105
7436
|
userIdentifier: params.userIdentifier,
|
|
7106
7437
|
pageNumber: params.pageNumber,
|
|
@@ -7196,90 +7527,18 @@ function registerWhoami(server, client) {
|
|
|
7196
7527
|
}
|
|
7197
7528
|
|
|
7198
7529
|
// src/tools/custom-chart/scaffold.ts
|
|
7199
|
-
import {
|
|
7200
|
-
|
|
7201
|
-
// src/tools/custom-chart/shared.ts
|
|
7202
|
-
import { z as z40 } from "zod";
|
|
7203
|
-
var slotInputSchema = slotSchema;
|
|
7204
|
-
var optionInputSchema = optionSchema;
|
|
7205
|
-
var manifestInputSchema = z40.record(z40.unknown()).describe("manifest.json as an object (schemaVersion 2). Validated with the same rules as the server.");
|
|
7206
|
-
var bundleInputShape = {
|
|
7207
|
-
manifest: manifestInputSchema.optional(),
|
|
7208
|
-
bundleJs: z40.string().optional().describe("dist/index.js from `databrain custom-chart build` (the shim plus the chart module)."),
|
|
7209
|
-
bundleCss: z40.string().optional().describe("The raw chart.css, kept for provenance."),
|
|
7210
|
-
iconSvg: z40.string().optional().describe("icon.svg, at most 16 KiB, no script, no foreignObject, no external href."),
|
|
7211
|
-
projectDir: z40.string().optional().describe("Local only: a chart project folder. The tool reads it instead of manifest/bundleJs/iconSvg.")
|
|
7212
|
-
};
|
|
7213
|
-
function formatProblems(problems) {
|
|
7214
|
-
if (problems.length === 0) return "No problems.";
|
|
7215
|
-
return problems.map((p) => `${p.path || "/"} ${p.code} ${p.message}`).join("\n");
|
|
7216
|
-
}
|
|
7217
|
-
function projectDirRefusal() {
|
|
7218
|
-
if (process.env.DATABRAIN_HOSTED_PUBLIC_URL) {
|
|
7219
|
-
return "projectDir is not available on the hosted server. Pass manifest, bundleJs, bundleCss and iconSvg instead.";
|
|
7220
|
-
}
|
|
7221
|
-
return null;
|
|
7222
|
-
}
|
|
7223
|
-
async function resolveBundleInput(params, options) {
|
|
7224
|
-
if (params.projectDir) {
|
|
7225
|
-
const refusal = projectDirRefusal();
|
|
7226
|
-
if (refusal) return { problems: [], error: refusal, source: "projectDir" };
|
|
7227
|
-
if (params.manifest || params.bundleJs || params.iconSvg) {
|
|
7228
|
-
return { problems: [], error: "Pass either projectDir or manifest/bundleJs/iconSvg, not both.", source: "projectDir" };
|
|
7229
|
-
}
|
|
7230
|
-
if (options.build) {
|
|
7231
|
-
const result = await buildProject(params.projectDir, { write: false });
|
|
7232
|
-
if (!result.ok || !result.outputs) return { problems: result.problems, source: "projectDir" };
|
|
7233
|
-
const { manifest, bundleJs, bundleCss, iconSvg } = result.outputs;
|
|
7234
|
-
return { bundle: { manifest, bundleJs, bundleCss, iconSvg }, problems: [], source: "projectDir" };
|
|
7235
|
-
}
|
|
7236
|
-
const dist = readDist(params.projectDir);
|
|
7237
|
-
if (!dist.bundle) return { problems: dist.problems, source: "projectDir" };
|
|
7238
|
-
return { bundle: dist.bundle, problems: [], source: "projectDir" };
|
|
7239
|
-
}
|
|
7240
|
-
if (!params.manifest) {
|
|
7241
|
-
return { problems: [], error: "manifest is required (or pass projectDir when running locally).", source: "arguments" };
|
|
7242
|
-
}
|
|
7243
|
-
return {
|
|
7244
|
-
bundle: {
|
|
7245
|
-
manifest: params.manifest,
|
|
7246
|
-
bundleJs: params.bundleJs,
|
|
7247
|
-
bundleCss: params.bundleCss,
|
|
7248
|
-
iconSvg: params.iconSvg
|
|
7249
|
-
},
|
|
7250
|
-
problems: [],
|
|
7251
|
-
source: "arguments"
|
|
7252
|
-
};
|
|
7253
|
-
}
|
|
7254
|
-
async function remoteValidate(client, bundle) {
|
|
7255
|
-
try {
|
|
7256
|
-
const result = await createCustomChartsApi(client).validate(bundle);
|
|
7257
|
-
return { status: "ok", result };
|
|
7258
|
-
} catch (err) {
|
|
7259
|
-
if (err instanceof DatabrainApiError) {
|
|
7260
|
-
if (err.code === "MISSING_SERVICE_TOKEN") {
|
|
7261
|
-
return { status: "skipped", reason: "No service token is configured, so only the local validator ran." };
|
|
7262
|
-
}
|
|
7263
|
-
return { status: "failed", reason: err.message, problems: apiProblems(err) };
|
|
7264
|
-
}
|
|
7265
|
-
return { status: "failed", reason: err.message, problems: [] };
|
|
7266
|
-
}
|
|
7267
|
-
}
|
|
7268
|
-
function isCompleteBundle(bundle) {
|
|
7269
|
-
return typeof bundle.bundleJs === "string" && typeof bundle.iconSvg === "string";
|
|
7270
|
-
}
|
|
7271
|
-
|
|
7272
|
-
// src/tools/custom-chart/scaffold.ts
|
|
7530
|
+
import { transformSync } from "esbuild";
|
|
7531
|
+
import { z as z42 } from "zod";
|
|
7273
7532
|
function registerCustomChartScaffold(server, _client) {
|
|
7274
7533
|
server.tool(
|
|
7275
7534
|
"custom_chart_scaffold",
|
|
7276
|
-
"Return the files of a new custom chart project (manifest.json, chart.ts, chart.css, icon.svg, sample data, guide). Nothing is
|
|
7535
|
+
"Return the files of a new custom chart project (manifest.json, chart.ts, chart.css, icon.svg, sample data, guide). Nothing is persisted. For a hosted AI app, edit the returned draft and pass chartTs to validate, preview and upload without local files.",
|
|
7277
7536
|
{
|
|
7278
|
-
key:
|
|
7279
|
-
name:
|
|
7280
|
-
description:
|
|
7281
|
-
slots:
|
|
7282
|
-
options:
|
|
7537
|
+
key: z42.string().describe("Chart key, becomes chart type custom:<key>. Lowercase letters, digits, _ and -, 64 max."),
|
|
7538
|
+
name: z42.string().describe("Display name shown in the chart picker, 1 to 256 characters."),
|
|
7539
|
+
description: z42.string().optional().describe("Optional description, up to 500 characters."),
|
|
7540
|
+
slots: z42.array(slotInputSchema).optional().describe("Data slots. Defaults to category (dimension), value (measure), target (optional measure)."),
|
|
7541
|
+
options: z42.array(optionInputSchema).optional().describe("Chart options. Defaults to the template options (labels, subtitle, radius, colour, sort).")
|
|
7283
7542
|
},
|
|
7284
7543
|
READ_ONLY,
|
|
7285
7544
|
async (params) => {
|
|
@@ -7302,7 +7561,19 @@ ${formatProblems(result.problems)}`);
|
|
|
7302
7561
|
key: params.key,
|
|
7303
7562
|
chartType: `custom:${params.key}`,
|
|
7304
7563
|
files: result.files,
|
|
7305
|
-
|
|
7564
|
+
draft: {
|
|
7565
|
+
code: transformSync(result.files["chart.ts"], { loader: "ts", format: "esm" }).code.replace(/export\s*\{[^}]*\};?\s*$/, ""),
|
|
7566
|
+
css: result.files["chart.css"],
|
|
7567
|
+
manifest: JSON.parse(result.files["manifest.json"]),
|
|
7568
|
+
sampleData: (() => {
|
|
7569
|
+
const sample = JSON.parse(result.files["sample-data.json"]);
|
|
7570
|
+
return { columns: sample.columns.map(({ id: id2, label, type, slotId }) => ({ id: id2, label, type, ...slotId ? { slotId } : {} })), rows: sample.rows };
|
|
7571
|
+
})(),
|
|
7572
|
+
name: params.name,
|
|
7573
|
+
description: params.description ?? "",
|
|
7574
|
+
iconSvg: result.files["icon.svg"]
|
|
7575
|
+
},
|
|
7576
|
+
nextStep: "Hosted: pass draft to custom_chart_edit with a description and workspaceId. Then validate/preview/upload using chartTs=draft.code, manifest, bundleCss=draft.css and iconSvg. Upload saves a version; publishing requires explicit confirmation. Local project: " + result.nextStep
|
|
7306
7577
|
},
|
|
7307
7578
|
null,
|
|
7308
7579
|
2
|
|
@@ -7371,18 +7642,18 @@ function registerCustomChartValidate(server, client) {
|
|
|
7371
7642
|
}
|
|
7372
7643
|
|
|
7373
7644
|
// src/tools/custom-chart/upload.ts
|
|
7374
|
-
import { z as
|
|
7645
|
+
import { z as z43 } from "zod";
|
|
7375
7646
|
function registerCustomChartUpload(server, client) {
|
|
7376
7647
|
const api = createCustomChartsApi(client);
|
|
7377
7648
|
server.tool(
|
|
7378
7649
|
"custom_chart_upload",
|
|
7379
7650
|
"Upload a built custom chart bundle to the company. Creates the chart (private, version 1) or a new preview version. Defaults to dry_run; apply requires explicit confirmation and can publish in the same call.",
|
|
7380
7651
|
{
|
|
7381
|
-
mode:
|
|
7382
|
-
confirm:
|
|
7383
|
-
publish:
|
|
7384
|
-
name:
|
|
7385
|
-
description:
|
|
7652
|
+
mode: z43.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run validates and returns the plan; apply uploads."),
|
|
7653
|
+
confirm: z43.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
|
|
7654
|
+
publish: z43.boolean().default(false).describe("After a successful apply, publish the uploaded version so every builder can use it."),
|
|
7655
|
+
name: z43.string().optional().describe("Display name override. Defaults to manifest.name."),
|
|
7656
|
+
description: z43.string().optional().describe("Description override. Defaults to manifest.description."),
|
|
7386
7657
|
...bundleInputShape
|
|
7387
7658
|
},
|
|
7388
7659
|
WRITE_CREATE,
|
|
@@ -7523,17 +7794,17 @@ function registerListCustomCharts(server, client) {
|
|
|
7523
7794
|
}
|
|
7524
7795
|
|
|
7525
7796
|
// src/tools/custom-chart/publish.ts
|
|
7526
|
-
import { z as
|
|
7797
|
+
import { z as z44 } from "zod";
|
|
7527
7798
|
function registerPublishCustomChart(server, client) {
|
|
7528
7799
|
const api = createCustomChartsApi(client);
|
|
7529
7800
|
server.tool(
|
|
7530
7801
|
"publish_custom_chart",
|
|
7531
7802
|
"Publish a custom chart version so every builder in the company can use chart type custom:<key>. Defaults to the preview version. Defaults to dry_run; apply requires explicit confirmation.",
|
|
7532
7803
|
{
|
|
7533
|
-
key:
|
|
7534
|
-
versionId:
|
|
7535
|
-
mode:
|
|
7536
|
-
confirm:
|
|
7804
|
+
key: z44.string().describe("The chart key (manifest.id), for example revenue-bars."),
|
|
7805
|
+
versionId: z44.string().optional().describe("A specific version id to publish. Defaults to the preview version."),
|
|
7806
|
+
mode: z44.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run shows what would be published; apply publishes."),
|
|
7807
|
+
confirm: z44.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`)
|
|
7537
7808
|
},
|
|
7538
7809
|
WRITE_IDEMPOTENT,
|
|
7539
7810
|
async (params) => {
|
|
@@ -7629,6 +7900,7 @@ var TOOL_TITLES = {
|
|
|
7629
7900
|
apply_semantic_change: "Apply semantic model change",
|
|
7630
7901
|
eval_semantic_model: "Evaluate semantic model",
|
|
7631
7902
|
list_widgets: "List widgets",
|
|
7903
|
+
render_metric_tile: "Render a saved metric tile",
|
|
7632
7904
|
validate_metric_spec: "Validate metric spec",
|
|
7633
7905
|
create_or_update_metric_from_query: "Create or update metric from SQL",
|
|
7634
7906
|
create_workspace_metric: "Create workspace metric",
|
|
@@ -7728,6 +8000,7 @@ function registerAllTools(server, client) {
|
|
|
7728
8000
|
registerGenerateWidget(server, client);
|
|
7729
8001
|
registerListScheduledReports(server, client);
|
|
7730
8002
|
registerCustomChartScaffold(server, client);
|
|
8003
|
+
registerCustomChartAuthor(server, client);
|
|
7731
8004
|
registerCustomChartValidate(server, client);
|
|
7732
8005
|
registerCustomChartUpload(server, client);
|
|
7733
8006
|
registerListCustomCharts(server, client);
|
|
@@ -9619,7 +9892,7 @@ The v2 source is one YAML document. Use the configured service credential and a
|
|
|
9619
9892
|
|
|
9620
9893
|
1. \`search_semantic_model\` finds entities by question. \`get_semantic_entity\` reads one entity by kind and name. Column names use table.column.
|
|
9621
9894
|
2. \`propose_semantic_change\` takes complete source YAML, a YAML patch or one structured change. It returns candidate source, validation errors, a plan and the stored expected hash without writing.
|
|
9622
|
-
3. \`eval_semantic_model\`
|
|
9895
|
+
3. \`eval_semantic_model\` checks closed-world assertions against the proposed source; optional questions use id, question and exercises (exact entity names). It does not execute SQL.
|
|
9623
9896
|
4. \`apply_semantic_change\` defaults to a dry run. Live apply requires the exact reviewed source, \`expected_content_hash\`, \`dry_run: false\` and user supplied \`confirm: "APPLY_TO_PRODUCTION"\`. Never supply confirmation yourself. Apply changes the live model for all consumers. On a conflict, prepare a fresh proposal and review it again.
|
|
9624
9897
|
|
|
9625
9898
|
YAML patches merge mappings and named lists by name. Other lists replace. Empty lists clear; null removes a field. Structured changes use operation upsert or remove, kind, name, and value for upsert. Column changes also require table. Patch output normalizes formatting and comments, so review the returned source before apply.
|
|
@@ -10700,7 +10973,7 @@ var populateSemanticLayerPrompt = `You are helping the user populate the semanti
|
|
|
10700
10973
|
If \`list_datamarts\` reports \`semanticLayerVersion: "v2"\`, or the selected datamart is already known to use v2, use this workflow instead of the legacy steps below:
|
|
10701
10974
|
1. Inspect relevant entities with \`search_semantic_model\` and \`get_semantic_entity\`, using the datamart ID.
|
|
10702
10975
|
2. Call \`propose_semantic_change\` with a YAML patch or structured change. Show its errors and plan. Correct errors before continuing.
|
|
10703
|
-
3. Call \`eval_semantic_model\` with the returned source. Show the verdicts and let the user review the candidate.
|
|
10976
|
+
3. Call \`eval_semantic_model\` with the returned source. For scored assertions, supply questions with id, question and exercises (exact model entity names); this does not run SQL. Show the verdicts and let the user review the candidate.
|
|
10704
10977
|
4. Stop for the user's explicit \`APPLY_TO_PRODUCTION\` confirmation. Never supply it yourself.
|
|
10705
10978
|
5. Call \`apply_semantic_change\` with the exact reviewed source, its \`expected_content_hash\`, \`dry_run: false\` and the user's confirmation. A conflict requires a new proposal and review.
|
|
10706
10979
|
6. Call \`get_semantic_entity\` and inspect the changed entity. Do not call \`update_semantic_layer\` for a v2 model.
|
|
@@ -11066,20 +11339,24 @@ function registerAllPrompts(server) {
|
|
|
11066
11339
|
}
|
|
11067
11340
|
|
|
11068
11341
|
export {
|
|
11069
|
-
createServiceBundle,
|
|
11070
11342
|
READ_ONLY,
|
|
11071
11343
|
WRITE_CREATE,
|
|
11072
|
-
APPLY_TO_PRODUCTION_CONFIRMATION,
|
|
11073
11344
|
CHART_URI,
|
|
11074
11345
|
KPI_URI,
|
|
11075
11346
|
APP_MIME,
|
|
11347
|
+
MAX_CUSTOM_BUNDLE_CHARS,
|
|
11348
|
+
CUSTOM_BUNDLE_CHUNK_CHARS,
|
|
11076
11349
|
rowsSchema,
|
|
11077
11350
|
referenceSchema,
|
|
11351
|
+
dateRangeSchema,
|
|
11078
11352
|
viewSchema,
|
|
11079
11353
|
pageSchema,
|
|
11354
|
+
bounded,
|
|
11080
11355
|
appMeta,
|
|
11081
11356
|
enableChartApps,
|
|
11082
11357
|
chartResult,
|
|
11358
|
+
createServiceBundle,
|
|
11359
|
+
APPLY_TO_PRODUCTION_CONFIRMATION,
|
|
11083
11360
|
draftSchema,
|
|
11084
11361
|
PREVIEW_PATH,
|
|
11085
11362
|
RENDER_TYPES,
|
|
@@ -11090,4 +11367,4 @@ export {
|
|
|
11090
11367
|
registerAllResources,
|
|
11091
11368
|
registerAllPrompts
|
|
11092
11369
|
};
|
|
11093
|
-
//# sourceMappingURL=chunk-
|
|
11370
|
+
//# sourceMappingURL=chunk-OLOC2MTA.js.map
|