@databrainhq/mcp-server 0.3.0-rc.4 → 0.3.0-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +56 -1
  2. package/dist/apps/server.d.ts +6 -6
  3. package/dist/apps/server.js +3 -2
  4. package/dist/apps/stdio.js +4 -3
  5. package/dist/apps/stdio.js.map +1 -1
  6. package/dist/{chunk-CX3CS62P.js → chunk-3GF7WSKK.js} +4 -2
  7. package/dist/chunk-3GF7WSKK.js.map +1 -0
  8. package/dist/{chunk-NZGQJBJV.js → chunk-7G5QBEPC.js} +8 -6
  9. package/dist/{chunk-NZGQJBJV.js.map → chunk-7G5QBEPC.js.map} +1 -1
  10. package/dist/{chunk-3JFMWXRO.js → chunk-SDFC7PKT.js} +2 -2
  11. package/dist/chunk-SKU37UVJ.js +1934 -0
  12. package/dist/chunk-SKU37UVJ.js.map +1 -0
  13. package/dist/{chunk-I35U2LBX.js → chunk-Z2SF3K6P.js} +955 -1118
  14. package/dist/chunk-Z2SF3K6P.js.map +1 -0
  15. package/dist/cli.d.ts +15 -0
  16. package/dist/cli.js +495 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/custom-chart/runtime-shim.js +146 -0
  19. package/dist/custom-chart/template/AGENTS.md +374 -0
  20. package/dist/custom-chart/template/README.md +374 -0
  21. package/dist/custom-chart/template/chart.css +28 -0
  22. package/dist/custom-chart/template/chart.ts +192 -0
  23. package/dist/custom-chart/template/custom-chart.d.ts +78 -0
  24. package/dist/custom-chart/template/icon.svg +1 -0
  25. package/dist/custom-chart/template/manifest.json +23 -0
  26. package/dist/custom-chart/template/package.json +14 -0
  27. package/dist/custom-chart/template/sample-data.json +18 -0
  28. package/dist/custom-chart/template/tsconfig.json +12 -0
  29. package/dist/index.js +11 -7
  30. package/dist/index.js.map +1 -1
  31. package/dist/{main-QHUNQWID.js → main-NGBJJTV6.js} +34 -8
  32. package/dist/main-NGBJJTV6.js.map +1 -0
  33. package/dist/{test-only-B4EVUNFU.js → test-only-TJ3DQUYW.js} +2 -2
  34. package/package.json +6 -3
  35. package/dist/chunk-CX3CS62P.js.map +0 -1
  36. package/dist/chunk-I35U2LBX.js.map +0 -1
  37. package/dist/main-QHUNQWID.js.map +0 -1
  38. /package/dist/{chunk-3JFMWXRO.js.map → chunk-SDFC7PKT.js.map} +0 -0
  39. /package/dist/{test-only-B4EVUNFU.js.map → test-only-TJ3DQUYW.js.map} +0 -0
@@ -1,620 +1,68 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/utils/logger.ts
4
- var LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
5
- var currentLevel = process.env.DATABRAIN_LOG_LEVEL || "warn";
6
- function log(level, message, data) {
7
- if (LOG_LEVELS[level] < LOG_LEVELS[currentLevel]) return;
8
- const line = `[databrain-mcp] [${level.toUpperCase()}] ${message}`;
9
- if (data !== void 0) {
10
- process.stderr.write(`${line} ${JSON.stringify(data)}
11
- `);
12
- } else {
13
- process.stderr.write(`${line}
14
- `);
15
- }
16
- }
17
- var logger = {
18
- debug: (msg, data) => log("debug", msg, data),
19
- info: (msg, data) => log("info", msg, data),
20
- warn: (msg, data) => log("warn", msg, data),
21
- error: (msg, data) => log("error", msg, data)
22
- };
23
-
24
- // package.json
25
- var package_default = {
26
- name: "@databrainhq/mcp-server",
27
- version: "0.3.0-rc.4",
28
- description: "MCP server for Databrain embedded analytics - connect databases, create datamarts, configure embeds, and generate frontend code",
29
- type: "module",
30
- bin: {
31
- "mcp-server": "dist/index.js",
32
- "databrain-mcp-server": "dist/index.js",
33
- "databrain-mcp-chart-apps": "dist/apps/stdio.js"
34
- },
35
- main: "dist/index.js",
36
- types: "dist/index.d.ts",
37
- files: [
38
- "dist",
39
- "skills",
40
- ".claude-plugin",
41
- ".mcp.json"
42
- ],
43
- scripts: {
44
- build: "tsup",
45
- dev: "tsx src/index.ts",
46
- test: "vitest run",
47
- "test:watch": "vitest",
48
- "test:live": "vitest run tests/integration/prompt-flows.test.ts",
49
- lint: "tsc --noEmit",
50
- prepublishOnly: "npm run build"
51
- },
52
- keywords: [
53
- "mcp",
54
- "databrain",
55
- "embedded-analytics",
56
- "ai",
57
- "model-context-protocol"
58
- ],
59
- license: "MIT",
60
- dependencies: {
61
- "@modelcontextprotocol/sdk": "^1.12.1",
62
- "@posthog/mcp": "^0.10.7",
63
- "posthog-node": "^5.21.2",
64
- zod: "^3.24.0"
65
- },
66
- devDependencies: {
67
- "@types/node": "^25.3.5",
68
- tsup: "^8.4.0",
69
- tsx: "^4.19.0",
70
- typescript: "^5.7.0",
71
- vitest: "^3.0.0"
72
- },
73
- publishConfig: {
74
- access: "public"
75
- },
76
- repository: {
77
- type: "git",
78
- url: "git+https://github.com/databrainhq/mcp-server.git"
79
- },
80
- engines: {
81
- node: ">=18.0.0"
82
- }
83
- };
84
-
85
- // src/version.ts
86
- var MCP_SERVER_VERSION = package_default.version;
87
-
88
- // src/utils/cache.ts
89
- var TtlCache = class {
90
- store = /* @__PURE__ */ new Map();
91
- get(key) {
92
- const entry = this.store.get(key);
93
- if (!entry) return void 0;
94
- if (Date.now() > entry.expiresAt) {
95
- this.store.delete(key);
96
- return void 0;
97
- }
98
- return entry.value;
99
- }
100
- set(key, value, ttlMs) {
101
- this.store.set(key, { value, expiresAt: Date.now() + ttlMs });
102
- }
103
- invalidateByPrefix(prefix) {
104
- for (const key of this.store.keys()) {
105
- if (key.startsWith(prefix)) {
106
- this.store.delete(key);
107
- }
108
- }
109
- }
110
- clear() {
111
- this.store.clear();
112
- }
113
- };
114
-
115
- // src/utils/errors.ts
116
- var DatabrainApiError = class extends Error {
117
- constructor(status, code, message, details) {
118
- super(message);
119
- this.status = status;
120
- this.code = code;
121
- this.details = details;
122
- this.name = "DatabrainApiError";
123
- }
124
- toToolResponse() {
125
- const parts = [`Error: ${this.message}`];
126
- const detailDump = this.formatDetails();
127
- if (detailDump) parts.push(detailDump);
128
- const hint = this.hint();
129
- if (hint) parts.push(hint);
130
- return {
131
- content: [{ type: "text", text: parts.join("\n\n") }],
132
- isError: true
133
- };
134
- }
135
- /**
136
- * Surface API-returned details for 4xx errors so the LLM can recover or
137
- * report the real reason (validation messages, field names, etc.). Skips
138
- * fields already collapsed into `message`/`code`, and skips 5xx where the
139
- * payload usually isn't actionable.
140
- */
141
- formatDetails() {
142
- if (this.status < 400 || this.status >= 500) return "";
143
- if (!this.details || typeof this.details !== "object") return "";
144
- const skipKeys = /* @__PURE__ */ new Set(["message", "detail", "error", "code"]);
145
- const extra = {};
146
- for (const [k, v] of Object.entries(this.details)) {
147
- if (skipKeys.has(k)) continue;
148
- if (v === null || v === void 0) continue;
149
- extra[k] = v;
150
- }
151
- if (Object.keys(extra).length === 0) return "";
152
- try {
153
- return `API details: ${JSON.stringify(extra)}`;
154
- } catch {
155
- return "";
156
- }
157
- }
158
- hint() {
159
- if (this.status === 401 || this.status === 403) {
160
- if (this.code === "OAUTH_API_ERROR") {
161
- return "Hint: Check that the backend accepts the configured OAuth issuer and resource, and that the service principal has permission for this operation. Static fallback applies only to token acquisition failures.";
162
- }
163
- if (this.code === "INVALID_SERVICE_TOKEN" || this.code === "MISSING_SERVICE_TOKEN" || this.message.toLowerCase().includes("service token")) {
164
- return "Hint: The service token (DATABRAIN_SERVICE_TOKEN) is invalid, expired, or missing. It must be set in the MCP server config (not at runtime). Check Settings > Service Tokens in the Databrain UI.";
165
- }
166
- return "Hint: The per-data-app API token is invalid or expired. Use `create_api_token` to generate a new one. Do not ask the user \u2014 manage tokens via MCP tools.";
167
- }
168
- if (this.status === 400) {
169
- return "Hint: The request was invalid. Check parameter values match expected types and formats. Use the corresponding list tool to verify IDs and names.";
170
- }
171
- if (this.status === 404) {
172
- return "Hint: The requested resource was not found. Verify the ID or name is correct using the corresponding list tool.";
173
- }
174
- if (this.status === 409) {
175
- return "Hint: A resource with this name already exists. Use a different name or list existing resources first.";
176
- }
177
- if (this.status === 422) {
178
- return "Hint: The request parameters are invalid. Check the required fields and their formats.";
179
- }
180
- if (this.status === 429) {
181
- return "Hint: API rate limit reached. Wait a moment and try again.";
182
- }
183
- if (this.status >= 500) {
184
- return "Hint: The Databrain API is experiencing issues. Try again in a few moments.";
185
- }
186
- return "";
187
- }
188
- };
189
- function toolError(message) {
190
- return {
191
- content: [{ type: "text", text: message }],
192
- isError: true
193
- };
194
- }
195
- function stringify(value) {
196
- if (typeof value === "string") return value;
197
- try {
198
- return JSON.stringify(value, null, 2);
199
- } catch {
200
- return String(value);
201
- }
202
- }
203
- function toolSuccess(text) {
204
- return {
205
- content: [{ type: "text", text }]
206
- };
207
- }
208
-
209
- // src/utils/tool-context.ts
210
- import { AsyncLocalStorage } from "async_hooks";
211
- var toolContext = new AsyncLocalStorage();
212
-
213
- // src/client/oauth-client-credentials.ts
214
- import { z } from "zod";
215
- var oauthHttpsUrl = z.string().refine((value) => {
216
- try {
217
- const url = new URL(value);
218
- return url.protocol === "https:" && !url.username && !url.password && !url.hash;
219
- } catch {
220
- return false;
221
- }
222
- }, "OAuth URLs must use HTTPS without userinfo or fragments");
223
- var oauthConfigSchema = z.object({
224
- issuer: oauthHttpsUrl.refine((value) => {
225
- try {
226
- return !new URL(value).search;
227
- } catch {
228
- return false;
229
- }
230
- }, "OAuth issuer must not contain a query"),
231
- clientId: z.string().min(1),
232
- clientSecret: z.string().min(1),
233
- resource: oauthHttpsUrl,
234
- scope: z.string().min(1).optional(),
235
- authMethod: z.enum(["client_secret_basic", "client_secret_post"]).default("client_secret_basic")
236
- });
237
- var metadataSchema = z.object({
238
- issuer: z.string(),
239
- token_endpoint: oauthHttpsUrl,
240
- token_endpoint_auth_methods_supported: z.array(z.string()).optional(),
241
- grant_types_supported: z.array(z.string()).optional()
242
- });
243
- var tokenSchema = z.object({
244
- access_token: z.string().regex(/^[A-Za-z0-9\-._~+/]+=*$/),
245
- token_type: z.string().refine((value) => value.toLowerCase() === "bearer"),
246
- expires_in: z.number().finite().positive().optional()
247
- });
248
- var TOKEN_TIMEOUT_MS = 1e4;
249
- var FAILURE_COOLDOWN_MS = 3e4;
250
- var MAX_REFRESH_SKEW_MS = 6e4;
251
- function authError() {
252
- return new Error("OAuth token acquisition failed. Check the configured issuer, client credentials, resource and scopes.");
253
- }
254
- var OAuthClientCredentials = class {
255
- config;
256
- tokenEndpoint;
257
- cached;
258
- pending;
259
- retryAt = 0;
260
- constructor(config) {
261
- const parsed = oauthConfigSchema.safeParse(config);
262
- if (!parsed.success) throw authError();
263
- this.config = Object.freeze(parsed.data);
264
- }
265
- getStatus() {
266
- if (this.cached && Date.now() < this.cached.renewAt) return "ready";
267
- if (this.retryAt > 0) return "unavailable";
268
- return "configured";
269
- }
270
- async getToken() {
271
- if (this.cached && Date.now() < this.cached.renewAt) return this.cached.value;
272
- if (this.pending) return this.pending;
273
- if (Date.now() < this.retryAt) throw authError();
274
- this.cached = void 0;
275
- this.pending = this.acquire().catch(() => {
276
- this.retryAt = Date.now() + FAILURE_COOLDOWN_MS;
277
- throw authError();
278
- }).finally(() => {
279
- this.pending = void 0;
280
- });
281
- return this.pending;
282
- }
283
- async discoverTokenEndpoint(signal) {
284
- if (this.tokenEndpoint) return this.tokenEndpoint;
285
- const issuer = new URL(this.config.issuer);
286
- const path = issuer.pathname.replace(/\/$/, "");
287
- const candidates = [.../* @__PURE__ */ new Set([
288
- `${issuer.origin}/.well-known/oauth-authorization-server${path}`,
289
- `${issuer.origin}/.well-known/openid-configuration${path}`,
290
- `${issuer.origin}${path}/.well-known/openid-configuration`
291
- ])];
292
- for (const url of candidates) {
293
- const response = await fetch(url, {
294
- headers: { Accept: "application/json" },
295
- redirect: "error",
296
- signal
297
- });
298
- if (response.status === 404 || response.status === 405) continue;
299
- if (!response.ok) throw authError();
300
- const metadata = metadataSchema.parse(await response.json());
301
- if (metadata.issuer !== this.config.issuer) throw authError();
302
- const methods = metadata.token_endpoint_auth_methods_supported ?? ["client_secret_basic"];
303
- if (!methods.includes(this.config.authMethod)) throw authError();
304
- if (metadata.grant_types_supported && !metadata.grant_types_supported.includes("client_credentials")) {
305
- throw authError();
306
- }
307
- this.tokenEndpoint = metadata.token_endpoint;
308
- return this.tokenEndpoint;
309
- }
310
- throw authError();
311
- }
312
- async acquire() {
313
- const signal = AbortSignal.timeout(TOKEN_TIMEOUT_MS);
314
- const endpoint = await this.discoverTokenEndpoint(signal);
315
- const body = new URLSearchParams({
316
- grant_type: "client_credentials",
317
- resource: this.config.resource
318
- });
319
- if (this.config.scope) body.set("scope", this.config.scope);
320
- const headers = {
321
- Accept: "application/json",
322
- "Content-Type": "application/x-www-form-urlencoded"
323
- };
324
- if (this.config.authMethod === "client_secret_post") {
325
- body.set("client_id", this.config.clientId);
326
- body.set("client_secret", this.config.clientSecret);
327
- } else {
328
- const encode = (value) => new URLSearchParams({ value }).toString().slice(6);
329
- headers.Authorization = `Basic ${Buffer.from(`${encode(this.config.clientId)}:${encode(this.config.clientSecret)}`).toString("base64")}`;
330
- }
331
- const startedAt = Date.now();
332
- const response = await fetch(endpoint, {
333
- method: "POST",
334
- headers,
335
- body: body.toString(),
336
- redirect: "error",
337
- signal
338
- });
339
- if (!response.ok) throw authError();
340
- const token = tokenSchema.parse(await response.json());
341
- if (token.expires_in !== void 0) {
342
- const lifetimeMs = token.expires_in * 1e3;
343
- const expiresAt = startedAt + lifetimeMs;
344
- if (!Number.isFinite(expiresAt) || Date.now() >= expiresAt) throw authError();
345
- this.cached = {
346
- value: token.access_token,
347
- renewAt: expiresAt - Math.min(MAX_REFRESH_SKEW_MS, lifetimeMs * 0.1)
348
- };
349
- }
350
- this.retryAt = 0;
351
- return token.access_token;
352
- }
353
- };
354
-
355
- // src/client/databrain-client.ts
356
- var MCP_USER_AGENT = `databrain-mcp-server/${MCP_SERVER_VERSION}`;
357
- var MAX_RETRIES = 3;
358
- var REQUEST_TIMEOUT_MS = 3e4;
359
- var NON_TRANSIENT_PATTERNS = [
360
- "out of context",
361
- "Definition not found",
362
- "not a data question",
363
- "Query generation failed",
364
- "not found",
365
- "not authenticated",
366
- "Invalid Service Token",
367
- "service token"
368
- ];
369
- var DatabrainClient = class {
370
- constructor(config) {
371
- this.config = config;
372
- if (config.oauth) {
373
- if (!oauthHttpsUrl.safeParse(config.apiUrl).success) {
374
- throw new Error("OAuth requires an HTTPS Databrain API URL without userinfo or fragments.");
375
- }
376
- this.oauth = new OAuthClientCredentials(config.oauth);
377
- this.config = Object.freeze({ ...config });
378
- }
379
- }
380
- cache = new TtlCache();
381
- runtimeApiToken;
382
- oauth;
383
- oauthFallback = false;
384
- hasApiToken() {
385
- return !!(this.runtimeApiToken || this.config.apiToken);
386
- }
387
- setRuntimeApiToken(token) {
388
- this.runtimeApiToken = token;
389
- logger.info("Runtime API token set for this session");
390
- }
391
- getServiceToken() {
392
- return this.config.serviceToken;
393
- }
394
- getApiUrl() {
395
- return this.config.apiUrl;
396
- }
397
- getDemoDomain() {
398
- return this.config.demoDomain;
399
- }
400
- /**
401
- * Describes which credential is currently used as the bearer for outgoing
402
- * API calls. Used by `whoami` so the LLM can give the user a truthful answer.
403
- */
404
- getActiveAuth() {
405
- if (this.oauth) {
406
- if (this.oauthFallback) return { source: "service-token", oauthFallback: true };
407
- return { source: "oauth-client-credentials", status: this.oauth.getStatus() };
408
- }
409
- if (this.config.serviceToken) return { source: "service-token" };
410
- if (this.runtimeApiToken || this.config.apiToken) return { source: "api-token" };
411
- return { source: "none" };
412
- }
413
- async getToken(auth) {
414
- if (auth === "none") return void 0;
415
- if (auth === "service") {
416
- if (this.oauth) {
417
- try {
418
- const token = await this.oauth.getToken();
419
- this.oauthFallback = false;
420
- return token;
421
- } catch {
422
- if (!this.config.serviceToken) {
423
- throw new DatabrainApiError(
424
- 0,
425
- "OAUTH_TOKEN_ERROR",
426
- "OAuth token acquisition failed. Check the configured issuer, client credentials, resource and scopes."
427
- );
428
- }
429
- if (!this.oauthFallback) logger.warn("OAuth unavailable; using the configured static service token.");
430
- this.oauthFallback = true;
431
- }
432
- }
433
- return this.config.serviceToken;
434
- }
435
- return this.runtimeApiToken || this.config.apiToken;
436
- }
437
- async request(options) {
438
- return this.requestInternal(options);
439
- }
440
- async requestInternal(options) {
441
- const usesOAuth = !!this.oauth && options.auth === "service" && !options.token;
442
- const cacheAllowed = !usesOAuth;
443
- if (options.invalidateCache) {
444
- for (const prefix of options.invalidateCache) {
445
- this.cache.invalidateByPrefix(prefix);
446
- }
447
- }
448
- if (cacheAllowed && options.cacheKey && options.cacheTtlMs) {
449
- const cached = this.cache.get(options.cacheKey);
450
- if (cached !== void 0) {
451
- logger.debug(`Cache hit: ${options.cacheKey}`);
452
- return cached;
453
- }
454
- }
455
- let token = options.token ?? await this.getToken(options.auth);
456
- if (options.auth !== "none" && !options.token && !token) {
457
- if (options.auth === "service") {
458
- throw new DatabrainApiError(
459
- 401,
460
- "MISSING_SERVICE_TOKEN",
461
- "DATABRAIN_SERVICE_TOKEN is required for this operation (org-level token for discovery and data app management). It must be set in the MCP server configuration \u2014 do not ask the user for it at runtime."
462
- );
463
- }
464
- throw new DatabrainApiError(
465
- 401,
466
- "MISSING_API_TOKEN",
467
- "A per-data-app API token is required for this embedding operation but none is configured. Call `create_api_token` to generate one, or `set_api_token` if you already have the value. Do not ask the user for this token \u2014 create it via the MCP tools."
468
- );
469
- }
470
- let url = `${this.config.apiUrl}${options.path}`;
471
- if (options.query) {
472
- const params = new URLSearchParams(options.query);
473
- url += `?${params.toString()}`;
474
- }
475
- let lastError;
476
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
477
- try {
478
- if (usesOAuth && attempt > 1) token = await this.getToken(options.auth);
479
- const headers = {
480
- "Content-Type": "application/json",
481
- Accept: "application/json",
482
- "User-Agent": MCP_USER_AGENT,
483
- "X-Databrain-Client": MCP_USER_AGENT,
484
- ...options.headers
485
- };
486
- const ctx = toolContext.getStore();
487
- if (ctx) {
488
- headers["X-Databrain-MCP-Tool"] = ctx.tool;
489
- headers["X-Databrain-MCP-Tool-Category"] = ctx.category;
490
- headers["X-Databrain-MCP-Workflow-Intent"] = ctx.workflowIntent;
491
- headers["X-Databrain-MCP-Input-Mode"] = ctx.inputMode;
492
- }
493
- if (token) {
494
- if (options.auth === "none") {
495
- headers["x-plugin-token"] = token;
496
- headers["Origin"] = "https://mcp.usedatabrain.com";
497
- } else {
498
- headers["Authorization"] = `Bearer ${token}`;
499
- }
500
- }
501
- const response = await fetch(url, {
502
- method: options.method,
503
- headers,
504
- body: options.body ? JSON.stringify(options.body) : void 0,
505
- signal: AbortSignal.timeout(options.timeoutMs || REQUEST_TIMEOUT_MS),
506
- ...usesOAuth ? { redirect: "error" } : {}
507
- });
508
- if (!response.ok) {
509
- if (usesOAuth) {
510
- throw new DatabrainApiError(
511
- response.status,
512
- "OAUTH_API_ERROR",
513
- `Databrain API request failed with status ${response.status} using configured service credentials.`
514
- );
515
- }
516
- const errorBody = await response.text();
517
- logger.warn(`[API Error] ${options.method} ${options.path} status=${response.status} body=${errorBody.slice(0, 500)}`);
518
- let parsed = {};
519
- try {
520
- parsed = JSON.parse(errorBody);
521
- } catch {
522
- }
523
- const errorMsg = parsed.message || parsed.detail || (typeof parsed.error === "string" ? parsed.error : JSON.stringify(parsed.error)) || `API request failed with status ${response.status}`;
524
- const error = new DatabrainApiError(
525
- response.status,
526
- parsed.code || `HTTP_${response.status}`,
527
- errorMsg,
528
- parsed
529
- );
530
- const isTransient = response.status === 429 || response.status >= 500 && !NON_TRANSIENT_PATTERNS.some((p) => errorMsg.includes(p));
531
- if (isTransient && attempt < MAX_RETRIES) {
532
- const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 8e3);
533
- logger.warn(`Retrying in ${delay}ms (attempt ${attempt}/${MAX_RETRIES})`, {
534
- status: response.status,
535
- path: options.path
536
- });
537
- await new Promise((r) => setTimeout(r, delay));
538
- lastError = error;
539
- continue;
540
- }
541
- throw error;
542
- }
543
- const text = await response.text();
544
- let data = text ? JSON.parse(text) : {};
545
- if (!options.skipDataUnwrap && data && typeof data === "object" && !Array.isArray(data) && "data" in data) {
546
- const inner = data.data;
547
- if (inner !== null && inner !== void 0) {
548
- data = inner;
549
- }
550
- }
551
- if (cacheAllowed && options.cacheKey && options.cacheTtlMs) {
552
- this.cache.set(options.cacheKey, data, options.cacheTtlMs);
553
- }
554
- return data;
555
- } catch (err) {
556
- if (err instanceof DatabrainApiError) throw err;
557
- if (attempt < MAX_RETRIES && err?.name === "TimeoutError" && !options.noRetryOnTimeout) {
558
- const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 8e3);
559
- logger.warn(`Request timed out, retrying in ${delay}ms`, { path: options.path });
560
- await new Promise((r) => setTimeout(r, delay));
561
- lastError = err;
562
- continue;
563
- }
564
- throw new DatabrainApiError(
565
- 0,
566
- "NETWORK_ERROR",
567
- usesOAuth ? "Failed to connect to Databrain API using configured service credentials." : `Failed to connect to Databrain API: ${err.message}`
568
- );
569
- }
570
- }
571
- throw lastError || new DatabrainApiError(0, "UNKNOWN", "Request failed after retries");
572
- }
573
- };
2
+ import {
3
+ CustomChartsApi,
4
+ DatabrainApiError,
5
+ apiProblems,
6
+ buildProject,
7
+ bundleSizeBytes,
8
+ contentHash,
9
+ createCustomChartsApi,
10
+ logger,
11
+ optionSchema,
12
+ planUpload,
13
+ readDist,
14
+ scaffoldProject,
15
+ slotSchema,
16
+ stringify,
17
+ toolContext,
18
+ toolError,
19
+ toolSuccess,
20
+ validateBundle
21
+ } from "./chunk-SKU37UVJ.js";
574
22
 
575
23
  // src/apps/contract.ts
576
- import { z as z2 } from "zod";
24
+ import { z } from "zod";
577
25
  var CHART_URI = "ui://databrain/chart-tile";
578
26
  var KPI_URI = "ui://databrain/kpi-tile";
579
27
  var APP_MIME = "text/html;profile=mcp-app";
580
28
  var RESOURCE_URI_META_KEY = "ui/resourceUri";
581
29
  var PAGE_SIZE = 50;
582
30
  var MAX_RESULT_CHARS = 125e3;
583
- var id = z2.string().trim().min(1).max(256);
584
- var cell = z2.union([z2.string().max(2e3), z2.number().finite(), z2.boolean(), z2.null()]);
585
- var rowsSchema = z2.array(z2.record(cell)).max(PAGE_SIZE);
586
- var referenceSchema = z2.object({ embedId: id, clientId: id, widgetId: id }).strict();
587
- var point = z2.object({
31
+ var id = z.string().trim().min(1).max(256);
32
+ var cell = z.union([z.string().max(2e3), z.number().finite(), z.boolean(), z.null()]);
33
+ var rowsSchema = z.array(z.record(cell)).max(PAGE_SIZE);
34
+ var referenceSchema = z.object({ embedId: id, clientId: id, widgetId: id }).strict();
35
+ var point = z.object({
588
36
  key: id,
589
- label: z2.string().max(2e3),
37
+ label: z.string().max(2e3),
590
38
  value: cell,
591
- x: z2.number().min(0),
592
- y: z2.number().min(0),
593
- width: z2.number().positive(),
594
- height: z2.number().positive(),
39
+ x: z.number().min(0),
40
+ y: z.number().min(0),
41
+ width: z.number().positive(),
42
+ height: z.number().positive(),
595
43
  drillId: id.optional()
596
44
  }).strict();
597
- var dateRangeSchema = z2.object({
598
- from: z2.string().date(),
599
- to: z2.string().date()
45
+ var dateRangeSchema = z.object({
46
+ from: z.string().date(),
47
+ to: z.string().date()
600
48
  }).strict().refine((value) => value.from <= value.to, "Date range must be ordered");
601
- var viewSchema = z2.object({
602
- cursor: z2.string().min(1).max(2048).optional(),
49
+ var viewSchema = z.object({
50
+ cursor: z.string().min(1).max(2048).optional(),
603
51
  dateRange: dateRangeSchema.optional(),
604
52
  drillId: id.optional()
605
53
  }).strict();
606
- var pageSchema = z2.object({
607
- kind: z2.enum(["chart", "kpi"]),
608
- title: z2.string().max(500),
609
- chartSpec: z2.object({ type: z2.string().max(80) }).strict(),
54
+ var pageSchema = z.object({
55
+ kind: z.enum(["chart", "kpi"]),
56
+ title: z.string().max(500),
57
+ chartSpec: z.object({ type: z.string().max(80) }).strict(),
610
58
  rows: rowsSchema,
611
- columns: z2.array(z2.string().max(128)).max(20),
612
- svg: z2.string().max(1e5).optional(),
613
- width: z2.number().int().min(240).max(1200),
614
- height: z2.number().int().min(120).max(800),
615
- points: z2.array(point).max(PAGE_SIZE),
59
+ columns: z.array(z.string().max(128)).max(20),
60
+ svg: z.string().max(1e5).optional(),
61
+ width: z.number().int().min(240).max(1200),
62
+ height: z.number().int().min(120).max(800),
63
+ points: z.array(point).max(PAGE_SIZE),
616
64
  value: cell.optional(),
617
- nextCursor: z2.string().min(1).max(2048).optional()
65
+ nextCursor: z.string().min(1).max(2048).optional()
618
66
  }).strict().superRefine((page, ctx) => {
619
67
  if (page.kind === "chart" && !page.svg) ctx.addIssue({ code: "custom", message: "SVG required" });
620
68
  if (page.kind === "kpi" && (page.svg || page.points.length || page.value === void 0)) {
@@ -739,11 +187,20 @@ var TOOL_METADATA = {
739
187
  effect: "read"
740
188
  },
741
189
  // Reports
742
- list_scheduled_reports: { category: "reports", workflowIntent: "reports", effect: "read" }
190
+ list_scheduled_reports: { category: "reports", workflowIntent: "reports", effect: "read" },
191
+ // Custom chart types (DAT-925)
192
+ // Returns template files; nothing is written or persisted.
193
+ custom_chart_scaffold: { category: "custom_charts", workflowIntent: "custom_chart", effect: "read" },
194
+ // Local rules plus POST /validate, which never persists.
195
+ custom_chart_validate: { category: "custom_charts", workflowIntent: "custom_chart", effect: "read" },
196
+ // dry_run is the default, but mode:"apply" creates a chart or a version (and may publish).
197
+ custom_chart_upload: { category: "custom_charts", workflowIntent: "custom_chart", effect: "commit" },
198
+ list_custom_charts: { category: "custom_charts", workflowIntent: "custom_chart", effect: "read" },
199
+ publish_custom_chart: { category: "custom_charts", workflowIntent: "custom_chart", effect: "commit" }
743
200
  };
744
201
 
745
202
  // src/tools/data-app/list-data-apps.ts
746
- import { z as z3 } from "zod";
203
+ import { z as z2 } from "zod";
747
204
 
748
205
  // src/api/dashboards-api.ts
749
206
  var DashboardsApi = class {
@@ -1038,7 +495,7 @@ var QueryApi = class {
1038
495
  async startSemanticLayerGeneration(datamartId) {
1039
496
  return this.client.request({
1040
497
  method: "POST",
1041
- path: `/schema/datamart/${encodeURIComponent(datamartId)}/semantic-layer/auto-generate`,
498
+ path: `/api/v2/data-app/datamarts/${encodeURIComponent(datamartId)}/semantic-layer/generate`,
1042
499
  auth: "service",
1043
500
  invalidateCache: ["semantic_layer_"]
1044
501
  });
@@ -1046,7 +503,7 @@ var QueryApi = class {
1046
503
  async getSemanticLayerGenerationStatus(datamartId) {
1047
504
  return this.client.request({
1048
505
  method: "GET",
1049
- path: `/schema/datamart/${encodeURIComponent(datamartId)}/semantic-layer/generation-status`,
506
+ path: `/api/v2/data-app/datamarts/${encodeURIComponent(datamartId)}/semantic-layer/generation-status`,
1050
507
  auth: "service"
1051
508
  });
1052
509
  }
@@ -2015,6 +1472,7 @@ function createServiceBundle(client) {
2015
1472
  const guestTokenApi = new GuestTokenApi(client);
2016
1473
  const queryApi = new QueryApi(client);
2017
1474
  const scheduledReportsApi = new ScheduledReportsApi(client);
1475
+ const customChartsApi = new CustomChartsApi(client);
2018
1476
  const tenantsApi = new TenantsApi(client);
2019
1477
  const widgetsApi = new WidgetsApi(client);
2020
1478
  const workspacesApi = new WorkspacesApi(client);
@@ -2029,6 +1487,7 @@ function createServiceBundle(client) {
2029
1487
  workspacesApi,
2030
1488
  queryApi,
2031
1489
  scheduledReportsApi,
1490
+ customChartsApi,
2032
1491
  tenantsApi,
2033
1492
  widgetsApi,
2034
1493
  embedService: new EmbedService(embedsApi, datamartsApi),
@@ -2079,7 +1538,7 @@ function registerListDataApps(server, client) {
2079
1538
  "list_data_apps",
2080
1539
  "List data apps in your organization. Start here for embed setup.",
2081
1540
  {
2082
- workspaceName: z3.string().optional().describe("Filter by workspace name")
1541
+ workspaceName: z2.string().optional().describe("Filter by workspace name")
2083
1542
  },
2084
1543
  READ_ONLY,
2085
1544
  async (params) => {
@@ -2108,7 +1567,7 @@ function registerListDataApps(server, client) {
2108
1567
  }
2109
1568
 
2110
1569
  // src/tools/data-app/list-tenants.ts
2111
- import { z as z4 } from "zod";
1570
+ import { z as z3 } from "zod";
2112
1571
  var isNonEmptyString4 = (value) => typeof value === "string" && value.trim().length > 0;
2113
1572
  function workspaceIdFrom(workspace) {
2114
1573
  return isNonEmptyString4(workspace.id) && workspace.id || isNonEmptyString4(workspace._id) && workspace._id || isNonEmptyString4(workspace.workspaceId) && workspace.workspaceId || null;
@@ -2146,12 +1605,12 @@ function registerListTenants(server, client) {
2146
1605
  "list_tenants",
2147
1606
  "List available tenants/clients from the workspace tenancy database. Use this to pick a valid clientId before creating guest tokens or editing customer dashboards.",
2148
1607
  {
2149
- workspaceName: z4.string().optional().describe("Workspace name. Preferred for non-technical callers; the tool resolves it to a workspaceId."),
2150
- workspaceId: z4.string().optional().describe("Technical workspace UUID. Provide this instead of workspaceName if already known."),
2151
- searchValue: z4.string().optional().describe("Optional case-insensitive search string for tenant/client labels."),
2152
- limit: z4.number().int().positive().max(500).optional().describe("Maximum number of tenants to return. Backend may apply connector-specific limits."),
2153
- datasourceId: z4.string().optional().describe("Optional datasource UUID override when the workspace has multiple datasource contexts."),
2154
- datamartId: z4.string().optional().describe("Optional datamart UUID override when tenant lookup should use a specific datamart.")
1608
+ workspaceName: z3.string().optional().describe("Workspace name. Preferred for non-technical callers; the tool resolves it to a workspaceId."),
1609
+ workspaceId: z3.string().optional().describe("Technical workspace UUID. Provide this instead of workspaceName if already known."),
1610
+ searchValue: z3.string().optional().describe("Optional case-insensitive search string for tenant/client labels."),
1611
+ limit: z3.number().int().positive().max(500).optional().describe("Maximum number of tenants to return. Backend may apply connector-specific limits."),
1612
+ datasourceId: z3.string().optional().describe("Optional datasource UUID override when the workspace has multiple datasource contexts."),
1613
+ datamartId: z3.string().optional().describe("Optional datamart UUID override when tenant lookup should use a specific datamart.")
2155
1614
  },
2156
1615
  READ_ONLY,
2157
1616
  async (params) => {
@@ -2245,15 +1704,15 @@ function registerListTenants(server, client) {
2245
1704
  }
2246
1705
 
2247
1706
  // src/tools/data-app/create-data-app.ts
2248
- import { z as z5 } from "zod";
1707
+ import { z as z4 } from "zod";
2249
1708
  function registerCreateDataApp(server, client) {
2250
1709
  server.tool(
2251
1710
  "create_data_app",
2252
1711
  "Create a new data app.",
2253
1712
  {
2254
- name: z5.string().describe("Data app name"),
2255
- workspaceName: z5.string().optional().describe("Optional workspace name if required by backend"),
2256
- body: z5.record(z5.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/")
1713
+ name: z4.string().describe("Data app name"),
1714
+ workspaceName: z4.string().optional().describe("Optional workspace name if required by backend"),
1715
+ body: z4.record(z4.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/")
2257
1716
  },
2258
1717
  WRITE_CREATE,
2259
1718
  async (params) => {
@@ -2287,7 +1746,7 @@ function registerCreateDataApp(server, client) {
2287
1746
  }
2288
1747
 
2289
1748
  // src/tools/data-app/list-api-tokens.ts
2290
- import { z as z6 } from "zod";
1749
+ import { z as z5 } from "zod";
2291
1750
 
2292
1751
  // src/utils/resolve-resource.ts
2293
1752
  function resolveResource(options) {
@@ -2356,7 +1815,7 @@ function registerListApiTokens(server, client) {
2356
1815
  "list_api_tokens",
2357
1816
  "List API tokens for a data app. Omit dataAppName to auto-resolve.",
2358
1817
  {
2359
- dataAppName: z6.string().optional().describe("Data app name. Omit to list available data apps.")
1818
+ dataAppName: z5.string().optional().describe("Data app name. Omit to list available data apps.")
2360
1819
  },
2361
1820
  READ_ONLY,
2362
1821
  async (params) => {
@@ -2401,16 +1860,16 @@ function registerListApiTokens(server, client) {
2401
1860
  }
2402
1861
 
2403
1862
  // src/tools/data-app/create-api-token.ts
2404
- import { z as z7 } from "zod";
1863
+ import { z as z6 } from "zod";
2405
1864
  function registerCreateApiToken(server, client) {
2406
1865
  const services = createServiceBundle(client);
2407
1866
  server.tool(
2408
1867
  "create_api_token",
2409
1868
  "Create an API token for a data app and activate it for this session. Omit dataAppName to auto-resolve.",
2410
1869
  {
2411
- dataAppName: z7.string().optional().describe("Data app name. Omit to list available data apps."),
2412
- name: z7.string().optional().describe("Optional token name"),
2413
- body: z7.record(z7.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/api-tokens")
1870
+ dataAppName: z6.string().optional().describe("Data app name. Omit to list available data apps."),
1871
+ name: z6.string().optional().describe("Optional token name"),
1872
+ body: z6.record(z6.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/api-tokens")
2414
1873
  },
2415
1874
  WRITE_CREATE,
2416
1875
  async (params) => {
@@ -2462,16 +1921,16 @@ Save this in backend secrets immediately. This value may not be returned again.`
2462
1921
  }
2463
1922
 
2464
1923
  // src/tools/data-app/rotate-api-token.ts
2465
- import { z as z8 } from "zod";
1924
+ import { z as z7 } from "zod";
2466
1925
  function registerRotateApiToken(server, client) {
2467
1926
  const services = createServiceBundle(client);
2468
1927
  server.tool(
2469
1928
  "rotate_api_token",
2470
1929
  "Rotate (invalidate and replace) an API token. Previous token becomes stale.",
2471
1930
  {
2472
- dataAppName: z8.string().optional().describe("Data app name. Omit to list available data apps."),
2473
- tokenId: z8.string().optional().describe("Optional token ID to rotate"),
2474
- body: z8.record(z8.unknown()).optional().describe("Raw API body passthrough for rotate endpoint")
1931
+ dataAppName: z7.string().optional().describe("Data app name. Omit to list available data apps."),
1932
+ tokenId: z7.string().optional().describe("Optional token ID to rotate"),
1933
+ body: z7.record(z7.unknown()).optional().describe("Raw API body passthrough for rotate endpoint")
2475
1934
  },
2476
1935
  DESTRUCTIVE,
2477
1936
  async (params) => {
@@ -2590,14 +2049,14 @@ function registerListDatasources(server, client) {
2590
2049
  }
2591
2050
 
2592
2051
  // src/tools/infrastructure/discover-datasource-schema.ts
2593
- import { z as z9 } from "zod";
2052
+ import { z as z8 } from "zod";
2594
2053
  function registerDiscoverDatasourceSchema(server, client) {
2595
2054
  const services = createServiceBundle(client);
2596
2055
  server.tool(
2597
2056
  "discover_datasource_schema",
2598
2057
  "Inspect tables and columns in a datasource. Omit datasourceName to list available datasources.",
2599
2058
  {
2600
- datasourceName: z9.string().optional().describe(
2059
+ datasourceName: z8.string().optional().describe(
2601
2060
  "Name of the datasource to inspect. Omit to list available datasources."
2602
2061
  )
2603
2062
  },
@@ -2680,13 +2139,13 @@ Fix the connection or add a new database in the **Databrain UI**.`
2680
2139
  }
2681
2140
 
2682
2141
  // src/tools/infrastructure/list-datamarts.ts
2683
- import { z as z10 } from "zod";
2142
+ import { z as z9 } from "zod";
2684
2143
  function registerListDatamarts(server, client) {
2685
2144
  server.tool(
2686
2145
  "list_datamarts",
2687
2146
  "List datamarts with table counts and semantic layer scores.",
2688
2147
  {
2689
- dataAppName: z10.string().optional().describe("Filter by data app name")
2148
+ dataAppName: z9.string().optional().describe("Filter by data app name")
2690
2149
  },
2691
2150
  READ_ONLY,
2692
2151
  async (params) => {
@@ -2775,7 +2234,7 @@ function registerListWorkspaces(server, client) {
2775
2234
  }
2776
2235
 
2777
2236
  // src/tools/infrastructure/create-datamart.ts
2778
- import { z as z11 } from "zod";
2237
+ import { z as z10 } from "zod";
2779
2238
 
2780
2239
  // src/tools/widgets/production-guards.ts
2781
2240
  var APPLY_TO_PRODUCTION_CONFIRMATION = "APPLY_TO_PRODUCTION";
@@ -2793,29 +2252,29 @@ function genericProductionSafetyNote() {
2793
2252
  }
2794
2253
 
2795
2254
  // src/tools/infrastructure/create-datamart.ts
2796
- var columnSchema = z11.object({
2797
- name: z11.string(),
2798
- alias: z11.string().optional(),
2799
- label: z11.string().optional(),
2800
- isHide: z11.boolean().optional(),
2801
- isCustomColumn: z11.boolean().optional(),
2802
- sql: z11.string().optional()
2255
+ var columnSchema = z10.object({
2256
+ name: z10.string(),
2257
+ alias: z10.string().optional(),
2258
+ label: z10.string().optional(),
2259
+ isHide: z10.boolean().optional(),
2260
+ isCustomColumn: z10.boolean().optional(),
2261
+ sql: z10.string().optional()
2803
2262
  });
2804
- var tableSchema = z11.object({
2805
- name: z11.string(),
2806
- schemaName: z11.string().optional(),
2807
- clientColumn: z11.string().optional(),
2808
- label: z11.string().optional(),
2809
- columns: z11.array(columnSchema)
2263
+ var tableSchema = z10.object({
2264
+ name: z10.string(),
2265
+ schemaName: z10.string().optional(),
2266
+ clientColumn: z10.string().optional(),
2267
+ label: z10.string().optional(),
2268
+ columns: z10.array(columnSchema)
2810
2269
  });
2811
- var relationshipSchema = z11.object({
2812
- parentTableName: z11.string(),
2813
- parentColumnName: z11.string(),
2814
- childTableName: z11.string(),
2815
- childColumnName: z11.string(),
2816
- cardinality: z11.enum(["OneToOne", "OneToMany", "ManyToOne", "ManyToMany"]),
2817
- join: z11.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN"]),
2818
- relationshipName: z11.string()
2270
+ var relationshipSchema = z10.object({
2271
+ parentTableName: z10.string(),
2272
+ parentColumnName: z10.string(),
2273
+ childTableName: z10.string(),
2274
+ childColumnName: z10.string(),
2275
+ cardinality: z10.enum(["OneToOne", "OneToMany", "ManyToOne", "ManyToMany"]),
2276
+ join: z10.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN"]),
2277
+ relationshipName: z10.string()
2819
2278
  });
2820
2279
  function datamartSuccessMessage(name, result) {
2821
2280
  return `Datamart "${name}" created successfully (ID: ${result.id || result._id || "UNKNOWN"}).
@@ -2836,21 +2295,21 @@ function registerCreateDatamart(server, client) {
2836
2295
  "create_datamart",
2837
2296
  "Create a production datamart from selected tables and relationships. Defaults to dry-run; apply requires explicit confirmation.",
2838
2297
  {
2839
- mode: z11.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run returns the plan only; apply creates the datamart"),
2840
- confirm: z11.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
2841
- name: z11.string().describe("Unique datamart name"),
2842
- datasourceName: z11.string().optional().describe("Name of the datasource to use. Omit to list available datasources."),
2843
- tableList: z11.array(tableSchema).optional().describe("Array of tables with their columns"),
2844
- relationships: z11.array(relationshipSchema).optional().describe("Join definitions between tables"),
2845
- tenancySettings: z11.object({
2846
- tenancyLevel: z11.enum(["TABLE", "DATABASE"]).optional(),
2847
- clientColumnType: z11.string().optional(),
2848
- schemaName: z11.string().optional(),
2849
- tableName: z11.string().optional(),
2850
- tableClientNameColumn: z11.string().optional(),
2851
- tablePrimaryKeyColumn: z11.string().optional()
2298
+ mode: z10.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run returns the plan only; apply creates the datamart"),
2299
+ confirm: z10.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
2300
+ name: z10.string().describe("Unique datamart name"),
2301
+ datasourceName: z10.string().optional().describe("Name of the datasource to use. Omit to list available datasources."),
2302
+ tableList: z10.array(tableSchema).optional().describe("Array of tables with their columns"),
2303
+ relationships: z10.array(relationshipSchema).optional().describe("Join definitions between tables"),
2304
+ tenancySettings: z10.object({
2305
+ tenancyLevel: z10.enum(["TABLE", "DATABASE"]).optional(),
2306
+ clientColumnType: z10.string().optional(),
2307
+ schemaName: z10.string().optional(),
2308
+ tableName: z10.string().optional(),
2309
+ tableClientNameColumn: z10.string().optional(),
2310
+ tablePrimaryKeyColumn: z10.string().optional()
2852
2311
  }).optional().describe("Multi-tenant configuration"),
2853
- body: z11.record(z11.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/datamarts")
2312
+ body: z10.record(z10.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/datamarts")
2854
2313
  },
2855
2314
  WRITE_CREATE,
2856
2315
  async (params) => {
@@ -2945,14 +2404,14 @@ function registerCreateDatamart(server, client) {
2945
2404
  }
2946
2405
 
2947
2406
  // src/tools/infrastructure/sync-datasource.ts
2948
- import { z as z12 } from "zod";
2407
+ import { z as z11 } from "zod";
2949
2408
  function registerSyncDatasource(server, client) {
2950
2409
  const datasourcesApi = new DatasourcesApi(client);
2951
2410
  server.tool(
2952
2411
  "sync_datasource",
2953
2412
  "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.",
2954
2413
  {
2955
- datasourceName: z12.string().optional().describe("Name of the datasource to sync. Omit to list available datasources.")
2414
+ datasourceName: z11.string().optional().describe("Name of the datasource to sync. Omit to list available datasources.")
2956
2415
  },
2957
2416
  WRITE_IDEMPOTENT,
2958
2417
  async (params) => {
@@ -3001,7 +2460,7 @@ The schema metadata will be refreshed. You can now use \`discover_datasource_sch
3001
2460
  }
3002
2461
 
3003
2462
  // src/tools/embed/create-embed.ts
3004
- import { z as z13 } from "zod";
2463
+ import { z as z12 } from "zod";
3005
2464
  var DEFAULT_ACCESS_SETTINGS2 = {
3006
2465
  isAllowMetricCreation: false,
3007
2466
  isAllowMetricDeletion: false,
@@ -3014,18 +2473,18 @@ var DEFAULT_ACCESS_SETTINGS2 = {
3014
2473
  isAllowCreateDashboardView: false,
3015
2474
  metricCreationMode: "DRAG_DROP"
3016
2475
  };
3017
- var accessSettingsSchema = z13.object({
3018
- datamartName: z13.string().describe("Name of the datamart (required)"),
3019
- isAllowMetricCreation: z13.boolean().optional().describe("Allow end-users to create metrics"),
3020
- metricCreationMode: z13.enum(["DRAG_DROP", "CHAT"]).optional().describe("Metric creation mode"),
3021
- isAllowMetricDeletion: z13.boolean().optional().describe("Allow metric deletion"),
3022
- isAllowMetricUpdate: z13.boolean().optional().describe("Allow metric editing"),
3023
- isAllowMetricLayoutChange: z13.boolean().optional().describe("Allow layout changes"),
3024
- isAllowManageMetrics: z13.boolean().optional().describe("Show manage metrics UI"),
3025
- isAllowUnderlyingData: z13.boolean().optional().describe("Allow viewing underlying data"),
3026
- isAllowAiPilot: z13.boolean().optional().describe("Enable AI pilot features"),
3027
- isAllowEmailReports: z13.boolean().optional().describe("Allow email report scheduling"),
3028
- isAllowCreateDashboardView: z13.boolean().optional().describe("Allow creating dashboard views")
2476
+ var accessSettingsSchema = z12.object({
2477
+ datamartName: z12.string().describe("Name of the datamart (required)"),
2478
+ isAllowMetricCreation: z12.boolean().optional().describe("Allow end-users to create metrics"),
2479
+ metricCreationMode: z12.enum(["DRAG_DROP", "CHAT"]).optional().describe("Metric creation mode"),
2480
+ isAllowMetricDeletion: z12.boolean().optional().describe("Allow metric deletion"),
2481
+ isAllowMetricUpdate: z12.boolean().optional().describe("Allow metric editing"),
2482
+ isAllowMetricLayoutChange: z12.boolean().optional().describe("Allow layout changes"),
2483
+ isAllowManageMetrics: z12.boolean().optional().describe("Show manage metrics UI"),
2484
+ isAllowUnderlyingData: z12.boolean().optional().describe("Allow viewing underlying data"),
2485
+ isAllowAiPilot: z12.boolean().optional().describe("Enable AI pilot features"),
2486
+ isAllowEmailReports: z12.boolean().optional().describe("Allow email report scheduling"),
2487
+ isAllowCreateDashboardView: z12.boolean().optional().describe("Allow creating dashboard views")
3029
2488
  }).describe("Embed access settings. Only datamartName is required \u2014 all flags default to false.");
3030
2489
  function registerCreateEmbed(server, client) {
3031
2490
  const services = createServiceBundle(client);
@@ -3033,23 +2492,23 @@ function registerCreateEmbed(server, client) {
3033
2492
  "create_embed",
3034
2493
  "Create an embed. Provide dashboardId, workspaceName, accessSettings.datamartName. Add clientId for multi-tenant per-client dashboards. All isAllow* flags default to false.",
3035
2494
  {
3036
- body: z13.record(z13.unknown()).optional().describe(
2495
+ body: z12.record(z12.unknown()).optional().describe(
3037
2496
  "Raw API body passthrough. If provided, all other fields are ignored."
3038
2497
  ),
3039
- dashboardId: z13.string().optional().describe("Dashboard identifier for embed creation"),
3040
- workspaceName: z13.string().optional().describe("Workspace name. Omit to auto-resolve if only one exists."),
3041
- embedType: z13.enum(["dashboard", "metric"]).optional().describe("Embed type"),
3042
- metricId: z13.string().optional().describe("Required for metric embed type"),
3043
- name: z13.string().optional().describe("Human-readable embed config name"),
2498
+ dashboardId: z12.string().optional().describe("Dashboard identifier for embed creation"),
2499
+ workspaceName: z12.string().optional().describe("Workspace name. Omit to auto-resolve if only one exists."),
2500
+ embedType: z12.enum(["dashboard", "metric"]).optional().describe("Embed type"),
2501
+ metricId: z12.string().optional().describe("Required for metric embed type"),
2502
+ name: z12.string().optional().describe("Human-readable embed config name"),
3044
2503
  accessSettings: accessSettingsSchema.optional(),
3045
2504
  // Multi-tenant (dashboard embed) params
3046
- clientId: z13.string().optional().describe(
2505
+ clientId: z12.string().optional().describe(
3047
2506
  "Client identifier for multi-tenant isolation. When provided, creates a dashboard embed (each client gets their own dashboard copy)."
3048
2507
  ),
3049
- templateDashboardId: z13.string().optional().describe("Template dashboard ID to clone metrics from"),
3050
- isImportMetrics: z13.boolean().optional().describe("Import metrics from template dashboard"),
3051
- metricImportMode: z13.enum(["COPY", "LINK"]).optional().describe("COPY duplicates metrics, LINK shares them"),
3052
- isRenameDashboard: z13.boolean().optional().describe("Rename the dashboard for this client")
2508
+ templateDashboardId: z12.string().optional().describe("Template dashboard ID to clone metrics from"),
2509
+ isImportMetrics: z12.boolean().optional().describe("Import metrics from template dashboard"),
2510
+ metricImportMode: z12.enum(["COPY", "LINK"]).optional().describe("COPY duplicates metrics, LINK shares them"),
2511
+ isRenameDashboard: z12.boolean().optional().describe("Rename the dashboard for this client")
3053
2512
  },
3054
2513
  WRITE_CREATE,
3055
2514
  async (params) => {
@@ -3151,14 +2610,14 @@ Next: run \`get_embed_details\` to verify the embed configuration.`
3151
2610
  }
3152
2611
 
3153
2612
  // src/tools/embed/list-embeds.ts
3154
- import { z as z14 } from "zod";
2613
+ import { z as z13 } from "zod";
3155
2614
  function registerListEmbeds(server, client) {
3156
2615
  const services = createServiceBundle(client);
3157
2616
  server.tool(
3158
2617
  "list_embeds",
3159
2618
  "List embed configurations. Requires an API token (use create_api_token first). Use `list_tenants` for authoritative client discovery from the tenancy database.",
3160
2619
  {
3161
- dataAppName: z14.string().optional().describe("Filter by data app name")
2620
+ dataAppName: z13.string().optional().describe("Filter by data app name")
3162
2621
  },
3163
2622
  READ_ONLY,
3164
2623
  async (params) => {
@@ -3209,14 +2668,14 @@ function registerListEmbeds(server, client) {
3209
2668
  }
3210
2669
 
3211
2670
  // src/tools/embed/get-embed-details.ts
3212
- import { z as z15 } from "zod";
2671
+ import { z as z14 } from "zod";
3213
2672
  function registerGetEmbedDetails(server, client) {
3214
2673
  const services = createServiceBundle(client);
3215
2674
  server.tool(
3216
2675
  "get_embed_details",
3217
2676
  "Get full embed configuration including access settings and theme.",
3218
2677
  {
3219
- embedId: z15.string().describe("ID of the embed")
2678
+ embedId: z14.string().describe("ID of the embed")
3220
2679
  },
3221
2680
  READ_ONLY,
3222
2681
  async (params) => {
@@ -3232,7 +2691,7 @@ function registerGetEmbedDetails(server, client) {
3232
2691
  }
3233
2692
 
3234
2693
  // src/tools/embed/update-embed.ts
3235
- import { z as z16 } from "zod";
2694
+ import { z as z15 } from "zod";
3236
2695
 
3237
2696
  // src/tools/embed/helpers.ts
3238
2697
  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.";
@@ -3270,15 +2729,15 @@ function registerUpdateEmbed(server, client) {
3270
2729
  "update_embed",
3271
2730
  "Update embed access settings, permissions, theme, or name.",
3272
2731
  {
3273
- embedId: z16.string().describe("ID of the embed to update"),
3274
- body: z16.record(z16.unknown()).optional().describe(
2732
+ embedId: z15.string().describe("ID of the embed to update"),
2733
+ body: z15.record(z15.unknown()).optional().describe(
3275
2734
  "Raw API body for PUT /api/v2/data-app/embeds. If provided, it is sent directly (passthrough mode)."
3276
2735
  ),
3277
- name: z16.string().optional().describe("New name for the embed"),
3278
- accessSettings: z16.record(z16.unknown()).optional().describe(
2736
+ name: z15.string().optional().describe("New name for the embed"),
2737
+ accessSettings: z15.record(z15.unknown()).optional().describe(
3279
2738
  "Partial update of access settings. See `get_embed_details` for current settings."
3280
2739
  ),
3281
- theme: z16.record(z16.unknown()).optional().describe(
2740
+ theme: z15.record(z15.unknown()).optional().describe(
3282
2741
  "Theme configuration object. See databrain://theme-reference for the full schema."
3283
2742
  )
3284
2743
  },
@@ -3324,7 +2783,7 @@ function registerUpdateEmbed(server, client) {
3324
2783
  }
3325
2784
 
3326
2785
  // src/tools/embed/customize-theme.ts
3327
- import { z as z17 } from "zod";
2786
+ import { z as z16 } from "zod";
3328
2787
  var THEME_PRESETS = {
3329
2788
  light: {
3330
2789
  general: {
@@ -3458,7 +2917,7 @@ var ACCESS_PRESETS = {
3458
2917
  metricCreationMode: "CHAT"
3459
2918
  }
3460
2919
  };
3461
- var fontFamilyEnum = z17.enum([
2920
+ var fontFamilyEnum = z16.enum([
3462
2921
  "Inter",
3463
2922
  "Roboto",
3464
2923
  "Open Sans",
@@ -3470,17 +2929,17 @@ var fontFamilyEnum = z17.enum([
3470
2929
  "Nunito",
3471
2930
  "system-ui"
3472
2931
  ]).describe("Font family");
3473
- var tooltipFontSchema = z17.object({
3474
- fontFamily: z17.string().optional(),
3475
- fontSize: z17.number().optional(),
3476
- fontWeight: z17.number().optional(),
3477
- fontColor: z17.string().optional()
2932
+ var tooltipFontSchema = z16.object({
2933
+ fontFamily: z16.string().optional(),
2934
+ fontSize: z16.number().optional(),
2935
+ fontWeight: z16.number().optional(),
2936
+ fontColor: z16.string().optional()
3478
2937
  }).optional();
3479
- var fontConfigInputSchema = z17.object({
3480
- fontSize: z17.number().optional(),
3481
- fontFamily: z17.string().optional(),
3482
- fontWeight: z17.number().optional(),
3483
- color: z17.string().optional()
2938
+ var fontConfigInputSchema = z16.object({
2939
+ fontSize: z16.number().optional(),
2940
+ fontFamily: z16.string().optional(),
2941
+ fontWeight: z16.number().optional(),
2942
+ color: z16.string().optional()
3484
2943
  }).optional();
3485
2944
  function mapToFrontendFontConfig(input) {
3486
2945
  if (!input) return void 0;
@@ -3491,23 +2950,23 @@ function mapToFrontendFontConfig(input) {
3491
2950
  ...input.color && { color: input.color }
3492
2951
  };
3493
2952
  }
3494
- var axisSchema = z17.object({
3495
- hideAxisLines: z17.boolean().optional().describe("Hide axis lines"),
3496
- hideSplitLines: z17.boolean().optional().describe("Hide grid/split lines"),
3497
- hideAxisLabels: z17.boolean().optional().describe("Hide axis labels"),
3498
- hideAxisTicks: z17.boolean().optional().describe("Hide axis tick marks"),
3499
- axisName: z17.string().optional().describe("Axis title text"),
3500
- axisNameOffset: z17.number().optional().describe("Axis title offset from axis"),
3501
- axisLabelMargin: z17.number().optional().describe("Margin between axis labels and chart area in pixels"),
3502
- fontSize: z17.number().optional().describe("Axis label font size"),
3503
- fontFamily: z17.string().optional().describe("Axis label font family"),
3504
- fontWeight: z17.number().optional().describe("Axis label font weight"),
3505
- color: z17.string().optional().describe("Axis label color (hex)"),
3506
- axisColor: z17.string().optional().describe("Axis line color (hex)"),
3507
- axisNameFontConfig: z17.object({
3508
- fontFamily: z17.string().optional(),
3509
- fontSize: z17.number().optional(),
3510
- fontWeight: z17.number().optional()
2953
+ var axisSchema = z16.object({
2954
+ hideAxisLines: z16.boolean().optional().describe("Hide axis lines"),
2955
+ hideSplitLines: z16.boolean().optional().describe("Hide grid/split lines"),
2956
+ hideAxisLabels: z16.boolean().optional().describe("Hide axis labels"),
2957
+ hideAxisTicks: z16.boolean().optional().describe("Hide axis tick marks"),
2958
+ axisName: z16.string().optional().describe("Axis title text"),
2959
+ axisNameOffset: z16.number().optional().describe("Axis title offset from axis"),
2960
+ axisLabelMargin: z16.number().optional().describe("Margin between axis labels and chart area in pixels"),
2961
+ fontSize: z16.number().optional().describe("Axis label font size"),
2962
+ fontFamily: z16.string().optional().describe("Axis label font family"),
2963
+ fontWeight: z16.number().optional().describe("Axis label font weight"),
2964
+ color: z16.string().optional().describe("Axis label color (hex)"),
2965
+ axisColor: z16.string().optional().describe("Axis line color (hex)"),
2966
+ axisNameFontConfig: z16.object({
2967
+ fontFamily: z16.string().optional(),
2968
+ fontSize: z16.number().optional(),
2969
+ fontWeight: z16.number().optional()
3511
2970
  }).optional().describe("Axis title font configuration")
3512
2971
  }).optional();
3513
2972
  function buildAdminTheme(p, presetBase) {
@@ -3663,94 +3122,94 @@ function registerCustomizeTheme(server, client) {
3663
3122
  "customize_embed_theme",
3664
3123
  `Theme an embed: presets (${Object.keys(THEME_PRESETS).join("/")}), colors, fonts, cards, chart appearance, responsive layout, or access presets (${Object.keys(ACCESS_PRESETS).join("/")}).`,
3665
3124
  {
3666
- embedId: z17.string().describe("ID of the embed to theme"),
3667
- body: z17.record(z17.unknown()).optional().describe(
3125
+ embedId: z16.string().describe("ID of the embed to theme"),
3126
+ body: z16.record(z16.unknown()).optional().describe(
3668
3127
  "Raw API body for PUT /api/v2/data-app/embeds. If provided, sent directly and all convenience fields are ignored."
3669
3128
  ),
3670
3129
  // --- Presets ---
3671
- themePreset: z17.enum(["light", "dark", "corporate", "minimal"]).optional().describe("Apply a theme preset as the base. Granular params below override preset values."),
3672
- accessPreset: z17.enum(["view-only", "power-user", "export-only", "ai-enabled"]).optional().describe("Apply an access settings preset"),
3130
+ themePreset: z16.enum(["light", "dark", "corporate", "minimal"]).optional().describe("Apply a theme preset as the base. Granular params below override preset values."),
3131
+ accessPreset: z16.enum(["view-only", "power-user", "export-only", "ai-enabled"]).optional().describe("Apply an access settings preset"),
3673
3132
  // --- General ---
3674
3133
  fontFamily: fontFamilyEnum.optional().describe("Global font family"),
3675
- datePickerFormat: z17.string().optional().describe("Date picker display format"),
3134
+ datePickerFormat: z16.string().optional().describe("Date picker display format"),
3676
3135
  // --- Colors ---
3677
- primaryColor: z17.string().optional().describe("Primary accent color (hex)"),
3678
- backgroundColor: z17.string().optional().describe("Overall background color (hex)"),
3679
- textColor: z17.string().optional().describe("Default text color (hex)"),
3680
- ctaColor: z17.string().optional().describe("Call-to-action button color (hex)"),
3681
- ctaTextColor: z17.string().optional().describe("CTA button text color (hex)"),
3136
+ primaryColor: z16.string().optional().describe("Primary accent color (hex)"),
3137
+ backgroundColor: z16.string().optional().describe("Overall background color (hex)"),
3138
+ textColor: z16.string().optional().describe("Default text color (hex)"),
3139
+ ctaColor: z16.string().optional().describe("Call-to-action button color (hex)"),
3140
+ ctaTextColor: z16.string().optional().describe("CTA button text color (hex)"),
3682
3141
  // --- Dashboard ---
3683
- dashboardBackgroundColor: z17.string().optional().describe("Dashboard container background (hex)"),
3684
- selectBoxSize: z17.enum(["small", "medium", "large"]).optional().describe("Filter select box size"),
3685
- selectBoxVariant: z17.enum(["floating", "outlined", "standard"]).optional().describe("Filter select box style"),
3686
- selectBoxBorderRadius: z17.string().optional().describe('Select box border radius (e.g. "20px")'),
3687
- selectBoxTextColor: z17.string().optional().describe("Select box text color (hex)"),
3688
- metricCardColor: z17.string().optional().describe("Metric card background color (hex)"),
3142
+ dashboardBackgroundColor: z16.string().optional().describe("Dashboard container background (hex)"),
3143
+ selectBoxSize: z16.enum(["small", "medium", "large"]).optional().describe("Filter select box size"),
3144
+ selectBoxVariant: z16.enum(["floating", "outlined", "standard"]).optional().describe("Filter select box style"),
3145
+ selectBoxBorderRadius: z16.string().optional().describe('Select box border radius (e.g. "20px")'),
3146
+ selectBoxTextColor: z16.string().optional().describe("Select box text color (hex)"),
3147
+ metricCardColor: z16.string().optional().describe("Metric card background color (hex)"),
3689
3148
  // --- Card Title ---
3690
- cardTitleFontSize: z17.string().optional().describe('Card title font size (e.g. "16px")'),
3691
- cardTitleFontWeight: z17.string().optional().describe('Card title font weight (e.g. "600")'),
3692
- cardTitleColor: z17.string().optional().describe("Card title text color (hex)"),
3149
+ cardTitleFontSize: z16.string().optional().describe('Card title font size (e.g. "16px")'),
3150
+ cardTitleFontWeight: z16.string().optional().describe('Card title font weight (e.g. "600")'),
3151
+ cardTitleColor: z16.string().optional().describe("Card title text color (hex)"),
3693
3152
  // --- Card Description ---
3694
- cardDescriptionFontSize: z17.string().optional().describe("Card description font size"),
3695
- cardDescriptionFontWeight: z17.string().optional().describe("Card description font weight"),
3696
- cardDescriptionColor: z17.string().optional().describe("Card description text color (hex)"),
3153
+ cardDescriptionFontSize: z16.string().optional().describe("Card description font size"),
3154
+ cardDescriptionFontWeight: z16.string().optional().describe("Card description font weight"),
3155
+ cardDescriptionColor: z16.string().optional().describe("Card description text color (hex)"),
3697
3156
  // --- Card Customization ---
3698
- cardPadding: z17.string().optional().describe("Card internal padding"),
3699
- cardBorderRadius: z17.string().optional().describe("Card border radius"),
3700
- cardShadow: z17.string().optional().describe("Card box-shadow CSS value"),
3701
- disableShadowOnHover: z17.boolean().optional().describe("Disable shadow on card hover"),
3702
- disableCardStroke: z17.boolean().optional().describe("Remove card border stroke"),
3703
- cardStrokeColor: z17.string().optional().describe("Card border stroke color (hex)"),
3157
+ cardPadding: z16.string().optional().describe("Card internal padding"),
3158
+ cardBorderRadius: z16.string().optional().describe("Card border radius"),
3159
+ cardShadow: z16.string().optional().describe("Card box-shadow CSS value"),
3160
+ disableShadowOnHover: z16.boolean().optional().describe("Disable shadow on card hover"),
3161
+ disableCardStroke: z16.boolean().optional().describe("Remove card border stroke"),
3162
+ cardStrokeColor: z16.string().optional().describe("Card border stroke color (hex)"),
3704
3163
  // --- Chart Colors (admin theme level) ---
3705
- chartFontFamily: z17.string().optional().describe("Font family for chart text"),
3706
- chartFontColor: z17.string().optional().describe("Font color for chart text (hex)"),
3707
- chartColors: z17.array(z17.string()).optional().describe("Array of chart series colors (hex)"),
3708
- chartPaletteName: z17.string().optional().describe("Name for a custom palette"),
3709
- chartPaletteColors: z17.array(z17.string()).optional().describe("9 colors for a custom palette"),
3164
+ chartFontFamily: z16.string().optional().describe("Font family for chart text"),
3165
+ chartFontColor: z16.string().optional().describe("Font color for chart text (hex)"),
3166
+ chartColors: z16.array(z16.string()).optional().describe("Array of chart series colors (hex)"),
3167
+ chartPaletteName: z16.string().optional().describe("Name for a custom palette"),
3168
+ chartPaletteColors: z16.array(z16.string()).optional().describe("9 colors for a custom palette"),
3710
3169
  // --- Chart Tooltip (admin theme level) ---
3711
3170
  tooltipHeader: tooltipFontSchema.describe("Tooltip header font config"),
3712
3171
  tooltipLabel: tooltipFontSchema.describe("Tooltip label font config"),
3713
3172
  tooltipValue: tooltipFontSchema.describe("Tooltip value font config"),
3714
3173
  // --- Component Theme ---
3715
- buttonPrimaryColor: z17.string().optional().describe("Primary button background (hex)"),
3716
- buttonPrimaryTextColor: z17.string().optional().describe("Primary button text (hex)"),
3717
- buttonSecondaryColor: z17.string().optional().describe("Secondary button background (hex)"),
3718
- buttonSecondaryTextColor: z17.string().optional().describe("Secondary button text (hex)"),
3719
- checkboxCheckedColor: z17.string().optional().describe("Checked checkbox color (hex)"),
3720
- checkboxUncheckedColor: z17.string().optional().describe("Unchecked checkbox color (hex)"),
3721
- switchEnabledColor: z17.string().optional().describe("Enabled switch color (hex)"),
3722
- switchDisabledColor: z17.string().optional().describe("Disabled switch color (hex)"),
3723
- breadcrumbFontFamily: z17.string().optional().describe("Drill breadcrumb font family"),
3724
- breadcrumbFontColor: z17.string().optional().describe("Drill breadcrumb font color (hex)"),
3725
- breadcrumbActiveColor: z17.string().optional().describe("Active breadcrumb color (hex)"),
3726
- filterBadgeColor: z17.string().optional().describe("Multi-select filter badge color (hex)"),
3727
- filterBadgeTextColor: z17.string().optional().describe("Filter badge text color (hex)"),
3728
- datePickerColor: z17.string().optional().describe("Date picker accent color (hex)"),
3174
+ buttonPrimaryColor: z16.string().optional().describe("Primary button background (hex)"),
3175
+ buttonPrimaryTextColor: z16.string().optional().describe("Primary button text (hex)"),
3176
+ buttonSecondaryColor: z16.string().optional().describe("Secondary button background (hex)"),
3177
+ buttonSecondaryTextColor: z16.string().optional().describe("Secondary button text (hex)"),
3178
+ checkboxCheckedColor: z16.string().optional().describe("Checked checkbox color (hex)"),
3179
+ checkboxUncheckedColor: z16.string().optional().describe("Unchecked checkbox color (hex)"),
3180
+ switchEnabledColor: z16.string().optional().describe("Enabled switch color (hex)"),
3181
+ switchDisabledColor: z16.string().optional().describe("Disabled switch color (hex)"),
3182
+ breadcrumbFontFamily: z16.string().optional().describe("Drill breadcrumb font family"),
3183
+ breadcrumbFontColor: z16.string().optional().describe("Drill breadcrumb font color (hex)"),
3184
+ breadcrumbActiveColor: z16.string().optional().describe("Active breadcrumb color (hex)"),
3185
+ filterBadgeColor: z16.string().optional().describe("Multi-select filter badge color (hex)"),
3186
+ filterBadgeTextColor: z16.string().optional().describe("Filter badge text color (hex)"),
3187
+ datePickerColor: z16.string().optional().describe("Date picker accent color (hex)"),
3729
3188
  // --- Responsive Breakpoints ---
3730
- breakpointXl: z17.number().optional().describe("XL breakpoint width in px"),
3731
- breakpointLg: z17.number().optional().describe("LG breakpoint width in px"),
3732
- breakpointMd: z17.number().optional().describe("MD breakpoint width in px"),
3733
- breakpointSm: z17.number().optional().describe("SM breakpoint width in px"),
3734
- breakpointXs: z17.number().optional().describe("XS breakpoint width in px"),
3189
+ breakpointXl: z16.number().optional().describe("XL breakpoint width in px"),
3190
+ breakpointLg: z16.number().optional().describe("LG breakpoint width in px"),
3191
+ breakpointMd: z16.number().optional().describe("MD breakpoint width in px"),
3192
+ breakpointSm: z16.number().optional().describe("SM breakpoint width in px"),
3193
+ breakpointXs: z16.number().optional().describe("XS breakpoint width in px"),
3735
3194
  // --- Metric Layout Columns ---
3736
- layoutColsXl: z17.number().optional().describe("Grid columns at XL breakpoint"),
3737
- layoutColsLg: z17.number().optional().describe("Grid columns at LG breakpoint"),
3738
- layoutColsMd: z17.number().optional().describe("Grid columns at MD breakpoint"),
3739
- layoutColsSm: z17.number().optional().describe("Grid columns at SM breakpoint"),
3740
- layoutColsXs: z17.number().optional().describe("Grid columns at XS breakpoint"),
3741
- layoutColsXxs: z17.number().optional().describe("Grid columns at XXS breakpoint"),
3195
+ layoutColsXl: z16.number().optional().describe("Grid columns at XL breakpoint"),
3196
+ layoutColsLg: z16.number().optional().describe("Grid columns at LG breakpoint"),
3197
+ layoutColsMd: z16.number().optional().describe("Grid columns at MD breakpoint"),
3198
+ layoutColsSm: z16.number().optional().describe("Grid columns at SM breakpoint"),
3199
+ layoutColsXs: z16.number().optional().describe("Grid columns at XS breakpoint"),
3200
+ layoutColsXxs: z16.number().optional().describe("Grid columns at XXS breakpoint"),
3742
3201
  // --- Chart Appearance (detailed) ---
3743
3202
  tooltipHeaderStyle: fontConfigInputSchema.describe("Tooltip header: fontSize, fontFamily, fontWeight, color"),
3744
3203
  tooltipLabelStyle: fontConfigInputSchema.describe("Tooltip label: fontSize, fontFamily, fontWeight, color"),
3745
3204
  tooltipValueStyle: fontConfigInputSchema.describe("Tooltip value: fontSize, fontFamily, fontWeight, color"),
3746
- labelPosition: z17.enum(["hidden", "top", "left", "right", "bottom", "inside"]).optional().describe("Data label position on charts"),
3747
- radialLabelPosition: z17.enum(["outside", "inside"]).optional().describe("Label position for radial/pie charts"),
3748
- marginTop: z17.number().optional().describe("Chart top margin in pixels"),
3749
- marginLeft: z17.number().optional().describe("Chart left margin in pixels"),
3750
- marginRight: z17.number().optional().describe("Chart right margin in pixels"),
3751
- marginBottom: z17.number().optional().describe("Chart bottom margin in pixels"),
3752
- legendShow: z17.boolean().optional().describe("Show or hide chart legend"),
3753
- legendPosition: z17.enum([
3205
+ labelPosition: z16.enum(["hidden", "top", "left", "right", "bottom", "inside"]).optional().describe("Data label position on charts"),
3206
+ radialLabelPosition: z16.enum(["outside", "inside"]).optional().describe("Label position for radial/pie charts"),
3207
+ marginTop: z16.number().optional().describe("Chart top margin in pixels"),
3208
+ marginLeft: z16.number().optional().describe("Chart left margin in pixels"),
3209
+ marginRight: z16.number().optional().describe("Chart right margin in pixels"),
3210
+ marginBottom: z16.number().optional().describe("Chart bottom margin in pixels"),
3211
+ legendShow: z16.boolean().optional().describe("Show or hide chart legend"),
3212
+ legendPosition: z16.enum([
3754
3213
  "top-left",
3755
3214
  "top-center",
3756
3215
  "top-right",
@@ -3760,17 +3219,17 @@ function registerCustomizeTheme(server, client) {
3760
3219
  "bottom-center",
3761
3220
  "bottom-right"
3762
3221
  ]).optional().describe("Legend position"),
3763
- legendLayout: z17.enum(["horizontal", "vertical"]).optional().describe("Legend layout direction"),
3764
- legendShape: z17.enum(["circle", "rect", "roundRect", "triangle", "diamond", "arrow", "none"]).optional().describe("Legend icon shape"),
3765
- legendFontSize: z17.number().optional().describe("Legend font size"),
3766
- legendFontWeight: z17.number().optional().describe("Legend font weight"),
3767
- legendFontFamily: z17.string().optional().describe("Legend font family"),
3768
- legendColor: z17.string().optional().describe("Legend text color (hex)"),
3769
- legendTruncateLength: z17.number().optional().describe("Max characters before truncating legend"),
3770
- disableLegendScrolling: z17.boolean().optional().describe("Disable scrolling in legend"),
3771
- enableVariableLegendPosition: z17.boolean().optional().describe("Allow user to drag legend"),
3772
- legendTop: z17.number().optional().describe("Custom legend top offset"),
3773
- legendLeft: z17.number().optional().describe("Custom legend left offset"),
3222
+ legendLayout: z16.enum(["horizontal", "vertical"]).optional().describe("Legend layout direction"),
3223
+ legendShape: z16.enum(["circle", "rect", "roundRect", "triangle", "diamond", "arrow", "none"]).optional().describe("Legend icon shape"),
3224
+ legendFontSize: z16.number().optional().describe("Legend font size"),
3225
+ legendFontWeight: z16.number().optional().describe("Legend font weight"),
3226
+ legendFontFamily: z16.string().optional().describe("Legend font family"),
3227
+ legendColor: z16.string().optional().describe("Legend text color (hex)"),
3228
+ legendTruncateLength: z16.number().optional().describe("Max characters before truncating legend"),
3229
+ disableLegendScrolling: z16.boolean().optional().describe("Disable scrolling in legend"),
3230
+ enableVariableLegendPosition: z16.boolean().optional().describe("Allow user to drag legend"),
3231
+ legendTop: z16.number().optional().describe("Custom legend top offset"),
3232
+ legendLeft: z16.number().optional().describe("Custom legend left offset"),
3774
3233
  verticalAxis: axisSchema.describe("Vertical (Y) axis configuration"),
3775
3234
  horizontalAxis: axisSchema.describe("Horizontal (X) axis configuration")
3776
3235
  },
@@ -3845,7 +3304,7 @@ Use \`get_embed_details\` to view the full current theme configuration.` + GUEST
3845
3304
  }
3846
3305
 
3847
3306
  // src/tools/embed/get-demo-link.ts
3848
- import { z as z18 } from "zod";
3307
+ import { z as z17 } from "zod";
3849
3308
  var HOUR_MS = 60 * 60 * 1e3;
3850
3309
  var DEFAULT_EXPIRY_HOURS = 24;
3851
3310
  var MAX_EXPIRY_HOURS = 24 * 30;
@@ -3855,11 +3314,11 @@ function registerGetDemoLink(server, client) {
3855
3314
  "get_demo_link",
3856
3315
  `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.`,
3857
3316
  {
3858
- clientId: z18.string().describe("End-customer identifier to scope the preview to."),
3859
- dashboardName: z18.string().optional().describe("Dashboard name (non-technical). Preferred \u2014 pass with optional `workspaceName`."),
3860
- workspaceName: z18.string().optional().describe("Workspace name. Only needed if `dashboardName` is ambiguous."),
3861
- embedId: z18.string().optional().describe("Technical alternative to `dashboardName`."),
3862
- expiryHours: z18.number().min(1).max(MAX_EXPIRY_HOURS).optional().describe(`Token TTL in hours. Defaults to ${DEFAULT_EXPIRY_HOURS}. Max ${MAX_EXPIRY_HOURS} (30 days).`)
3317
+ clientId: z17.string().describe("End-customer identifier to scope the preview to."),
3318
+ dashboardName: z17.string().optional().describe("Dashboard name (non-technical). Preferred \u2014 pass with optional `workspaceName`."),
3319
+ workspaceName: z17.string().optional().describe("Workspace name. Only needed if `dashboardName` is ambiguous."),
3320
+ embedId: z17.string().optional().describe("Technical alternative to `dashboardName`."),
3321
+ expiryHours: z17.number().min(1).max(MAX_EXPIRY_HOURS).optional().describe(`Token TTL in hours. Defaults to ${DEFAULT_EXPIRY_HOURS}. Max ${MAX_EXPIRY_HOURS} (30 days).`)
3863
3322
  },
3864
3323
  WRITE_CREATE,
3865
3324
  async (params) => {
@@ -3915,70 +3374,70 @@ function registerGetDemoLink(server, client) {
3915
3374
  }
3916
3375
 
3917
3376
  // src/tools/frontend/generate-guest-token.ts
3918
- import { z as z19 } from "zod";
3377
+ import { z as z18 } from "zod";
3919
3378
  function registerGenerateGuestToken(server, client) {
3920
3379
  const services = createServiceBundle(client);
3921
3380
  server.tool(
3922
3381
  "generate_guest_token",
3923
3382
  "Generate a guest token for frontend embedding. Requires clientId. Omit dataAppName to auto-resolve.",
3924
3383
  {
3925
- clientId: z19.string().describe('Unique end-user identifier for multi-tenant filtering. Use "None" if no tenancy.'),
3926
- dataAppName: z19.string().optional().describe("Name of the data app. Omit to list available data apps."),
3927
- body: z19.record(z19.unknown()).optional().describe(
3384
+ clientId: z18.string().describe('Unique end-user identifier for multi-tenant filtering. Use "None" if no tenancy.'),
3385
+ dataAppName: z18.string().optional().describe("Name of the data app. Omit to list available data apps."),
3386
+ body: z18.record(z18.unknown()).optional().describe(
3928
3387
  "Raw API body for POST /api/v2/guest-token/create. If provided, sent directly and convenience fields are ignored."
3929
3388
  ),
3930
- params: z19.object({
3931
- allowedEmbeds: z19.array(z19.string()).optional().describe("Restrict token to specific embed IDs"),
3932
- rlsSettings: z19.array(z19.object({
3933
- metricId: z19.string().describe("Metric ID"),
3934
- values: z19.record(z19.unknown()).describe("RLS values by column name")
3389
+ params: z18.object({
3390
+ allowedEmbeds: z18.array(z18.string()).optional().describe("Restrict token to specific embed IDs"),
3391
+ rlsSettings: z18.array(z18.object({
3392
+ metricId: z18.string().describe("Metric ID"),
3393
+ values: z18.record(z18.unknown()).describe("RLS values by column name")
3935
3394
  })).optional().describe("Legacy alias used in docs/examples; forwarded as params.rlsSettings"),
3936
- appFilters: z19.array(z19.object({
3937
- metricId: z19.string().describe("Metric ID"),
3938
- values: z19.record(z19.unknown()).describe("Filter values by column name")
3395
+ appFilters: z18.array(z18.object({
3396
+ metricId: z18.string().describe("Metric ID"),
3397
+ values: z18.record(z18.unknown()).describe("Filter values by column name")
3939
3398
  })).optional().describe("Metric-level app filters"),
3940
- dashboardAppFilters: z19.array(z19.object({
3941
- dashboardId: z19.string().describe("Dashboard ID"),
3942
- values: z19.record(z19.unknown()).describe("Filter values by dashboard filter name"),
3943
- isShowOnUrl: z19.boolean().optional().describe("Whether filters are shown in URL params")
3399
+ dashboardAppFilters: z18.array(z18.object({
3400
+ dashboardId: z18.string().describe("Dashboard ID"),
3401
+ values: z18.record(z18.unknown()).describe("Filter values by dashboard filter name"),
3402
+ isShowOnUrl: z18.boolean().optional().describe("Whether filters are shown in URL params")
3944
3403
  })).optional().describe("Dashboard-level app filters"),
3945
- hideDashboardFilters: z19.array(z19.string()).optional().describe("Dashboard filter names to hide"),
3946
- userIdentifier: z19.string().optional().describe("End-user identifier for private metrics support"),
3947
- timezone: z19.string().optional().describe('IANA timezone, e.g. "America/New_York"')
3404
+ hideDashboardFilters: z18.array(z18.string()).optional().describe("Dashboard filter names to hide"),
3405
+ userIdentifier: z18.string().optional().describe("End-user identifier for private metrics support"),
3406
+ timezone: z18.string().optional().describe('IANA timezone, e.g. "America/New_York"')
3948
3407
  }).optional().describe("Advanced token params"),
3949
3408
  // Legacy convenience fields (auto-mapped into params for backward compatibility)
3950
- timezone: z19.string().optional().describe("Legacy: moved under params.timezone"),
3951
- allowedEmbeds: z19.array(z19.string()).optional().describe("Legacy: moved under params.allowedEmbeds"),
3952
- rlsSettings: z19.array(z19.object({
3953
- metricId: z19.string().describe("Metric ID"),
3954
- values: z19.record(z19.unknown()).describe("RLS values by column name")
3409
+ timezone: z18.string().optional().describe("Legacy: moved under params.timezone"),
3410
+ allowedEmbeds: z18.array(z18.string()).optional().describe("Legacy: moved under params.allowedEmbeds"),
3411
+ rlsSettings: z18.array(z18.object({
3412
+ metricId: z18.string().describe("Metric ID"),
3413
+ values: z18.record(z18.unknown()).describe("RLS values by column name")
3955
3414
  })).optional().describe("Legacy: moved under params.rlsSettings"),
3956
- appFilters: z19.array(z19.object({
3957
- metricId: z19.string().describe("Metric ID"),
3958
- values: z19.record(z19.unknown()).describe("Filter values by column name")
3415
+ appFilters: z18.array(z18.object({
3416
+ metricId: z18.string().describe("Metric ID"),
3417
+ values: z18.record(z18.unknown()).describe("Filter values by column name")
3959
3418
  })).optional().describe("Legacy: moved under params.appFilters"),
3960
- dashboardAppFilters: z19.array(z19.object({
3961
- dashboardId: z19.string().describe("Dashboard ID to apply filters to"),
3962
- values: z19.record(z19.unknown()).describe("Filter values for this dashboard"),
3963
- isShowOnUrl: z19.boolean().optional().describe("Whether filters are shown in URL params")
3419
+ dashboardAppFilters: z18.array(z18.object({
3420
+ dashboardId: z18.string().describe("Dashboard ID to apply filters to"),
3421
+ values: z18.record(z18.unknown()).describe("Filter values for this dashboard"),
3422
+ isShowOnUrl: z18.boolean().optional().describe("Whether filters are shown in URL params")
3964
3423
  })).optional().describe("Legacy: moved under params.dashboardAppFilters"),
3965
- permissions: z19.object({
3966
- isEnableArchiveMetrics: z19.boolean().optional(),
3967
- isEnableManageMetrics: z19.boolean().optional(),
3968
- isEnableCreateDashboardView: z19.boolean().optional(),
3969
- isEnableMetricUpdation: z19.boolean().optional(),
3970
- isEnableCustomizeLayout: z19.boolean().optional(),
3971
- isEnableUnderlyingData: z19.boolean().optional(),
3972
- isEnableDownloadMetrics: z19.boolean().optional(),
3973
- isShowSideBar: z19.boolean().optional(),
3974
- isShowDashboardName: z19.boolean().optional(),
3975
- isDisableMetricCreation: z19.boolean().optional()
3424
+ permissions: z18.object({
3425
+ isEnableArchiveMetrics: z18.boolean().optional(),
3426
+ isEnableManageMetrics: z18.boolean().optional(),
3427
+ isEnableCreateDashboardView: z18.boolean().optional(),
3428
+ isEnableMetricUpdation: z18.boolean().optional(),
3429
+ isEnableCustomizeLayout: z18.boolean().optional(),
3430
+ isEnableUnderlyingData: z18.boolean().optional(),
3431
+ isEnableDownloadMetrics: z18.boolean().optional(),
3432
+ isShowSideBar: z18.boolean().optional(),
3433
+ isShowDashboardName: z18.boolean().optional(),
3434
+ isDisableMetricCreation: z18.boolean().optional()
3976
3435
  }).optional().describe("Runtime permission overrides (merge with and override embed-level settings)"),
3977
- theme: z19.record(z19.unknown()).optional().describe("Runtime theme overrides (overrides embed-level theme)"),
3978
- hideDashboardFilters: z19.array(z19.string()).optional().describe("Legacy: moved under params.hideDashboardFilters"),
3979
- userIdentifier: z19.string().optional().describe("Legacy: moved under params.userIdentifier"),
3980
- expiryTime: z19.number().optional().describe("Token TTL in milliseconds"),
3981
- datasourceName: z19.string().optional().describe("Datasource name for multi-datasource routing")
3436
+ theme: z18.record(z18.unknown()).optional().describe("Runtime theme overrides (overrides embed-level theme)"),
3437
+ hideDashboardFilters: z18.array(z18.string()).optional().describe("Legacy: moved under params.hideDashboardFilters"),
3438
+ userIdentifier: z18.string().optional().describe("Legacy: moved under params.userIdentifier"),
3439
+ expiryTime: z18.number().optional().describe("Token TTL in milliseconds"),
3440
+ datasourceName: z18.string().optional().describe("Datasource name for multi-datasource routing")
3982
3441
  },
3983
3442
  WRITE_CREATE,
3984
3443
  async (params) => {
@@ -4031,7 +3490,7 @@ Next step: Use \`generate_embed_code\` to get copy-paste-ready frontend code.`
4031
3490
  }
4032
3491
 
4033
3492
  // src/tools/frontend/generate-embed-code.ts
4034
- import { z as z20 } from "zod";
3493
+ import { z as z19 } from "zod";
4035
3494
 
4036
3495
  // src/templates/react.ts
4037
3496
  function kebabCase(str) {
@@ -5027,22 +4486,22 @@ function registerGenerateEmbedCode(server, _client) {
5027
4486
  "generate_embed_code",
5028
4487
  "Generate framework-specific embed code (React, Next.js, Vue, Angular, Svelte, SolidJS, vanilla HTML).",
5029
4488
  {
5030
- embedId: z20.string().describe("The embed ID to generate code for"),
5031
- framework: z20.enum(FRAMEWORKS).describe("Frontend framework"),
5032
- embedType: z20.enum(["dashboard", "metric"]).optional().default("dashboard"),
5033
- options: z20.object({
5034
- enableDownloadCsv: z20.boolean().optional(),
5035
- enableEmailCsv: z20.boolean().optional(),
5036
- enableDownloadAllMetrics: z20.boolean().optional(),
5037
- enableDownloadAllPdf: z20.boolean().optional(),
5038
- disableFullscreen: z20.boolean().optional(),
5039
- isHideTablePreview: z20.boolean().optional(),
5040
- isHideChartSettings: z20.boolean().optional(),
5041
- isStickyDashboardFilters: z20.boolean().optional(),
5042
- enableMultiMetricFilters: z20.boolean().optional(),
5043
- themeName: z20.string().optional(),
5044
- customTheme: z20.record(z20.unknown()).optional(),
5045
- optionsIcon: z20.enum(["kebab-menu-vertical", "download"]).optional()
4489
+ embedId: z19.string().describe("The embed ID to generate code for"),
4490
+ framework: z19.enum(FRAMEWORKS).describe("Frontend framework"),
4491
+ embedType: z19.enum(["dashboard", "metric"]).optional().default("dashboard"),
4492
+ options: z19.object({
4493
+ enableDownloadCsv: z19.boolean().optional(),
4494
+ enableEmailCsv: z19.boolean().optional(),
4495
+ enableDownloadAllMetrics: z19.boolean().optional(),
4496
+ enableDownloadAllPdf: z19.boolean().optional(),
4497
+ disableFullscreen: z19.boolean().optional(),
4498
+ isHideTablePreview: z19.boolean().optional(),
4499
+ isHideChartSettings: z19.boolean().optional(),
4500
+ isStickyDashboardFilters: z19.boolean().optional(),
4501
+ enableMultiMetricFilters: z19.boolean().optional(),
4502
+ themeName: z19.string().optional(),
4503
+ customTheme: z19.record(z19.unknown()).optional(),
4504
+ optionsIcon: z19.enum(["kebab-menu-vertical", "download"]).optional()
5046
4505
  }).optional()
5047
4506
  },
5048
4507
  READ_ONLY,
@@ -5082,17 +4541,17 @@ ${generated.instructions}`;
5082
4541
  }
5083
4542
 
5084
4543
  // src/tools/analytics/list-dashboards.ts
5085
- import { z as z21 } from "zod";
4544
+ import { z as z20 } from "zod";
5086
4545
  function registerListDashboards(server, client) {
5087
4546
  const services = createServiceBundle(client);
5088
4547
  server.tool(
5089
4548
  "list_dashboards",
5090
4549
  "List dashboards globally or for a specific data app.",
5091
4550
  {
5092
- scope: z21.enum(["auto", "global", "data_app"]).optional().describe(
4551
+ scope: z20.enum(["auto", "global", "data_app"]).optional().describe(
5093
4552
  "Dashboard listing scope. auto: use data_app when dataAppName + API token are available, otherwise global."
5094
4553
  ),
5095
- dataAppName: z21.string().optional().describe("Data app name for data_app scope. Ignored for global scope.")
4554
+ dataAppName: z20.string().optional().describe("Data app name for data_app scope. Ignored for global scope.")
5096
4555
  },
5097
4556
  READ_ONLY,
5098
4557
  async (params) => {
@@ -5127,36 +4586,36 @@ function registerListDashboards(server, client) {
5127
4586
  }
5128
4587
 
5129
4588
  // src/tools/dashboard/schema.ts
5130
- import { z as z22 } from "zod";
5131
- var dashboardPositionSchema = z22.object({
5132
- x: z22.number().int().min(0).max(23),
5133
- y: z22.number().int().min(0),
5134
- w: z22.number().int().min(1).max(24),
5135
- h: z22.number().int().min(1).max(40)
4589
+ import { z as z21 } from "zod";
4590
+ var dashboardPositionSchema = z21.object({
4591
+ x: z21.number().int().min(0).max(23),
4592
+ y: z21.number().int().min(0),
4593
+ w: z21.number().int().min(1).max(24),
4594
+ h: z21.number().int().min(1).max(40)
5136
4595
  }).strict();
5137
- var dashboardChartSchema = z22.object({
5138
- type: z22.string(),
5139
- metricRef: z22.string().optional(),
5140
- query: z22.string().optional(),
5141
- companyIntegrationId: z22.string().optional(),
5142
- chartOptions: z22.record(z22.unknown()).optional()
4596
+ var dashboardChartSchema = z21.object({
4597
+ type: z21.string(),
4598
+ metricRef: z21.string().optional(),
4599
+ query: z21.string().optional(),
4600
+ companyIntegrationId: z21.string().optional(),
4601
+ chartOptions: z21.record(z21.unknown()).optional()
5143
4602
  }).passthrough();
5144
- var dashboardTileSchema = z22.object({
5145
- id: z22.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/),
5146
- kind: z22.literal("databrain-chart"),
4603
+ var dashboardTileSchema = z21.object({
4604
+ id: z21.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/),
4605
+ kind: z21.literal("databrain-chart"),
5147
4606
  position: dashboardPositionSchema,
5148
- title: z22.string().min(1).max(200),
4607
+ title: z21.string().min(1).max(200),
5149
4608
  chart: dashboardChartSchema.optional()
5150
4609
  }).strict();
5151
4610
  var dashboardDocumentShape = {
5152
- schemaVersion: z22.literal(1),
5153
- title: z22.string().min(1).max(200),
5154
- description: z22.string().max(2e3).optional(),
5155
- layout: z22.object({ columns: z22.union([z22.literal(12), z22.literal(24)]).default(24) }).strict().optional(),
5156
- tiles: z22.array(dashboardTileSchema).min(1).max(50)
4611
+ schemaVersion: z21.literal(1),
4612
+ title: z21.string().min(1).max(200),
4613
+ description: z21.string().max(2e3).optional(),
4614
+ layout: z21.object({ columns: z21.union([z21.literal(12), z21.literal(24)]).default(24) }).strict().optional(),
4615
+ tiles: z21.array(dashboardTileSchema).min(1).max(50)
5157
4616
  };
5158
- var workspaceNameSchema = z22.string().min(1).describe("Target workspace name");
5159
- var idempotencyKeySchema = z22.string().regex(
4617
+ var workspaceNameSchema = z21.string().min(1).describe("Target workspace name");
4618
+ var idempotencyKeySchema = z21.string().regex(
5160
4619
  /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
5161
4620
  "idempotencyKey must be a UUID v4"
5162
4621
  ).describe("UUID v4 unique to this workspace dashboard create request");
@@ -5209,7 +4668,7 @@ ${stringify(validation)}`
5209
4668
  }
5210
4669
 
5211
4670
  // src/tools/dashboard/generate-dashboard-tiles.ts
5212
- import { z as z23 } from "zod";
4671
+ import { z as z22 } from "zod";
5213
4672
  function registerGenerateDashboardTiles(server, client) {
5214
4673
  const services = createServiceBundle(client);
5215
4674
  server.tool(
@@ -5217,15 +4676,15 @@ function registerGenerateDashboardTiles(server, client) {
5217
4676
  "Generate multiple workspace dashboard tile drafts without persisting them.",
5218
4677
  {
5219
4678
  workspaceName: workspaceNameSchema,
5220
- dataAppName: z23.string().optional().describe("Data app in the workspace; auto-resolved when unique"),
5221
- integrationId: z23.string().optional().describe("Datasource UUID override"),
5222
- integrationName: z23.string().optional().describe("Datasource name override"),
5223
- tiles: z23.array(
5224
- z23.object({
5225
- id: z23.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/),
5226
- title: z23.string().min(1).max(200),
4679
+ dataAppName: z22.string().optional().describe("Data app in the workspace; auto-resolved when unique"),
4680
+ integrationId: z22.string().optional().describe("Datasource UUID override"),
4681
+ integrationName: z22.string().optional().describe("Datasource name override"),
4682
+ tiles: z22.array(
4683
+ z22.object({
4684
+ id: z22.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/),
4685
+ title: z22.string().min(1).max(200),
5227
4686
  position: dashboardPositionSchema,
5228
- userInputs: z23.record(z23.unknown())
4687
+ userInputs: z22.record(z22.unknown())
5229
4688
  }).strict()
5230
4689
  ).min(1).max(50)
5231
4690
  },
@@ -5309,14 +4768,14 @@ function registerGenerateDashboardTiles(server, client) {
5309
4768
  }
5310
4769
 
5311
4770
  // src/tools/dashboard/get-dashboard.ts
5312
- import { z as z24 } from "zod";
4771
+ import { z as z23 } from "zod";
5313
4772
  function registerGetDashboard(server, client) {
5314
4773
  server.tool(
5315
4774
  "get_dashboard",
5316
4775
  "Retrieve a workspace dashboard projected back into the versioned tile-list contract.",
5317
4776
  {
5318
4777
  workspaceName: workspaceNameSchema,
5319
- dashboardId: z24.string().min(1).describe("Dashboard identifier returned by create_dashboard")
4778
+ dashboardId: z23.string().min(1).describe("Dashboard identifier returned by create_dashboard")
5320
4779
  },
5321
4780
  READ_ONLY,
5322
4781
  async (params) => {
@@ -5341,18 +4800,18 @@ function registerGetDashboard(server, client) {
5341
4800
 
5342
4801
  // src/apps/preview.ts
5343
4802
  import { createHash } from "crypto";
5344
- import { z as z25 } from "zod";
5345
- var draftSchema = z25.object({
5346
- question: z25.string().min(1).max(2e3),
5347
- sql: z25.string().min(1).max(2e4),
5348
- datamartName: z25.string().min(1).max(256),
5349
- dimensions: z25.array(z25.string().max(128)).max(20).default([]),
5350
- measures: z25.array(z25.string().max(128)).max(20).default([]),
5351
- chartType: z25.string().min(1).max(80),
5352
- title: z25.string().min(1).max(200),
5353
- sessionId: z25.string().max(256).optional(),
4803
+ import { z as z24 } from "zod";
4804
+ var draftSchema = z24.object({
4805
+ question: z24.string().min(1).max(2e3),
4806
+ sql: z24.string().min(1).max(2e4),
4807
+ datamartName: z24.string().min(1).max(256),
4808
+ dimensions: z24.array(z24.string().max(128)).max(20).default([]),
4809
+ measures: z24.array(z24.string().max(128)).max(20).default([]),
4810
+ chartType: z24.string().min(1).max(80),
4811
+ title: z24.string().min(1).max(200),
4812
+ sessionId: z24.string().max(256).optional(),
5354
4813
  /** Workspaces built on this datamart. One means Save needs no choice. */
5355
- workspaces: z25.array(z25.string().max(256)).max(50).default([])
4814
+ workspaces: z24.array(z24.string().max(256)).max(50).default([])
5356
4815
  }).strict();
5357
4816
  var PREVIEW_PATH = "/api/v2/data-app/chart-preview";
5358
4817
  var METRIC_PATH = "/api/mcp/metrics/create";
@@ -5479,7 +4938,24 @@ async function saveDraftAsDashboard(client, draft, input) {
5479
4938
  }
5480
4939
 
5481
4940
  // src/tools/query/ask-question.ts
5482
- import { z as z26 } from "zod";
4941
+ import { z as z25 } from "zod";
4942
+
4943
+ // src/tools/query/answer-format.ts
4944
+ function formatConfidence(value) {
4945
+ if (typeof value !== "number" || !Number.isFinite(value)) return null;
4946
+ const percent = value > 1 ? value : value * 100;
4947
+ return `${Math.round(Math.min(Math.max(percent, 0), 100))}%`;
4948
+ }
4949
+ function formatProse(value) {
4950
+ if (Array.isArray(value)) {
4951
+ const items = value.filter((item) => typeof item === "string" && item.trim() !== "").map((item) => `- ${item.trim()}`);
4952
+ return items.length ? items.join("\n") : null;
4953
+ }
4954
+ if (typeof value === "string" && value.trim() !== "") return value.trim();
4955
+ return null;
4956
+ }
4957
+
4958
+ // src/tools/query/ask-question.ts
5483
4959
  function normalizePreviewType(type) {
5484
4960
  const t = (type || "").toLowerCase().trim();
5485
4961
  if (RENDER_TYPES.includes(t)) return t;
@@ -5519,16 +4995,16 @@ function registerAskQuestion(server, client) {
5519
4995
  "ask_question",
5520
4996
  "Ask a natural language question \u2014 converts to SQL, returns data + chart suggestion. Requires datamartName with a populated semantic layer.",
5521
4997
  {
5522
- question: z26.string().describe(
4998
+ question: z25.string().describe(
5523
4999
  'The natural language question to ask (e.g. "What was total revenue last month?")'
5524
5000
  ),
5525
- datamartName: z26.string().optional().describe(
5001
+ datamartName: z25.string().optional().describe(
5526
5002
  "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."
5527
5003
  ),
5528
- sessionId: z26.string().optional().describe(
5004
+ sessionId: z25.string().optional().describe(
5529
5005
  "Session ID from a previous ask_question response, for follow-up questions."
5530
5006
  ),
5531
- timezone: z26.string().optional().describe('IANA timezone (e.g. "America/New_York"). Defaults to UTC.')
5007
+ timezone: z25.string().optional().describe('IANA timezone (e.g. "America/New_York"). Defaults to UTC.')
5532
5008
  },
5533
5009
  WRITE_CREATE,
5534
5010
  async (params) => {
@@ -5660,23 +5136,24 @@ You can verify with \`get_semantic_layer\` using datamartName "${datamartName}".
5660
5136
  parts.push(
5661
5137
  `**Suggested chart type:** ${result.suggestedChartType}`
5662
5138
  );
5663
- if (result.confidence !== null) {
5664
- parts.push(
5665
- `**Confidence:** ${Math.round(result.confidence * 100)}%`
5666
- );
5139
+ const confidence = formatConfidence(result.confidence);
5140
+ if (confidence) {
5141
+ parts.push(`**Confidence:** ${confidence}`);
5667
5142
  }
5668
5143
  parts.push(
5669
5144
  `**Rows returned:** ${data.length} | **Time:** ${result.timeTaken}ms`
5670
5145
  );
5671
- if (result.reasoning) {
5146
+ const reasoning = formatProse(result.reasoning);
5147
+ if (reasoning) {
5672
5148
  parts.push(`
5673
5149
  ### Reasoning
5674
- ${result.reasoning}`);
5150
+ ${reasoning}`);
5675
5151
  }
5676
- if (result.summary) {
5152
+ const summary = formatProse(result.summary);
5153
+ if (summary) {
5677
5154
  parts.push(`
5678
5155
  ### Summary
5679
- ${result.summary}`);
5156
+ ${summary}`);
5680
5157
  }
5681
5158
  parts.push(`
5682
5159
  ### Data
@@ -5723,14 +5200,14 @@ To fix this:
5723
5200
  }
5724
5201
 
5725
5202
  // src/tools/query/get-semantic-layer.ts
5726
- import { z as z27 } from "zod";
5203
+ import { z as z26 } from "zod";
5727
5204
  function registerGetSemanticLayer(server, client) {
5728
5205
  const services = createServiceBundle(client);
5729
5206
  server.tool(
5730
5207
  "get_semantic_layer",
5731
5208
  "Get semantic layer metadata for a datamart: table/column descriptions, synonyms, completion score. Omit datamartName to auto-resolve.",
5732
5209
  {
5733
- datamartName: z27.string().optional().describe(
5210
+ datamartName: z26.string().optional().describe(
5734
5211
  "Name of the datamart to inspect. Omit to list available datamarts."
5735
5212
  )
5736
5213
  },
@@ -5840,15 +5317,15 @@ ${result.feedback}`);
5840
5317
  }
5841
5318
 
5842
5319
  // src/tools/query/update-semantic-layer.ts
5843
- import { z as z28 } from "zod";
5844
- var columnSchema2 = z28.object({
5845
- name: z28.string().describe("Column name (must exist in the datamart)"),
5846
- description: z28.string().optional().describe("Business-friendly description of what this column represents"),
5847
- synonyms: z28.array(z28.string()).optional().describe(
5320
+ import { z as z27 } from "zod";
5321
+ var columnSchema2 = z27.object({
5322
+ name: z27.string().describe("Column name (must exist in the datamart)"),
5323
+ description: z27.string().optional().describe("Business-friendly description of what this column represents"),
5324
+ synonyms: z27.array(z27.string()).optional().describe(
5848
5325
  'Alternative names users might use for this column (e.g. ["revenue", "sales amount"])'
5849
5326
  ),
5850
- miscellaneousInfo: z28.string().optional().describe("Additional context such as valid values, units, or caveats"),
5851
- columnType: z28.enum([
5327
+ miscellaneousInfo: z27.string().optional().describe("Additional context such as valid values, units, or caveats"),
5328
+ columnType: z27.enum([
5852
5329
  "String",
5853
5330
  "Long String",
5854
5331
  "String (Custom)",
@@ -5859,18 +5336,25 @@ var columnSchema2 = z28.object({
5859
5336
  "Identifier",
5860
5337
  "Number",
5861
5338
  "JSON"
5862
- ]).nullable().optional().describe('Semantic column type. Use "Identifier" for IDs/foreign keys, "Number" for numeric values, "String" for text. Omit for date and boolean columns.')
5339
+ ]).nullable().optional().describe('Semantic column type. Use "Identifier" for IDs/foreign keys, "Number" for numeric values, "String" for text. Omit for date and boolean columns.'),
5340
+ // The public PUT accepts these three as well (docs: Update Semantic Layer).
5341
+ // They were missing here, and zod strips unknown keys, so a caller who set
5342
+ // `isIdentifier: true` got a success message and a row with `false`.
5343
+ columnTypeConfig: z27.union([z27.record(z27.string(), z27.string()), z27.string(), z27.object({ lowerLimit: z27.number(), upperLimit: z27.number() }), z27.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."),
5344
+ isIdentifier: z27.boolean().optional().describe("Mark the column as an identifier (primary or foreign key)."),
5345
+ isNotIndexed: z27.boolean().optional().describe("Exclude the column from AI indexing.")
5863
5346
  });
5864
- var tableSchema2 = z28.object({
5865
- name: z28.string().describe("Table name (must exist in the datamart)"),
5866
- description: z28.string().optional().describe(
5347
+ var tableSchema2 = z27.object({
5348
+ name: z27.string().describe("Table name (must exist in the datamart)"),
5349
+ schemaName: z27.string().optional().describe("Schema the table lives in, when the datamart has the same table name in more than one schema."),
5350
+ description: z27.string().optional().describe(
5867
5351
  "Business-friendly description of what this table represents"
5868
5352
  ),
5869
- synonyms: z28.array(z28.string()).optional().describe("Alternative names for this table"),
5870
- miscellaneousInfo: z28.string().optional().describe(
5353
+ synonyms: z27.array(z27.string()).optional().describe("Alternative names for this table"),
5354
+ miscellaneousInfo: z27.string().optional().describe(
5871
5355
  "Additional context such as how the table relates to other tables"
5872
5356
  ),
5873
- columns: z28.array(columnSchema2).optional().describe("Column-level semantic definitions to update")
5357
+ columns: z27.array(columnSchema2).optional().describe("Column-level semantic definitions to update")
5874
5358
  });
5875
5359
  function columnKey(tableName, columnName) {
5876
5360
  return `${tableName.toLowerCase()}\0${columnName.toLowerCase()}`;
@@ -5902,6 +5386,7 @@ function normalizeTablesForApi(tables, datatypesByColumn) {
5902
5386
  if (!tables) return void 0;
5903
5387
  return tables.map((table) => {
5904
5388
  const normalized = { name: table.name };
5389
+ if (table.schemaName !== void 0) normalized.schemaName = table.schemaName;
5905
5390
  if (table.description !== void 0) normalized.description = table.description;
5906
5391
  if (table.synonyms !== void 0) normalized.synonyms = table.synonyms;
5907
5392
  if (table.miscellaneousInfo !== void 0) {
@@ -5925,6 +5410,15 @@ function normalizeTablesForApi(tables, datatypesByColumn) {
5925
5410
  normalizedColumn.columnType = column.columnType;
5926
5411
  }
5927
5412
  }
5413
+ if (column.columnTypeConfig !== void 0) {
5414
+ normalizedColumn.columnTypeConfig = column.columnTypeConfig;
5415
+ }
5416
+ if (column.isIdentifier !== void 0) {
5417
+ normalizedColumn.isIdentifier = column.isIdentifier;
5418
+ }
5419
+ if (column.isNotIndexed !== void 0) {
5420
+ normalizedColumn.isNotIndexed = column.isNotIndexed;
5421
+ }
5928
5422
  return normalizedColumn;
5929
5423
  });
5930
5424
  }
@@ -5937,13 +5431,13 @@ function registerUpdateSemanticLayer(server, client) {
5937
5431
  "update_semantic_layer",
5938
5432
  "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.",
5939
5433
  {
5940
- datamartName: z28.string().optional().describe(
5434
+ datamartName: z27.string().optional().describe(
5941
5435
  "Name of the datamart to update. Omit to list available datamarts."
5942
5436
  ),
5943
- tables: z28.array(tableSchema2).optional().describe(
5437
+ tables: z27.array(tableSchema2).optional().describe(
5944
5438
  "Table and column definitions to update. Only include tables/columns you want to change."
5945
5439
  ),
5946
- feedback: z28.string().optional().describe(
5440
+ feedback: z27.string().optional().describe(
5947
5441
  'General feedback or context about the datamart (e.g. "This datamart contains e-commerce data for the US market").'
5948
5442
  )
5949
5443
  },
@@ -6005,7 +5499,7 @@ You can now:
6005
5499
  }
6006
5500
 
6007
5501
  // src/tools/query/semantic-generation.ts
6008
- import { z as z29 } from "zod";
5502
+ import { z as z28 } from "zod";
6009
5503
  function nonEmptyString(value) {
6010
5504
  return typeof value === "string" && value.trim().length > 0 ? value : null;
6011
5505
  }
@@ -6062,35 +5556,6 @@ async function resolveDatamartFromWorkspace(params) {
6062
5556
  workspaceName: resolution.name
6063
5557
  };
6064
5558
  }
6065
- async function findDatamartIdInLinkedWorkspaces(params) {
6066
- const services = createServiceBundle(params.client);
6067
- const workspaces = await services.workspacesApi.listWorkspaces();
6068
- const matches = workspaces.map((workspace) => {
6069
- const record = workspace;
6070
- return {
6071
- name: nonEmptyString(record.name),
6072
- datamartName: workspaceDatamartNameFrom(record),
6073
- datamartId: workspaceDatamartIdFrom(record)
6074
- };
6075
- }).filter(
6076
- (workspace) => workspace.datamartName?.toLowerCase() === params.datamartName.toLowerCase() && workspace.datamartId
6077
- );
6078
- const uniqueIds = [...new Set(matches.map((workspace) => workspace.datamartId))];
6079
- if (uniqueIds.length === 1 && uniqueIds[0]) {
6080
- return {
6081
- kind: "resolved",
6082
- datamartId: uniqueIds[0],
6083
- workspaceName: matches.find((workspace) => workspace.datamartId === uniqueIds[0])?.name || void 0
6084
- };
6085
- }
6086
- if (uniqueIds.length > 1) {
6087
- return {
6088
- kind: "ambiguous",
6089
- workspaceNames: matches.map((workspace) => workspace.name).filter((name) => !!name)
6090
- };
6091
- }
6092
- return { kind: "none" };
6093
- }
6094
5559
  async function resolveDatamart(params) {
6095
5560
  if (params.datamartId) {
6096
5561
  return {
@@ -6121,30 +5586,10 @@ async function resolveDatamart(params) {
6121
5586
  if (resolution.kind !== "resolved") return { ok: false, response: resolution.response };
6122
5587
  const datamartId = datamartIdFrom(resolution.selected);
6123
5588
  if (!datamartId) {
6124
- const workspaceMatch = await findDatamartIdInLinkedWorkspaces({
6125
- client: params.client,
6126
- datamartName: resolution.name
6127
- });
6128
- if (workspaceMatch.kind === "resolved") {
6129
- return {
6130
- ok: true,
6131
- datamartName: resolution.name,
6132
- datamartId: workspaceMatch.datamartId,
6133
- workspaceName: workspaceMatch.workspaceName
6134
- };
6135
- }
6136
- if (workspaceMatch.kind === "ambiguous") {
6137
- return {
6138
- ok: false,
6139
- response: toolError(
6140
- `Datamart "${resolution.name}" is linked from multiple workspaces with different IDs. Call ${params.toolName} again with workspaceName. Matching workspaces: ${workspaceMatch.workspaceNames.join(", ")}`
6141
- )
6142
- };
6143
- }
6144
5589
  return {
6145
5590
  ok: false,
6146
5591
  response: toolError(
6147
- `Datamart "${resolution.name}" resolved by name but no id/_id/datamartId/dataMartId was returned and no linked workspace exposed its ID. Pass workspaceName for a workspace linked to this datamart, pass datamartId directly, or update the datamart list API to include IDs.`
5592
+ `Datamart "${resolution.name}" did not include an ID in the datamart list. Refresh the list and try again, or pass datamartId directly.`
6148
5593
  )
6149
5594
  };
6150
5595
  }
@@ -6160,9 +5605,9 @@ function registerStartSemanticLayerGeneration(server, client) {
6160
5605
  "start_semantic_layer_generation",
6161
5606
  "Start backend semantic-layer auto-generation for a datamart. Returns an async job id to poll.",
6162
5607
  {
6163
- datamartName: z29.string().optional().describe("Datamart name. Omit to list available datamarts"),
6164
- workspaceName: z29.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
6165
- datamartId: z29.string().optional().describe("Datamart UUID. Use only when already known.")
5608
+ datamartName: z28.string().optional().describe("Datamart name. Omit to list available datamarts"),
5609
+ workspaceName: z28.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
5610
+ datamartId: z28.string().optional().describe("Datamart UUID. Use only when already known.")
6166
5611
  },
6167
5612
  WRITE_CREATE,
6168
5613
  async (params) => {
@@ -6204,9 +5649,9 @@ function registerGetSemanticLayerGenerationStatus(server, client) {
6204
5649
  "get_semantic_layer_generation_status",
6205
5650
  "Get semantic-layer auto-generation job status for a datamart.",
6206
5651
  {
6207
- datamartName: z29.string().optional().describe("Datamart name. Omit to list available datamarts"),
6208
- workspaceName: z29.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
6209
- datamartId: z29.string().optional().describe("Datamart UUID. Use only when already known.")
5652
+ datamartName: z28.string().optional().describe("Datamart name. Omit to list available datamarts"),
5653
+ workspaceName: z28.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
5654
+ datamartId: z28.string().optional().describe("Datamart UUID. Use only when already known.")
6210
5655
  },
6211
5656
  READ_ONLY,
6212
5657
  async (params) => {
@@ -6243,7 +5688,7 @@ function registerGetSemanticLayerGenerationStatus(server, client) {
6243
5688
  }
6244
5689
 
6245
5690
  // src/tools/widgets/list-widgets.ts
6246
- import { z as z30 } from "zod";
5691
+ import { z as z29 } from "zod";
6247
5692
  function widgetReferenceId(widget) {
6248
5693
  for (const candidate of [widget.externalMetricId, widget.id, widget._id, widget.metricId]) {
6249
5694
  if (typeof candidate === "string" && candidate.trim().length > 0) return candidate.trim();
@@ -6256,10 +5701,10 @@ function registerListWidgets(server, client) {
6256
5701
  "list_widgets",
6257
5702
  "List widgets on a customer dashboard. 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.",
6258
5703
  {
6259
- clientId: z30.string().describe("Client/tenant identifier for this dashboard"),
6260
- dashboardName: z30.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6261
- workspaceName: z30.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6262
- embedId: z30.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier.")
5704
+ clientId: z29.string().describe("Client/tenant identifier for this dashboard"),
5705
+ dashboardName: z29.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
5706
+ workspaceName: z29.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
5707
+ embedId: z29.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier.")
6263
5708
  },
6264
5709
  READ_ONLY,
6265
5710
  async (params) => {
@@ -6323,29 +5768,29 @@ function registerListWidgets(server, client) {
6323
5768
  }
6324
5769
 
6325
5770
  // src/tools/widgets/add-widget.ts
6326
- import { z as z31 } from "zod";
5771
+ import { z as z30 } from "zod";
6327
5772
  function registerAddWidget(server, client) {
6328
5773
  const services = createServiceBundle(client);
6329
5774
  server.tool(
6330
5775
  "add_widget",
6331
5776
  "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.",
6332
5777
  {
6333
- clientId: z31.string().describe("Client/tenant identifier (widget is created in their scope)"),
6334
- dashboardName: z31.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6335
- workspaceName: z31.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6336
- embedId: z31.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6337
- dataAppName: z31.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
6338
- templateMetricId: z31.string().optional().describe("Source widget metricId to clone from. Copies query, chartOptions, and datasource fields; caller can override via other params."),
6339
- name: z31.string().optional().describe("Widget name (overrides template)"),
6340
- metricId: z31.string().optional().describe("Unique slug for the new widget (auto-generated if omitted)"),
6341
- query: z31.string().optional().describe("SQL query body (overrides template)"),
6342
- chartOptions: z31.record(z31.unknown()).optional().describe("Chart config JSON (chart type, colors, legend, axes). Overrides template."),
6343
- description: z31.string().optional(),
6344
- companyIntegrationId: z31.string().optional().describe("Datasource UUID override. Auto-resolved from `embedId` or sole active datasource; leave blank unless the user explicitly picked one by ID."),
6345
- integrationName: z31.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.'),
6346
- isPublished: z31.boolean().optional().describe("Publish immediately (default true)"),
6347
- body: z31.record(z31.unknown()).optional().describe("Raw createMetric body passthrough. If provided, used as-is (after defaults). Overrides all other params."),
6348
- widgets: z31.array(z31.record(z31.unknown())).min(1).max(50).optional().describe("Batch of raw createMetric bodies. Dashboard attachment is added to each body.")
5778
+ clientId: z30.string().describe("Client/tenant identifier (widget is created in their scope)"),
5779
+ dashboardName: z30.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
5780
+ workspaceName: z30.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
5781
+ embedId: z30.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
5782
+ dataAppName: z30.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
5783
+ templateMetricId: z30.string().optional().describe("Source widget metricId to clone from. Copies query, chartOptions, and datasource fields; caller can override via other params."),
5784
+ name: z30.string().optional().describe("Widget name (overrides template)"),
5785
+ metricId: z30.string().optional().describe("Unique slug for the new widget (auto-generated if omitted)"),
5786
+ query: z30.string().optional().describe("SQL query body (overrides template)"),
5787
+ chartOptions: z30.record(z30.unknown()).optional().describe("Chart config JSON (chart type, colors, legend, axes). Overrides template."),
5788
+ description: z30.string().optional(),
5789
+ companyIntegrationId: z30.string().optional().describe("Datasource UUID override. Auto-resolved from `embedId` or sole active datasource; leave blank unless the user explicitly picked one by ID."),
5790
+ integrationName: z30.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.'),
5791
+ isPublished: z30.boolean().optional().describe("Publish immediately (default true)"),
5792
+ body: z30.record(z30.unknown()).optional().describe("Raw createMetric body passthrough. If provided, used as-is (after defaults). Overrides all other params."),
5793
+ widgets: z30.array(z30.record(z30.unknown())).min(1).max(50).optional().describe("Batch of raw createMetric bodies. Dashboard attachment is added to each body.")
6349
5794
  },
6350
5795
  WRITE_CREATE,
6351
5796
  async (params) => {
@@ -6493,20 +5938,20 @@ Use \`list_widgets\` to verify, or \`update_widget\`/\`remove_widget\` to adjust
6493
5938
  }
6494
5939
 
6495
5940
  // src/tools/widgets/update-widget.ts
6496
- import { z as z32 } from "zod";
5941
+ import { z as z31 } from "zod";
6497
5942
  function registerUpdateWidget(server, client) {
6498
5943
  const services = createServiceBundle(client);
6499
5944
  server.tool(
6500
5945
  "update_widget",
6501
5946
  "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.",
6502
5947
  {
6503
- externalMetricId: z32.string().describe("Internal uuid of the widget (externalMetrics.id). Not the user-facing metricId slug."),
6504
- clientId: z32.string().describe("Client/tenant identifier"),
6505
- dashboardName: z32.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6506
- workspaceName: z32.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6507
- embedId: z32.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6508
- dataAppName: z32.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank."),
6509
- set: z32.record(z32.unknown()).describe("Partial patch. Keys can include: name, query, chartOptions, description, isPublished, isLive, selectedGroupBy, drillDownSettings, limit, etc.")
5948
+ externalMetricId: z31.string().describe("Internal uuid of the widget (externalMetrics.id). Not the user-facing metricId slug."),
5949
+ clientId: z31.string().describe("Client/tenant identifier"),
5950
+ dashboardName: z31.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
5951
+ workspaceName: z31.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
5952
+ embedId: z31.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
5953
+ dataAppName: z31.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank."),
5954
+ set: z31.record(z31.unknown()).describe("Partial patch. Keys can include: name, query, chartOptions, description, isPublished, isLive, selectedGroupBy, drillDownSettings, limit, etc.")
6510
5955
  },
6511
5956
  WRITE_IDEMPOTENT,
6512
5957
  async (params) => {
@@ -6569,19 +6014,19 @@ Use \`list_widgets\` to verify.`
6569
6014
  }
6570
6015
 
6571
6016
  // src/tools/widgets/remove-widget.ts
6572
- import { z as z33 } from "zod";
6017
+ import { z as z32 } from "zod";
6573
6018
  function registerRemoveWidget(server, client) {
6574
6019
  const services = createServiceBundle(client);
6575
6020
  server.tool(
6576
6021
  "remove_widget",
6577
6022
  "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.",
6578
6023
  {
6579
- externalMetricId: z33.string().describe("Internal uuid of the widget (externalMetrics.id)"),
6580
- clientId: z33.string().describe("Client/tenant identifier \u2014 the widget will be hidden for this client only"),
6581
- dashboardName: z33.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6582
- workspaceName: z33.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6583
- embedId: z33.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6584
- dataAppName: z33.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank.")
6024
+ externalMetricId: z32.string().describe("Internal uuid of the widget (externalMetrics.id)"),
6025
+ clientId: z32.string().describe("Client/tenant identifier \u2014 the widget will be hidden for this client only"),
6026
+ dashboardName: z32.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6027
+ workspaceName: z32.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6028
+ embedId: z32.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6029
+ dataAppName: z32.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank.")
6585
6030
  },
6586
6031
  DESTRUCTIVE,
6587
6032
  async (params) => {
@@ -6637,21 +6082,21 @@ Note: this is a per-client soft delete. The widget remains available for other c
6637
6082
  }
6638
6083
 
6639
6084
  // src/tools/widgets/generate-widget.ts
6640
- import { z as z34 } from "zod";
6085
+ import { z as z33 } from "zod";
6641
6086
  function registerGenerateWidget(server, client) {
6642
6087
  const services = createServiceBundle(client);
6643
6088
  const tool = server.tool(
6644
6089
  "generate_widget",
6645
6090
  "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.",
6646
6091
  {
6647
- clientId: z34.string().describe("Client/tenant identifier"),
6648
- userInputs: z34.record(z34.unknown()).describe('Prompt + context object. Typical shape: { prompt: "calls per day over last 30 days", schema: {...}, examples: [...] }. Backend decides which keys it honors.'),
6649
- dashboardName: z34.string().optional().describe("Dashboard name (non-technical). Preferred input for CS/CX flows \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6650
- workspaceName: z34.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6651
- embedId: z34.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6652
- dataAppName: z34.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
6653
- integrationId: z34.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."),
6654
- integrationName: z34.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.")
6092
+ clientId: z33.string().describe("Client/tenant identifier"),
6093
+ userInputs: z33.record(z33.unknown()).describe('Prompt + context object. Typical shape: { prompt: "calls per day over last 30 days", schema: {...}, examples: [...] }. Backend decides which keys it honors.'),
6094
+ dashboardName: z33.string().optional().describe("Dashboard name (non-technical). Preferred input for CS/CX flows \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6095
+ workspaceName: z33.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6096
+ embedId: z33.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6097
+ dataAppName: z33.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
6098
+ integrationId: z33.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."),
6099
+ integrationName: z33.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.")
6655
6100
  },
6656
6101
  READ_ONLY,
6657
6102
  async (params) => {
@@ -6736,9 +6181,9 @@ function registerGenerateWidget(server, client) {
6736
6181
  }
6737
6182
 
6738
6183
  // src/tools/widgets/create-or-update-metric-from-query.ts
6739
- import { z as z35 } from "zod";
6740
- var modeSchema = z35.enum(["dry_run", "apply"]).default("dry_run");
6741
- var conflictStrategySchema = z35.enum(["fail", "update", "create_new"]).default("fail");
6184
+ import { z as z34 } from "zod";
6185
+ var modeSchema = z34.enum(["dry_run", "apply"]).default("dry_run");
6186
+ var conflictStrategySchema = z34.enum(["fail", "update", "create_new"]).default("fail");
6742
6187
  function makePlanText(plan) {
6743
6188
  return JSON.stringify(plan, null, 2);
6744
6189
  }
@@ -6755,22 +6200,22 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
6755
6200
  "[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.",
6756
6201
  {
6757
6202
  mode: modeSchema.describe("dry_run returns the plan only; apply executes after confirmation"),
6758
- confirm: z35.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
6203
+ confirm: z34.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
6759
6204
  conflictStrategy: conflictStrategySchema.describe("What to do if metricId already exists on the target dashboard"),
6760
- clientId: z35.string().describe("Client/tenant identifier for the dashboard widget scope"),
6761
- workspaceName: z35.string().describe("Target workspace name"),
6762
- dashboardName: z35.string().optional().describe("Dashboard name to attach to"),
6763
- embedId: z35.string().optional().describe("Technical embed id alternative to dashboardName"),
6764
- dataAppName: z35.string().optional().describe("Optional data app override"),
6765
- name: z35.string().describe("Metric/widget display name"),
6766
- metricId: z35.string().describe("Stable metric slug to create or update"),
6767
- query: z35.string().describe("SQL query already validated against the warehouse"),
6768
- description: z35.string().optional().describe("Metric description"),
6769
- chartOptions: z35.record(z35.unknown()).optional().describe("Chart configuration JSON"),
6770
- companyIntegrationId: z35.string().optional().describe("Datasource UUID override"),
6771
- integrationName: z35.string().optional().describe("Datasource display name override"),
6772
- isPublished: z35.boolean().optional().describe("Publish after apply. Defaults to false for production safety"),
6773
- requestedBy: z35.string().optional().describe("Human operator/request context to store on the metric when supported")
6205
+ clientId: z34.string().describe("Client/tenant identifier for the dashboard widget scope"),
6206
+ workspaceName: z34.string().describe("Target workspace name"),
6207
+ dashboardName: z34.string().optional().describe("Dashboard name to attach to"),
6208
+ embedId: z34.string().optional().describe("Technical embed id alternative to dashboardName"),
6209
+ dataAppName: z34.string().optional().describe("Optional data app override"),
6210
+ name: z34.string().describe("Metric/widget display name"),
6211
+ metricId: z34.string().describe("Stable metric slug to create or update"),
6212
+ query: z34.string().describe("SQL query already validated against the warehouse"),
6213
+ description: z34.string().optional().describe("Metric description"),
6214
+ chartOptions: z34.record(z34.unknown()).optional().describe("Chart configuration JSON"),
6215
+ companyIntegrationId: z34.string().optional().describe("Datasource UUID override"),
6216
+ integrationName: z34.string().optional().describe("Datasource display name override"),
6217
+ isPublished: z34.boolean().optional().describe("Publish after apply. Defaults to false for production safety"),
6218
+ requestedBy: z34.string().optional().describe("Human operator/request context to store on the metric when supported")
6774
6219
  },
6775
6220
  WRITE_IDEMPOTENT,
6776
6221
  async (params) => {
@@ -6930,8 +6375,8 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
6930
6375
  }
6931
6376
 
6932
6377
  // src/tools/widgets/create-workspace-metric.ts
6933
- import { z as z36 } from "zod";
6934
- var modeSchema2 = z36.enum(["dry_run", "apply"]).default("dry_run");
6378
+ import { z as z35 } from "zod";
6379
+ var modeSchema2 = z35.enum(["dry_run", "apply"]).default("dry_run");
6935
6380
  function registerCreateWorkspaceMetric(server, client) {
6936
6381
  server.tool(
6937
6382
  "create_workspace_metric",
@@ -6940,16 +6385,18 @@ function registerCreateWorkspaceMetric(server, client) {
6940
6385
  mode: modeSchema2.describe(
6941
6386
  "dry_run validates the SQL only (no write); apply creates or updates the metric after confirmation"
6942
6387
  ),
6943
- confirm: z36.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
6944
- workspaceName: z36.string().describe("Target workspace name"),
6945
- name: z36.string().describe("Metric display name"),
6946
- query: z36.string().describe("SQL query to validate and store"),
6947
- metricId: z36.string().optional().describe(
6388
+ confirm: z35.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
6389
+ workspaceName: z35.string().describe("Target workspace name"),
6390
+ name: z35.string().describe("Metric display name"),
6391
+ query: z35.string().describe(
6392
+ "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)."
6393
+ ),
6394
+ metricId: z35.string().optional().describe(
6948
6395
  "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."
6949
6396
  ),
6950
- description: z36.string().optional().describe("Metric description"),
6951
- datasourceId: z36.string().optional().describe("Datasource UUID override; defaults to the workspace datasource"),
6952
- isPublished: z36.boolean().optional().describe(
6397
+ description: z35.string().optional().describe("Metric description"),
6398
+ datasourceId: z35.string().optional().describe("Datasource UUID override; defaults to the workspace datasource"),
6399
+ isPublished: z35.boolean().optional().describe(
6953
6400
  "Publish after apply. Defaults to false. Applies to creates; on updates the existing publish state is preserved."
6954
6401
  )
6955
6402
  },
@@ -7005,25 +6452,25 @@ function registerCreateWorkspaceMetric(server, client) {
7005
6452
  }
7006
6453
 
7007
6454
  // src/tools/widgets/validate-metric-spec.ts
7008
- import { z as z37 } from "zod";
6455
+ import { z as z36 } from "zod";
7009
6456
  function registerValidateMetricSpec(server, client) {
7010
6457
  const services = createServiceBundle(client);
7011
6458
  server.tool(
7012
6459
  "validate_metric_spec",
7013
6460
  "Validate a production metric spec from SQL without saving it. Resolves dashboard, data app, datasource, conflicts, and required fields.",
7014
6461
  {
7015
- clientId: z37.string().describe("Client/tenant identifier used to validate dashboard widget scope"),
7016
- workspaceName: z37.string().describe("Target production-backed workspace name"),
7017
- dashboardName: z37.string().optional().describe("Dashboard name to attach to"),
7018
- embedId: z37.string().optional().describe("Technical embed id alternative to dashboardName"),
7019
- dataAppName: z37.string().optional().describe("Optional data app override"),
7020
- name: z37.string().describe("Metric/widget display name"),
7021
- metricId: z37.string().describe("Stable metric slug to create or update"),
7022
- query: z37.string().describe("SQL query already validated against the warehouse"),
7023
- description: z37.string().optional().describe("Metric description"),
7024
- chartOptions: z37.record(z37.unknown()).optional().describe("Chart configuration JSON"),
7025
- companyIntegrationId: z37.string().optional().describe("Datasource UUID override"),
7026
- integrationName: z37.string().optional().describe("Datasource display name override")
6462
+ clientId: z36.string().describe("Client/tenant identifier used to validate dashboard widget scope"),
6463
+ workspaceName: z36.string().describe("Target production-backed workspace name"),
6464
+ dashboardName: z36.string().optional().describe("Dashboard name to attach to"),
6465
+ embedId: z36.string().optional().describe("Technical embed id alternative to dashboardName"),
6466
+ dataAppName: z36.string().optional().describe("Optional data app override"),
6467
+ name: z36.string().describe("Metric/widget display name"),
6468
+ metricId: z36.string().describe("Stable metric slug to create or update"),
6469
+ query: z36.string().describe("SQL query already validated against the warehouse"),
6470
+ description: z36.string().optional().describe("Metric description"),
6471
+ chartOptions: z36.record(z36.unknown()).optional().describe("Chart configuration JSON"),
6472
+ companyIntegrationId: z36.string().optional().describe("Datasource UUID override"),
6473
+ integrationName: z36.string().optional().describe("Datasource display name override")
7027
6474
  },
7028
6475
  READ_ONLY,
7029
6476
  async (params) => {
@@ -7114,20 +6561,20 @@ function registerValidateMetricSpec(server, client) {
7114
6561
  }
7115
6562
 
7116
6563
  // src/tools/reports/list-scheduled-reports.ts
7117
- import { z as z38 } from "zod";
6564
+ import { z as z37 } from "zod";
7118
6565
  function registerListScheduledReports(server, client) {
7119
6566
  const services = createServiceBundle(client);
7120
6567
  server.tool(
7121
6568
  "list_scheduled_reports",
7122
6569
  "List configured scheduled reports for a specific client/dashboard embed. Shows schedule metadata, not delivery success history.",
7123
6570
  {
7124
- clientId: z38.string().describe("Client/tenant identifier. Required by the current backend API for non-empty results"),
7125
- dashboardName: z38.string().optional().describe("Dashboard name to inspect"),
7126
- workspaceName: z38.string().optional().describe("Workspace name if dashboardName is ambiguous"),
7127
- embedId: z38.string().optional().describe("Technical embed id alternative to dashboardName"),
7128
- userIdentifier: z38.string().optional().describe("Optional creator/user filter"),
7129
- pageNumber: z38.number().int().positive().optional().describe("Page number when pagination is enabled"),
7130
- isPagination: z38.boolean().optional().describe("Enable backend pagination")
6571
+ clientId: z37.string().describe("Client/tenant identifier. Required by the current backend API for non-empty results"),
6572
+ dashboardName: z37.string().optional().describe("Dashboard name to inspect"),
6573
+ workspaceName: z37.string().optional().describe("Workspace name if dashboardName is ambiguous"),
6574
+ embedId: z37.string().optional().describe("Technical embed id alternative to dashboardName"),
6575
+ userIdentifier: z37.string().optional().describe("Optional creator/user filter"),
6576
+ pageNumber: z37.number().int().positive().optional().describe("Page number when pagination is enabled"),
6577
+ isPagination: z37.boolean().optional().describe("Enable backend pagination")
7131
6578
  },
7132
6579
  READ_ONLY,
7133
6580
  async (params) => {
@@ -7236,6 +6683,400 @@ function registerWhoami(server, client) {
7236
6683
  );
7237
6684
  }
7238
6685
 
6686
+ // src/tools/custom-chart/scaffold.ts
6687
+ import { z as z39 } from "zod";
6688
+
6689
+ // src/tools/custom-chart/shared.ts
6690
+ import { z as z38 } from "zod";
6691
+ var slotInputSchema = slotSchema;
6692
+ var optionInputSchema = optionSchema;
6693
+ var manifestInputSchema = z38.record(z38.unknown()).describe("manifest.json as an object (schemaVersion 2). Validated with the same rules as the server.");
6694
+ var bundleInputShape = {
6695
+ manifest: manifestInputSchema.optional(),
6696
+ bundleJs: z38.string().optional().describe("dist/index.js from `databrain custom-chart build` (the shim plus the chart module)."),
6697
+ bundleCss: z38.string().optional().describe("The raw chart.css, kept for provenance."),
6698
+ iconSvg: z38.string().optional().describe("icon.svg, at most 16 KiB, no script, no foreignObject, no external href."),
6699
+ projectDir: z38.string().optional().describe("Local only: a chart project folder. The tool reads it instead of manifest/bundleJs/iconSvg.")
6700
+ };
6701
+ function formatProblems(problems) {
6702
+ if (problems.length === 0) return "No problems.";
6703
+ return problems.map((p) => `${p.path || "/"} ${p.code} ${p.message}`).join("\n");
6704
+ }
6705
+ function projectDirRefusal() {
6706
+ if (process.env.DATABRAIN_HOSTED_PUBLIC_URL) {
6707
+ return "projectDir is not available on the hosted server. Pass manifest, bundleJs, bundleCss and iconSvg instead.";
6708
+ }
6709
+ return null;
6710
+ }
6711
+ async function resolveBundleInput(params, options) {
6712
+ if (params.projectDir) {
6713
+ const refusal = projectDirRefusal();
6714
+ if (refusal) return { problems: [], error: refusal, source: "projectDir" };
6715
+ if (params.manifest || params.bundleJs || params.iconSvg) {
6716
+ return { problems: [], error: "Pass either projectDir or manifest/bundleJs/iconSvg, not both.", source: "projectDir" };
6717
+ }
6718
+ if (options.build) {
6719
+ const result = await buildProject(params.projectDir, { write: false });
6720
+ if (!result.ok || !result.outputs) return { problems: result.problems, source: "projectDir" };
6721
+ const { manifest, bundleJs, bundleCss, iconSvg } = result.outputs;
6722
+ return { bundle: { manifest, bundleJs, bundleCss, iconSvg }, problems: [], source: "projectDir" };
6723
+ }
6724
+ const dist = readDist(params.projectDir);
6725
+ if (!dist.bundle) return { problems: dist.problems, source: "projectDir" };
6726
+ return { bundle: dist.bundle, problems: [], source: "projectDir" };
6727
+ }
6728
+ if (!params.manifest) {
6729
+ return { problems: [], error: "manifest is required (or pass projectDir when running locally).", source: "arguments" };
6730
+ }
6731
+ return {
6732
+ bundle: {
6733
+ manifest: params.manifest,
6734
+ bundleJs: params.bundleJs,
6735
+ bundleCss: params.bundleCss,
6736
+ iconSvg: params.iconSvg
6737
+ },
6738
+ problems: [],
6739
+ source: "arguments"
6740
+ };
6741
+ }
6742
+ async function remoteValidate(client, bundle) {
6743
+ try {
6744
+ const result = await createCustomChartsApi(client).validate(bundle);
6745
+ return { status: "ok", result };
6746
+ } catch (err) {
6747
+ if (err instanceof DatabrainApiError) {
6748
+ if (err.code === "MISSING_SERVICE_TOKEN") {
6749
+ return { status: "skipped", reason: "No service token is configured, so only the local validator ran." };
6750
+ }
6751
+ return { status: "failed", reason: err.message, problems: apiProblems(err) };
6752
+ }
6753
+ return { status: "failed", reason: err.message, problems: [] };
6754
+ }
6755
+ }
6756
+ function isCompleteBundle(bundle) {
6757
+ return typeof bundle.bundleJs === "string" && typeof bundle.iconSvg === "string";
6758
+ }
6759
+
6760
+ // src/tools/custom-chart/scaffold.ts
6761
+ function registerCustomChartScaffold(server, _client) {
6762
+ server.tool(
6763
+ "custom_chart_scaffold",
6764
+ "Return the files of a new custom chart project (manifest.json, chart.ts, chart.css, icon.svg, sample data, guide). Nothing is written or persisted; write the files yourself.",
6765
+ {
6766
+ key: z39.string().describe("Chart key, becomes chart type custom:<key>. Lowercase letters, digits, _ and -, 64 max."),
6767
+ name: z39.string().describe("Display name shown in the chart picker, 1 to 256 characters."),
6768
+ description: z39.string().optional().describe("Optional description, up to 500 characters."),
6769
+ slots: z39.array(slotInputSchema).optional().describe("Data slots. Defaults to category (dimension), value (measure), target (optional measure)."),
6770
+ options: z39.array(optionInputSchema).optional().describe("Chart options. Defaults to the template options (labels, subtitle, radius, colour, sort).")
6771
+ },
6772
+ READ_ONLY,
6773
+ async (params) => {
6774
+ try {
6775
+ const result = scaffoldProject({
6776
+ key: params.key,
6777
+ name: params.name,
6778
+ description: params.description,
6779
+ slots: params.slots,
6780
+ options: params.options
6781
+ });
6782
+ if (!result.ok) {
6783
+ return toolError(`The requested manifest is not valid:
6784
+ ${formatProblems(result.problems)}`);
6785
+ }
6786
+ return toolSuccess(
6787
+ JSON.stringify(
6788
+ {
6789
+ status: "scaffold_only",
6790
+ key: params.key,
6791
+ chartType: `custom:${params.key}`,
6792
+ files: result.files,
6793
+ nextStep: result.nextStep
6794
+ },
6795
+ null,
6796
+ 2
6797
+ )
6798
+ );
6799
+ } catch (err) {
6800
+ return toolError(`Unexpected error: ${err.message}`);
6801
+ }
6802
+ }
6803
+ );
6804
+ }
6805
+
6806
+ // src/tools/custom-chart/validate.ts
6807
+ function registerCustomChartValidate(server, client) {
6808
+ server.tool(
6809
+ "custom_chart_validate",
6810
+ "Validate a custom chart manifest and bundle with the local rules and, when a service token is configured, the server rules. Never persists anything.",
6811
+ bundleInputShape,
6812
+ READ_ONLY,
6813
+ async (params) => {
6814
+ try {
6815
+ const resolved = await resolveBundleInput(params, { build: true });
6816
+ if (resolved.error) return toolError(resolved.error);
6817
+ if (!resolved.bundle) {
6818
+ return toolSuccess(JSON.stringify({ ok: false, problems: resolved.problems, source: resolved.source }, null, 2));
6819
+ }
6820
+ const { bundle } = resolved;
6821
+ const problems = validateBundle(bundle);
6822
+ const complete = isCompleteBundle(bundle);
6823
+ const result = {
6824
+ ok: problems.length === 0,
6825
+ problems,
6826
+ source: resolved.source,
6827
+ contentHash: complete && problems.length === 0 ? contentHash(bundle) : void 0,
6828
+ sizeBytes: complete && problems.length === 0 ? bundleSizeBytes(bundle) : void 0
6829
+ };
6830
+ if (!complete) {
6831
+ result.note = "Only the manifest was checked. Pass bundleJs and iconSvg (or projectDir) to check the whole package.";
6832
+ } else if (problems.length === 0) {
6833
+ const remote = await remoteValidate(client, bundle);
6834
+ if (remote.status === "ok") {
6835
+ result.remote = remote.result;
6836
+ if (!remote.result.ok) {
6837
+ result.ok = false;
6838
+ result.problems = remote.result.problems;
6839
+ }
6840
+ if (remote.result.contentHash) result.contentHash = remote.result.contentHash;
6841
+ if (typeof remote.result.sizeBytes === "number") result.sizeBytes = remote.result.sizeBytes;
6842
+ } else if (remote.status === "skipped") {
6843
+ result.remote = { status: "skipped", reason: remote.reason };
6844
+ } else {
6845
+ result.remote = { status: "failed", reason: remote.reason };
6846
+ if (remote.problems.length > 0) {
6847
+ result.ok = false;
6848
+ result.problems = remote.problems;
6849
+ }
6850
+ }
6851
+ }
6852
+ result.nextStep = result.ok ? "Upload with custom_chart_upload (mode dry_run first) or `databrain custom-chart upload`." : "Fix every problem (path, code, message), then validate again.";
6853
+ return toolSuccess(JSON.stringify(result, null, 2));
6854
+ } catch (err) {
6855
+ return toolError(`Unexpected error: ${err.message}`);
6856
+ }
6857
+ }
6858
+ );
6859
+ }
6860
+
6861
+ // src/tools/custom-chart/upload.ts
6862
+ import { z as z40 } from "zod";
6863
+ function registerCustomChartUpload(server, client) {
6864
+ const api = createCustomChartsApi(client);
6865
+ server.tool(
6866
+ "custom_chart_upload",
6867
+ "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.",
6868
+ {
6869
+ mode: z40.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run validates and returns the plan; apply uploads."),
6870
+ confirm: z40.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
6871
+ publish: z40.boolean().default(false).describe("After a successful apply, publish the uploaded version so every builder can use it."),
6872
+ name: z40.string().optional().describe("Display name override. Defaults to manifest.name."),
6873
+ description: z40.string().optional().describe("Description override. Defaults to manifest.description."),
6874
+ ...bundleInputShape
6875
+ },
6876
+ WRITE_CREATE,
6877
+ async (params) => {
6878
+ try {
6879
+ const mode = params.mode || "dry_run";
6880
+ const resolved = await resolveBundleInput(params, { build: false });
6881
+ if (resolved.error) return toolError(resolved.error);
6882
+ if (!resolved.bundle) {
6883
+ return toolError(`The project could not be read:
6884
+ ${formatProblems(resolved.problems)}`);
6885
+ }
6886
+ const { bundle } = resolved;
6887
+ if (!isCompleteBundle(bundle)) {
6888
+ return toolError("manifest, bundleJs and iconSvg are all required to upload. Run `databrain custom-chart build` and pass dist/index.js, dist/manifest.json and dist/icon.svg (or projectDir when local).");
6889
+ }
6890
+ const problems = validateBundle(bundle);
6891
+ if (problems.length > 0) {
6892
+ return toolError(`The bundle is not valid. Fix these and try again:
6893
+ ${formatProblems(problems)}`);
6894
+ }
6895
+ const manifest = bundle.manifest;
6896
+ const key = manifest.id;
6897
+ const body = {
6898
+ manifest,
6899
+ bundleJs: bundle.bundleJs,
6900
+ bundleCss: bundle.bundleCss,
6901
+ iconSvg: bundle.iconSvg,
6902
+ ...params.name ? { name: params.name } : {},
6903
+ ...params.description ? { description: params.description } : {}
6904
+ };
6905
+ const localHash = contentHash(bundle);
6906
+ const localSize = bundleSizeBytes(bundle);
6907
+ if (mode === "dry_run") {
6908
+ const remote = await remoteValidate(client, bundle);
6909
+ let existing;
6910
+ let planNote;
6911
+ try {
6912
+ existing = (await api.list()).find((chart) => chart.key === key);
6913
+ } catch (err) {
6914
+ planNote = err instanceof DatabrainApiError && err.code === "MISSING_SERVICE_TOKEN" ? "No service token is configured, so the plan could not check whether the chart exists." : `Could not list custom charts: ${err.message}`;
6915
+ }
6916
+ const plan = planUpload(key, existing);
6917
+ const remoteOk = remote.status !== "ok" || remote.result.ok;
6918
+ return toolSuccess(
6919
+ JSON.stringify(
6920
+ {
6921
+ status: "dry_run_only",
6922
+ safety: genericProductionSafetyNote(),
6923
+ ok: remoteOk,
6924
+ key,
6925
+ chartType: `custom:${key}`,
6926
+ contentHash: remote.status === "ok" && remote.result.contentHash ? remote.result.contentHash : localHash,
6927
+ sizeBytes: remote.status === "ok" && typeof remote.result.sizeBytes === "number" ? remote.result.sizeBytes : localSize,
6928
+ problems: remote.status === "ok" ? remote.result.problems : remote.status === "failed" ? remote.problems : [],
6929
+ remote: remote.status === "ok" ? "validated by the server" : remote.reason,
6930
+ plan: plan.summary,
6931
+ ...planNote ? { planNote } : {},
6932
+ publishAfterUpload: params.publish === true,
6933
+ requiredApplyConfirmation: APPLY_TO_PRODUCTION_CONFIRMATION,
6934
+ nextStep: remoteOk ? `Review this plan. To execute, call again with mode="apply" and confirm="${APPLY_TO_PRODUCTION_CONFIRMATION}"${params.publish ? " (publish=true will publish the new version right away)" : ""}.` : "Fix every problem, rebuild, then run dry_run again."
6935
+ },
6936
+ null,
6937
+ 2
6938
+ )
6939
+ );
6940
+ }
6941
+ const guardError = validateProductionApply({ confirm: params.confirm });
6942
+ if (guardError) return toolError(guardError);
6943
+ const uploaded = await api.upload(body);
6944
+ const versionNumber = uploaded.version?.versionNumber;
6945
+ const lines = [];
6946
+ if (uploaded.created === "none") {
6947
+ lines.push(`This exact bundle is already v${versionNumber ?? "?"} of custom:${key}. Nothing was created.`);
6948
+ } else if (uploaded.created === "chart") {
6949
+ lines.push(`Created custom chart custom:${key} (v${versionNumber ?? 1}, status private).`);
6950
+ } else {
6951
+ lines.push(`Uploaded v${versionNumber ?? "?"} of custom:${key}. It is in preview until you publish it.`);
6952
+ }
6953
+ lines.push(`contentHash: ${uploaded.version?.contentHash ?? localHash}`);
6954
+ if (params.publish) {
6955
+ const published = await api.publish(key, uploaded.version?.id);
6956
+ lines.push(`Published v${versionNumber ?? "?"} of custom:${key}. Status: ${published.chart?.status ?? "published"}. Every dashboard using custom:${key} now renders this version.`);
6957
+ } else if (uploaded.created !== "none" || uploaded.chart?.status !== "published") {
6958
+ lines.push(`To make it available to builders: publish_custom_chart with key "${key}" and confirm="${APPLY_TO_PRODUCTION_CONFIRMATION}", or Settings > Developers > Custom charts.`);
6959
+ }
6960
+ return toolSuccess(lines.join("\n"));
6961
+ } catch (err) {
6962
+ if (err instanceof DatabrainApiError) {
6963
+ const problems = apiProblems(err);
6964
+ if (problems.length > 0) {
6965
+ return toolError(`The server refused the bundle: ${err.message}
6966
+ ${formatProblems(problems)}`);
6967
+ }
6968
+ return err.toToolResponse();
6969
+ }
6970
+ return toolError(`Unexpected error: ${err.message}`);
6971
+ }
6972
+ }
6973
+ );
6974
+ }
6975
+
6976
+ // src/tools/custom-chart/list.ts
6977
+ function registerListCustomCharts(server, client) {
6978
+ server.tool(
6979
+ "list_custom_charts",
6980
+ "List the custom chart types of the company (key, name, status, published and preview versions). Uses the service token.",
6981
+ {},
6982
+ READ_ONLY,
6983
+ async () => {
6984
+ try {
6985
+ const charts = await createCustomChartsApi(client).list();
6986
+ const rows = charts.map((chart) => ({
6987
+ key: chart.key,
6988
+ chartType: `custom:${chart.key}`,
6989
+ name: chart.name,
6990
+ status: chart.status,
6991
+ publishedVersionId: chart.publishedVersionId ?? null,
6992
+ publishedVersionNumber: typeof chart.publishedVersionNumber === "number" ? chart.publishedVersionNumber : void 0,
6993
+ previewVersionId: chart.previewVersionId ?? null,
6994
+ previewVersionNumber: typeof chart.previewVersionNumber === "number" ? chart.previewVersionNumber : void 0,
6995
+ versionsCount: typeof chart.versionsCount === "number" ? chart.versionsCount : void 0,
6996
+ updatedAt: chart.updatedAt,
6997
+ createdBy: chart.createdBy ?? null
6998
+ }));
6999
+ if (rows.length === 0) {
7000
+ return toolSuccess(
7001
+ "No custom charts yet. Scaffold one with custom_chart_scaffold, build it with `databrain custom-chart build`, then upload with custom_chart_upload."
7002
+ );
7003
+ }
7004
+ return toolSuccess(JSON.stringify({ count: rows.length, charts: rows }, null, 2));
7005
+ } catch (err) {
7006
+ if (err instanceof DatabrainApiError) return err.toToolResponse();
7007
+ return toolError(`Unexpected error: ${err.message}`);
7008
+ }
7009
+ }
7010
+ );
7011
+ }
7012
+
7013
+ // src/tools/custom-chart/publish.ts
7014
+ import { z as z41 } from "zod";
7015
+ function registerPublishCustomChart(server, client) {
7016
+ const api = createCustomChartsApi(client);
7017
+ server.tool(
7018
+ "publish_custom_chart",
7019
+ "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.",
7020
+ {
7021
+ key: z41.string().describe("The chart key (manifest.id), for example revenue-bars."),
7022
+ versionId: z41.string().optional().describe("A specific version id to publish. Defaults to the preview version."),
7023
+ mode: z41.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run shows what would be published; apply publishes."),
7024
+ confirm: z41.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`)
7025
+ },
7026
+ WRITE_IDEMPOTENT,
7027
+ async (params) => {
7028
+ try {
7029
+ const mode = params.mode || "dry_run";
7030
+ const detail = await api.get(params.key);
7031
+ const versions = Array.isArray(detail.versions) ? detail.versions : [];
7032
+ const targetId = params.versionId ?? detail.previewVersionId ?? null;
7033
+ const target = versions.find((version) => version.id === targetId);
7034
+ if (!targetId) {
7035
+ return toolError(
7036
+ `custom:${params.key} has no preview version to publish${detail.publishedVersionId ? ` (v published: ${detail.publishedVersionId})` : ""}. Upload a new version first, or pass versionId.`
7037
+ );
7038
+ }
7039
+ if (params.versionId && versions.length > 0 && !target) {
7040
+ return toolError(`custom:${params.key} has no version with id ${params.versionId}. Versions: ${versions.map((v) => `${v.id} (v${v.versionNumber})`).join(", ")}.`);
7041
+ }
7042
+ if (detail.publishedVersionId === targetId) {
7043
+ return toolSuccess(`custom:${params.key} already has ${target ? `v${target.versionNumber}` : targetId} published. Nothing to do.`);
7044
+ }
7045
+ if (mode === "dry_run") {
7046
+ return toolSuccess(
7047
+ JSON.stringify(
7048
+ {
7049
+ status: "dry_run_only",
7050
+ safety: genericProductionSafetyNote(),
7051
+ key: params.key,
7052
+ chartType: `custom:${params.key}`,
7053
+ name: detail.name,
7054
+ currentStatus: detail.status,
7055
+ currentPublishedVersionId: detail.publishedVersionId ?? null,
7056
+ willPublish: { versionId: targetId, versionNumber: target?.versionNumber, contentHash: target?.contentHash },
7057
+ impact: `Every dashboard using custom:${params.key} will render this version.`,
7058
+ requiredApplyConfirmation: APPLY_TO_PRODUCTION_CONFIRMATION,
7059
+ nextStep: `Ask the user to confirm, then call again with mode="apply" and confirm="${APPLY_TO_PRODUCTION_CONFIRMATION}".`
7060
+ },
7061
+ null,
7062
+ 2
7063
+ )
7064
+ );
7065
+ }
7066
+ const guardError = validateProductionApply({ confirm: params.confirm });
7067
+ if (guardError) return toolError(guardError);
7068
+ const result = await api.publish(params.key, targetId);
7069
+ return toolSuccess(
7070
+ `Published ${target ? `v${target.versionNumber}` : targetId} of custom:${params.key} (${result.chart?.name ?? detail.name}). Status: ${result.chart?.status ?? "published"}. Every dashboard using custom:${params.key} now renders this version.`
7071
+ );
7072
+ } catch (err) {
7073
+ if (err instanceof DatabrainApiError) return err.toToolResponse();
7074
+ return toolError(`Unexpected error: ${err.message}`);
7075
+ }
7076
+ }
7077
+ );
7078
+ }
7079
+
7239
7080
  // src/tools/registry.ts
7240
7081
  function instrumentToolRegistration(server) {
7241
7082
  const original = server.tool.bind(server);
@@ -7300,6 +7141,11 @@ function registerAllTools(server, client) {
7300
7141
  registerRemoveWidget(server, client);
7301
7142
  registerGenerateWidget(server, client);
7302
7143
  registerListScheduledReports(server, client);
7144
+ registerCustomChartScaffold(server, client);
7145
+ registerCustomChartValidate(server, client);
7146
+ registerCustomChartUpload(server, client);
7147
+ registerListCustomCharts(server, client);
7148
+ registerPublishCustomChart(server, client);
7303
7149
  }
7304
7150
 
7305
7151
  // src/resources/content/getting-started.ts
@@ -10608,15 +10454,6 @@ function registerAllPrompts(server) {
10608
10454
  }
10609
10455
 
10610
10456
  export {
10611
- oauthHttpsUrl,
10612
- oauthConfigSchema,
10613
- DatabrainApiError,
10614
- toolError,
10615
- toolSuccess,
10616
- logger,
10617
- MCP_SERVER_VERSION,
10618
- toolContext,
10619
- DatabrainClient,
10620
10457
  createServiceBundle,
10621
10458
  READ_ONLY,
10622
10459
  WRITE_CREATE,
@@ -10640,4 +10477,4 @@ export {
10640
10477
  registerAllResources,
10641
10478
  registerAllPrompts
10642
10479
  };
10643
- //# sourceMappingURL=chunk-I35U2LBX.js.map
10480
+ //# sourceMappingURL=chunk-Z2SF3K6P.js.map