@databrainhq/mcp-server 0.3.0-rc.13 → 0.3.0-rc.15

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 (50) hide show
  1. package/.mcp.json +6 -1
  2. package/dist/apps/server.d.ts +102 -389
  3. package/dist/apps/server.js +5 -5
  4. package/dist/apps/stdio.d.ts +1 -2
  5. package/dist/apps/stdio.js +6 -6
  6. package/dist/{bundle-TDQXWIPM.js → bundle-QJNCUMNY.js} +2 -2
  7. package/dist/{chunk-Q5EYDKSA.js → chunk-2F7V6RZR.js} +511 -107
  8. package/dist/chunk-2F7V6RZR.js.map +1 -0
  9. package/dist/chunk-3AH56XX7.js +28 -0
  10. package/dist/chunk-3AH56XX7.js.map +1 -0
  11. package/dist/{chunk-AEQTEE5C.js → chunk-4E57ZRZO.js} +302 -63
  12. package/dist/chunk-4E57ZRZO.js.map +1 -0
  13. package/dist/{chunk-PSJVYD4W.js → chunk-6MD7KOAU.js} +11723 -10560
  14. package/dist/chunk-6MD7KOAU.js.map +1 -0
  15. package/dist/{chunk-HCYZN7I3.js → chunk-FJLRZJ4A.js} +10 -7
  16. package/dist/chunk-FJLRZJ4A.js.map +1 -0
  17. package/dist/{chunk-X44C5GW2.js → chunk-MUZ3KEJX.js} +63 -40
  18. package/dist/chunk-MUZ3KEJX.js.map +1 -0
  19. package/dist/{chunk-DTFYHLYN.js → chunk-NXE2A4WV.js} +26 -11
  20. package/dist/chunk-NXE2A4WV.js.map +1 -0
  21. package/dist/{chunk-233VGFHY.js → chunk-QQ6KNULX.js} +317 -147
  22. package/dist/chunk-QQ6KNULX.js.map +1 -0
  23. package/dist/chunk-V5JINLOA.js +506 -0
  24. package/dist/chunk-V5JINLOA.js.map +1 -0
  25. package/dist/cli.d.ts +16 -15
  26. package/dist/cli.js +134 -36
  27. package/dist/cli.js.map +1 -1
  28. package/dist/custom-chart/custom-runtime.js +6 -6
  29. package/dist/custom-chart/template/chart.ts +116 -28
  30. package/dist/custom-chart/template/icon.svg +1 -1
  31. package/dist/custom-chart/template/manifest.json +66 -12
  32. package/dist/custom-chart/template/sample-data.json +21 -3
  33. package/dist/index.d.ts +1 -2
  34. package/dist/index.js +67 -13
  35. package/dist/index.js.map +1 -1
  36. package/dist/{main-VEKNKWL5.js → main-2FKQQLWX.js} +489 -174
  37. package/dist/main-2FKQQLWX.js.map +1 -0
  38. package/dist/{test-only-LJGOY2QA.js → test-only-VH3FF5ML.js} +15 -9
  39. package/dist/test-only-VH3FF5ML.js.map +1 -0
  40. package/package.json +20 -9
  41. package/dist/chunk-233VGFHY.js.map +0 -1
  42. package/dist/chunk-AEQTEE5C.js.map +0 -1
  43. package/dist/chunk-DTFYHLYN.js.map +0 -1
  44. package/dist/chunk-HCYZN7I3.js.map +0 -1
  45. package/dist/chunk-PSJVYD4W.js.map +0 -1
  46. package/dist/chunk-Q5EYDKSA.js.map +0 -1
  47. package/dist/chunk-X44C5GW2.js.map +0 -1
  48. package/dist/main-VEKNKWL5.js.map +0 -1
  49. package/dist/test-only-LJGOY2QA.js.map +0 -1
  50. /package/dist/{bundle-TDQXWIPM.js.map → bundle-QJNCUMNY.js.map} +0 -0
@@ -22,84 +22,18 @@ import {
22
22
  saveDraftAsDashboard,
23
23
  viewSchema,
24
24
  workspacesForDatamart
25
- } from "./chunk-PSJVYD4W.js";
25
+ } from "./chunk-6MD7KOAU.js";
26
26
  import {
27
27
  CustomChartsApi,
28
28
  DatabrainApiError,
29
29
  DatabrainClient,
30
30
  toolContext
31
- } from "./chunk-AEQTEE5C.js";
31
+ } from "./chunk-4E57ZRZO.js";
32
32
  import {
33
33
  contentHash,
34
34
  readCustomChartBrowserRuntime,
35
35
  validateBundle
36
- } from "./chunk-Q5EYDKSA.js";
37
-
38
- // src/apps/custom-chart-assets.ts
39
- import { z } from "zod";
40
- var assets = /* @__PURE__ */ new Map();
41
- function registerCustomChartAssets(server, client) {
42
- const runtime = server.tool(
43
- "load_custom_chart_runtime",
44
- "Load the trusted app/embed sandbox adapter for a custom chart tile. App-only; no company data or chart code.",
45
- {},
46
- READ_ONLY,
47
- async () => bounded({
48
- content: [{ type: "text", text: "Custom chart sandbox runtime." }],
49
- _meta: { "databrain/custom-chart-runtime": readCustomChartBrowserRuntime() }
50
- })
51
- );
52
- runtime.update({ _meta: appMeta(["app"]) });
53
- const api = new CustomChartsApi(client);
54
- const tool = server.tool(
55
- "load_custom_chart_bundle",
56
- "Load one bounded chunk of the company published custom chart bundle for the tile. No draft version, credentials, or query rows.",
57
- {
58
- key: z.string().regex(/^[a-z][a-z0-9_-]{0,63}$/),
59
- offset: z.number().int().min(0).max(MAX_CUSTOM_BUNDLE_CHARS).default(0),
60
- contentHash: z.string().regex(/^[a-f0-9]{64}$/).optional()
61
- },
62
- READ_ONLY,
63
- async ({ key, offset, contentHash: expected }) => {
64
- try {
65
- const detail = await api.get(key);
66
- if (detail.status !== "published" || !detail.publishedVersionId) throw new Error("not published");
67
- const publishedHash = detail.versions?.find((version) => version.id === detail.publishedVersionId)?.contentHash;
68
- const cached = publishedHash && /^[a-f0-9]{64}$/.test(publishedHash) ? assets.get(publishedHash) : void 0;
69
- let hash = publishedHash, asset = cached && cached.expires > Date.now() ? cached.text : void 0;
70
- if (asset === void 0) {
71
- const raw = await api.bundle(key, detail.publishedVersionId);
72
- const bundle = { ...raw, bundleCss: raw.bundleCss ?? void 0 };
73
- if (validateBundle(bundle).length || bundle.manifest.id !== key) throw new Error("invalid bundle");
74
- hash = contentHash(bundle);
75
- if (publishedHash && publishedHash !== hash) throw new Error("published hash mismatch");
76
- asset = JSON.stringify({ manifest: bundle.manifest, bundleJs: bundle.bundleJs, bundleCss: bundle.bundleCss ?? "", iconSvg: bundle.iconSvg });
77
- if (assets.size >= 4) assets.delete(assets.keys().next().value);
78
- assets.set(hash, { text: asset, expires: Date.now() + 6e4 });
79
- }
80
- if (expected && hash !== expected) throw new Error("published version changed");
81
- if (offset > asset.length) throw new Error("offset");
82
- const chunk = asset.slice(offset, offset + CUSTOM_BUNDLE_CHUNK_CHARS);
83
- return bounded({
84
- content: [{ type: "text", text: "Published custom chart asset chunk." }],
85
- structuredContent: {
86
- key,
87
- contentHash: hash,
88
- offset,
89
- chunk,
90
- nextOffset: offset + chunk.length < asset.length ? offset + chunk.length : null
91
- }
92
- });
93
- } catch {
94
- return { isError: true, content: [{
95
- type: "text",
96
- text: `Could not load published custom:${key}. It may be unpublished, changed, or unavailable to this sign-in.`
97
- }] };
98
- }
99
- }
100
- );
101
- tool.update({ _meta: appMeta(["app"]) });
102
- }
36
+ } from "./chunk-2F7V6RZR.js";
103
37
 
104
38
  // src/apps/registry.ts
105
39
  import { z as z2 } from "zod";
@@ -108,11 +42,18 @@ import { z as z2 } from "zod";
108
42
  var RENDER_PATH = "/api/v2/dashboard/mcp-chart";
109
43
  var UncachedChartClient = class extends DatabrainClient {
110
44
  constructor(delegate) {
111
- super({ apiUrl: delegate.getApiUrl(), demoDomain: delegate.getDemoDomain() });
45
+ super({
46
+ apiUrl: delegate.getApiUrl(),
47
+ demoDomain: delegate.getDemoDomain()
48
+ });
112
49
  this.delegate = delegate;
113
50
  }
114
51
  request(options) {
115
- return this.delegate.request({ ...options, cacheKey: void 0, cacheTtlMs: void 0 });
52
+ return this.delegate.request({
53
+ ...options,
54
+ cacheKey: void 0,
55
+ cacheTtlMs: void 0
56
+ });
116
57
  }
117
58
  };
118
59
  function createChartBackend(client) {
@@ -123,26 +64,31 @@ function createChartBackend(client) {
123
64
  const view = viewSchema.parse(rawView);
124
65
  if ("dashboardId" in reference) {
125
66
  if (!client.isHosted()) throw new Error("Hosted sign-in required");
126
- return pageSchema.parse(await client.request({
127
- method: "POST",
128
- path: "/api/v2/workspace/dashboard/chart",
129
- auth: "service",
130
- body: {
131
- action,
132
- dashboardId: reference.dashboardId,
133
- widgetId: reference.widgetId,
134
- ...reference.clientId ? { clientId: reference.clientId } : {},
135
- ...reference.display ? { display: reference.display } : {},
136
- ...view,
137
- pageSize: 50,
138
- width: 600,
139
- height: 320,
140
- animation: false
141
- },
142
- skipDataUnwrap: true
143
- }));
67
+ return pageSchema.parse(
68
+ await client.request({
69
+ method: "POST",
70
+ path: "/api/v2/workspace/dashboard/chart",
71
+ auth: "service",
72
+ body: {
73
+ action,
74
+ dashboardId: reference.dashboardId,
75
+ widgetId: reference.widgetId,
76
+ ...reference.clientId ? { clientId: reference.clientId } : {},
77
+ ...reference.display ? { display: reference.display } : {},
78
+ ...view,
79
+ pageSize: 50,
80
+ width: 600,
81
+ height: 320,
82
+ animation: false
83
+ },
84
+ skipDataUnwrap: true
85
+ })
86
+ );
144
87
  }
145
- const app = await services.widgetService.resolveDataAppName(reference.dataAppName, reference.embedId);
88
+ const app = await services.widgetService.resolveDataAppName(
89
+ reference.dataAppName,
90
+ reference.embedId
91
+ );
146
92
  if (!app.ok) throw new Error("Chart scope unavailable");
147
93
  const scope = await services.widgetService.ensureReadableDashboardScope({
148
94
  embedId: reference.embedId,
@@ -184,6 +130,97 @@ function createChartBackend(client) {
184
130
  };
185
131
  }
186
132
 
133
+ // src/apps/custom-chart-assets.ts
134
+ import { z } from "zod";
135
+ var assets = /* @__PURE__ */ new Map();
136
+ function registerCustomChartAssets(server, client) {
137
+ const runtime = server.tool(
138
+ "load_custom_chart_runtime",
139
+ "Load the trusted app/embed sandbox adapter for a custom chart tile. App-only; no company data or chart code.",
140
+ {},
141
+ READ_ONLY,
142
+ async () => bounded({
143
+ content: [
144
+ { type: "text", text: "Custom chart sandbox runtime." }
145
+ ],
146
+ _meta: {
147
+ "databrain/custom-chart-runtime": readCustomChartBrowserRuntime()
148
+ }
149
+ })
150
+ );
151
+ runtime.update({ _meta: appMeta(["app"]) });
152
+ const api = new CustomChartsApi(client);
153
+ const tool = server.tool(
154
+ "load_custom_chart_bundle",
155
+ "Load one bounded chunk of the company published custom chart bundle for the tile. No draft version, credentials, or query rows.",
156
+ {
157
+ key: z.string().regex(/^[a-z][a-z0-9_-]{0,63}$/),
158
+ offset: z.number().int().min(0).max(MAX_CUSTOM_BUNDLE_CHARS).default(0),
159
+ contentHash: z.string().regex(/^[a-f0-9]{64}$/).optional()
160
+ },
161
+ READ_ONLY,
162
+ async ({ key, offset, contentHash: expected }) => {
163
+ try {
164
+ const detail = await api.get(key);
165
+ if (detail.status !== "published" || !detail.publishedVersionId)
166
+ throw new Error("not published");
167
+ const publishedHash = detail.versions?.find(
168
+ (version) => version.id === detail.publishedVersionId
169
+ )?.contentHash;
170
+ const cached = publishedHash && /^[a-f0-9]{64}$/.test(publishedHash) ? assets.get(publishedHash) : void 0;
171
+ let hash = publishedHash, asset = cached && cached.expires > Date.now() ? cached.text : void 0;
172
+ if (asset === void 0) {
173
+ const raw = await api.bundle(key, detail.publishedVersionId);
174
+ const bundle = { ...raw, bundleCss: raw.bundleCss ?? void 0 };
175
+ if (validateBundle(bundle).length || bundle.manifest.id !== key)
176
+ throw new Error("invalid bundle");
177
+ hash = contentHash(bundle);
178
+ if (publishedHash && publishedHash !== hash)
179
+ throw new Error("published hash mismatch");
180
+ asset = JSON.stringify({
181
+ manifest: bundle.manifest,
182
+ bundleJs: bundle.bundleJs,
183
+ bundleCss: bundle.bundleCss ?? "",
184
+ iconSvg: bundle.iconSvg
185
+ });
186
+ if (assets.size >= 4) assets.delete(assets.keys().next().value);
187
+ assets.set(hash, { text: asset, expires: Date.now() + 6e4 });
188
+ }
189
+ if (expected && hash !== expected)
190
+ throw new Error("published version changed");
191
+ if (offset > asset.length) throw new Error("offset");
192
+ const chunk = asset.slice(offset, offset + CUSTOM_BUNDLE_CHUNK_CHARS);
193
+ return bounded({
194
+ content: [
195
+ {
196
+ type: "text",
197
+ text: "Published custom chart asset chunk."
198
+ }
199
+ ],
200
+ structuredContent: {
201
+ key,
202
+ contentHash: hash,
203
+ offset,
204
+ chunk,
205
+ nextOffset: offset + chunk.length < asset.length ? offset + chunk.length : null
206
+ }
207
+ });
208
+ } catch {
209
+ return {
210
+ isError: true,
211
+ content: [
212
+ {
213
+ type: "text",
214
+ text: `Could not load published custom:${key}. It may be unpublished, changed, or unavailable to this sign-in.`
215
+ }
216
+ ]
217
+ };
218
+ }
219
+ }
220
+ );
221
+ tool.update({ _meta: appMeta(["app"]) });
222
+ }
223
+
187
224
  // src/apps/tile.ts
188
225
  import { transformSync } from "esbuild";
189
226
  var TILE_SCRIPT = String.raw`(()=>{
@@ -377,7 +414,10 @@ host(r.hostContext||{});ready=true;notify(INIT,{});for(const m of early.splice(0
377
414
  if(tries<10)setTimeout(start,Math.min(8000,400*2**tries))}
378
415
  start();
379
416
  })();`;
380
- var SERVED_SCRIPT = transformSync(TILE_SCRIPT, { minify: true, target: "es2020" }).code;
417
+ var SERVED_SCRIPT = transformSync(TILE_SCRIPT, {
418
+ minify: true,
419
+ target: "es2020"
420
+ }).code;
381
421
  function tileHtml(kind = "chart") {
382
422
  return strip(`<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
383
423
  <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data:; font-src data:; connect-src 'none'; frame-src blob:; base-uri 'none'; form-action 'none'">
@@ -509,7 +549,10 @@ header,#status,#plot{animation:rise var(--s) var(--e) both}
509
549
  <details id="data" hidden><summary id="count">Show the data behind this</summary><div id="rows" tabindex="0" role="region" aria-label="Chart data"></div></details></main><script>${SERVED_SCRIPT}</script></body></html>`);
510
550
  }
511
551
  function strip(html) {
512
- return html.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\n{2,}/g, "\n").replace(/<style>([\s\S]*?)<\/style><\/head>/, (_m, css) => `<style>${css.replace(/;}/g, "}").replace(/\n/g, "")}</style></head>`);
552
+ return html.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\n{2,}/g, "\n").replace(
553
+ /<style>([\s\S]*?)<\/style><\/head>/,
554
+ (_m, css) => `<style>${css.replace(/;}/g, "}").replace(/\n/g, "")}</style></head>`
555
+ );
513
556
  }
514
557
 
515
558
  // src/apps/registry.ts
@@ -522,42 +565,93 @@ var actions = {
522
565
  function registerChartApps(server, client, backend = createChartBackend(client)) {
523
566
  for (const [name, description] of Object.entries(actions)) {
524
567
  const action = name;
525
- const tool = server.tool(name, description, {
526
- reference: referenceSchema,
527
- view: viewSchema.default({}),
528
- context: z2.string().max(200).optional().describe("Chart interaction context")
529
- }, READ_ONLY, async ({ reference, view }) => {
530
- try {
531
- if (action === "drill_down" && !view.drillId || action === "change_date_range" && !view.dateRange) throw new Error("Missing view selector");
532
- const page = pageSchema.parse(await toolContext.run(
533
- { tool: name, category: "embedding", workflowIntent: "chart_app", inputMode: "app" },
534
- () => backend.execute(action, reference, view)
535
- ));
536
- if (page.svg && (!/^\s*<svg\b/i.test(page.svg) || /<\s*(?:script|foreignObject|iframe|image|animate\w*|set)\b|<!DOCTYPE|<!ENTITY|\bon\w+\s*=|@(?:import|keyframes)|\banimation\s*:|(?:href\s*=\s*["'](?!#)|url\s*\(\s*["']?(?!#))/i.test(page.svg))) {
537
- throw new Error("Unsafe SVG");
568
+ const tool = server.tool(
569
+ name,
570
+ description,
571
+ {
572
+ reference: referenceSchema,
573
+ view: viewSchema.default({}),
574
+ context: z2.string().max(200).optional().describe("Chart interaction context")
575
+ },
576
+ READ_ONLY,
577
+ async ({ reference, view }) => {
578
+ try {
579
+ if (action === "drill_down" && !view.drillId || action === "change_date_range" && !view.dateRange)
580
+ throw new Error("Missing view selector");
581
+ const page = pageSchema.parse(
582
+ await toolContext.run(
583
+ {
584
+ tool: name,
585
+ category: "embedding",
586
+ workflowIntent: "chart_app",
587
+ inputMode: "app"
588
+ },
589
+ () => backend.execute(action, reference, view)
590
+ )
591
+ );
592
+ if (page.svg && (!/^\s*<svg\b/i.test(page.svg) || /<\s*(?:script|foreignObject|iframe|image|animate\w*|set)\b|<!DOCTYPE|<!ENTITY|\bon\w+\s*=|@(?:import|keyframes)|\banimation\s*:|(?:href\s*=\s*["'](?!#)|url\s*\(\s*["']?(?!#))/i.test(
593
+ page.svg
594
+ ))) {
595
+ throw new Error("Unsafe SVG");
596
+ }
597
+ return chartResult(
598
+ `${page.kind === "kpi" ? "KPI" : "Chart"} ready. ${page.rows.length} rows in this page.${page.fallback ? ` ${page.fallback}` : ""}${page.description ? ` ${page.description}` : ""}`,
599
+ {
600
+ ...page,
601
+ reference,
602
+ view
603
+ }
604
+ );
605
+ } catch (error) {
606
+ if (error instanceof DatabrainApiError) return error.toToolResponse();
607
+ return {
608
+ isError: true,
609
+ content: [
610
+ {
611
+ type: "text",
612
+ text: "Chart unavailable. Check dashboard access and the chart renderer integration, then retry."
613
+ }
614
+ ]
615
+ };
538
616
  }
539
- return chartResult(`${page.kind === "kpi" ? "KPI" : "Chart"} ready. ${page.rows.length} rows in this page.${page.fallback ? " " + page.fallback : ""}${page.description ? " " + page.description : ""}`, {
540
- ...page,
541
- reference,
542
- view
543
- });
544
- } catch (error) {
545
- if (error instanceof DatabrainApiError) return error.toToolResponse();
546
- return { isError: true, content: [{
547
- type: "text",
548
- text: "Chart unavailable. Check dashboard access and the chart renderer integration, then retry."
549
- }] };
550
617
  }
551
- });
618
+ );
552
619
  tool.update({ _meta: appMeta(["app"]) });
553
620
  }
554
621
  registerMetricTile(server, client, backend);
555
622
  registerDraftTools(server, client);
556
623
  registerCustomChartAssets(server, client);
557
- for (const [uri, kind] of [[CHART_URI, "chart"], [KPI_URI, "kpi"]]) {
558
- server.resource(`Databrain ${kind} tile`, uri, { mimeType: APP_MIME, description: `Interactive ${kind} using host theme and scoped server tools` }, async () => ({
559
- contents: [{ uri, mimeType: APP_MIME, text: tileHtml(kind), _meta: { ui: { prefersBorder: true, csp: { resourceDomains: ["data:", "blob:"], frameDomains: ["blob:"], connectDomains: [] } } } }]
560
- }));
624
+ for (const [uri, kind] of [
625
+ [CHART_URI, "chart"],
626
+ [KPI_URI, "kpi"]
627
+ ]) {
628
+ server.resource(
629
+ `Databrain ${kind} tile`,
630
+ uri,
631
+ {
632
+ mimeType: APP_MIME,
633
+ description: `Interactive ${kind} using host theme and scoped server tools`
634
+ },
635
+ async () => ({
636
+ contents: [
637
+ {
638
+ uri,
639
+ mimeType: APP_MIME,
640
+ text: tileHtml(kind),
641
+ _meta: {
642
+ ui: {
643
+ prefersBorder: true,
644
+ csp: {
645
+ resourceDomains: ["data:", "blob:"],
646
+ frameDomains: ["blob:"],
647
+ connectDomains: []
648
+ }
649
+ }
650
+ }
651
+ }
652
+ ]
653
+ })
654
+ );
561
655
  }
562
656
  }
563
657
  function registerDraftTools(server, client) {
@@ -574,9 +668,20 @@ function registerDraftTools(server, client) {
574
668
  async ({ draft, rows, chartType }) => {
575
669
  try {
576
670
  const page = await draftPage(client, draft, rows, chartType);
577
- return chartResult(`${page.kind === "table" ? "Table" : "Chart"} ready as ${chartType}.`, page);
671
+ return chartResult(
672
+ `${page.kind === "table" ? "Table" : "Chart"} ready as ${chartType}.`,
673
+ page
674
+ );
578
675
  } catch {
579
- return { isError: true, content: [{ type: "text", text: "Chart preview unavailable. Ask the question again to retry." }] };
676
+ return {
677
+ isError: true,
678
+ content: [
679
+ {
680
+ type: "text",
681
+ text: "Chart preview unavailable. Ask the question again to retry."
682
+ }
683
+ ]
684
+ };
580
685
  }
581
686
  }
582
687
  );
@@ -589,33 +694,62 @@ function registerDraftTools(server, client) {
589
694
  chartType: z2.enum(RENDER_TYPES),
590
695
  dashboardTitle: z2.string().trim().min(1).max(200),
591
696
  workspaceName: z2.string().trim().min(1).max(256).optional(),
592
- attemptId: z2.string().trim().min(1).max(64).optional().describe("Identifies one save attempt. Reuse it to retry safely; omit or change it to save again on purpose."),
697
+ attemptId: z2.string().trim().min(1).max(64).optional().describe(
698
+ "Identifies one save attempt. Reuse it to retry safely; omit or change it to save again on purpose."
699
+ ),
593
700
  confirm: z2.string().optional(),
594
701
  context: z2.string().max(200).optional().describe("Chart interaction context")
595
702
  },
596
703
  WRITE_CREATE,
597
- async ({ draft, chartType, dashboardTitle, workspaceName, attemptId, confirm }) => {
704
+ async ({
705
+ draft,
706
+ chartType,
707
+ dashboardTitle,
708
+ workspaceName,
709
+ attemptId,
710
+ confirm
711
+ }) => {
598
712
  if (confirm !== APPLY_TO_PRODUCTION_CONFIRMATION) {
599
- return { isError: true, content: [{
600
- type: "text",
601
- text: `Saving writes to production. Retry with confirm: "${APPLY_TO_PRODUCTION_CONFIRMATION}".`
602
- }] };
713
+ return {
714
+ isError: true,
715
+ content: [
716
+ {
717
+ type: "text",
718
+ text: `Saving writes to production. Retry with confirm: "${APPLY_TO_PRODUCTION_CONFIRMATION}".`
719
+ }
720
+ ]
721
+ };
603
722
  }
604
- const choices = await workspacesForDatamart(client, draft.datamartName, "authorize");
723
+ const choices = await workspacesForDatamart(
724
+ client,
725
+ draft.datamartName,
726
+ "authorize"
727
+ );
605
728
  const refuse = (text) => ({
606
729
  isError: true,
607
730
  content: [{ type: "text", text }],
608
731
  structuredContent: { kind: "save", ok: false, choices }
609
732
  });
610
- const target = workspaceName ? choices.find((w) => w.trim().toLowerCase() === workspaceName.toLowerCase()) : choices.length === 1 ? choices[0] : void 0;
733
+ const target = workspaceName ? choices.find(
734
+ (w) => w.trim().toLowerCase() === workspaceName.toLowerCase()
735
+ ) : choices.length === 1 ? choices[0] : void 0;
611
736
  if (workspaceName && !target) {
612
- return refuse(`Workspace "${workspaceName}" cannot run this datamart's SQL. Choose one of: ${choices.join(", ")}.`);
737
+ return refuse(
738
+ `Workspace "${workspaceName}" cannot run this datamart's SQL. Choose one of: ${choices.join(", ")}.`
739
+ );
613
740
  }
614
741
  if (!target) {
615
- return refuse(choices.length ? `Choose a workspace: ${choices.join(", ")}.` : `No workspace is built on datamart "${draft.datamartName}". Create one in Databrain, then save again.`);
742
+ return refuse(
743
+ choices.length ? `Choose a workspace: ${choices.join(", ")}.` : `No workspace is built on datamart "${draft.datamartName}". Create one in Databrain, then save again.`
744
+ );
616
745
  }
617
746
  try {
618
- const saved = await saveDraftAsDashboard(client, draft, { chartType, dashboardTitle, workspaceName: target, attemptId });
747
+ const saved = await saveDraftAsDashboard(client, draft, {
748
+ chartType,
749
+ dashboardTitle,
750
+ workspaceName: target,
751
+ attemptId
752
+ });
619
753
  return chartResult(
620
754
  `Saved "${saved.title}" to workspace ${saved.workspaceName} (dashboard ${saved.dashboardId}, metric ${saved.metricId}).`,
621
755
  { kind: "save", ok: true, ...saved, draft: { ...draft, chartType } }
@@ -624,10 +758,12 @@ function registerDraftTools(server, client) {
624
758
  const code = error instanceof DatabrainApiError ? error.code : "SAVE_FAILED";
625
759
  return {
626
760
  isError: true,
627
- content: [{
628
- type: "text",
629
- text: `Could not save (${code}). Check the workspace name and your access, then try again.`
630
- }],
761
+ content: [
762
+ {
763
+ type: "text",
764
+ text: `Could not save (${code}). Check the workspace name and your access, then try again.`
765
+ }
766
+ ],
631
767
  structuredContent: { kind: "save", ok: false, code }
632
768
  };
633
769
  }
@@ -650,17 +786,35 @@ function registerMetricTile(server, client, backend) {
650
786
  context: z2.string().max(200).optional()
651
787
  },
652
788
  READ_ONLY,
653
- async ({ workspaceName, workspaceId, metricId, dashboardId, display, clientId, dateRange }) => {
789
+ async ({
790
+ workspaceName,
791
+ workspaceId,
792
+ metricId,
793
+ dashboardId,
794
+ display,
795
+ clientId,
796
+ dateRange
797
+ }) => {
654
798
  try {
655
- if (!client.isHosted()) throw new Error("Use the hosted sign-in for workspace metrics. API-key connections retain list_widgets with an embed and clientId.");
799
+ if (!client.isHosted())
800
+ throw new Error(
801
+ "Use the hosted sign-in for workspace metrics. API-key connections retain list_widgets with an embed and clientId."
802
+ );
656
803
  const resolved = await client.request({
657
804
  method: "POST",
658
805
  path: "/api/v2/workspace/dashboard/widgets",
659
806
  auth: "service",
660
- body: { workspaceName, workspaceId, metricId, ...dashboardId ? { dashboardId } : {} }
807
+ body: {
808
+ workspaceName,
809
+ workspaceId,
810
+ metricId,
811
+ ...dashboardId ? { dashboardId } : {}
812
+ }
661
813
  });
662
814
  if (resolved.clientId && clientId !== resolved.clientId) {
663
- throw new Error(`CLIENT_REQUIRED: Pass clientId ${JSON.stringify(resolved.clientId)}, saved on this dashboard/workspace. No unscoped fallback is allowed.`);
815
+ throw new Error(
816
+ `CLIENT_REQUIRED: Pass clientId ${JSON.stringify(resolved.clientId)}, saved on this dashboard/workspace. No unscoped fallback is allowed.`
817
+ );
664
818
  }
665
819
  const reference = {
666
820
  dashboardId: resolved.dashboardId,
@@ -669,12 +823,28 @@ function registerMetricTile(server, client, backend) {
669
823
  ...clientId ? { clientId } : {}
670
824
  };
671
825
  const view = dateRange ? { dateRange } : {};
672
- const page = pageSchema.parse(await backend.execute("render_chart", reference, view));
673
- if (page.svg && (!/^\s*<svg\b/i.test(page.svg) || /<\s*(?:script|foreignObject|iframe|image|animate\w*|set)\b|<!DOCTYPE|<!ENTITY|\bon\w+\s*=|@(?:import|keyframes)|\banimation\s*:|(?:href\s*=\s*["'](?!#)|url\s*\(\s*["']?(?!#))/i.test(page.svg))) throw new Error("Unsafe SVG");
674
- return chartResult(`${page.kind === "kpi" ? "KPI" : "Chart"} ready. ${page.rows.length} rows in this page.${page.description ? " " + page.description : ""}`, { ...page, reference, view });
826
+ const page = pageSchema.parse(
827
+ await backend.execute("render_chart", reference, view)
828
+ );
829
+ if (page.svg && (!/^\s*<svg\b/i.test(page.svg) || /<\s*(?:script|foreignObject|iframe|image|animate\w*|set)\b|<!DOCTYPE|<!ENTITY|\bon\w+\s*=|@(?:import|keyframes)|\banimation\s*:|(?:href\s*=\s*["'](?!#)|url\s*\(\s*["']?(?!#))/i.test(
830
+ page.svg
831
+ )))
832
+ throw new Error("Unsafe SVG");
833
+ return chartResult(
834
+ `${page.kind === "kpi" ? "KPI" : "Chart"} ready. ${page.rows.length} rows in this page.${page.description ? ` ${page.description}` : ""}`,
835
+ { ...page, reference, view }
836
+ );
675
837
  } catch (error) {
676
838
  if (error instanceof DatabrainApiError) return error.toToolResponse();
677
- return { isError: true, content: [{ type: "text", text: error.message.startsWith("CLIENT_REQUIRED:") || error.message.startsWith("Use the hosted sign-in") ? error.message : "Chart unavailable. Check metric access and the saved tenant settings, then retry." }] };
839
+ return {
840
+ isError: true,
841
+ content: [
842
+ {
843
+ type: "text",
844
+ text: error.message.startsWith("CLIENT_REQUIRED:") || error.message.startsWith("Use the hosted sign-in") ? error.message : "Chart unavailable. Check metric access and the saved tenant settings, then retry."
845
+ }
846
+ ]
847
+ };
678
848
  }
679
849
  }
680
850
  );
@@ -684,4 +854,4 @@ function registerMetricTile(server, client, backend) {
684
854
  export {
685
855
  registerChartApps
686
856
  };
687
- //# sourceMappingURL=chunk-233VGFHY.js.map
857
+ //# sourceMappingURL=chunk-QQ6KNULX.js.map