@databrainhq/mcp-server 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -218,7 +218,7 @@ var DatabrainClient = class {
218
218
  throw new DatabrainApiError(
219
219
  401,
220
220
  "MISSING_API_TOKEN",
221
- "A per-data-app API token is required for this embedding operation but none is configured. Use `setup_embed_interactive` with step `ensure_api_token` to create or set one, or call `create_api_token` / `set_api_token` directly. Do not ask the user for this token \u2014 create it via the MCP tools."
221
+ "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."
222
222
  );
223
223
  }
224
224
  let url = `${this.config.apiUrl}${options.path}`;
@@ -242,7 +242,7 @@ var DatabrainClient = class {
242
242
  method: options.method,
243
243
  headers,
244
244
  body: options.body ? JSON.stringify(options.body) : void 0,
245
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
245
+ signal: AbortSignal.timeout(options.timeoutMs || REQUEST_TIMEOUT_MS)
246
246
  });
247
247
  if (!response.ok) {
248
248
  const errorBody = await response.text();
@@ -286,7 +286,7 @@ var DatabrainClient = class {
286
286
  return data;
287
287
  } catch (err) {
288
288
  if (err instanceof DatabrainApiError) throw err;
289
- if (attempt < MAX_RETRIES && err?.name === "TimeoutError") {
289
+ if (attempt < MAX_RETRIES && err?.name === "TimeoutError" && !options.noRetryOnTimeout) {
290
290
  const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 8e3);
291
291
  logger.warn(`Request timed out, retrying in ${delay}ms`, { path: options.path });
292
292
  await new Promise((r) => setTimeout(r, delay));
@@ -545,7 +545,9 @@ var QueryApi = class {
545
545
  method: "POST",
546
546
  path: "/api/v2/data-app/ask",
547
547
  body: params,
548
- auth: "service"
548
+ auth: "service",
549
+ timeoutMs: 3e5,
550
+ noRetryOnTimeout: true
549
551
  });
550
552
  }
551
553
  async getSemanticLayer(datamartName) {
@@ -1688,7 +1690,7 @@ Next: use \`generate_guest_token\` with the clientId and \`generate_embed_code\`
1688
1690
  - workspaceName: \`${body.workspaceName}\`
1689
1691
  - accessSettings.datamartName: \`${datamartName}\`
1690
1692
 
1691
- Next: run \`get_embed_details\` and \`diagnose_embed_health\` to verify update compatibility.`
1693
+ Next: run \`get_embed_details\` to verify the embed configuration.`
1692
1694
  );
1693
1695
  } catch (err) {
1694
1696
  if (err instanceof DatabrainApiError) return err.toToolResponse();
@@ -2375,612 +2377,71 @@ Use \`get_embed_details\` to view the full current theme configuration.` + GUEST
2375
2377
  );
2376
2378
  }
2377
2379
 
2378
- // src/tools/embed/configure-embed.ts
2379
- import { z as z15 } from "zod";
2380
- var stringFilterSchema = z15.object({
2381
- filterName: z15.string().describe("Name of the filter as it appears in the dashboard"),
2382
- options: z15.array(z15.object({
2383
- value: z15.string(),
2384
- label: z15.string()
2385
- })).describe("Dropdown options"),
2386
- defaultOption: z15.union([z15.string(), z15.array(z15.string())]).optional().describe("Default selected value (string or array for multi-select)")
2387
- });
2388
- var numberFilterSchema = z15.object({
2389
- filterName: z15.string().describe("Name of the filter"),
2390
- min: z15.number().describe("Minimum value"),
2391
- max: z15.number().describe("Maximum value")
2392
- });
2393
- var datePresetSchema = z15.object({
2394
- type: z15.string().optional().describe('Preset type (e.g. "Last", "This")'),
2395
- interval: z15.string().optional().describe('Interval (e.g. "Days", "Months", "Years")'),
2396
- timeGrain: z15.string().optional().describe("Time grain"),
2397
- label: z15.string().describe('Display label (e.g. "Last 30 Days")'),
2398
- startDate: z15.string().optional().describe("Start date (YYYY-MM-DD)"),
2399
- endDate: z15.string().optional().describe("End date (YYYY-MM-DD)")
2400
- });
2401
- var dateFilterSchema = z15.object({
2402
- filterName: z15.string().describe("Name of the filter"),
2403
- startDate: z15.string().optional().describe("Default start date (YYYY-MM-DD)"),
2404
- endDate: z15.string().optional().describe("Default end date (YYYY-MM-DD)"),
2405
- datePresetOptions: z15.array(datePresetSchema).optional().describe("Custom date presets to show in the date picker")
2406
- });
2407
- var sqlFilterSchema = z15.object({
2408
- filterName: z15.string().describe("Name of the filter"),
2409
- sql: z15.string().describe("SQL query to fetch filter options"),
2410
- columnName: z15.string().describe("Column from the SQL result to use as option values")
2411
- });
2412
- var SUPPORTED_LANGUAGES = [
2413
- "en",
2414
- "fr",
2415
- "es",
2416
- "de",
2417
- "it",
2418
- "pt",
2419
- "zh",
2420
- "ja",
2421
- "ko",
2422
- "hi",
2423
- "ar",
2424
- "ru"
2425
- ];
2426
- function registerConfigureEmbed(server, client) {
2427
- const services = createServiceBundle(client);
2428
- server.tool(
2429
- "configure_embed",
2430
- "Configure embed options (UI toggles, downloads, layout), filters (string/date/number/SQL), and i18n (language, translations).",
2431
- {
2432
- embedId: z15.string().describe("ID of the embed to configure"),
2433
- body: z15.record(z15.unknown()).optional().describe(
2434
- "Raw API body for PUT /api/v2/data-app/embeds. If provided, sent directly and convenience fields are ignored."
2435
- ),
2436
- // ===== OPTIONS (from configure_embed_options) =====
2437
- // Metric Operations
2438
- disableMetricCreation: z15.boolean().optional().describe("Disable creating new metrics"),
2439
- disableMetricUpdation: z15.boolean().optional().describe("Disable updating existing metrics"),
2440
- disableMetricDeletion: z15.boolean().optional().describe("Disable deleting metrics"),
2441
- disableManageMetrics: z15.boolean().optional().describe("Hide the manage metrics interface"),
2442
- // Layout
2443
- disableLayoutCustomization: z15.boolean().optional().describe("Prevent layout changes"),
2444
- disableSaveLayout: z15.boolean().optional().describe("Prevent saving layout changes"),
2445
- // Downloads & Export
2446
- enableDownloadCsv: z15.boolean().optional().describe("Enable CSV download for individual metrics"),
2447
- enableEmailCsv: z15.boolean().optional().describe("Enable emailing metric data as CSV"),
2448
- disableDownloadPng: z15.boolean().optional().describe("Disable PNG download in fullscreen"),
2449
- enableDownloadAllMetrics: z15.boolean().optional().describe("Enable bulk download of all metrics as CSV"),
2450
- enableDownloadAllPdf: z15.boolean().optional().describe("Enable downloading entire dashboard as PDF"),
2451
- disableDownloadDataNoFilters: z15.boolean().optional().describe("Prevent data download when no filters"),
2452
- disableDownloadUnderlyingDataNoFilters: z15.boolean().optional().describe("Prevent underlying data download without filters"),
2453
- // UI Visibility
2454
- hideDashboardName: z15.boolean().optional().describe("Hide the dashboard title"),
2455
- hideMetricCardShadow: z15.boolean().optional().describe("Remove card shadow effects"),
2456
- disableMetricCardBorder: z15.boolean().optional().describe("Remove metric card borders"),
2457
- showDashboardActions: z15.boolean().optional().describe("Show/hide dashboard action buttons"),
2458
- disableFullscreen: z15.boolean().optional().describe("Disable fullscreen mode"),
2459
- isHideTablePreview: z15.boolean().optional().describe("Hide table preview in fullscreen"),
2460
- isHideChartSettings: z15.boolean().optional().describe("Hide chart settings in fullscreen"),
2461
- shouldFitFullScreen: z15.boolean().optional().describe("Make fullscreen modal fit the component space"),
2462
- // Loaders
2463
- disableMainLoader: z15.boolean().optional().describe("Hide the main loading indicator"),
2464
- disableMetricLoader: z15.boolean().optional().describe("Hide metric-level loading indicators"),
2465
- // Data Access
2466
- disableUnderlyingData: z15.boolean().optional().describe("Disable access to underlying data"),
2467
- isShowNoDataFoundScreen: z15.boolean().optional().describe('Show a "no data" screen when data unavailable'),
2468
- // Scheduling
2469
- disableScheduleEmailReports: z15.boolean().optional().describe("Disable email report scheduling"),
2470
- // Interactions
2471
- isStickyDashboardFilters: z15.boolean().optional().describe("Make filters sticky when scrolling"),
2472
- enableMultiMetricFilters: z15.boolean().optional().describe("Enable cross-metric filter interactions"),
2473
- optionsIcon: z15.enum(["kebab-menu-vertical", "download"]).optional().describe("Menu icon style"),
2474
- enableTitleClickFullscreen: z15.boolean().optional().describe("Click metric title to enter fullscreen"),
2475
- // Export
2476
- exportMsgPosition: z15.enum([
2477
- "bottom",
2478
- "bottom-left",
2479
- "bottom-right",
2480
- "center",
2481
- "top",
2482
- "top-left",
2483
- "top-right"
2484
- ]).optional().describe('Position of "exporting" notification message'),
2485
- isModifyUrl: z15.boolean().optional().describe("Whether navigating metrics modifies the browser URL"),
2486
- // No-data visuals
2487
- noDataImg: z15.string().optional().describe("URL to custom image when no data found"),
2488
- noDataFoundSvg: z15.string().optional().describe("Inline SVG when no data found"),
2489
- // Metric description
2490
- longDescriptionWidth: z15.string().optional().describe('Width of long-description tooltip (e.g. "300px")'),
2491
- longDescriptionFontColor: z15.string().optional().describe("Font color for long-description tooltip (hex)"),
2492
- // Settings icon
2493
- settingsIconName: z15.string().optional().describe("Custom name/label for the settings icon"),
2494
- settingsIconSvg: z15.string().optional().describe("Inline SVG for a custom settings icon"),
2495
- // Advanced: per-chart settings
2496
- customChartSettings: z15.record(z15.unknown()).optional().describe("Per-chart override settings (JSON)"),
2497
- // Custom Messages
2498
- tokenExpiryMessage: z15.string().optional().describe("Custom message when token expires"),
2499
- tokenAbsentMessage: z15.string().optional().describe("Custom message when token is missing"),
2500
- // ===== FILTERS (from configure_filters) =====
2501
- stringFilters: z15.array(stringFilterSchema).optional().describe("String filter configurations with dropdown options and defaults"),
2502
- numberFilters: z15.array(numberFilterSchema).optional().describe("Number filter configurations with min/max defaults"),
2503
- dateFilters: z15.array(dateFilterSchema).optional().describe("Date filter configurations with default ranges and presets"),
2504
- sqlFilters: z15.array(sqlFilterSchema).optional().describe("SQL-based filter options for large option sets"),
2505
- hideDashboardFilters: z15.array(z15.string()).optional().describe("Filter names to hide from the dashboard UI"),
2506
- hideDatePickerOptions: z15.array(z15.string()).optional().describe('Date picker options to hide (e.g. ["this month", "yesterday"])'),
2507
- metricFilterPosition: z15.enum(["inside", "outside"]).optional().describe("Where to display metric-level filters"),
2508
- // ===== INTERNATIONALIZATION (from configure_internationalization) =====
2509
- language: z15.enum(SUPPORTED_LANGUAGES).optional().describe('Primary language code (e.g. "fr", "es", "ja")'),
2510
- calendarType: z15.enum(["default", "ind"]).optional().describe('Calendar system: "default" or "ind" (Indian)'),
2511
- translations: z15.array(z15.object({
2512
- key: z15.string().describe("Original text to translate (must match exactly)"),
2513
- translations: z15.record(z15.string()).describe("Language code \u2192 translation mapping")
2514
- })).optional().describe("Translation dictionary entries")
2515
- },
2516
- WRITE_IDEMPOTENT,
2517
- async (params) => {
2518
- const { embedId, body, ...opts } = params;
2519
- if (body) {
2520
- try {
2521
- const rawBody = { ...body };
2522
- if (!rawBody.embedId) rawBody.embedId = embedId;
2523
- const result = await services.embedsApi.updateEmbed(rawBody);
2524
- if (result.error) {
2525
- return toolError(`Embed configuration failed: ${stringify(result.error)}`);
2526
- }
2527
- return toolSuccess(
2528
- `Embed configured for "${embedId}" (raw passthrough mode).` + GUEST_TOKEN_REMINDER
2529
- );
2530
- } catch (err) {
2531
- const guardrail = await maybeBuildCorruptedEmbedGuardrail(client, embedId, "configure embed", err);
2532
- if (guardrail) return guardrail;
2533
- if (err instanceof DatabrainApiError) return err.toToolResponse();
2534
- return toolError(`Unexpected error: ${err.message}`);
2535
- }
2536
- }
2537
- const options = {};
2538
- const attrs = {};
2539
- const configured = [];
2540
- if (opts.disableMetricCreation !== void 0) options.disableMetricCreation = opts.disableMetricCreation;
2541
- if (opts.disableMetricUpdation !== void 0) options.disableMetricUpdation = opts.disableMetricUpdation;
2542
- if (opts.disableMetricDeletion !== void 0) options.disableMetricDeletion = opts.disableMetricDeletion;
2543
- if (opts.disableManageMetrics !== void 0) options.disableManageMetrics = opts.disableManageMetrics;
2544
- if (opts.disableLayoutCustomization !== void 0) options.disableLayoutCustomization = opts.disableLayoutCustomization;
2545
- if (opts.disableSaveLayout !== void 0) options.disableSaveLayout = opts.disableSaveLayout;
2546
- if (opts.disableDownloadDataNoFilters !== void 0) options.disableDownloadDataNoFilters = opts.disableDownloadDataNoFilters;
2547
- if (opts.disableDownloadUnderlyingDataNoFilters !== void 0) options.disableDownloadUnderlyingDataNoFilters = opts.disableDownloadUnderlyingDataNoFilters;
2548
- if (opts.hideDashboardName !== void 0) options.hideDashboardName = opts.hideDashboardName;
2549
- if (opts.hideMetricCardShadow !== void 0) options.hideMetricCardShadow = opts.hideMetricCardShadow;
2550
- if (opts.disableMetricCardBorder !== void 0) options.disableMetricCardBorder = opts.disableMetricCardBorder;
2551
- if (opts.showDashboardActions !== void 0) options.showDashboardActions = opts.showDashboardActions;
2552
- if (opts.shouldFitFullScreen !== void 0) options.shouldFitFullScreen = opts.shouldFitFullScreen;
2553
- if (opts.isShowNoDataFoundScreen !== void 0) options.isShowNoDataFoundScreen = opts.isShowNoDataFoundScreen;
2554
- if (opts.disableMainLoader !== void 0) options.disableMainLoader = opts.disableMainLoader;
2555
- if (opts.disableMetricLoader !== void 0) options.disableMetricLoader = opts.disableMetricLoader;
2556
- if (opts.disableUnderlyingData !== void 0) options.disableUnderlyingData = opts.disableUnderlyingData;
2557
- if (opts.disableScheduleEmailReports !== void 0) options.disableScheduleEmailReports = opts.disableScheduleEmailReports;
2558
- if (opts.exportMsgPosition) options.exportMsgPosition = opts.exportMsgPosition;
2559
- if (opts.isModifyUrl !== void 0) options.isModifyUrl = opts.isModifyUrl;
2560
- if (opts.enableDownloadCsv !== void 0) attrs.enableDownloadCsv = opts.enableDownloadCsv;
2561
- if (opts.enableEmailCsv !== void 0) attrs.enableEmailCsv = opts.enableEmailCsv;
2562
- if (opts.disableDownloadPng !== void 0) attrs.disableDownloadPng = opts.disableDownloadPng;
2563
- if (opts.enableDownloadAllMetrics !== void 0) attrs.enableDownloadAllMetrics = opts.enableDownloadAllMetrics;
2564
- if (opts.enableDownloadAllPdf !== void 0) attrs.enableDownloadAllPdf = opts.enableDownloadAllPdf;
2565
- if (opts.disableFullscreen !== void 0) attrs.disableFullscreen = opts.disableFullscreen;
2566
- if (opts.isHideTablePreview !== void 0) attrs.isHideTablePreview = opts.isHideTablePreview;
2567
- if (opts.isHideChartSettings !== void 0) attrs.isHideChartSettings = opts.isHideChartSettings;
2568
- if (opts.isStickyDashboardFilters !== void 0) attrs.isStickyDashboardFilters = opts.isStickyDashboardFilters;
2569
- if (opts.enableMultiMetricFilters !== void 0) attrs.enableMultiMetricFilters = opts.enableMultiMetricFilters;
2570
- if (opts.optionsIcon) attrs.optionsIcon = opts.optionsIcon;
2571
- if (opts.enableTitleClickFullscreen !== void 0) attrs.enableTitleClickFullscreen = opts.enableTitleClickFullscreen;
2572
- if (opts.noDataImg) attrs.noDataImg = opts.noDataImg;
2573
- if (opts.noDataFoundSvg) attrs.noDataFoundSvg = opts.noDataFoundSvg;
2574
- if (opts.customChartSettings) attrs.customChartSettings = opts.customChartSettings;
2575
- if (opts.longDescriptionWidth || opts.longDescriptionFontColor) {
2576
- attrs.longDescriptionConfig = {
2577
- ...opts.longDescriptionWidth && { width: opts.longDescriptionWidth },
2578
- ...opts.longDescriptionFontColor && { fontColor: opts.longDescriptionFontColor }
2579
- };
2580
- }
2581
- if (opts.settingsIconName || opts.settingsIconSvg) {
2582
- attrs.settingsIcon = {
2583
- ...opts.settingsIconName && { name: opts.settingsIconName },
2584
- ...opts.settingsIconSvg && { iconSvg: opts.settingsIconSvg }
2585
- };
2586
- }
2587
- const customMessages = {};
2588
- if (opts.tokenExpiryMessage) customMessages.tokenExpiry = opts.tokenExpiryMessage;
2589
- if (opts.tokenAbsentMessage) customMessages.tokenAbsent = opts.tokenAbsentMessage;
2590
- if (Object.keys(customMessages).length) options.customMessages = customMessages;
2591
- const globalFilterOptions = {};
2592
- if (opts.stringFilters) {
2593
- for (const f of opts.stringFilters) {
2594
- globalFilterOptions[f.filterName] = {
2595
- options: f.options,
2596
- ...f.defaultOption !== void 0 && { defaultOption: f.defaultOption }
2597
- };
2598
- }
2599
- configured.push(`${opts.stringFilters.length} string filter(s)`);
2600
- }
2601
- if (opts.numberFilters) {
2602
- for (const f of opts.numberFilters) {
2603
- globalFilterOptions[f.filterName] = {
2604
- defaultOption: { min: f.min, max: f.max }
2605
- };
2606
- }
2607
- configured.push(`${opts.numberFilters.length} number filter(s)`);
2608
- }
2609
- if (opts.dateFilters) {
2610
- for (const f of opts.dateFilters) {
2611
- const config = {};
2612
- if (f.startDate || f.endDate) {
2613
- config.defaultOption = {
2614
- ...f.startDate && { startDate: f.startDate },
2615
- ...f.endDate && { endDate: f.endDate }
2616
- };
2617
- }
2618
- if (f.datePresetOptions) config.datePresetOptions = f.datePresetOptions;
2619
- globalFilterOptions[f.filterName] = config;
2620
- }
2621
- configured.push(`${opts.dateFilters.length} date filter(s)`);
2622
- }
2623
- if (opts.sqlFilters) {
2624
- for (const f of opts.sqlFilters) {
2625
- globalFilterOptions[f.filterName] = {
2626
- sql: f.sql,
2627
- columnName: f.columnName
2628
- };
2629
- }
2630
- configured.push(`${opts.sqlFilters.length} SQL filter(s)`);
2631
- }
2632
- if (Object.keys(globalFilterOptions).length) options.globalFilterOptions = globalFilterOptions;
2633
- if (opts.hideDashboardFilters) {
2634
- attrs.hideDashboardFilters = opts.hideDashboardFilters;
2635
- configured.push(`hiding ${opts.hideDashboardFilters.length} filter(s)`);
2636
- }
2637
- if (opts.hideDatePickerOptions) {
2638
- options.hideDatePickerOptions = opts.hideDatePickerOptions;
2639
- configured.push(`hiding ${opts.hideDatePickerOptions.length} date picker option(s)`);
2640
- }
2641
- if (opts.metricFilterPosition) {
2642
- attrs.metricFilterPosition = opts.metricFilterPosition;
2643
- configured.push(`metric filter position: ${opts.metricFilterPosition}`);
2644
- }
2645
- if (opts.language) {
2646
- attrs.language = opts.language;
2647
- configured.push(`language: ${opts.language}`);
2648
- }
2649
- if (opts.calendarType) {
2650
- attrs.calendarType = opts.calendarType;
2651
- configured.push(`calendar: ${opts.calendarType}`);
2652
- }
2653
- if (opts.translations?.length) {
2654
- const dict = {};
2655
- for (const entry of opts.translations) {
2656
- dict[entry.key] = entry.translations;
2657
- }
2658
- attrs.translationDictionary = dict;
2659
- configured.push(`${opts.translations.length} translation(s)`);
2660
- }
2661
- const optionKeys = Object.keys(options).filter((k) => k !== "globalFilterOptions" && k !== "customMessages" && k !== "hideDatePickerOptions");
2662
- const attrKeys = Object.keys(attrs).filter(
2663
- (k) => k !== "hideDashboardFilters" && k !== "metricFilterPosition" && k !== "language" && k !== "calendarType" && k !== "translationDictionary"
2664
- );
2665
- if (optionKeys.length || attrKeys.length) {
2666
- configured.push(`${optionKeys.length + attrKeys.length} display option(s)`);
2667
- }
2668
- if (Object.keys(options).length === 0 && Object.keys(attrs).length === 0) {
2669
- return toolError("No configuration provided. Specify at least one option, filter, or i18n setting.");
2670
- }
2671
- try {
2672
- const patch = {};
2673
- if (Object.keys(options).length) patch.options = options;
2674
- if (Object.keys(attrs).length) patch.componentAttributes = attrs;
2675
- const result = await services.embedService.updateEmbed(embedId, patch);
2676
- if (result.error) {
2677
- return toolError(`Embed configuration failed: ${stringify(result.error)}`);
2678
- }
2679
- return toolSuccess(
2680
- `Embed configured for "${embedId}".
2681
-
2682
- **Configured:** ${configured.join(", ")}
2683
-
2684
- Use \`get_embed_details\` to view the full configuration.` + GUEST_TOKEN_REMINDER
2685
- );
2686
- } catch (err) {
2687
- const guardrail = await maybeBuildCorruptedEmbedGuardrail(client, embedId, "configure embed", err);
2688
- if (guardrail) return guardrail;
2689
- if (err instanceof DatabrainApiError) return err.toToolResponse();
2690
- return toolError(`Unexpected error: ${err.message}`);
2691
- }
2692
- }
2693
- );
2694
- }
2695
-
2696
- // src/tools/embed/diagnose-embed-health.ts
2697
- import { z as z16 } from "zod";
2698
- function asRecord2(value) {
2699
- return value && typeof value === "object" ? value : null;
2700
- }
2701
- function firstString(...values) {
2702
- for (const v of values) {
2703
- if (typeof v === "string" && v.trim().length > 0) return v;
2704
- }
2705
- return null;
2706
- }
2707
- function runRuntimeReadinessChecks(params) {
2708
- const payload = params.guestTokenBody ?? {
2709
- clientId: params.clientId,
2710
- dataAppName: params.dataAppName,
2711
- permissions: params.permissions
2712
- };
2713
- const checks = [];
2714
- const topLevelPermissions = payload.permissions;
2715
- const paramsObj = payload.params;
2716
- const nestedPermissions = paramsObj?.permissions;
2717
- checks.push({
2718
- check: "Top-level dataAppName",
2719
- status: typeof payload.dataAppName === "string" && payload.dataAppName.length > 0 ? "pass" : "fail",
2720
- details: typeof payload.dataAppName === "string" && payload.dataAppName.length > 0 ? "dataAppName is present at top level." : "Missing top-level dataAppName in guest token body."
2721
- });
2722
- checks.push({
2723
- check: "Top-level permissions object",
2724
- status: typeof topLevelPermissions === "object" && topLevelPermissions !== null ? "pass" : "fail",
2725
- details: typeof topLevelPermissions === "object" && topLevelPermissions !== null ? "permissions is present at top level." : "Missing top-level permissions object."
2726
- });
2727
- checks.push({
2728
- check: "No nested permissions in params",
2729
- status: nestedPermissions ? "fail" : "pass",
2730
- details: nestedPermissions ? "Found params.permissions. Move permissions to top level." : "No nested permissions found."
2731
- });
2732
- const disableMetricCreationFromToken = typeof topLevelPermissions === "object" && topLevelPermissions !== null ? topLevelPermissions.isDisableMetricCreation : void 0;
2733
- checks.push({
2734
- check: "Guest token metric creation gate",
2735
- status: disableMetricCreationFromToken === true ? "fail" : "pass",
2736
- details: disableMetricCreationFromToken === true ? "permissions.isDisableMetricCreation=true blocks + Create Metric." : "permissions.isDisableMetricCreation is not forcing metric creation off."
2737
- });
2738
- if (params.embedOptions) {
2739
- checks.push({
2740
- check: "Embed option metric creation gate",
2741
- status: params.embedOptions.disableMetricCreation === true ? "fail" : "pass",
2742
- details: params.embedOptions.disableMetricCreation === true ? "embed option disableMetricCreation=true blocks + Create Metric." : "Embed options are not forcing metric creation off."
2743
- });
2744
- }
2745
- if (params.dashboardRuntime) {
2746
- checks.push({
2747
- check: "Dashboard runtime: isAllowedToCreateMetrics",
2748
- status: params.dashboardRuntime.isAllowedToCreateMetrics === true ? "pass" : "fail",
2749
- details: params.dashboardRuntime.isAllowedToCreateMetrics === true ? "Dashboard runtime allows metric creation." : "Dashboard runtime says metric creation is not allowed."
2750
- });
2751
- checks.push({
2752
- check: "Dashboard runtime: metricCreationMode",
2753
- status: params.dashboardRuntime.metricCreationMode === "DRAG_DROP" ? "pass" : "warn",
2754
- details: `metricCreationMode is "${params.dashboardRuntime.metricCreationMode ?? "unknown"}".`
2755
- });
2756
- }
2757
- return checks;
2758
- }
2759
- function registerDiagnoseEmbedHealth(server, client) {
2760
- server.tool(
2761
- "diagnose_embed_health",
2762
- "Diagnose embed shape and required fields. Optionally validates runtime readiness if guestTokenBody or permissions are provided.",
2763
- {
2764
- embedId: z16.string().describe("Embed ID to diagnose"),
2765
- dataAppName: z16.string().optional().describe("Optional data app name to scope dashboard checks"),
2766
- // Runtime readiness params (absorbed from check_runtime_readiness)
2767
- guestTokenBody: z16.record(z16.unknown()).optional().describe(
2768
- "Exact request body sent to POST /api/v2/guest-token/create (for runtime validation)."
2769
- ),
2770
- permissions: z16.record(z16.unknown()).optional().describe(
2771
- "Guest token permissions (used when guestTokenBody is not provided)."
2772
- ),
2773
- embedOptions: z16.object({
2774
- disableMetricCreation: z16.boolean().optional()
2775
- }).optional().describe("Embed web component options (if known)."),
2776
- dashboardRuntime: z16.object({
2777
- isAllowedToCreateMetrics: z16.boolean().optional(),
2778
- metricCreationMode: z16.string().optional()
2779
- }).optional().describe("Runtime flags from dashboard response payload.")
2780
- },
2781
- READ_ONLY,
2782
- async (params) => {
2783
- try {
2784
- const raw = await client.request({
2785
- method: "GET",
2786
- path: "/api/v2/data-app/embeds",
2787
- auth: "api",
2788
- query: { embedId: params.embedId }
2789
- });
2790
- const embedList = Array.isArray(raw) ? raw : [raw];
2791
- const embed = embedList.find((e) => (e?.embedId || e?._id || e?.id) === params.embedId) || embedList[0];
2792
- const embedObj = asRecord2(embed);
2793
- if (!embedObj) {
2794
- return toolError(
2795
- `Embed "${params.embedId}" is not returned as an object by GET /embeds. This indicates malformed API data for this record.`
2796
- );
2797
- }
2798
- const embedMetadata = asRecord2(embedObj.embedMetadata);
2799
- const accessSettings = asRecord2(embedObj.accessSettings) || (embedMetadata ? asRecord2(embedMetadata.accessSettings) : null);
2800
- const dashboardIdCandidates = [
2801
- firstString(embedObj.dashboardId),
2802
- firstString(embedObj.externalDashboardId),
2803
- embedMetadata ? firstString(embedMetadata.dashboardId) : null
2804
- ].filter(Boolean);
2805
- const workspaceCandidates = [
2806
- firstString(embedObj.workspaceName),
2807
- firstString(embedObj.workspace),
2808
- firstString(embedObj.workspaceId),
2809
- embedMetadata ? firstString(embedMetadata.workspaceName) : null,
2810
- embedMetadata ? firstString(embedMetadata.workspaceId) : null
2811
- ].filter(Boolean);
2812
- const datamartName = firstString(
2813
- accessSettings?.datamartName,
2814
- embedObj.datamartName,
2815
- embedMetadata?.datamartName
2816
- );
2817
- let datamartsCount = 0;
2818
- let datamartNames = [];
2819
- try {
2820
- const datamarts = await client.request({
2821
- method: "GET",
2822
- path: "/api/v2/data-app/datamarts",
2823
- auth: "service",
2824
- cacheKey: "list_datamarts",
2825
- cacheTtlMs: 5 * 60 * 1e3
2826
- });
2827
- if (Array.isArray(datamarts)) {
2828
- datamartsCount = datamarts.length;
2829
- datamartNames = datamarts.map((d) => firstString(d?.name, d?.datamartName)).filter(Boolean).slice(0, 10);
2830
- }
2831
- } catch {
2832
- }
2833
- let dashboardsCount = 0;
2834
- let dashboardIdSample = [];
2835
- try {
2836
- const dashboardQuery = params.dataAppName && params.dataAppName.trim().length > 0 ? { dataAppName: params.dataAppName } : void 0;
2837
- const dashboards = await client.request({
2838
- method: "GET",
2839
- path: "/api/v2/data-app/dashboards",
2840
- auth: "api",
2841
- query: dashboardQuery,
2842
- cacheKey: "list_dashboards",
2843
- cacheTtlMs: 5 * 60 * 1e3
2844
- });
2845
- if (Array.isArray(dashboards)) {
2846
- dashboardsCount = dashboards.length;
2847
- dashboardIdSample = dashboards.map(
2848
- (d) => firstString(
2849
- d?.dashboardId,
2850
- d?.externalDashboardId,
2851
- d?._id,
2852
- d?.id,
2853
- d?.uid
2854
- )
2855
- ).filter(Boolean).slice(0, 10);
2856
- }
2857
- } catch {
2858
- }
2859
- const issues = [];
2860
- if (!firstString(embedObj.embedId, embedObj.id, embedObj._id)) {
2861
- issues.push("No stable embed identifier found in record");
2862
- }
2863
- if (!accessSettings) {
2864
- issues.push("Missing accessSettings");
2865
- }
2866
- if (!datamartName) {
2867
- issues.push("Missing datamartName (in accessSettings or embed metadata)");
2868
- }
2869
- if (dashboardIdCandidates.length === 0) {
2870
- issues.push("No dashboard identifier found on embed");
2871
- }
2872
- if (workspaceCandidates.length === 0) {
2873
- issues.push("No workspace identifier/name found on embed");
2874
- }
2875
- const health = issues.length === 0 ? "healthy-shape" : "inconsistent-shape";
2876
- let report = `Embed Health Report: \`${params.embedId}\`
2877
-
2878
- - **Status:** ${health}
2879
- - **Issues:** ${issues.length ? issues.join("; ") : "none detected in shape checks"}
2880
- - **Dashboard ID candidates:** ${dashboardIdCandidates.length ? dashboardIdCandidates.map((id) => `\`${id}\``).join(", ") : "none"}
2881
- - **Workspace candidates:** ${workspaceCandidates.length ? workspaceCandidates.map((id) => `\`${id}\``).join(", ") : "none"}
2882
- - **Resolved datamartName:** ${datamartName ? `\`${datamartName}\`` : "none"}
2883
- - **Datamarts visible:** ${datamartsCount}${datamartNames.length ? ` (sample: ${datamartNames.join(", ")})` : ""}
2884
- - **Dashboards visible:** ${dashboardsCount}${dashboardIdSample.length ? ` (ID sample: ${dashboardIdSample.map((id) => `\`${id}\``).join(", ")})` : ""}
2885
- `;
2886
- if (params.guestTokenBody || params.permissions || params.embedOptions || params.dashboardRuntime) {
2887
- const runtimeChecks = runRuntimeReadinessChecks({
2888
- guestTokenBody: params.guestTokenBody,
2889
- clientId: void 0,
2890
- dataAppName: params.dataAppName,
2891
- permissions: params.permissions,
2892
- embedOptions: params.embedOptions,
2893
- dashboardRuntime: params.dashboardRuntime
2894
- });
2895
- const hasFail = runtimeChecks.some((c) => c.status === "fail");
2896
- report += `
2897
- ### Runtime Readiness: ${hasFail ? "NOT_READY" : "READY"}
2898
-
2899
- `;
2900
- for (const c of runtimeChecks) {
2901
- const icon = c.status === "pass" ? "PASS" : c.status === "fail" ? "FAIL" : "WARN";
2902
- report += `- **[${icon}]** ${c.check}: ${c.details}
2903
- `;
2904
- }
2905
- }
2906
- report += `
2907
- Raw embed payload:
2908
- \`\`\`json
2909
- ` + JSON.stringify(embedObj, null, 2) + "\n```";
2910
- return toolSuccess(report);
2911
- } catch (err) {
2912
- if (err instanceof DatabrainApiError) return err.toToolResponse();
2913
- return toolError(`Unexpected error: ${err.message}`);
2914
- }
2915
- }
2916
- );
2917
- }
2918
-
2919
2380
  // src/tools/frontend/generate-guest-token.ts
2920
- import { z as z17 } from "zod";
2381
+ import { z as z15 } from "zod";
2921
2382
  function registerGenerateGuestToken(server, client) {
2922
2383
  const services = createServiceBundle(client);
2923
2384
  server.tool(
2924
2385
  "generate_guest_token",
2925
2386
  "Generate a guest token for frontend embedding. Requires clientId. Omit dataAppName to auto-resolve.",
2926
2387
  {
2927
- clientId: z17.string().describe('Unique end-user identifier for multi-tenant filtering. Use "None" if no tenancy.'),
2928
- dataAppName: z17.string().optional().describe("Name of the data app. Omit to list available data apps."),
2929
- body: z17.record(z17.unknown()).optional().describe(
2388
+ clientId: z15.string().describe('Unique end-user identifier for multi-tenant filtering. Use "None" if no tenancy.'),
2389
+ dataAppName: z15.string().optional().describe("Name of the data app. Omit to list available data apps."),
2390
+ body: z15.record(z15.unknown()).optional().describe(
2930
2391
  "Raw API body for POST /api/v2/guest-token/create. If provided, sent directly and convenience fields are ignored."
2931
2392
  ),
2932
- params: z17.object({
2933
- allowedEmbeds: z17.array(z17.string()).optional().describe("Restrict token to specific embed IDs"),
2934
- rlsSettings: z17.array(z17.object({
2935
- metricId: z17.string().describe("Metric ID"),
2936
- values: z17.record(z17.unknown()).describe("RLS values by column name")
2393
+ params: z15.object({
2394
+ allowedEmbeds: z15.array(z15.string()).optional().describe("Restrict token to specific embed IDs"),
2395
+ rlsSettings: z15.array(z15.object({
2396
+ metricId: z15.string().describe("Metric ID"),
2397
+ values: z15.record(z15.unknown()).describe("RLS values by column name")
2937
2398
  })).optional().describe("Legacy alias used in docs/examples; forwarded as params.rlsSettings"),
2938
- appFilters: z17.array(z17.object({
2939
- metricId: z17.string().describe("Metric ID"),
2940
- values: z17.record(z17.unknown()).describe("Filter values by column name")
2399
+ appFilters: z15.array(z15.object({
2400
+ metricId: z15.string().describe("Metric ID"),
2401
+ values: z15.record(z15.unknown()).describe("Filter values by column name")
2941
2402
  })).optional().describe("Metric-level app filters"),
2942
- dashboardAppFilters: z17.array(z17.object({
2943
- dashboardId: z17.string().describe("Dashboard ID"),
2944
- values: z17.record(z17.unknown()).describe("Filter values by dashboard filter name"),
2945
- isShowOnUrl: z17.boolean().optional().describe("Whether filters are shown in URL params")
2403
+ dashboardAppFilters: z15.array(z15.object({
2404
+ dashboardId: z15.string().describe("Dashboard ID"),
2405
+ values: z15.record(z15.unknown()).describe("Filter values by dashboard filter name"),
2406
+ isShowOnUrl: z15.boolean().optional().describe("Whether filters are shown in URL params")
2946
2407
  })).optional().describe("Dashboard-level app filters"),
2947
- hideDashboardFilters: z17.array(z17.string()).optional().describe("Dashboard filter names to hide"),
2948
- userIdentifier: z17.string().optional().describe("End-user identifier for private metrics support"),
2949
- timezone: z17.string().optional().describe('IANA timezone, e.g. "America/New_York"')
2408
+ hideDashboardFilters: z15.array(z15.string()).optional().describe("Dashboard filter names to hide"),
2409
+ userIdentifier: z15.string().optional().describe("End-user identifier for private metrics support"),
2410
+ timezone: z15.string().optional().describe('IANA timezone, e.g. "America/New_York"')
2950
2411
  }).optional().describe("Advanced token params"),
2951
2412
  // Legacy convenience fields (auto-mapped into params for backward compatibility)
2952
- timezone: z17.string().optional().describe("Legacy: moved under params.timezone"),
2953
- allowedEmbeds: z17.array(z17.string()).optional().describe("Legacy: moved under params.allowedEmbeds"),
2954
- rlsSettings: z17.array(z17.object({
2955
- metricId: z17.string().describe("Metric ID"),
2956
- values: z17.record(z17.unknown()).describe("RLS values by column name")
2413
+ timezone: z15.string().optional().describe("Legacy: moved under params.timezone"),
2414
+ allowedEmbeds: z15.array(z15.string()).optional().describe("Legacy: moved under params.allowedEmbeds"),
2415
+ rlsSettings: z15.array(z15.object({
2416
+ metricId: z15.string().describe("Metric ID"),
2417
+ values: z15.record(z15.unknown()).describe("RLS values by column name")
2957
2418
  })).optional().describe("Legacy: moved under params.rlsSettings"),
2958
- appFilters: z17.array(z17.object({
2959
- metricId: z17.string().describe("Metric ID"),
2960
- values: z17.record(z17.unknown()).describe("Filter values by column name")
2419
+ appFilters: z15.array(z15.object({
2420
+ metricId: z15.string().describe("Metric ID"),
2421
+ values: z15.record(z15.unknown()).describe("Filter values by column name")
2961
2422
  })).optional().describe("Legacy: moved under params.appFilters"),
2962
- dashboardAppFilters: z17.array(z17.object({
2963
- dashboardId: z17.string().describe("Dashboard ID to apply filters to"),
2964
- values: z17.record(z17.unknown()).describe("Filter values for this dashboard"),
2965
- isShowOnUrl: z17.boolean().optional().describe("Whether filters are shown in URL params")
2423
+ dashboardAppFilters: z15.array(z15.object({
2424
+ dashboardId: z15.string().describe("Dashboard ID to apply filters to"),
2425
+ values: z15.record(z15.unknown()).describe("Filter values for this dashboard"),
2426
+ isShowOnUrl: z15.boolean().optional().describe("Whether filters are shown in URL params")
2966
2427
  })).optional().describe("Legacy: moved under params.dashboardAppFilters"),
2967
- permissions: z17.object({
2968
- isEnableArchiveMetrics: z17.boolean().optional(),
2969
- isEnableManageMetrics: z17.boolean().optional(),
2970
- isEnableCreateDashboardView: z17.boolean().optional(),
2971
- isEnableMetricUpdation: z17.boolean().optional(),
2972
- isEnableCustomizeLayout: z17.boolean().optional(),
2973
- isEnableUnderlyingData: z17.boolean().optional(),
2974
- isEnableDownloadMetrics: z17.boolean().optional(),
2975
- isShowSideBar: z17.boolean().optional(),
2976
- isShowDashboardName: z17.boolean().optional(),
2977
- isDisableMetricCreation: z17.boolean().optional()
2428
+ permissions: z15.object({
2429
+ isEnableArchiveMetrics: z15.boolean().optional(),
2430
+ isEnableManageMetrics: z15.boolean().optional(),
2431
+ isEnableCreateDashboardView: z15.boolean().optional(),
2432
+ isEnableMetricUpdation: z15.boolean().optional(),
2433
+ isEnableCustomizeLayout: z15.boolean().optional(),
2434
+ isEnableUnderlyingData: z15.boolean().optional(),
2435
+ isEnableDownloadMetrics: z15.boolean().optional(),
2436
+ isShowSideBar: z15.boolean().optional(),
2437
+ isShowDashboardName: z15.boolean().optional(),
2438
+ isDisableMetricCreation: z15.boolean().optional()
2978
2439
  }).optional().describe("Runtime permission overrides (merge with and override embed-level settings)"),
2979
- theme: z17.record(z17.unknown()).optional().describe("Runtime theme overrides (overrides embed-level theme)"),
2980
- hideDashboardFilters: z17.array(z17.string()).optional().describe("Legacy: moved under params.hideDashboardFilters"),
2981
- userIdentifier: z17.string().optional().describe("Legacy: moved under params.userIdentifier"),
2982
- expiryTime: z17.number().optional().describe("Token TTL in milliseconds"),
2983
- datasourceName: z17.string().optional().describe("Datasource name for multi-datasource routing")
2440
+ theme: z15.record(z15.unknown()).optional().describe("Runtime theme overrides (overrides embed-level theme)"),
2441
+ hideDashboardFilters: z15.array(z15.string()).optional().describe("Legacy: moved under params.hideDashboardFilters"),
2442
+ userIdentifier: z15.string().optional().describe("Legacy: moved under params.userIdentifier"),
2443
+ expiryTime: z15.number().optional().describe("Token TTL in milliseconds"),
2444
+ datasourceName: z15.string().optional().describe("Datasource name for multi-datasource routing")
2984
2445
  },
2985
2446
  WRITE_CREATE,
2986
2447
  async (params) => {
@@ -3033,7 +2494,7 @@ Next step: Use \`generate_embed_code\` to get copy-paste-ready frontend code.`
3033
2494
  }
3034
2495
 
3035
2496
  // src/tools/frontend/generate-embed-code.ts
3036
- import { z as z18 } from "zod";
2497
+ import { z as z16 } from "zod";
3037
2498
 
3038
2499
  // src/templates/react.ts
3039
2500
  function kebabCase(str) {
@@ -4029,22 +3490,22 @@ function registerGenerateEmbedCode(server, _client) {
4029
3490
  "generate_embed_code",
4030
3491
  "Generate framework-specific embed code (React, Next.js, Vue, Angular, Svelte, SolidJS, vanilla HTML).",
4031
3492
  {
4032
- embedId: z18.string().describe("The embed ID to generate code for"),
4033
- framework: z18.enum(FRAMEWORKS).describe("Frontend framework"),
4034
- embedType: z18.enum(["dashboard", "metric"]).optional().default("dashboard"),
4035
- options: z18.object({
4036
- enableDownloadCsv: z18.boolean().optional(),
4037
- enableEmailCsv: z18.boolean().optional(),
4038
- enableDownloadAllMetrics: z18.boolean().optional(),
4039
- enableDownloadAllPdf: z18.boolean().optional(),
4040
- disableFullscreen: z18.boolean().optional(),
4041
- isHideTablePreview: z18.boolean().optional(),
4042
- isHideChartSettings: z18.boolean().optional(),
4043
- isStickyDashboardFilters: z18.boolean().optional(),
4044
- enableMultiMetricFilters: z18.boolean().optional(),
4045
- themeName: z18.string().optional(),
4046
- customTheme: z18.record(z18.unknown()).optional(),
4047
- optionsIcon: z18.enum(["kebab-menu-vertical", "download"]).optional()
3493
+ embedId: z16.string().describe("The embed ID to generate code for"),
3494
+ framework: z16.enum(FRAMEWORKS).describe("Frontend framework"),
3495
+ embedType: z16.enum(["dashboard", "metric"]).optional().default("dashboard"),
3496
+ options: z16.object({
3497
+ enableDownloadCsv: z16.boolean().optional(),
3498
+ enableEmailCsv: z16.boolean().optional(),
3499
+ enableDownloadAllMetrics: z16.boolean().optional(),
3500
+ enableDownloadAllPdf: z16.boolean().optional(),
3501
+ disableFullscreen: z16.boolean().optional(),
3502
+ isHideTablePreview: z16.boolean().optional(),
3503
+ isHideChartSettings: z16.boolean().optional(),
3504
+ isStickyDashboardFilters: z16.boolean().optional(),
3505
+ enableMultiMetricFilters: z16.boolean().optional(),
3506
+ themeName: z16.string().optional(),
3507
+ customTheme: z16.record(z16.unknown()).optional(),
3508
+ optionsIcon: z16.enum(["kebab-menu-vertical", "download"]).optional()
4048
3509
  }).optional()
4049
3510
  },
4050
3511
  READ_ONLY,
@@ -4084,17 +3545,17 @@ ${generated.instructions}`;
4084
3545
  }
4085
3546
 
4086
3547
  // src/tools/analytics/list-dashboards.ts
4087
- import { z as z19 } from "zod";
3548
+ import { z as z17 } from "zod";
4088
3549
  function registerListDashboards(server, client) {
4089
3550
  const services = createServiceBundle(client);
4090
3551
  server.tool(
4091
3552
  "list_dashboards",
4092
3553
  "List dashboards globally or for a specific data app.",
4093
3554
  {
4094
- scope: z19.enum(["auto", "global", "data_app"]).optional().describe(
3555
+ scope: z17.enum(["auto", "global", "data_app"]).optional().describe(
4095
3556
  "Dashboard listing scope. auto: use data_app when dataAppName + API token are available, otherwise global."
4096
3557
  ),
4097
- dataAppName: z19.string().optional().describe("Data app name for data_app scope. Ignored for global scope.")
3558
+ dataAppName: z17.string().optional().describe("Data app name for data_app scope. Ignored for global scope.")
4098
3559
  },
4099
3560
  READ_ONLY,
4100
3561
  async (params) => {
@@ -4128,617 +3589,8 @@ function registerListDashboards(server, client) {
4128
3589
  );
4129
3590
  }
4130
3591
 
4131
- // src/tools/orchestration/setup-embed-interactive.ts
4132
- import { z as z20 } from "zod";
4133
- var STEPS = [
4134
- "discover_data_apps",
4135
- "choose_data_app",
4136
- "create_data_app",
4137
- "ensure_api_token",
4138
- "discover_workspaces",
4139
- "choose_workspace",
4140
- "list_dashboards",
4141
- "choose_dashboard",
4142
- "create_embed",
4143
- "runtime_readiness",
4144
- "complete"
4145
- ];
4146
- var stateSchema = z20.object({
4147
- dataAppName: z20.string().optional(),
4148
- dataAppId: z20.string().optional(),
4149
- workspaceName: z20.string().optional(),
4150
- embedType: z20.enum(["dashboard", "metric"]).optional(),
4151
- dashboardId: z20.string().optional(),
4152
- metricId: z20.string().optional(),
4153
- datamartName: z20.string().optional(),
4154
- embedId: z20.string().optional(),
4155
- embedName: z20.string().optional(),
4156
- clientId: z20.string().optional(),
4157
- runtimeValidated: z20.boolean().optional()
4158
- });
4159
- function emit(status, step, state, message, options, nextStep) {
4160
- const payload = {
4161
- status,
4162
- step,
4163
- message,
4164
- state
4165
- };
4166
- if (options) payload.options = options;
4167
- if (nextStep) payload.nextStep = nextStep;
4168
- return toolSuccess(JSON.stringify(payload, null, 2));
4169
- }
4170
- function parseUserChoiceObject(userChoice) {
4171
- if (!userChoice) return null;
4172
- try {
4173
- const parsed = JSON.parse(userChoice);
4174
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
4175
- return parsed;
4176
- }
4177
- return null;
4178
- } catch {
4179
- return null;
4180
- }
4181
- }
4182
- function parseNameFromUserChoice(userChoice) {
4183
- if (!userChoice) return null;
4184
- const choiceObj = parseUserChoiceObject(userChoice);
4185
- if (choiceObj) {
4186
- const rawName = choiceObj.name;
4187
- if (typeof rawName === "string" && rawName.trim().length > 0) return rawName.trim();
4188
- return null;
4189
- }
4190
- const trimmed = userChoice.trim();
4191
- return trimmed.length > 0 ? trimmed : null;
4192
- }
4193
- function registerSetupEmbedInteractive(server, client) {
4194
- const services = createServiceBundle(client);
4195
- const listDashboardsForContext = async (currentState) => {
4196
- const dataAppName = currentState.dataAppName;
4197
- const workspaceName = currentState.workspaceName;
4198
- if (dataAppName && dataAppName.trim().length > 0 && client.hasApiToken()) {
4199
- const dashboards2 = await services.dashboardsApi.listDashboards(dataAppName);
4200
- if (Array.isArray(dashboards2) && dashboards2.length > 0) {
4201
- return { dashboards: dashboards2, scope: "data_app" };
4202
- }
4203
- }
4204
- if (workspaceName && workspaceName.trim().length > 0) {
4205
- const dashboards2 = await services.dashboardsApi.listDashboardsForWorkspace(workspaceName);
4206
- return { dashboards: dashboards2, scope: "workspace" };
4207
- }
4208
- const dashboards = await services.dashboardsApi.listDashboardsAcrossWorkspaces();
4209
- return { dashboards, scope: "global" };
4210
- };
4211
- server.tool(
4212
- "setup_embed_interactive",
4213
- "Step-by-step embed setup wizard. Pass step and state from previous call.",
4214
- {
4215
- step: z20.enum(STEPS).describe("Current orchestration step"),
4216
- state: stateSchema.optional().describe("State from previous step \u2014 pass forward unchanged"),
4217
- userChoice: z20.string().optional().describe("User response for CHOICE_REQUIRED or CONFIRM_REQUIRED")
4218
- },
4219
- WRITE_CREATE,
4220
- async (params) => {
4221
- const state = params.state ?? {};
4222
- const step = params.step;
4223
- try {
4224
- if (step === "discover_data_apps") {
4225
- const apps = await services.dataAppsApi.listDataApps();
4226
- if (!Array.isArray(apps) || apps.length === 0) {
4227
- return emit(
4228
- "CHOICE_REQUIRED",
4229
- step,
4230
- state,
4231
- "No data apps found. Ask user for a data app name to create one.",
4232
- [
4233
- { id: "create", label: "Create a new data app" },
4234
- { id: "stop", label: "Stop setup" }
4235
- ],
4236
- "create_data_app"
4237
- );
4238
- }
4239
- if (apps.length === 1) {
4240
- const app = services.discoveryService.normalizeDataApp(apps[0]);
4241
- return emit(
4242
- "CONFIRM_REQUIRED",
4243
- step,
4244
- {
4245
- ...state,
4246
- dataAppName: app.name,
4247
- dataAppId: app.dataAppId || void 0
4248
- },
4249
- `Found one data app: "${app.name}". Confirm to continue.`,
4250
- [{ id: app.name, label: `${app.name} (${app.dataAppId || "no-id"})` }],
4251
- "ensure_api_token"
4252
- );
4253
- }
4254
- const options = apps.map((a) => {
4255
- const n = services.discoveryService.normalizeDataApp(a);
4256
- return {
4257
- id: n.dataAppId || n.name,
4258
- label: `${n.name} (${n.dataAppId || "no-id"})`,
4259
- dataAppName: n.name,
4260
- dataAppId: n.dataAppId
4261
- };
4262
- });
4263
- return emit(
4264
- "CHOICE_REQUIRED",
4265
- step,
4266
- state,
4267
- `Found ${apps.length} data app(s). Ask the user which one to embed from.`,
4268
- options,
4269
- "choose_data_app"
4270
- );
4271
- }
4272
- if (step === "choose_data_app") {
4273
- if (!params.userChoice) {
4274
- return toolError("userChoice is required for choose_data_app.");
4275
- }
4276
- const apps = await services.dataAppsApi.listDataApps();
4277
- const q = params.userChoice.toLowerCase();
4278
- const match = apps.find((a) => {
4279
- const n2 = services.discoveryService.normalizeDataApp(a);
4280
- return n2.name.toLowerCase() === q || (n2.dataAppId || "").toLowerCase() === q || n2.name.toLowerCase().includes(q);
4281
- });
4282
- if (!match) {
4283
- return toolError(
4284
- `No data app matched "${params.userChoice}". Re-run discover_data_apps.`
4285
- );
4286
- }
4287
- const n = services.discoveryService.normalizeDataApp(match);
4288
- return emit(
4289
- "READY",
4290
- step,
4291
- {
4292
- ...state,
4293
- dataAppName: n.name,
4294
- dataAppId: n.dataAppId || void 0
4295
- },
4296
- `Selected data app: "${n.name}".`,
4297
- void 0,
4298
- "ensure_api_token"
4299
- );
4300
- }
4301
- if (step === "create_data_app") {
4302
- const name = parseNameFromUserChoice(params.userChoice);
4303
- if (!name) {
4304
- return toolError(
4305
- 'Provide data app name in userChoice. Accepts plain string or JSON: {"name":"my-data-app"}.'
4306
- );
4307
- }
4308
- const created = await client.request({
4309
- method: "POST",
4310
- path: "/api/v2/data-app/",
4311
- auth: "service",
4312
- body: { name },
4313
- invalidateCache: ["list_data_apps"]
4314
- });
4315
- return emit(
4316
- "READY",
4317
- step,
4318
- {
4319
- ...state,
4320
- dataAppName: name,
4321
- dataAppId: created.id || void 0
4322
- },
4323
- `Created data app "${name}".`,
4324
- void 0,
4325
- "ensure_api_token"
4326
- );
4327
- }
4328
- if (step === "ensure_api_token") {
4329
- const appName = state.dataAppName;
4330
- if (!appName) {
4331
- return toolError("state.dataAppName is required. Run discover_data_apps first.");
4332
- }
4333
- const tokens = await client.request({
4334
- method: "GET",
4335
- path: "/api/v2/data-app/api-tokens",
4336
- auth: "service",
4337
- query: { dataAppName: appName }
4338
- });
4339
- if (Array.isArray(tokens) && tokens.length > 0) {
4340
- const tokenValue = tokens[0].token || tokens[0].apiToken || tokens[0].key;
4341
- if (tokenValue) {
4342
- client.setRuntimeApiToken(tokenValue);
4343
- return emit(
4344
- "READY",
4345
- step,
4346
- { ...state, dataAppName: appName },
4347
- `API token found and activated for "${appName}".`,
4348
- void 0,
4349
- "discover_workspaces"
4350
- );
4351
- }
4352
- return emit(
4353
- "CHOICE_REQUIRED",
4354
- step,
4355
- { ...state, dataAppName: appName },
4356
- `"${appName}" has API token(s) but the value was not returned. Call \`create_api_token\` to generate a new one, or \`set_api_token\` if you have the value. Then re-run discover_workspaces.`,
4357
- [
4358
- { id: "create", label: "Create new API token (call create_api_token)" },
4359
- { id: "set", label: "Set existing token (call set_api_token)" }
4360
- ],
4361
- "discover_workspaces"
4362
- );
4363
- }
4364
- return emit(
4365
- "CHOICE_REQUIRED",
4366
- step,
4367
- { ...state, dataAppName: appName },
4368
- `No API token for "${appName}". Call \`create_api_token\` to generate one. Then proceed to discover_workspaces.`,
4369
- [
4370
- { id: "create", label: "Create API token now (call create_api_token)" }
4371
- ],
4372
- "discover_workspaces"
4373
- );
4374
- }
4375
- if (step === "discover_workspaces") {
4376
- const workspaces = await services.workspacesApi.listWorkspaces();
4377
- if (!Array.isArray(workspaces) || workspaces.length === 0) {
4378
- return emit(
4379
- "READY",
4380
- step,
4381
- state,
4382
- "No workspaces found. Create a workspace in Databrain UI first, then re-run this step."
4383
- );
4384
- }
4385
- if (workspaces.length === 1) {
4386
- const ws = workspaces[0];
4387
- const wsName = (typeof ws.name === "string" ? ws.name.trim() : "") || "Unnamed";
4388
- return emit(
4389
- "READY",
4390
- step,
4391
- { ...state, workspaceName: wsName },
4392
- `Found one workspace: "${wsName}". Auto-selected.`,
4393
- void 0,
4394
- "list_dashboards"
4395
- );
4396
- }
4397
- const options = workspaces.filter((ws) => typeof ws.name === "string" && ws.name.trim().length > 0).map((ws) => ({
4398
- id: ws.name,
4399
- label: `${ws.name} (${ws._id || ws.id || "no-id"})`,
4400
- workspaceName: ws.name
4401
- }));
4402
- return emit(
4403
- "CHOICE_REQUIRED",
4404
- step,
4405
- state,
4406
- `Found ${workspaces.length} workspace(s). Ask the user which workspace to use.`,
4407
- options,
4408
- "choose_workspace"
4409
- );
4410
- }
4411
- if (step === "choose_workspace") {
4412
- if (!params.userChoice) {
4413
- return toolError("userChoice is required for choose_workspace.");
4414
- }
4415
- const workspaces = await services.workspacesApi.listWorkspaces();
4416
- const q = params.userChoice.toLowerCase();
4417
- const match = workspaces.find((ws) => {
4418
- const name = (typeof ws.name === "string" ? ws.name.trim() : "").toLowerCase();
4419
- const wsId = (ws._id || ws.id || "").toLowerCase();
4420
- return name === q || wsId === q || name.includes(q);
4421
- });
4422
- if (!match) {
4423
- return toolError(
4424
- `No workspace matched "${params.userChoice}". Re-run discover_workspaces.`
4425
- );
4426
- }
4427
- const wsName = (typeof match.name === "string" ? match.name.trim() : "") || "Unnamed";
4428
- return emit(
4429
- "READY",
4430
- step,
4431
- { ...state, workspaceName: wsName },
4432
- `Selected workspace: "${wsName}".`,
4433
- void 0,
4434
- "list_dashboards"
4435
- );
4436
- }
4437
- if (step === "list_dashboards") {
4438
- const { dashboards, scope } = await listDashboardsForContext(state);
4439
- if (!Array.isArray(dashboards) || dashboards.length === 0) {
4440
- const scopeMsg = scope === "data_app" ? `No dashboards found for data app "${state.dataAppName || "unknown"}".` : scope === "workspace" ? `No dashboards found in workspace "${state.workspaceName || "unknown"}". Create a dashboard in Databrain UI first, then re-run this step.` : "No dashboards found in available workspaces. Create a dashboard in Databrain UI first, then re-run this step.";
4441
- return emit("READY", step, state, scopeMsg);
4442
- }
4443
- const options = dashboards.map((d) => {
4444
- const n = services.discoveryService.normalizeDashboard(d);
4445
- return {
4446
- id: n.dashboardId || n.name,
4447
- label: `${n.name} (${n.dashboardId || "no-id"})${n.workspaceName ? ` \u2014 workspace: ${n.workspaceName}` : ""}`,
4448
- dashboardId: n.dashboardId,
4449
- workspaceName: n.workspaceName
4450
- };
4451
- });
4452
- const foundMsg = scope === "data_app" ? `Found ${dashboards.length} dashboard(s) for data app "${state.dataAppName || "unknown"}". Ask the user which dashboard to embed.` : scope === "workspace" ? `Found ${dashboards.length} dashboard(s) in workspace "${state.workspaceName || "unknown"}". Ask the user which dashboard to embed.` : `Found ${dashboards.length} dashboard(s) across workspaces. Ask the user which dashboard to embed.`;
4453
- return emit(
4454
- "CHOICE_REQUIRED",
4455
- step,
4456
- state,
4457
- foundMsg,
4458
- options,
4459
- "choose_dashboard"
4460
- );
4461
- }
4462
- if (step === "choose_dashboard") {
4463
- if (!params.userChoice) {
4464
- return toolError("userChoice is required for choose_dashboard. Pass dashboardId or name.");
4465
- }
4466
- const { dashboards } = await listDashboardsForContext(state);
4467
- const q = params.userChoice.toLowerCase();
4468
- const match = dashboards.find((d) => {
4469
- const n2 = services.discoveryService.normalizeDashboard(d);
4470
- return (n2.dashboardId || "").toLowerCase() === q || (n2.name || "").toLowerCase() === q || (n2.name || "").toLowerCase().includes(q);
4471
- });
4472
- if (!match) {
4473
- return toolError(
4474
- `No dashboard matched "${params.userChoice}". Re-run list_dashboards.`
4475
- );
4476
- }
4477
- const n = services.discoveryService.normalizeDashboard(match);
4478
- let resolvedDatamartName = state.datamartName;
4479
- const resolveAppId = state.dataAppId || n.workspaceName;
4480
- if (!resolvedDatamartName && resolveAppId) {
4481
- try {
4482
- const datamarts = await services.datamartsApi.listDatamartsByDataAppId(resolveAppId);
4483
- const names = Array.from(
4484
- new Set(
4485
- datamarts.map((dm) => dm.name || dm.datamartName).filter((v) => typeof v === "string" && v.trim().length > 0)
4486
- )
4487
- );
4488
- if (names.length === 1) resolvedDatamartName = names[0];
4489
- } catch {
4490
- }
4491
- }
4492
- return emit(
4493
- "READY",
4494
- step,
4495
- {
4496
- ...state,
4497
- dashboardId: n.dashboardId,
4498
- embedType: "dashboard",
4499
- ...n.workspaceName ? { workspaceName: n.workspaceName } : {},
4500
- ...resolvedDatamartName ? { datamartName: resolvedDatamartName } : {}
4501
- },
4502
- `Selected dashboard: "${n.name}" (${n.dashboardId}).${n.workspaceName ? ` workspaceName=${n.workspaceName}.` : ""}${resolvedDatamartName ? ` datamartName=${resolvedDatamartName}.` : ""} Proceeding to create embed.`,
4503
- void 0,
4504
- "create_embed"
4505
- );
4506
- }
4507
- if (step === "create_embed") {
4508
- const choiceObj = parseUserChoiceObject(params.userChoice);
4509
- const merged = {
4510
- ...state,
4511
- ...choiceObj || {}
4512
- };
4513
- const dashboardId = merged.dashboardId;
4514
- const metricId = merged.metricId;
4515
- let resolvedWorkspaceName = merged.workspaceName;
4516
- const embedType = merged.embedType?.toLowerCase() === "metric" ? "metric" : "dashboard";
4517
- const datamartName = merged.datamartName;
4518
- const embedName = merged.embedName || (embedType === "dashboard" && dashboardId ? `embed-${dashboardId}` : embedType === "metric" && metricId ? `embed-metric-${metricId}` : void 0);
4519
- let resolvedDatamartName = datamartName;
4520
- let resolveAppId = merged.dataAppId;
4521
- let availableDatamartNames = [];
4522
- if (!resolveAppId) {
4523
- const appName = merged.dataAppName;
4524
- if (appName) {
4525
- try {
4526
- const apps = await services.dataAppsApi.listDataApps();
4527
- const app = apps.find((a) => {
4528
- const n = services.discoveryService.normalizeDataApp(a);
4529
- return n.name.toLowerCase() === appName.toLowerCase() || (n.dataAppId || "").toLowerCase() === appName.toLowerCase();
4530
- });
4531
- if (app) {
4532
- resolveAppId = services.discoveryService.normalizeDataApp(app).dataAppId || void 0;
4533
- }
4534
- } catch {
4535
- }
4536
- }
4537
- }
4538
- if (embedType === "dashboard" && dashboardId && merged.dataAppName && client.hasApiToken()) {
4539
- try {
4540
- const scopedDashboards = await services.dashboardsApi.listDashboards(
4541
- merged.dataAppName
4542
- );
4543
- const dashboardMatch = scopedDashboards.find((d) => {
4544
- const n = services.discoveryService.normalizeDashboard(d);
4545
- return (n.dashboardId || "").toLowerCase() === dashboardId.toLowerCase();
4546
- });
4547
- if (!dashboardMatch) {
4548
- return emit(
4549
- "CHOICE_REQUIRED",
4550
- step,
4551
- merged,
4552
- `dashboardId "${dashboardId}" is not available under data app "${merged.dataAppName || "unknown"}". Re-run list_dashboards and choose from the current data app scope.`,
4553
- [],
4554
- "list_dashboards"
4555
- );
4556
- }
4557
- const dashboardNormalized = services.discoveryService.normalizeDashboard(dashboardMatch);
4558
- if (dashboardNormalized.workspaceName) {
4559
- if (resolvedWorkspaceName && resolvedWorkspaceName !== dashboardNormalized.workspaceName) {
4560
- return emit(
4561
- "CHOICE_REQUIRED",
4562
- step,
4563
- merged,
4564
- `workspaceName "${resolvedWorkspaceName}" does not match dashboard workspace "${dashboardNormalized.workspaceName}". Use the dashboard workspace.`,
4565
- [
4566
- {
4567
- id: "use_dashboard_workspace",
4568
- label: dashboardNormalized.workspaceName,
4569
- workspaceName: dashboardNormalized.workspaceName
4570
- }
4571
- ],
4572
- "create_embed"
4573
- );
4574
- }
4575
- resolvedWorkspaceName = dashboardNormalized.workspaceName;
4576
- }
4577
- } catch {
4578
- }
4579
- }
4580
- if (embedType === "dashboard" && !resolvedDatamartName) {
4581
- if (resolveAppId) {
4582
- try {
4583
- const datamarts = await services.datamartsApi.listDatamartsByDataAppId(
4584
- resolveAppId
4585
- );
4586
- const names = Array.from(
4587
- new Set(
4588
- datamarts.map((dm) => dm.name || dm.datamartName).filter(
4589
- (v) => typeof v === "string" && v.trim().length > 0
4590
- )
4591
- )
4592
- );
4593
- availableDatamartNames = names;
4594
- if (names.length === 1) {
4595
- resolvedDatamartName = names[0];
4596
- } else if (names.length > 1) {
4597
- return emit(
4598
- "CHOICE_REQUIRED",
4599
- step,
4600
- merged,
4601
- "Multiple datamarts are available for this data app. Ask the user to select one for accessSettings.datamartName.",
4602
- names.map((name) => ({
4603
- id: name,
4604
- label: name,
4605
- datamartName: name
4606
- })),
4607
- "create_embed"
4608
- );
4609
- }
4610
- } catch {
4611
- }
4612
- }
4613
- }
4614
- if (embedType === "dashboard" && resolvedDatamartName && resolveAppId && availableDatamartNames.length === 0) {
4615
- try {
4616
- const datamarts = await services.datamartsApi.listDatamartsByDataAppId(resolveAppId);
4617
- availableDatamartNames = Array.from(
4618
- new Set(
4619
- datamarts.map((dm) => dm.name || dm.datamartName).filter((v) => typeof v === "string" && v.trim().length > 0)
4620
- )
4621
- );
4622
- } catch {
4623
- }
4624
- }
4625
- if (embedType === "dashboard" && resolvedDatamartName && availableDatamartNames.length > 0 && !availableDatamartNames.includes(resolvedDatamartName)) {
4626
- return emit(
4627
- "CHOICE_REQUIRED",
4628
- step,
4629
- merged,
4630
- `datamartName "${resolvedDatamartName}" is invalid for this data app. Choose one of the available datamarts.`,
4631
- availableDatamartNames.map((name) => ({
4632
- id: name,
4633
- label: name,
4634
- datamartName: name
4635
- })),
4636
- "create_embed"
4637
- );
4638
- }
4639
- const missing = [];
4640
- if (!resolvedWorkspaceName) missing.push("workspaceName");
4641
- if (!dashboardId) missing.push("dashboardId");
4642
- if (embedType === "metric" && !metricId) missing.push("metricId");
4643
- if (embedType === "dashboard" && !resolvedDatamartName) missing.push("datamartName");
4644
- if (missing.length > 0) {
4645
- return emit(
4646
- "CHOICE_REQUIRED",
4647
- step,
4648
- {
4649
- ...merged,
4650
- ...resolvedDatamartName ? { datamartName: resolvedDatamartName } : {}
4651
- },
4652
- `Missing required fields for embed creation: ${missing.join(", ")}. Provide via userChoice JSON.`,
4653
- [
4654
- {
4655
- id: "provide_fields",
4656
- label: 'Provide JSON: {"dashboardId":"...","workspaceName":"...","datamartName":"..."}'
4657
- }
4658
- ],
4659
- "create_embed"
4660
- );
4661
- }
4662
- const created = await services.embedService.createEmbedStrict({
4663
- dashboardId,
4664
- metricId,
4665
- workspaceName: resolvedWorkspaceName,
4666
- embedType,
4667
- name: embedName,
4668
- accessSettings: {
4669
- datamartName: resolvedDatamartName,
4670
- isAllowMetricCreation: false,
4671
- isAllowMetricDeletion: false,
4672
- isAllowMetricUpdate: false,
4673
- isAllowMetricLayoutChange: false,
4674
- isAllowManageMetrics: false,
4675
- isAllowUnderlyingData: false,
4676
- isAllowAiPilot: false,
4677
- isAllowEmailReports: false,
4678
- isAllowCreateDashboardView: false,
4679
- metricCreationMode: "DRAG_DROP"
4680
- }
4681
- });
4682
- const embedId = created.embedId || created.id || created._id || void 0;
4683
- return emit(
4684
- "READY",
4685
- step,
4686
- { ...merged, embedType, embedId },
4687
- `Embed created successfully${embedId ? ` (embedId: ${embedId})` : ""}. Next: run runtime readiness validation before finishing setup.`,
4688
- void 0,
4689
- "runtime_readiness"
4690
- );
4691
- }
4692
- if (step === "runtime_readiness") {
4693
- const appName = state.dataAppName;
4694
- if (!appName) {
4695
- return toolError("state.dataAppName is required. Run discover_data_apps first.");
4696
- }
4697
- const choiceObj = parseUserChoiceObject(params.userChoice);
4698
- const clientId = choiceObj?.clientId || state.clientId || "None";
4699
- const permissions = {
4700
- isEnableManageMetrics: true,
4701
- isEnableMetricUpdation: true,
4702
- isEnableCustomizeLayout: true,
4703
- isEnableCreateDashboardView: true,
4704
- isDisableMetricCreation: false,
4705
- ...choiceObj?.permissions || {}
4706
- };
4707
- await services.guestTokenService.generateToken({
4708
- clientId,
4709
- dataAppName: appName,
4710
- permissions
4711
- });
4712
- return emit(
4713
- "DONE",
4714
- step,
4715
- {
4716
- ...state,
4717
- clientId,
4718
- runtimeValidated: true
4719
- },
4720
- "Runtime contract validated by generating a guest token with top-level clientId/dataAppName/permissions. Next: run generate_embed_code and then continue with brand-my-embed or secure-my-embed as needed."
4721
- );
4722
- }
4723
- if (step === "complete") {
4724
- return emit(
4725
- "DONE",
4726
- step,
4727
- state,
4728
- "Setup complete. Recommended before handoff: run runtime_readiness. Then use brand-my-embed, secure-my-embed, setup-filters, or localize-embed prompts for further customization."
4729
- );
4730
- }
4731
- return toolError(`Unknown step: ${step}`);
4732
- } catch (err) {
4733
- if (err instanceof DatabrainApiError) return err.toToolResponse();
4734
- return toolError(`Unexpected error: ${err.message}`);
4735
- }
4736
- }
4737
- );
4738
- }
4739
-
4740
3592
  // src/tools/query/ask-question.ts
4741
- import { z as z21 } from "zod";
3593
+ import { z as z18 } from "zod";
4742
3594
  var MAX_DISPLAY_ROWS = 50;
4743
3595
  function formatDataTable(data, columns) {
4744
3596
  if (!Array.isArray(data) || data.length === 0) return "_No data returned._";
@@ -4768,16 +3620,16 @@ function registerAskQuestion(server, client) {
4768
3620
  "ask_question",
4769
3621
  "Ask a natural language question \u2014 converts to SQL, returns data + chart suggestion. Requires datamartName with a populated semantic layer.",
4770
3622
  {
4771
- question: z21.string().describe(
3623
+ question: z18.string().describe(
4772
3624
  'The natural language question to ask (e.g. "What was total revenue last month?")'
4773
3625
  ),
4774
- datamartName: z21.string().optional().describe(
3626
+ datamartName: z18.string().optional().describe(
4775
3627
  "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."
4776
3628
  ),
4777
- sessionId: z21.string().optional().describe(
3629
+ sessionId: z18.string().optional().describe(
4778
3630
  "Session ID from a previous ask_question response, for follow-up questions."
4779
3631
  ),
4780
- timezone: z21.string().optional().describe('IANA timezone (e.g. "America/New_York"). Defaults to UTC.')
3632
+ timezone: z18.string().optional().describe('IANA timezone (e.g. "America/New_York"). Defaults to UTC.')
4781
3633
  },
4782
3634
  WRITE_CREATE,
4783
3635
  async (params) => {
@@ -4942,14 +3794,14 @@ To fix this:
4942
3794
  }
4943
3795
 
4944
3796
  // src/tools/query/get-semantic-layer.ts
4945
- import { z as z22 } from "zod";
3797
+ import { z as z19 } from "zod";
4946
3798
  function registerGetSemanticLayer(server, client) {
4947
3799
  const services = createServiceBundle(client);
4948
3800
  server.tool(
4949
3801
  "get_semantic_layer",
4950
3802
  "Get semantic layer metadata for a datamart: table/column descriptions, synonyms, completion score. Omit datamartName to auto-resolve.",
4951
3803
  {
4952
- datamartName: z22.string().optional().describe(
3804
+ datamartName: z19.string().optional().describe(
4953
3805
  "Name of the datamart to inspect. Omit to list available datamarts."
4954
3806
  )
4955
3807
  },
@@ -5059,15 +3911,15 @@ ${result.feedback}`);
5059
3911
  }
5060
3912
 
5061
3913
  // src/tools/query/update-semantic-layer.ts
5062
- import { z as z23 } from "zod";
5063
- var columnSchema = z23.object({
5064
- name: z23.string().describe("Column name (must exist in the datamart)"),
5065
- description: z23.string().optional().describe("Business-friendly description of what this column represents"),
5066
- synonyms: z23.array(z23.string()).optional().describe(
3914
+ import { z as z20 } from "zod";
3915
+ var columnSchema = z20.object({
3916
+ name: z20.string().describe("Column name (must exist in the datamart)"),
3917
+ description: z20.string().optional().describe("Business-friendly description of what this column represents"),
3918
+ synonyms: z20.array(z20.string()).optional().describe(
5067
3919
  'Alternative names users might use for this column (e.g. ["revenue", "sales amount"])'
5068
3920
  ),
5069
- miscellaneousInfo: z23.string().optional().describe("Additional context such as valid values, units, or caveats"),
5070
- columnType: z23.enum([
3921
+ miscellaneousInfo: z20.string().optional().describe("Additional context such as valid values, units, or caveats"),
3922
+ columnType: z20.enum([
5071
3923
  "String",
5072
3924
  "Long String",
5073
3925
  "String (Custom)",
@@ -5080,16 +3932,16 @@ var columnSchema = z23.object({
5080
3932
  "JSON"
5081
3933
  ]).nullable().optional().describe('Semantic column type. Use "Identifier" for IDs/foreign keys, "Number" for numeric values, "String" for text. Omit for date columns.')
5082
3934
  });
5083
- var tableSchema = z23.object({
5084
- name: z23.string().describe("Table name (must exist in the datamart)"),
5085
- description: z23.string().optional().describe(
3935
+ var tableSchema = z20.object({
3936
+ name: z20.string().describe("Table name (must exist in the datamart)"),
3937
+ description: z20.string().optional().describe(
5086
3938
  "Business-friendly description of what this table represents"
5087
3939
  ),
5088
- synonyms: z23.array(z23.string()).optional().describe("Alternative names for this table"),
5089
- miscellaneousInfo: z23.string().optional().describe(
3940
+ synonyms: z20.array(z20.string()).optional().describe("Alternative names for this table"),
3941
+ miscellaneousInfo: z20.string().optional().describe(
5090
3942
  "Additional context such as how the table relates to other tables"
5091
3943
  ),
5092
- columns: z23.array(columnSchema).optional().describe("Column-level semantic definitions to update")
3944
+ columns: z20.array(columnSchema).optional().describe("Column-level semantic definitions to update")
5093
3945
  });
5094
3946
  function registerUpdateSemanticLayer(server, client) {
5095
3947
  const services = createServiceBundle(client);
@@ -5097,13 +3949,13 @@ function registerUpdateSemanticLayer(server, client) {
5097
3949
  "update_semantic_layer",
5098
3950
  "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.",
5099
3951
  {
5100
- datamartName: z23.string().optional().describe(
3952
+ datamartName: z20.string().optional().describe(
5101
3953
  "Name of the datamart to update. Omit to list available datamarts."
5102
3954
  ),
5103
- tables: z23.array(tableSchema).optional().describe(
3955
+ tables: z20.array(tableSchema).optional().describe(
5104
3956
  "Table and column definitions to update. Only include tables/columns you want to change."
5105
3957
  ),
5106
- feedback: z23.string().optional().describe(
3958
+ feedback: z20.string().optional().describe(
5107
3959
  'General feedback or context about the datamart (e.g. "This datamart contains e-commerce data for the US market").'
5108
3960
  )
5109
3961
  },
@@ -5177,12 +4029,9 @@ function registerAllTools(server, client) {
5177
4029
  registerGetEmbedDetails(server, client);
5178
4030
  registerUpdateEmbed(server, client);
5179
4031
  registerCustomizeTheme(server, client);
5180
- registerConfigureEmbed(server, client);
5181
- registerDiagnoseEmbedHealth(server, client);
5182
4032
  registerGenerateGuestToken(server, client);
5183
4033
  registerGenerateEmbedCode(server, client);
5184
4034
  registerListDashboards(server, client);
5185
- registerSetupEmbedInteractive(server, client);
5186
4035
  registerAskQuestion(server, client);
5187
4036
  registerGetSemanticLayer(server, client);
5188
4037
  registerUpdateSemanticLayer(server, client);
@@ -7658,7 +6507,7 @@ Databrain supports 4 core use cases for embedding analytics. Each maps to a spec
7658
6507
  **Two modes:**
7659
6508
  - **DRAG_DROP:** Visual metric builder \u2014 users drag columns, pick chart types. No AI needed.
7660
6509
  - **CHAT:** AI-powered natural language \u2014 users type questions. Requires LLM connector in Databrain admin.
7661
- **Tool sequence:** \`list_embeds\` \u2192 \`update_embed\` (set accessSettings) \u2192 \`configure_embed\` \u2192 \`generate_guest_token\` (with permissions)
6510
+ **Tool sequence:** \`list_embeds\` \u2192 \`update_embed\` (set accessSettings and options) \u2192 \`generate_guest_token\` (with permissions)
7662
6511
 
7663
6512
  ## Use Case 4: Explore and Query Data
7664
6513
  **When to use:** You want to explore available data and ask questions using natural language.
@@ -7881,7 +6730,7 @@ If creating new: resolve datamartName by calling \`list_datamarts\`, then call \
7881
6730
  - \`accessSettings: { datamartName: "<resolved>" }\` \u2014 only datamartName is needed, the tool fills in all other required defaults automatically. Do NOT add isAllow* fields yourself.
7882
6731
 
7883
6732
  **Step 6 \u2014 Verify Embed**
7884
- Call \`get_embed_details\` and \`diagnose_embed_health\` to verify the embed is properly configured.
6733
+ Call \`get_embed_details\` to verify the embed is properly configured.
7885
6734
  Show the user the embed ID and status.
7886
6735
 
7887
6736
  **Step 7 \u2014 Generate Guest Token**
@@ -8011,7 +6860,7 @@ For CHAT mode:
8011
6860
  \`\`\`
8012
6861
 
8013
6862
  **Step 4 \u2014 Configure Embed Options**
8014
- Call \`configure_embed\` to ensure the UI supports metric creation:
6863
+ Call \`update_embed\` (body passthrough with options) to ensure the UI supports metric creation:
8015
6864
  - \`disableMetricCreation\`: false
8016
6865
  - \`hideCreateMetricButton\`: false
8017
6866
  - \`hideChartSettings\`: false (so users can customize their created metrics)
@@ -8082,7 +6931,7 @@ Use \`customize_embed_theme\` with the chart appearance params to fine-tune char
8082
6931
  Ask if they want to customize tooltip fonts, axis styling, or label positions.
8083
6932
 
8084
6933
  **Step 5 \u2014 Configure Display Options**
8085
- Use \`configure_embed\` to clean up the UI:
6934
+ Use \`update_embed\` (body passthrough) to clean up the UI:
8086
6935
  - Ask if they want to hide the dashboard name (\`hideDashboardName\`)
8087
6936
  - Ask about card shadows and borders
8088
6937
  - Ask about download/export options they want to show
@@ -8124,7 +6973,7 @@ Call \`list_embeds\` and ask the user which embed to localize. Then ask:
8124
6973
  - What is the default/primary language?
8125
6974
 
8126
6975
  **Step 2 \u2014 Set the Primary Language**
8127
- Use \`configure_embed\` with the \`language\` parameter set to their primary language code (e.g. "fr" for French).
6976
+ Use \`update_embed\` (body passthrough with componentAttributes) with the \`language\` parameter set to their primary language code (e.g. "fr" for French).
8128
6977
 
8129
6978
  **Step 3 \u2014 Gather Translations**
8130
6979
  Call \`get_embed_details\` to understand what text is in the dashboard. Then help the user build translations:
@@ -8146,28 +6995,28 @@ For each text element, ask for translations in their target languages. Build tra
8146
6995
  - If a translation is missing for a language, the original key text is shown as fallback
8147
6996
 
8148
6997
  **Step 4 \u2014 Apply Translations**
8149
- Use \`configure_embed\` with the \`translations\` array containing all entries.
6998
+ Use \`update_embed\` (body passthrough with componentAttributes.translationDictionary) with the translations.
8150
6999
 
8151
7000
  **Step 5 \u2014 Configure Calendar (if needed)**
8152
7001
  Ask if they need a different calendar system:
8153
7002
  - \`"default"\` \u2014 standard Gregorian calendar
8154
7003
  - \`"ind"\` \u2014 Indian calendar format
8155
7004
 
8156
- Set via \`calendarType\` in \`configure_embed\` (\`language\` and \`calendarType\` params).
7005
+ Set via \`calendarType\` in \`update_embed\` (body passthrough with componentAttributes).
8157
7006
 
8158
7007
  **Step 6 \u2014 Set Custom Messages**
8159
7008
  Ask if they want to translate system messages:
8160
7009
  - Token expiry message (shown when session expires)
8161
7010
  - Token absent message (shown when no auth token)
8162
7011
 
8163
- Use \`configure_embed\` with \`tokenExpiryMessage\` and \`tokenAbsentMessage\` in their primary language.
7012
+ Use \`update_embed\` (body passthrough with options.customMessages) with \`tokenExpiry\` and \`tokenAbsent\` in their primary language.
8164
7013
 
8165
7014
  **Step 7 \u2014 Test**
8166
7015
  Tell the user to refresh the embed and verify translations appear correctly. If any text isn't translating, double-check that the key matches exactly.
8167
7016
 
8168
7017
  Remind the user: translations can also be applied per-user at the web component level using the \`translation-dictionary\` attribute, which is useful for dynamically switching languages based on user preferences.
8169
7018
 
8170
- **Tip:** The \`language\`, \`calendarType\`, \`hideDashboardFilters\`, and filter settings are all available in the same \`configure_embed\` tool.
7019
+ **Tip:** The \`language\`, \`calendarType\`, \`hideDashboardFilters\`, and filter settings are all configurable via \`update_embed\` body passthrough.
8171
7020
 
8172
7021
  **Important:** After changing language or translation settings, existing guest tokens still carry the old configuration. Remind the user to **regenerate guest tokens** (or reload their app) for changes to take effect.`;
8173
7022
 
@@ -8184,10 +7033,10 @@ Ask the user about their end-user roles. Common patterns:
8184
7033
  Ask: "What roles do your end-users have? What should each role be allowed to do?"
8185
7034
 
8186
7035
  **Step 2 \u2014 Lock Down the Embed Defaults**
8187
- Use \`configure_embed\` to set restrictive defaults (principle of least privilege):
7036
+ Use \`update_embed\` (body passthrough with options) to set restrictive defaults (principle of least privilege):
8188
7037
 
8189
7038
  \`\`\`
8190
- configure_embed(embedId,
7039
+ update_embed(embedId, body: { options: {
8191
7040
  disableMetricCreation: true,
8192
7041
  disableMetricUpdation: true,
8193
7042
  disableMetricDeletion: true,
@@ -8291,7 +7140,7 @@ Ask the user:
8291
7140
  - "Do any filters have very large option lists?" (100+ options \u2014 these benefit from SQL-based filters)
8292
7141
 
8293
7142
  **Step 3 \u2014 Configure String Filters**
8294
- For text/category filters, use \`configure_embed\` with \`stringFilters\`:
7143
+ For text/category filters, use \`update_embed\` (body passthrough with options.globalFilterOptions) for \`stringFilters\`:
8295
7144
 
8296
7145
  \`\`\`
8297
7146
  stringFilters: [{
@@ -8420,15 +7269,15 @@ Ask the user what they want to do:
8420
7269
 
8421
7270
  **Permissions / Access Settings:**
8422
7271
  - What's enabled vs disabled
8423
- - Suggest \`configure_embed\` for changes
7272
+ - Suggest \`update_embed\` for changes
8424
7273
 
8425
7274
  **Filters:**
8426
7275
  - Current filter configuration
8427
- - Suggest \`configure_embed\` with filter params for changes
7276
+ - Suggest \`update_embed\` (body passthrough with options.globalFilterOptions) for changes
8428
7277
 
8429
7278
  **Internationalization:**
8430
7279
  - Current language and translations
8431
- - Suggest \`configure_embed\` with \`language\` and \`translations\` params for changes
7280
+ - Suggest \`update_embed\` (body passthrough with componentAttributes) for changes
8432
7281
 
8433
7282
  3. Ask: "Would you like to modify any of these settings?"
8434
7283
 
@@ -8677,7 +7526,7 @@ function createServer(config) {
8677
7526
  4 core use cases \u2014 match the user's scenario to the right prompt:
8678
7527
  1. Embed existing dashboard \u2192 prompt: embed-existing-dashboard (list_data_apps \u2192 create_api_token \u2192 list_dashboards \u2192 create_embed \u2192 generate_guest_token \u2192 generate_embed_code)
8679
7528
  2. Blank dashboard per client \u2192 prompt: embed-blank-dashboard (list_data_apps \u2192 list_datamarts \u2192 create_embed with clientId per client \u2192 generate_guest_token \u2192 generate_embed_code)
8680
- 3. Enable metric creation \u2192 prompt: enable-metric-creation (list_embeds \u2192 update_embed accessSettings \u2192 configure_embed \u2192 generate_guest_token with permissions)
7529
+ 3. Enable metric creation \u2192 prompt: enable-metric-creation (list_embeds \u2192 update_embed accessSettings \u2192 generate_guest_token with permissions)
8681
7530
  4. Explore & query data \u2192 prompt: query-data (list_datamarts \u2192 get_semantic_layer \u2192 if empty, auto-generate descriptions with update_semantic_layer or use populate-semantic-layer prompt \u2192 ask_question)
8682
7531
 
8683
7532
  Infrastructure CREATION (datasources, datamarts, workspaces) is done in the Databrain UI.
@@ -8685,6 +7534,8 @@ However, the **semantic layer** is populated and maintained entirely via MCP too
8685
7534
 
8686
7535
  After embed setup, customize with: brand-my-embed, setup-filters, localize-embed, secure-my-embed.
8687
7536
  Use generate_guest_token for frontend auth and generate_embed_code for framework snippets.
7537
+ For advanced embed configuration (UI toggles, filters, i18n), use update_embed with a body passthrough.
7538
+ To diagnose embed issues, use get_embed_details and reason about the response.
8688
7539
  Do NOT ask the user for any tokens \u2014 manage them via MCP tools.
8689
7540
 
8690
7541
  IMPORTANT \u2014 accessSettings defaults: