@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 +115 -1264
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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\`
|
|
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
|
|
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:
|
|
2928
|
-
dataAppName:
|
|
2929
|
-
body:
|
|
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:
|
|
2933
|
-
allowedEmbeds:
|
|
2934
|
-
rlsSettings:
|
|
2935
|
-
metricId:
|
|
2936
|
-
values:
|
|
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:
|
|
2939
|
-
metricId:
|
|
2940
|
-
values:
|
|
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:
|
|
2943
|
-
dashboardId:
|
|
2944
|
-
values:
|
|
2945
|
-
isShowOnUrl:
|
|
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:
|
|
2948
|
-
userIdentifier:
|
|
2949
|
-
timezone:
|
|
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:
|
|
2953
|
-
allowedEmbeds:
|
|
2954
|
-
rlsSettings:
|
|
2955
|
-
metricId:
|
|
2956
|
-
values:
|
|
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:
|
|
2959
|
-
metricId:
|
|
2960
|
-
values:
|
|
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:
|
|
2963
|
-
dashboardId:
|
|
2964
|
-
values:
|
|
2965
|
-
isShowOnUrl:
|
|
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:
|
|
2968
|
-
isEnableArchiveMetrics:
|
|
2969
|
-
isEnableManageMetrics:
|
|
2970
|
-
isEnableCreateDashboardView:
|
|
2971
|
-
isEnableMetricUpdation:
|
|
2972
|
-
isEnableCustomizeLayout:
|
|
2973
|
-
isEnableUnderlyingData:
|
|
2974
|
-
isEnableDownloadMetrics:
|
|
2975
|
-
isShowSideBar:
|
|
2976
|
-
isShowDashboardName:
|
|
2977
|
-
isDisableMetricCreation:
|
|
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:
|
|
2980
|
-
hideDashboardFilters:
|
|
2981
|
-
userIdentifier:
|
|
2982
|
-
expiryTime:
|
|
2983
|
-
datasourceName:
|
|
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
|
|
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:
|
|
4033
|
-
framework:
|
|
4034
|
-
embedType:
|
|
4035
|
-
options:
|
|
4036
|
-
enableDownloadCsv:
|
|
4037
|
-
enableEmailCsv:
|
|
4038
|
-
enableDownloadAllMetrics:
|
|
4039
|
-
enableDownloadAllPdf:
|
|
4040
|
-
disableFullscreen:
|
|
4041
|
-
isHideTablePreview:
|
|
4042
|
-
isHideChartSettings:
|
|
4043
|
-
isStickyDashboardFilters:
|
|
4044
|
-
enableMultiMetricFilters:
|
|
4045
|
-
themeName:
|
|
4046
|
-
customTheme:
|
|
4047
|
-
optionsIcon:
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
3629
|
+
sessionId: z18.string().optional().describe(
|
|
4778
3630
|
"Session ID from a previous ask_question response, for follow-up questions."
|
|
4779
3631
|
),
|
|
4780
|
-
timezone:
|
|
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
|
|
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:
|
|
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
|
|
5063
|
-
var columnSchema =
|
|
5064
|
-
name:
|
|
5065
|
-
description:
|
|
5066
|
-
synonyms:
|
|
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:
|
|
5070
|
-
columnType:
|
|
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 =
|
|
5084
|
-
name:
|
|
5085
|
-
description:
|
|
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:
|
|
5089
|
-
miscellaneousInfo:
|
|
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:
|
|
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:
|
|
3952
|
+
datamartName: z20.string().optional().describe(
|
|
5101
3953
|
"Name of the datamart to update. Omit to list available datamarts."
|
|
5102
3954
|
),
|
|
5103
|
-
tables:
|
|
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:
|
|
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
|
|
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\`
|
|
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 \`
|
|
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 \`
|
|
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 \`
|
|
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 \`
|
|
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 \`
|
|
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 \`
|
|
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
|
|
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 \`
|
|
7036
|
+
Use \`update_embed\` (body passthrough with options) to set restrictive defaults (principle of least privilege):
|
|
8188
7037
|
|
|
8189
7038
|
\`\`\`
|
|
8190
|
-
|
|
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 \`
|
|
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 \`
|
|
7272
|
+
- Suggest \`update_embed\` for changes
|
|
8424
7273
|
|
|
8425
7274
|
**Filters:**
|
|
8426
7275
|
- Current filter configuration
|
|
8427
|
-
- Suggest \`
|
|
7276
|
+
- Suggest \`update_embed\` (body passthrough with options.globalFilterOptions) for changes
|
|
8428
7277
|
|
|
8429
7278
|
**Internationalization:**
|
|
8430
7279
|
- Current language and translations
|
|
8431
|
-
- Suggest \`
|
|
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
|
|
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:
|