@databrainhq/mcp-server 0.1.0 → 0.1.1-test
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -10
- package/dist/index.js +1873 -253
- package/dist/index.js.map +1 -1
- package/package.json +8 -1
package/dist/index.js
CHANGED
|
@@ -173,7 +173,7 @@ var DatabrainClient = class {
|
|
|
173
173
|
throw new DatabrainApiError(
|
|
174
174
|
401,
|
|
175
175
|
"MISSING_TOKEN",
|
|
176
|
-
`${tokenType} is required for this operation but is not configured.${options.auth === "api" ? " Use
|
|
176
|
+
`${tokenType} is required for this operation but is not configured.${options.auth === "api" ? " Use `set_api_token` to provide an existing token, or `create_api_token` to generate a new one." : " Set it in your MCP server environment variables."}`
|
|
177
177
|
);
|
|
178
178
|
}
|
|
179
179
|
let url = `${this.config.apiUrl}${options.path}`;
|
|
@@ -290,14 +290,117 @@ ${formatted}`);
|
|
|
290
290
|
);
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
// src/tools/
|
|
293
|
+
// src/tools/infrastructure/create-datasource.ts
|
|
294
294
|
import { z as z2 } from "zod";
|
|
295
|
+
var DATASOURCE_TYPES = [
|
|
296
|
+
"postgres",
|
|
297
|
+
"snowflake",
|
|
298
|
+
"bigquery",
|
|
299
|
+
"mysql",
|
|
300
|
+
"redshift",
|
|
301
|
+
"cockroachdb",
|
|
302
|
+
"mssql",
|
|
303
|
+
"clickhouse",
|
|
304
|
+
"singlestore",
|
|
305
|
+
"mongodb",
|
|
306
|
+
"databricks",
|
|
307
|
+
"elasticsearch",
|
|
308
|
+
"opensearch",
|
|
309
|
+
"firebolt",
|
|
310
|
+
"athena",
|
|
311
|
+
"trino",
|
|
312
|
+
"awss3",
|
|
313
|
+
"csv"
|
|
314
|
+
];
|
|
315
|
+
var schema = {
|
|
316
|
+
datasourceType: z2.enum(DATASOURCE_TYPES).describe("Database type to connect"),
|
|
317
|
+
credentials: z2.record(z2.unknown()).describe(
|
|
318
|
+
"Connection credentials object. Required fields vary by type. Always include `name` (unique datasource name). Postgres/MySQL: host, port, username, password, database. Snowflake: host, username, role, warehouse, database, schema, password. BigQuery: credentials_json, project_id, dataset_location."
|
|
319
|
+
),
|
|
320
|
+
tenancySettings: z2.object({
|
|
321
|
+
tenancyLevel: z2.enum(["TABLE", "DATABASE"]).optional(),
|
|
322
|
+
clientColumnType: z2.string().optional(),
|
|
323
|
+
schemaName: z2.string().optional(),
|
|
324
|
+
tableName: z2.string().optional(),
|
|
325
|
+
tableClientNameColumn: z2.string().optional(),
|
|
326
|
+
tablePrimaryKeyColumn: z2.string().optional()
|
|
327
|
+
}).optional().describe("Multi-tenant configuration")
|
|
328
|
+
};
|
|
329
|
+
function registerCreateDatasource(server, client) {
|
|
330
|
+
server.tool(
|
|
331
|
+
"create_datasource",
|
|
332
|
+
"Connect a new database to Databrain. Supports Postgres, Snowflake, BigQuery, MySQL, Redshift, and more.",
|
|
333
|
+
schema,
|
|
334
|
+
async (params) => {
|
|
335
|
+
try {
|
|
336
|
+
const body = {
|
|
337
|
+
datasourceType: params.datasourceType,
|
|
338
|
+
...params.credentials
|
|
339
|
+
};
|
|
340
|
+
if (params.tenancySettings) {
|
|
341
|
+
body.tenancySettings = params.tenancySettings;
|
|
342
|
+
}
|
|
343
|
+
const result = await client.request({
|
|
344
|
+
method: "POST",
|
|
345
|
+
path: "/api/v2/datasource/",
|
|
346
|
+
body,
|
|
347
|
+
auth: "service",
|
|
348
|
+
invalidateCache: ["list_datasources"]
|
|
349
|
+
});
|
|
350
|
+
return toolSuccess(
|
|
351
|
+
`Datasource "${result.name}" created successfully.
|
|
352
|
+
|
|
353
|
+
Next steps:
|
|
354
|
+
1. Use \`sync_datasource\` to refresh the schema metadata
|
|
355
|
+
2. Use \`create_data_app\` to create a data app
|
|
356
|
+
3. Use \`create_datamart\` to select tables for analytics`
|
|
357
|
+
);
|
|
358
|
+
} catch (err) {
|
|
359
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
360
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// src/tools/infrastructure/sync-datasource.ts
|
|
367
|
+
import { z as z3 } from "zod";
|
|
368
|
+
function registerSyncDatasource(server, client) {
|
|
369
|
+
server.tool(
|
|
370
|
+
"sync_datasource",
|
|
371
|
+
"Trigger a datasource schema sync to refresh table and column metadata.",
|
|
372
|
+
{
|
|
373
|
+
datasourceName: z3.string().describe("Name of the datasource to sync")
|
|
374
|
+
},
|
|
375
|
+
async (params) => {
|
|
376
|
+
try {
|
|
377
|
+
const result = await client.request({
|
|
378
|
+
method: "POST",
|
|
379
|
+
path: "/api/v2/datasource/sync",
|
|
380
|
+
body: { datasourceName: params.datasourceName },
|
|
381
|
+
auth: "service"
|
|
382
|
+
});
|
|
383
|
+
return toolSuccess(
|
|
384
|
+
`Datasource "${params.datasourceName}" sync triggered.
|
|
385
|
+
Status: ${result.status || "initiated"}
|
|
386
|
+
` + (result.message ? `Message: ${result.message}` : "") + "\n\nThe schema metadata will be refreshed. You can now use `create_datamart` to select tables."
|
|
387
|
+
);
|
|
388
|
+
} catch (err) {
|
|
389
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
390
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/tools/data-app/create-data-app.ts
|
|
397
|
+
import { z as z4 } from "zod";
|
|
295
398
|
function registerCreateDataApp(server, client) {
|
|
296
399
|
server.tool(
|
|
297
400
|
"create_data_app",
|
|
298
401
|
"Create a new data app \u2014 a logical container for embeds, tokens, and dashboards.",
|
|
299
402
|
{
|
|
300
|
-
name:
|
|
403
|
+
name: z4.string().describe("Unique alphanumeric name for the data app")
|
|
301
404
|
},
|
|
302
405
|
async (params) => {
|
|
303
406
|
try {
|
|
@@ -324,13 +427,13 @@ Next steps:
|
|
|
324
427
|
}
|
|
325
428
|
|
|
326
429
|
// src/tools/data-app/list-data-apps.ts
|
|
327
|
-
import { z as
|
|
430
|
+
import { z as z5 } from "zod";
|
|
328
431
|
function registerListDataApps(server, client) {
|
|
329
432
|
server.tool(
|
|
330
433
|
"list_data_apps",
|
|
331
434
|
"List all data apps in your Databrain organization.",
|
|
332
435
|
{
|
|
333
|
-
workspaceName:
|
|
436
|
+
workspaceName: z5.string().optional().describe("Filter by workspace name")
|
|
334
437
|
},
|
|
335
438
|
async (params) => {
|
|
336
439
|
try {
|
|
@@ -364,17 +467,35 @@ ${formatted}`);
|
|
|
364
467
|
}
|
|
365
468
|
|
|
366
469
|
// src/tools/data-app/create-api-token.ts
|
|
367
|
-
import { z as
|
|
470
|
+
import { z as z6 } from "zod";
|
|
368
471
|
function registerCreateApiToken(server, client) {
|
|
369
472
|
server.tool(
|
|
370
473
|
"create_api_token",
|
|
371
|
-
"Create an API token scoped to a data app.
|
|
474
|
+
"Create an API token scoped to a data app. There should be ONE API token per data app \u2014 do not create duplicates. Call `list_api_tokens` first to check if one already exists. If it does, use `set_api_token` instead.",
|
|
372
475
|
{
|
|
373
|
-
dataAppName:
|
|
374
|
-
name:
|
|
476
|
+
dataAppName: z6.string().describe("Name of the data app to scope the token to"),
|
|
477
|
+
name: z6.string().describe('Descriptive label for the token (e.g., "Production API Key")')
|
|
375
478
|
},
|
|
376
479
|
async (params) => {
|
|
377
480
|
try {
|
|
481
|
+
const existing = await client.request({
|
|
482
|
+
method: "GET",
|
|
483
|
+
path: "/api/v2/data-app/api-tokens",
|
|
484
|
+
auth: "service",
|
|
485
|
+
query: { dataAppName: params.dataAppName }
|
|
486
|
+
});
|
|
487
|
+
if (Array.isArray(existing) && existing.length > 0) {
|
|
488
|
+
const tokenNames = existing.map((t) => t.name || "Unnamed").join(", ");
|
|
489
|
+
return toolError(
|
|
490
|
+
`An API token already exists for "${params.dataAppName}": ${tokenNames}
|
|
491
|
+
|
|
492
|
+
There should be **one API token per data app** \u2014 do not create duplicates.
|
|
493
|
+
|
|
494
|
+
Options:
|
|
495
|
+
1. If you have the token \u2192 use \`set_api_token\` to configure it for this session
|
|
496
|
+
2. If the token is lost \u2192 use \`rotate_api_token\` to get a new one (old token stops working)`
|
|
497
|
+
);
|
|
498
|
+
}
|
|
378
499
|
const result = await client.request({
|
|
379
500
|
method: "POST",
|
|
380
501
|
path: "/api/v2/data-app/api-tokens",
|
|
@@ -388,8 +509,10 @@ function registerCreateApiToken(server, client) {
|
|
|
388
509
|
**Token:** \`${result.key}\`
|
|
389
510
|
|
|
390
511
|
\u26A0\uFE0F **IMPORTANT:** Save this token securely \u2014 it will not be shown again.
|
|
512
|
+
Store it as \`DATABRAIN_API_TOKEN\` in your backend environment.
|
|
513
|
+
This is the **only** API token needed for this data app \u2014 reuse it across sessions.
|
|
391
514
|
|
|
392
|
-
This token has been set for use in this session.
|
|
515
|
+
This token has been set for use in this session.
|
|
393
516
|
|
|
394
517
|
Next steps:
|
|
395
518
|
1. Use \`create_embed\` to configure an embed
|
|
@@ -404,13 +527,13 @@ Next steps:
|
|
|
404
527
|
}
|
|
405
528
|
|
|
406
529
|
// src/tools/data-app/list-api-tokens.ts
|
|
407
|
-
import { z as
|
|
530
|
+
import { z as z7 } from "zod";
|
|
408
531
|
function registerListApiTokens(server, client) {
|
|
409
532
|
server.tool(
|
|
410
533
|
"list_api_tokens",
|
|
411
534
|
"List API tokens for a data app.",
|
|
412
535
|
{
|
|
413
|
-
dataAppName:
|
|
536
|
+
dataAppName: z7.string().describe("Name of the data app")
|
|
414
537
|
},
|
|
415
538
|
async (params) => {
|
|
416
539
|
try {
|
|
@@ -440,13 +563,13 @@ ${formatted}`);
|
|
|
440
563
|
}
|
|
441
564
|
|
|
442
565
|
// src/tools/data-app/rotate-api-token.ts
|
|
443
|
-
import { z as
|
|
566
|
+
import { z as z8 } from "zod";
|
|
444
567
|
function registerRotateApiToken(server, client) {
|
|
445
568
|
server.tool(
|
|
446
569
|
"rotate_api_token",
|
|
447
570
|
"Rotate an API token for a data app. The old token is immediately invalidated.",
|
|
448
571
|
{
|
|
449
|
-
dataAppName:
|
|
572
|
+
dataAppName: z8.string().describe("Name of the data app")
|
|
450
573
|
},
|
|
451
574
|
async (params) => {
|
|
452
575
|
try {
|
|
@@ -473,14 +596,32 @@ Save it securely \u2014 it will not be shown again.`
|
|
|
473
596
|
);
|
|
474
597
|
}
|
|
475
598
|
|
|
599
|
+
// src/tools/data-app/set-api-token.ts
|
|
600
|
+
import { z as z9 } from "zod";
|
|
601
|
+
function registerSetApiToken(server, client) {
|
|
602
|
+
server.tool(
|
|
603
|
+
"set_api_token",
|
|
604
|
+
"Set an existing API token for this MCP session. Use this when you already have an API token (from a previous `create_api_token` call or from your team) but it is not configured in the DATABRAIN_API_TOKEN environment variable. This unlocks all embed, guest token, and query tools for the session.",
|
|
605
|
+
{
|
|
606
|
+
apiToken: z9.string().describe("The API token to use for this session")
|
|
607
|
+
},
|
|
608
|
+
async (params) => {
|
|
609
|
+
client.setRuntimeApiToken(params.apiToken);
|
|
610
|
+
return toolSuccess(
|
|
611
|
+
"API token set for this session.\n\nYou can now use all embed, guest token, and query tools.\n\nTo avoid this step in future sessions, set `DATABRAIN_API_TOKEN` in your MCP server environment variables."
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
|
|
476
617
|
// src/tools/datamart/list-datamarts.ts
|
|
477
|
-
import { z as
|
|
618
|
+
import { z as z10 } from "zod";
|
|
478
619
|
function registerListDatamarts(server, client) {
|
|
479
620
|
server.tool(
|
|
480
621
|
"list_datamarts",
|
|
481
622
|
"List datamarts with table and schema information.",
|
|
482
623
|
{
|
|
483
|
-
dataAppName:
|
|
624
|
+
dataAppName: z10.string().optional().describe("Filter by data app name")
|
|
484
625
|
},
|
|
485
626
|
async (params) => {
|
|
486
627
|
try {
|
|
@@ -515,14 +656,86 @@ ${formatted}`);
|
|
|
515
656
|
);
|
|
516
657
|
}
|
|
517
658
|
|
|
659
|
+
// src/tools/datamart/create-datamart.ts
|
|
660
|
+
import { z as z11 } from "zod";
|
|
661
|
+
var columnSchema = z11.object({
|
|
662
|
+
name: z11.string(),
|
|
663
|
+
alias: z11.string().optional(),
|
|
664
|
+
label: z11.string().optional(),
|
|
665
|
+
isHide: z11.boolean().optional(),
|
|
666
|
+
isCustomColumn: z11.boolean().optional(),
|
|
667
|
+
sql: z11.string().optional()
|
|
668
|
+
});
|
|
669
|
+
var tableSchema = z11.object({
|
|
670
|
+
name: z11.string(),
|
|
671
|
+
schemaName: z11.string().optional(),
|
|
672
|
+
clientColumn: z11.string().optional(),
|
|
673
|
+
label: z11.string().optional(),
|
|
674
|
+
columns: z11.array(columnSchema)
|
|
675
|
+
});
|
|
676
|
+
var relationshipSchema = z11.object({
|
|
677
|
+
parentTableName: z11.string(),
|
|
678
|
+
parentColumnName: z11.string(),
|
|
679
|
+
childTableName: z11.string(),
|
|
680
|
+
childColumnName: z11.string(),
|
|
681
|
+
cardinality: z11.enum(["OneToOne", "OneToMany", "ManyToOne", "ManyToMany"]),
|
|
682
|
+
join: z11.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN"]),
|
|
683
|
+
relationshipName: z11.string()
|
|
684
|
+
});
|
|
685
|
+
function registerCreateDatamart(server, client) {
|
|
686
|
+
server.tool(
|
|
687
|
+
"create_datamart",
|
|
688
|
+
"Create a datamart \u2014 a curated subset of tables for analytics. Select tables, columns, and define relationships.",
|
|
689
|
+
{
|
|
690
|
+
name: z11.string().describe("Unique datamart name"),
|
|
691
|
+
datasourceName: z11.string().describe("Name of the datasource to use"),
|
|
692
|
+
tableList: z11.array(tableSchema).describe("Array of tables with their columns"),
|
|
693
|
+
relationships: z11.array(relationshipSchema).optional().describe("Join definitions between tables"),
|
|
694
|
+
tenancySettings: z11.object({
|
|
695
|
+
tenancyLevel: z11.enum(["TABLE", "DATABASE"]).optional(),
|
|
696
|
+
clientColumnType: z11.string().optional(),
|
|
697
|
+
schemaName: z11.string().optional(),
|
|
698
|
+
tableName: z11.string().optional(),
|
|
699
|
+
tableClientNameColumn: z11.string().optional(),
|
|
700
|
+
tablePrimaryKeyColumn: z11.string().optional()
|
|
701
|
+
}).optional().describe("Multi-tenant configuration")
|
|
702
|
+
},
|
|
703
|
+
async (params) => {
|
|
704
|
+
try {
|
|
705
|
+
const result = await client.request({
|
|
706
|
+
method: "POST",
|
|
707
|
+
path: "/api/v2/data-app/datamarts",
|
|
708
|
+
body: params,
|
|
709
|
+
auth: "service",
|
|
710
|
+
invalidateCache: ["list_datamarts"]
|
|
711
|
+
});
|
|
712
|
+
if (result.error) {
|
|
713
|
+
return toolError(`Datamart creation failed: ${result.error}`);
|
|
714
|
+
}
|
|
715
|
+
return toolSuccess(
|
|
716
|
+
`Datamart "${params.name}" created successfully (ID: ${result.id}).
|
|
717
|
+
|
|
718
|
+
Next steps:
|
|
719
|
+
1. Use \`generate_semantic_layer\` to make this datamart AI-ready (recommended for AI chat mode)
|
|
720
|
+
2. Create a dashboard in the Databrain UI using this datamart
|
|
721
|
+
3. Use \`create_embed\` to embed the dashboard`
|
|
722
|
+
);
|
|
723
|
+
} catch (err) {
|
|
724
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
725
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
|
|
518
731
|
// src/tools/datamart/get-semantic-layer-status.ts
|
|
519
|
-
import { z as
|
|
732
|
+
import { z as z12 } from "zod";
|
|
520
733
|
function registerGetSemanticLayerStatus(server, client) {
|
|
521
734
|
server.tool(
|
|
522
735
|
"get_semantic_layer_status",
|
|
523
736
|
"Check progress of a semantic layer generation job.",
|
|
524
737
|
{
|
|
525
|
-
datamartId:
|
|
738
|
+
datamartId: z12.string().describe("ID of the datamart")
|
|
526
739
|
},
|
|
527
740
|
async (params) => {
|
|
528
741
|
try {
|
|
@@ -552,24 +765,55 @@ Progress: ${progress}
|
|
|
552
765
|
);
|
|
553
766
|
}
|
|
554
767
|
|
|
768
|
+
// src/tools/datamart/generate-semantic-layer.ts
|
|
769
|
+
import { z as z13 } from "zod";
|
|
770
|
+
function registerGenerateSemanticLayer(server, client) {
|
|
771
|
+
server.tool(
|
|
772
|
+
"generate_semantic_layer",
|
|
773
|
+
"Trigger AI-powered semantic layer auto-generation for a datamart. Required for AI chat mode to work well. This is an async job \u2014 use `get_semantic_layer_status` to check progress.",
|
|
774
|
+
{
|
|
775
|
+
datamartId: z13.string().describe("ID of the datamart to generate semantic layer for")
|
|
776
|
+
},
|
|
777
|
+
async (params) => {
|
|
778
|
+
try {
|
|
779
|
+
const result = await client.request({
|
|
780
|
+
method: "POST",
|
|
781
|
+
path: `/api/schema/datamart/${params.datamartId}/semantic-layer/auto-generate`,
|
|
782
|
+
body: {},
|
|
783
|
+
auth: "service"
|
|
784
|
+
});
|
|
785
|
+
return toolSuccess(
|
|
786
|
+
"Semantic layer generation started.\n\n" + (result.jobId ? `Job ID: ${result.jobId}
|
|
787
|
+
` : "") + `Status: ${result.status || "initiated"}
|
|
788
|
+
|
|
789
|
+
Use \`get_semantic_layer_status\` to check progress. This may take a few minutes depending on the number of tables and columns.`
|
|
790
|
+
);
|
|
791
|
+
} catch (err) {
|
|
792
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
793
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
|
|
555
799
|
// src/tools/embed/create-embed.ts
|
|
556
|
-
import { z as
|
|
557
|
-
var accessSettingsSchema =
|
|
558
|
-
datamartName:
|
|
559
|
-
isAllowAiPilot:
|
|
560
|
-
isAllowEmailReports:
|
|
561
|
-
isAllowManageMetrics:
|
|
562
|
-
isAllowCreateDashboardView:
|
|
563
|
-
isAllowMetricCreation:
|
|
564
|
-
isAllowMetricDeletion:
|
|
565
|
-
isAllowMetricLayoutChange:
|
|
566
|
-
isAllowMetricUpdate:
|
|
567
|
-
isAllowUnderlyingData:
|
|
568
|
-
metricCreationMode:
|
|
569
|
-
joinModel:
|
|
570
|
-
tableTenancySettings:
|
|
571
|
-
name:
|
|
572
|
-
clientColumn:
|
|
800
|
+
import { z as z14 } from "zod";
|
|
801
|
+
var accessSettingsSchema = z14.object({
|
|
802
|
+
datamartName: z14.string(),
|
|
803
|
+
isAllowAiPilot: z14.boolean().optional().default(false),
|
|
804
|
+
isAllowEmailReports: z14.boolean(),
|
|
805
|
+
isAllowManageMetrics: z14.boolean(),
|
|
806
|
+
isAllowCreateDashboardView: z14.boolean(),
|
|
807
|
+
isAllowMetricCreation: z14.boolean(),
|
|
808
|
+
isAllowMetricDeletion: z14.boolean(),
|
|
809
|
+
isAllowMetricLayoutChange: z14.boolean(),
|
|
810
|
+
isAllowMetricUpdate: z14.boolean(),
|
|
811
|
+
isAllowUnderlyingData: z14.boolean(),
|
|
812
|
+
metricCreationMode: z14.enum(["DRAG_DROP", "CHAT"]),
|
|
813
|
+
joinModel: z14.enum(["single", "multi"]).optional().default("multi"),
|
|
814
|
+
tableTenancySettings: z14.array(z14.object({
|
|
815
|
+
name: z14.string(),
|
|
816
|
+
clientColumn: z14.string()
|
|
573
817
|
})).optional()
|
|
574
818
|
});
|
|
575
819
|
function registerCreateEmbed(server, client) {
|
|
@@ -577,11 +821,11 @@ function registerCreateEmbed(server, client) {
|
|
|
577
821
|
"create_embed",
|
|
578
822
|
"Create an embed configuration for a dashboard or metric. Requires an API token.",
|
|
579
823
|
{
|
|
580
|
-
dashboardId:
|
|
581
|
-
embedType:
|
|
582
|
-
workspaceName:
|
|
583
|
-
metricId:
|
|
584
|
-
name:
|
|
824
|
+
dashboardId: z14.string().describe("ID of the dashboard to embed"),
|
|
825
|
+
embedType: z14.enum(["dashboard", "metric"]).describe("Type of embed"),
|
|
826
|
+
workspaceName: z14.string().describe("Workspace name"),
|
|
827
|
+
metricId: z14.string().optional().describe('Required if embedType is "metric"'),
|
|
828
|
+
name: z14.string().optional().describe("Human-readable name for the embed"),
|
|
585
829
|
accessSettings: accessSettingsSchema.describe("Access and permission settings for the embed")
|
|
586
830
|
},
|
|
587
831
|
async (params) => {
|
|
@@ -612,13 +856,13 @@ Next steps:
|
|
|
612
856
|
}
|
|
613
857
|
|
|
614
858
|
// src/tools/embed/list-embeds.ts
|
|
615
|
-
import { z as
|
|
859
|
+
import { z as z15 } from "zod";
|
|
616
860
|
function registerListEmbeds(server, client) {
|
|
617
861
|
server.tool(
|
|
618
862
|
"list_embeds",
|
|
619
863
|
"List all embed configurations. Requires an API token.",
|
|
620
864
|
{
|
|
621
|
-
dataAppName:
|
|
865
|
+
dataAppName: z15.string().optional().describe("Filter by data app name")
|
|
622
866
|
},
|
|
623
867
|
async (params) => {
|
|
624
868
|
try {
|
|
@@ -656,13 +900,13 @@ ${formatted}`);
|
|
|
656
900
|
}
|
|
657
901
|
|
|
658
902
|
// src/tools/embed/get-embed-details.ts
|
|
659
|
-
import { z as
|
|
903
|
+
import { z as z16 } from "zod";
|
|
660
904
|
function registerGetEmbedDetails(server, client) {
|
|
661
905
|
server.tool(
|
|
662
906
|
"get_embed_details",
|
|
663
907
|
"Get detailed configuration for a specific embed, including all access settings.",
|
|
664
908
|
{
|
|
665
|
-
embedId:
|
|
909
|
+
embedId: z16.string().describe("ID of the embed")
|
|
666
910
|
},
|
|
667
911
|
async (params) => {
|
|
668
912
|
try {
|
|
@@ -687,26 +931,43 @@ function registerGetEmbedDetails(server, client) {
|
|
|
687
931
|
}
|
|
688
932
|
|
|
689
933
|
// src/tools/embed/update-embed.ts
|
|
690
|
-
import { z as
|
|
934
|
+
import { z as z17 } from "zod";
|
|
935
|
+
|
|
936
|
+
// src/tools/embed/helpers.ts
|
|
937
|
+
var GUEST_TOKEN_REMINDER = "\n\n\u26A0\uFE0F **Guest tokens must be regenerated** for these changes to take effect. Existing guest tokens still carry the old settings. Call `generate_guest_token` to create a new token with the updated configuration.";
|
|
938
|
+
async function getEmbedForUpdate(client, embedId) {
|
|
939
|
+
const existing = await client.request({
|
|
940
|
+
method: "GET",
|
|
941
|
+
path: "/api/v2/data-app/embeds",
|
|
942
|
+
auth: "api",
|
|
943
|
+
query: { embedId }
|
|
944
|
+
});
|
|
945
|
+
return {
|
|
946
|
+
embedId,
|
|
947
|
+
accessSettings: existing.accessSettings ?? {}
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// src/tools/embed/update-embed.ts
|
|
691
952
|
function registerUpdateEmbed(server, client) {
|
|
692
953
|
server.tool(
|
|
693
954
|
"update_embed",
|
|
694
955
|
"Update embed configuration \u2014 access settings, permissions, theme, or name.",
|
|
695
956
|
{
|
|
696
|
-
embedId:
|
|
697
|
-
name:
|
|
698
|
-
accessSettings:
|
|
957
|
+
embedId: z17.string().describe("ID of the embed to update"),
|
|
958
|
+
name: z17.string().optional().describe("New name for the embed"),
|
|
959
|
+
accessSettings: z17.record(z17.unknown()).optional().describe(
|
|
699
960
|
"Partial update of access settings. See `get_embed_details` for current settings."
|
|
700
961
|
),
|
|
701
|
-
theme:
|
|
962
|
+
theme: z17.record(z17.unknown()).optional().describe(
|
|
702
963
|
"Theme configuration object. See databrain://theme-reference for the full schema."
|
|
703
964
|
)
|
|
704
965
|
},
|
|
705
966
|
async (params) => {
|
|
706
967
|
try {
|
|
707
|
-
const body =
|
|
968
|
+
const body = await getEmbedForUpdate(client, params.embedId);
|
|
708
969
|
if (params.name) body.name = params.name;
|
|
709
|
-
if (params.accessSettings) body.accessSettings = params.accessSettings;
|
|
970
|
+
if (params.accessSettings) body.accessSettings = { ...body.accessSettings, ...params.accessSettings };
|
|
710
971
|
if (params.theme) body.theme = params.theme;
|
|
711
972
|
const result = await client.request({
|
|
712
973
|
method: "PUT",
|
|
@@ -718,7 +979,7 @@ function registerUpdateEmbed(server, client) {
|
|
|
718
979
|
if (result.error) {
|
|
719
980
|
return toolError(`Embed update failed: ${stringify(result.error)}`);
|
|
720
981
|
}
|
|
721
|
-
return toolSuccess(`Embed "${params.embedId}" updated successfully.`);
|
|
982
|
+
return toolSuccess(`Embed "${params.embedId}" updated successfully.` + GUEST_TOKEN_REMINDER);
|
|
722
983
|
} catch (err) {
|
|
723
984
|
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
724
985
|
return toolError(`Unexpected error: ${err.message}`);
|
|
@@ -728,13 +989,13 @@ function registerUpdateEmbed(server, client) {
|
|
|
728
989
|
}
|
|
729
990
|
|
|
730
991
|
// src/tools/embed/delete-embed.ts
|
|
731
|
-
import { z as
|
|
992
|
+
import { z as z18 } from "zod";
|
|
732
993
|
function registerDeleteEmbed(server, client) {
|
|
733
994
|
server.tool(
|
|
734
995
|
"delete_embed",
|
|
735
996
|
"Delete an embed configuration. This is irreversible.",
|
|
736
997
|
{
|
|
737
|
-
embedId:
|
|
998
|
+
embedId: z18.string().describe("ID of the embed to delete")
|
|
738
999
|
},
|
|
739
1000
|
async (params) => {
|
|
740
1001
|
try {
|
|
@@ -754,14 +1015,14 @@ function registerDeleteEmbed(server, client) {
|
|
|
754
1015
|
}
|
|
755
1016
|
|
|
756
1017
|
// src/tools/embed/rename-embed.ts
|
|
757
|
-
import { z as
|
|
1018
|
+
import { z as z19 } from "zod";
|
|
758
1019
|
function registerRenameEmbed(server, client) {
|
|
759
1020
|
server.tool(
|
|
760
1021
|
"rename_embed",
|
|
761
1022
|
"Rename an embed configuration.",
|
|
762
1023
|
{
|
|
763
|
-
embedId:
|
|
764
|
-
name:
|
|
1024
|
+
embedId: z19.string().describe("ID of the embed to rename"),
|
|
1025
|
+
name: z19.string().describe("New name for the embed")
|
|
765
1026
|
},
|
|
766
1027
|
async (params) => {
|
|
767
1028
|
try {
|
|
@@ -782,7 +1043,7 @@ function registerRenameEmbed(server, client) {
|
|
|
782
1043
|
}
|
|
783
1044
|
|
|
784
1045
|
// src/tools/embed/apply-theme-preset.ts
|
|
785
|
-
import { z as
|
|
1046
|
+
import { z as z20 } from "zod";
|
|
786
1047
|
var THEME_PRESETS = {
|
|
787
1048
|
light: {
|
|
788
1049
|
general: {
|
|
@@ -928,25 +1189,760 @@ var ACCESS_PRESETS = {
|
|
|
928
1189
|
};
|
|
929
1190
|
function registerApplyThemePreset(server, client) {
|
|
930
1191
|
server.tool(
|
|
931
|
-
"apply_embed_preset",
|
|
932
|
-
`Apply a theme and/or access preset to an embed. Theme presets: ${Object.keys(THEME_PRESETS).join(", ")}. Access presets: ${Object.keys(ACCESS_PRESETS).join(", ")}. You can apply one or both.`,
|
|
1192
|
+
"apply_embed_preset",
|
|
1193
|
+
`Apply a theme and/or access preset to an embed. Theme presets: ${Object.keys(THEME_PRESETS).join(", ")}. Access presets: ${Object.keys(ACCESS_PRESETS).join(", ")}. You can apply one or both.`,
|
|
1194
|
+
{
|
|
1195
|
+
embedId: z20.string().describe("ID of the embed to update"),
|
|
1196
|
+
themePreset: z20.enum(["light", "dark", "corporate", "minimal"]).optional().describe("Theme preset to apply"),
|
|
1197
|
+
accessPreset: z20.enum(["view-only", "power-user", "export-only", "ai-enabled"]).optional().describe("Access settings preset to apply")
|
|
1198
|
+
},
|
|
1199
|
+
async (params) => {
|
|
1200
|
+
if (!params.themePreset && !params.accessPreset) {
|
|
1201
|
+
return toolError("Provide at least one of themePreset or accessPreset.");
|
|
1202
|
+
}
|
|
1203
|
+
try {
|
|
1204
|
+
const body = await getEmbedForUpdate(client, params.embedId);
|
|
1205
|
+
if (params.themePreset) {
|
|
1206
|
+
body.theme = THEME_PRESETS[params.themePreset];
|
|
1207
|
+
}
|
|
1208
|
+
if (params.accessPreset) {
|
|
1209
|
+
body.accessSettings = { ...body.accessSettings, ...ACCESS_PRESETS[params.accessPreset] };
|
|
1210
|
+
}
|
|
1211
|
+
const result = await client.request({
|
|
1212
|
+
method: "PUT",
|
|
1213
|
+
path: "/api/v2/data-app/embeds",
|
|
1214
|
+
body,
|
|
1215
|
+
auth: "api",
|
|
1216
|
+
invalidateCache: ["list_embeds"]
|
|
1217
|
+
});
|
|
1218
|
+
if (result.error) {
|
|
1219
|
+
return toolError(`Failed to apply preset: ${stringify(result.error)}`);
|
|
1220
|
+
}
|
|
1221
|
+
const applied = [];
|
|
1222
|
+
if (params.themePreset) applied.push(`theme: **${params.themePreset}**`);
|
|
1223
|
+
if (params.accessPreset) applied.push(`access: **${params.accessPreset}**`);
|
|
1224
|
+
let details = "";
|
|
1225
|
+
if (params.accessPreset) {
|
|
1226
|
+
const settings = ACCESS_PRESETS[params.accessPreset];
|
|
1227
|
+
const enabled = Object.entries(settings).filter(([, v]) => v === true).map(([k]) => k).join(", ");
|
|
1228
|
+
details = `
|
|
1229
|
+
|
|
1230
|
+
Enabled settings: ${enabled || "none"}`;
|
|
1231
|
+
if (settings.metricCreationMode) {
|
|
1232
|
+
details += `
|
|
1233
|
+
Metric creation mode: ${settings.metricCreationMode}`;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return toolSuccess(
|
|
1237
|
+
`Preset applied to embed "${params.embedId}": ${applied.join(", ")}.${details}` + GUEST_TOKEN_REMINDER
|
|
1238
|
+
);
|
|
1239
|
+
} catch (err) {
|
|
1240
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
1241
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// src/tools/embed/customize-theme.ts
|
|
1248
|
+
import { z as z21 } from "zod";
|
|
1249
|
+
var fontFamilyEnum = z21.enum([
|
|
1250
|
+
"Inter",
|
|
1251
|
+
"Roboto",
|
|
1252
|
+
"Open Sans",
|
|
1253
|
+
"Lato",
|
|
1254
|
+
"Poppins",
|
|
1255
|
+
"Montserrat",
|
|
1256
|
+
"IBM Plex Sans",
|
|
1257
|
+
"Source Sans Pro",
|
|
1258
|
+
"Nunito",
|
|
1259
|
+
"system-ui"
|
|
1260
|
+
]).describe("Font family");
|
|
1261
|
+
var tooltipFontSchema = z21.object({
|
|
1262
|
+
fontFamily: z21.string().optional(),
|
|
1263
|
+
fontSize: z21.number().optional(),
|
|
1264
|
+
fontWeight: z21.number().optional(),
|
|
1265
|
+
fontColor: z21.string().optional()
|
|
1266
|
+
}).optional();
|
|
1267
|
+
function registerCustomizeTheme(server, client) {
|
|
1268
|
+
server.tool(
|
|
1269
|
+
"customize_embed_theme",
|
|
1270
|
+
"Customize the visual theme of an embed \u2014 colors, typography, card styling, dashboard layout, and chart appearance. Apply granular theming without needing to know the full JSON schema.",
|
|
1271
|
+
{
|
|
1272
|
+
embedId: z21.string().describe("ID of the embed to theme"),
|
|
1273
|
+
// General
|
|
1274
|
+
fontFamily: fontFamilyEnum.optional().describe("Global font family"),
|
|
1275
|
+
datePickerFormat: z21.string().optional().describe("Date picker display format"),
|
|
1276
|
+
// Colors
|
|
1277
|
+
primaryColor: z21.string().optional().describe("Primary accent color (hex)"),
|
|
1278
|
+
backgroundColor: z21.string().optional().describe("Overall background color (hex)"),
|
|
1279
|
+
textColor: z21.string().optional().describe("Default text color (hex)"),
|
|
1280
|
+
ctaColor: z21.string().optional().describe("Call-to-action button color (hex)"),
|
|
1281
|
+
ctaTextColor: z21.string().optional().describe("CTA button text color (hex)"),
|
|
1282
|
+
// Dashboard
|
|
1283
|
+
dashboardBackgroundColor: z21.string().optional().describe("Dashboard container background color (hex)"),
|
|
1284
|
+
selectBoxSize: z21.enum(["small", "medium", "large"]).optional().describe("Filter select box size"),
|
|
1285
|
+
selectBoxVariant: z21.enum(["floating", "outlined", "standard"]).optional().describe("Filter select box style"),
|
|
1286
|
+
selectBoxBorderRadius: z21.string().optional().describe('Select box border radius (e.g. "20px")'),
|
|
1287
|
+
selectBoxTextColor: z21.string().optional().describe("Select box text color (hex)"),
|
|
1288
|
+
// Card Title
|
|
1289
|
+
cardTitleFontSize: z21.string().optional().describe('Card title font size (e.g. "16px")'),
|
|
1290
|
+
cardTitleFontWeight: z21.string().optional().describe('Card title font weight (e.g. "600")'),
|
|
1291
|
+
cardTitleColor: z21.string().optional().describe("Card title text color (hex)"),
|
|
1292
|
+
// Card Description
|
|
1293
|
+
cardDescriptionFontSize: z21.string().optional().describe('Card description font size (e.g. "13px")'),
|
|
1294
|
+
cardDescriptionFontWeight: z21.string().optional().describe('Card description font weight (e.g. "400")'),
|
|
1295
|
+
cardDescriptionColor: z21.string().optional().describe("Card description text color (hex)"),
|
|
1296
|
+
// Card Customization
|
|
1297
|
+
cardPadding: z21.string().optional().describe('Card internal padding (e.g. "20px")'),
|
|
1298
|
+
cardBorderRadius: z21.string().optional().describe('Card border radius (e.g. "12px")'),
|
|
1299
|
+
cardShadow: z21.string().optional().describe("Card box-shadow CSS value"),
|
|
1300
|
+
disableShadowOnHover: z21.boolean().optional().describe("Disable shadow effect on card hover"),
|
|
1301
|
+
disableCardStroke: z21.boolean().optional().describe("Remove card border stroke"),
|
|
1302
|
+
cardStrokeColor: z21.string().optional().describe("Card border stroke color (hex)"),
|
|
1303
|
+
// Chart
|
|
1304
|
+
chartFontFamily: z21.string().optional().describe("Font family for chart text"),
|
|
1305
|
+
chartFontColor: z21.string().optional().describe("Font color for chart text (hex)"),
|
|
1306
|
+
chartColors: z21.array(z21.string()).optional().describe("Array of chart series colors (hex values)"),
|
|
1307
|
+
chartPaletteName: z21.string().optional().describe("Name for a custom palette"),
|
|
1308
|
+
chartPaletteColors: z21.array(z21.string()).optional().describe("9 colors for a custom palette"),
|
|
1309
|
+
// Chart Tooltip
|
|
1310
|
+
tooltipHeader: tooltipFontSchema.describe("Tooltip header font config"),
|
|
1311
|
+
tooltipLabel: tooltipFontSchema.describe("Tooltip label font config"),
|
|
1312
|
+
tooltipValue: tooltipFontSchema.describe("Tooltip value font config"),
|
|
1313
|
+
// Component-level theme (buttons, checkboxes, switches, etc.)
|
|
1314
|
+
buttonPrimaryColor: z21.string().optional().describe("Primary button background color (hex)"),
|
|
1315
|
+
buttonPrimaryTextColor: z21.string().optional().describe("Primary button text color (hex)"),
|
|
1316
|
+
buttonSecondaryColor: z21.string().optional().describe("Secondary button background color (hex)"),
|
|
1317
|
+
buttonSecondaryTextColor: z21.string().optional().describe("Secondary button text color (hex)"),
|
|
1318
|
+
checkboxCheckedColor: z21.string().optional().describe("Checked checkbox color (hex)"),
|
|
1319
|
+
checkboxUncheckedColor: z21.string().optional().describe("Unchecked checkbox color (hex)"),
|
|
1320
|
+
switchEnabledColor: z21.string().optional().describe("Enabled switch color (hex)"),
|
|
1321
|
+
switchDisabledColor: z21.string().optional().describe("Disabled switch color (hex)"),
|
|
1322
|
+
breadcrumbFontFamily: z21.string().optional().describe("Drill breadcrumb font family"),
|
|
1323
|
+
breadcrumbFontColor: z21.string().optional().describe("Drill breadcrumb font color (hex)"),
|
|
1324
|
+
breadcrumbActiveColor: z21.string().optional().describe("Active breadcrumb color (hex)"),
|
|
1325
|
+
filterBadgeColor: z21.string().optional().describe("Multi-select filter badge color (hex)"),
|
|
1326
|
+
filterBadgeTextColor: z21.string().optional().describe("Multi-select filter badge text color (hex)")
|
|
1327
|
+
},
|
|
1328
|
+
async (params) => {
|
|
1329
|
+
const { embedId, ...themeParams } = params;
|
|
1330
|
+
const adminTheme = {};
|
|
1331
|
+
const general = {};
|
|
1332
|
+
if (themeParams.fontFamily) general.fontFamily = themeParams.fontFamily;
|
|
1333
|
+
if (themeParams.datePickerFormat) general.datePickerFormat = themeParams.datePickerFormat;
|
|
1334
|
+
if (Object.keys(general).length) adminTheme.general = general;
|
|
1335
|
+
const dashboard = {};
|
|
1336
|
+
if (themeParams.dashboardBackgroundColor) dashboard.backgroundColor = themeParams.dashboardBackgroundColor;
|
|
1337
|
+
if (themeParams.ctaColor) dashboard.ctaColor = themeParams.ctaColor;
|
|
1338
|
+
if (themeParams.ctaTextColor) dashboard.ctaTextColor = themeParams.ctaTextColor;
|
|
1339
|
+
if (themeParams.selectBoxSize) dashboard.selectBoxSize = themeParams.selectBoxSize;
|
|
1340
|
+
if (themeParams.selectBoxVariant) dashboard.selectBoxVariant = themeParams.selectBoxVariant;
|
|
1341
|
+
if (themeParams.selectBoxBorderRadius) dashboard.selectBoxBorderRadius = themeParams.selectBoxBorderRadius;
|
|
1342
|
+
if (themeParams.selectBoxTextColor) dashboard.selectBoxTextColor = themeParams.selectBoxTextColor;
|
|
1343
|
+
if (Object.keys(dashboard).length) adminTheme.dashboard = dashboard;
|
|
1344
|
+
const cardTitle = {};
|
|
1345
|
+
if (themeParams.cardTitleFontSize) cardTitle.fontSize = themeParams.cardTitleFontSize;
|
|
1346
|
+
if (themeParams.cardTitleFontWeight) cardTitle.fontWeight = themeParams.cardTitleFontWeight;
|
|
1347
|
+
if (themeParams.cardTitleColor) cardTitle.color = themeParams.cardTitleColor;
|
|
1348
|
+
if (Object.keys(cardTitle).length) adminTheme.cardTitle = cardTitle;
|
|
1349
|
+
const cardDescription = {};
|
|
1350
|
+
if (themeParams.cardDescriptionFontSize) cardDescription.fontSize = themeParams.cardDescriptionFontSize;
|
|
1351
|
+
if (themeParams.cardDescriptionFontWeight) cardDescription.fontWeight = themeParams.cardDescriptionFontWeight;
|
|
1352
|
+
if (themeParams.cardDescriptionColor) cardDescription.color = themeParams.cardDescriptionColor;
|
|
1353
|
+
if (Object.keys(cardDescription).length) adminTheme.cardDescription = cardDescription;
|
|
1354
|
+
const cardCustomization = {};
|
|
1355
|
+
if (themeParams.cardPadding) cardCustomization.padding = themeParams.cardPadding;
|
|
1356
|
+
if (themeParams.cardBorderRadius) cardCustomization.borderRadius = themeParams.cardBorderRadius;
|
|
1357
|
+
if (themeParams.cardShadow) cardCustomization.shadow = themeParams.cardShadow;
|
|
1358
|
+
if (themeParams.disableShadowOnHover !== void 0) cardCustomization.disableShadowOnHover = themeParams.disableShadowOnHover;
|
|
1359
|
+
if (themeParams.disableCardStroke !== void 0) cardCustomization.disableStroke = themeParams.disableCardStroke;
|
|
1360
|
+
if (themeParams.cardStrokeColor) cardCustomization.metricStrokeColor = themeParams.cardStrokeColor;
|
|
1361
|
+
if (Object.keys(cardCustomization).length) adminTheme.cardCustomization = cardCustomization;
|
|
1362
|
+
const chart = {};
|
|
1363
|
+
if (themeParams.chartFontFamily) chart.chartFontFamily = themeParams.chartFontFamily;
|
|
1364
|
+
if (themeParams.chartFontColor) chart.chartFontColor = themeParams.chartFontColor;
|
|
1365
|
+
if (themeParams.chartColors) chart.paletteOptions = themeParams.chartColors;
|
|
1366
|
+
if (themeParams.chartPaletteName && themeParams.chartPaletteColors) {
|
|
1367
|
+
chart.palettes = [{ name: themeParams.chartPaletteName, colors: themeParams.chartPaletteColors }];
|
|
1368
|
+
chart.selected = themeParams.chartPaletteName;
|
|
1369
|
+
}
|
|
1370
|
+
const tooltip = {};
|
|
1371
|
+
if (themeParams.tooltipHeader) tooltip.header = themeParams.tooltipHeader;
|
|
1372
|
+
if (themeParams.tooltipLabel) tooltip.label = themeParams.tooltipLabel;
|
|
1373
|
+
if (themeParams.tooltipValue) tooltip.value = themeParams.tooltipValue;
|
|
1374
|
+
if (Object.keys(tooltip).length) chart.tooltip = tooltip;
|
|
1375
|
+
if (Object.keys(chart).length) adminTheme.chart = chart;
|
|
1376
|
+
const componentTheme = {};
|
|
1377
|
+
if (themeParams.buttonPrimaryColor || themeParams.buttonPrimaryTextColor || themeParams.buttonSecondaryColor || themeParams.buttonSecondaryTextColor) {
|
|
1378
|
+
componentTheme.button = {
|
|
1379
|
+
...themeParams.buttonPrimaryColor && { primary: themeParams.buttonPrimaryColor },
|
|
1380
|
+
...themeParams.buttonPrimaryTextColor && { primaryText: themeParams.buttonPrimaryTextColor },
|
|
1381
|
+
...themeParams.buttonSecondaryColor && { secondary: themeParams.buttonSecondaryColor },
|
|
1382
|
+
...themeParams.buttonSecondaryTextColor && { secondaryText: themeParams.buttonSecondaryTextColor }
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
if (themeParams.checkboxCheckedColor || themeParams.checkboxUncheckedColor) {
|
|
1386
|
+
componentTheme.checkbox = {
|
|
1387
|
+
...themeParams.checkboxCheckedColor && { checked: themeParams.checkboxCheckedColor },
|
|
1388
|
+
...themeParams.checkboxUncheckedColor && { unChecked: themeParams.checkboxUncheckedColor }
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
if (themeParams.switchEnabledColor || themeParams.switchDisabledColor) {
|
|
1392
|
+
componentTheme.switch = {
|
|
1393
|
+
...themeParams.switchEnabledColor && { enabled: themeParams.switchEnabledColor },
|
|
1394
|
+
...themeParams.switchDisabledColor && { disabled: themeParams.switchDisabledColor }
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
if (themeParams.breadcrumbFontFamily || themeParams.breadcrumbFontColor || themeParams.breadcrumbActiveColor) {
|
|
1398
|
+
componentTheme.drillBreadCrumbs = {
|
|
1399
|
+
...themeParams.breadcrumbFontFamily && { fontFamily: themeParams.breadcrumbFontFamily },
|
|
1400
|
+
...themeParams.breadcrumbFontColor && { fontColor: themeParams.breadcrumbFontColor },
|
|
1401
|
+
...themeParams.breadcrumbActiveColor && { activeColor: themeParams.breadcrumbActiveColor }
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
if (themeParams.filterBadgeColor || themeParams.filterBadgeTextColor) {
|
|
1405
|
+
componentTheme.multiSelectFilterDropdown = {
|
|
1406
|
+
...themeParams.filterBadgeColor && { badgeColor: themeParams.filterBadgeColor },
|
|
1407
|
+
...themeParams.filterBadgeTextColor && { badgeTextColor: themeParams.filterBadgeTextColor }
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
const body = await getEmbedForUpdate(client, embedId);
|
|
1411
|
+
if (Object.keys(adminTheme).length) body.adminThemeOptions = adminTheme;
|
|
1412
|
+
if (Object.keys(componentTheme).length) body.theme = componentTheme;
|
|
1413
|
+
if (themeParams.primaryColor) {
|
|
1414
|
+
if (!body.adminThemeOptions) body.adminThemeOptions = {};
|
|
1415
|
+
const g = body.adminThemeOptions.general || {};
|
|
1416
|
+
g.primaryColor = themeParams.primaryColor;
|
|
1417
|
+
body.adminThemeOptions.general = g;
|
|
1418
|
+
}
|
|
1419
|
+
if (themeParams.backgroundColor) {
|
|
1420
|
+
if (!body.adminThemeOptions) body.adminThemeOptions = {};
|
|
1421
|
+
const g = body.adminThemeOptions.general || {};
|
|
1422
|
+
g.backgroundColor = themeParams.backgroundColor;
|
|
1423
|
+
body.adminThemeOptions.general = g;
|
|
1424
|
+
}
|
|
1425
|
+
if (themeParams.textColor) {
|
|
1426
|
+
if (!body.adminThemeOptions) body.adminThemeOptions = {};
|
|
1427
|
+
const g = body.adminThemeOptions.general || {};
|
|
1428
|
+
g.textColor = themeParams.textColor;
|
|
1429
|
+
body.adminThemeOptions.general = g;
|
|
1430
|
+
}
|
|
1431
|
+
if (Object.keys(body).length <= 1) {
|
|
1432
|
+
return toolError("No theme properties provided. Specify at least one property to customize.");
|
|
1433
|
+
}
|
|
1434
|
+
try {
|
|
1435
|
+
const result = await client.request({
|
|
1436
|
+
method: "PUT",
|
|
1437
|
+
path: "/api/v2/data-app/embeds",
|
|
1438
|
+
body,
|
|
1439
|
+
auth: "api",
|
|
1440
|
+
invalidateCache: ["list_embeds"]
|
|
1441
|
+
});
|
|
1442
|
+
if (result.error) {
|
|
1443
|
+
return toolError(`Theme update failed: ${stringify(result.error)}`);
|
|
1444
|
+
}
|
|
1445
|
+
const sections = [];
|
|
1446
|
+
if (Object.keys(adminTheme).length) sections.push(`admin theme (${Object.keys(adminTheme).join(", ")})`);
|
|
1447
|
+
if (Object.keys(componentTheme).length) sections.push(`component theme (${Object.keys(componentTheme).join(", ")})`);
|
|
1448
|
+
return toolSuccess(
|
|
1449
|
+
`Theme updated for embed "${embedId}".
|
|
1450
|
+
|
|
1451
|
+
**Updated sections:** ${sections.join(", ")}
|
|
1452
|
+
|
|
1453
|
+
Use \`get_embed_details\` to view the full current theme configuration.` + GUEST_TOKEN_REMINDER
|
|
1454
|
+
);
|
|
1455
|
+
} catch (err) {
|
|
1456
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
1457
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// src/tools/embed/configure-chart-appearance.ts
|
|
1464
|
+
import { z as z22 } from "zod";
|
|
1465
|
+
var fontConfigSchema = z22.object({
|
|
1466
|
+
fontSize: z22.number().optional(),
|
|
1467
|
+
fontFamily: z22.string().optional(),
|
|
1468
|
+
fontWeight: z22.number().optional(),
|
|
1469
|
+
color: z22.string().optional()
|
|
1470
|
+
}).optional();
|
|
1471
|
+
var axisSchema = z22.object({
|
|
1472
|
+
hideAxisLines: z22.boolean().optional().describe("Hide axis lines"),
|
|
1473
|
+
hideSplitLines: z22.boolean().optional().describe("Hide grid/split lines"),
|
|
1474
|
+
hideAxisLabels: z22.boolean().optional().describe("Hide axis labels"),
|
|
1475
|
+
hideAxisTicks: z22.boolean().optional().describe("Hide axis tick marks"),
|
|
1476
|
+
axisName: z22.string().optional().describe("Axis title text"),
|
|
1477
|
+
axisNameOffset: z22.number().optional().describe("Axis title offset from axis"),
|
|
1478
|
+
fontSize: z22.number().optional().describe("Axis label font size"),
|
|
1479
|
+
fontFamily: z22.string().optional().describe("Axis label font family"),
|
|
1480
|
+
fontWeight: z22.number().optional().describe("Axis label font weight"),
|
|
1481
|
+
color: z22.string().optional().describe("Axis label color (hex)"),
|
|
1482
|
+
axisColor: z22.string().optional().describe("Axis line color (hex)"),
|
|
1483
|
+
axisNameFontConfig: z22.object({
|
|
1484
|
+
fontFamily: z22.string().optional(),
|
|
1485
|
+
fontSize: z22.number().optional(),
|
|
1486
|
+
fontWeight: z22.number().optional()
|
|
1487
|
+
}).optional().describe("Axis title font configuration")
|
|
1488
|
+
}).optional();
|
|
1489
|
+
function registerConfigureChartAppearance(server, client) {
|
|
1490
|
+
server.tool(
|
|
1491
|
+
"configure_chart_appearance",
|
|
1492
|
+
"Configure granular chart appearance for an embed \u2014 tooltip styles, legend position/shape, axis configuration, label positioning, margins, and colors.",
|
|
1493
|
+
{
|
|
1494
|
+
embedId: z22.string().describe("ID of the embed to configure"),
|
|
1495
|
+
// Chart Colors
|
|
1496
|
+
chartColors: z22.array(z22.string()).optional().describe("Array of chart series colors (hex values)"),
|
|
1497
|
+
// Tooltip
|
|
1498
|
+
tooltipHeaderStyle: fontConfigSchema.describe("Tooltip header font: fontSize, fontFamily, fontWeight, color"),
|
|
1499
|
+
tooltipLabelStyle: fontConfigSchema.describe("Tooltip label font: fontSize, fontFamily, fontWeight, color"),
|
|
1500
|
+
tooltipValueStyle: fontConfigSchema.describe("Tooltip value font: fontSize, fontFamily, fontWeight, color"),
|
|
1501
|
+
// Labels
|
|
1502
|
+
labelPosition: z22.enum(["hidden", "top", "left", "right", "bottom", "inside"]).optional().describe("Data label position on charts"),
|
|
1503
|
+
radialLabelPosition: z22.enum(["outside", "inside"]).optional().describe("Label position for radial/pie charts"),
|
|
1504
|
+
// Margins
|
|
1505
|
+
marginTop: z22.number().optional().describe("Chart top margin in pixels"),
|
|
1506
|
+
marginLeft: z22.number().optional().describe("Chart left margin in pixels"),
|
|
1507
|
+
marginRight: z22.number().optional().describe("Chart right margin in pixels"),
|
|
1508
|
+
marginBottom: z22.number().optional().describe("Chart bottom margin in pixels"),
|
|
1509
|
+
// Legend
|
|
1510
|
+
legendShow: z22.boolean().optional().describe("Show or hide the chart legend"),
|
|
1511
|
+
legendPosition: z22.enum([
|
|
1512
|
+
"top-left",
|
|
1513
|
+
"top-center",
|
|
1514
|
+
"top-right",
|
|
1515
|
+
"left-center",
|
|
1516
|
+
"right-center",
|
|
1517
|
+
"bottom-left",
|
|
1518
|
+
"bottom-center",
|
|
1519
|
+
"bottom-right"
|
|
1520
|
+
]).optional().describe("Fixed legend position"),
|
|
1521
|
+
legendLayout: z22.enum(["horizontal", "vertical"]).optional().describe("Legend layout direction"),
|
|
1522
|
+
legendShape: z22.enum(["circle", "rect", "roundRect", "triangle", "diamond", "arrow", "none"]).optional().describe("Legend icon shape"),
|
|
1523
|
+
legendFontSize: z22.number().optional().describe("Legend font size"),
|
|
1524
|
+
legendFontWeight: z22.number().optional().describe("Legend font weight"),
|
|
1525
|
+
legendFontFamily: z22.string().optional().describe("Legend font family"),
|
|
1526
|
+
legendColor: z22.string().optional().describe("Legend text color (hex)"),
|
|
1527
|
+
legendTruncateLength: z22.number().optional().describe("Max characters before truncating legend labels"),
|
|
1528
|
+
disableLegendScrolling: z22.boolean().optional().describe("Disable scrolling in legend"),
|
|
1529
|
+
enableVariableLegendPosition: z22.boolean().optional().describe("Allow user to drag/reposition legend"),
|
|
1530
|
+
legendTop: z22.number().optional().describe("Custom legend top offset"),
|
|
1531
|
+
legendLeft: z22.number().optional().describe("Custom legend left offset"),
|
|
1532
|
+
// Axes
|
|
1533
|
+
verticalAxis: axisSchema.describe("Vertical (Y) axis configuration"),
|
|
1534
|
+
horizontalAxis: axisSchema.describe("Horizontal (X) axis configuration")
|
|
1535
|
+
},
|
|
1536
|
+
async (params) => {
|
|
1537
|
+
const { embedId, ...chartParams } = params;
|
|
1538
|
+
const chartAppearance = {};
|
|
1539
|
+
const chartTooltip = {};
|
|
1540
|
+
if (chartParams.tooltipHeaderStyle) chartTooltip.tooltipHeader = chartParams.tooltipHeaderStyle;
|
|
1541
|
+
if (chartParams.tooltipLabelStyle) chartTooltip.labelStyle = chartParams.tooltipLabelStyle;
|
|
1542
|
+
if (chartParams.tooltipValueStyle) chartTooltip.valueStyle = chartParams.tooltipValueStyle;
|
|
1543
|
+
if (Object.keys(chartTooltip).length) chartAppearance.chartTooltip = chartTooltip;
|
|
1544
|
+
const chartLabel = {};
|
|
1545
|
+
if (chartParams.labelPosition) chartLabel.position = chartParams.labelPosition;
|
|
1546
|
+
if (chartParams.radialLabelPosition) chartLabel.radialChartposition = chartParams.radialLabelPosition;
|
|
1547
|
+
if (Object.keys(chartLabel).length) chartAppearance.chartLabel = chartLabel;
|
|
1548
|
+
if (chartParams.marginTop !== void 0 || chartParams.marginLeft !== void 0 || chartParams.marginRight !== void 0 || chartParams.marginBottom !== void 0) {
|
|
1549
|
+
chartAppearance.chartMargin = {
|
|
1550
|
+
...chartParams.marginTop !== void 0 && { marginTop: chartParams.marginTop },
|
|
1551
|
+
...chartParams.marginLeft !== void 0 && { marginLeft: chartParams.marginLeft },
|
|
1552
|
+
...chartParams.marginRight !== void 0 && { marginRight: chartParams.marginRight },
|
|
1553
|
+
...chartParams.marginBottom !== void 0 && { marginBottom: chartParams.marginBottom }
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
const chartLegend = {};
|
|
1557
|
+
if (chartParams.legendShow !== void 0) chartLegend.show = chartParams.legendShow;
|
|
1558
|
+
if (chartParams.legendPosition) chartLegend.fixedPosition = chartParams.legendPosition;
|
|
1559
|
+
if (chartParams.legendLayout) chartLegend.legendAppearance = chartParams.legendLayout;
|
|
1560
|
+
if (chartParams.legendShape) chartLegend.legendShape = chartParams.legendShape;
|
|
1561
|
+
if (chartParams.legendFontSize !== void 0) chartLegend.fontSize = chartParams.legendFontSize;
|
|
1562
|
+
if (chartParams.legendFontWeight !== void 0) chartLegend.fontWeight = chartParams.legendFontWeight;
|
|
1563
|
+
if (chartParams.legendFontFamily) chartLegend.fontFamily = chartParams.legendFontFamily;
|
|
1564
|
+
if (chartParams.legendColor) chartLegend.color = chartParams.legendColor;
|
|
1565
|
+
if (chartParams.legendTruncateLength !== void 0) chartLegend.truncateLegend = chartParams.legendTruncateLength;
|
|
1566
|
+
if (chartParams.disableLegendScrolling !== void 0) chartLegend.disableLegendScrolling = chartParams.disableLegendScrolling;
|
|
1567
|
+
if (chartParams.enableVariableLegendPosition !== void 0) chartLegend.enableVariablePosition = chartParams.enableVariableLegendPosition;
|
|
1568
|
+
if (chartParams.legendTop !== void 0) chartLegend.top = chartParams.legendTop;
|
|
1569
|
+
if (chartParams.legendLeft !== void 0) chartLegend.left = chartParams.legendLeft;
|
|
1570
|
+
if (Object.keys(chartLegend).length) chartAppearance.chartLegend = chartLegend;
|
|
1571
|
+
if (chartParams.verticalAxis) chartAppearance.verticalAxis = chartParams.verticalAxis;
|
|
1572
|
+
if (chartParams.horizontalAxis) chartAppearance.horizontalAxis = chartParams.horizontalAxis;
|
|
1573
|
+
const options = {};
|
|
1574
|
+
if (Object.keys(chartAppearance).length) options.chartAppearance = chartAppearance;
|
|
1575
|
+
if (chartParams.chartColors) options.chartColors = chartParams.chartColors;
|
|
1576
|
+
if (Object.keys(options).length === 0) {
|
|
1577
|
+
return toolError("No chart appearance properties provided. Specify at least one property to configure.");
|
|
1578
|
+
}
|
|
1579
|
+
try {
|
|
1580
|
+
const body = await getEmbedForUpdate(client, embedId);
|
|
1581
|
+
body.options = options;
|
|
1582
|
+
const result = await client.request({
|
|
1583
|
+
method: "PUT",
|
|
1584
|
+
path: "/api/v2/data-app/embeds",
|
|
1585
|
+
body,
|
|
1586
|
+
auth: "api",
|
|
1587
|
+
invalidateCache: ["list_embeds"]
|
|
1588
|
+
});
|
|
1589
|
+
if (result.error) {
|
|
1590
|
+
return toolError(`Chart appearance update failed: ${stringify(result.error)}`);
|
|
1591
|
+
}
|
|
1592
|
+
const configured = [];
|
|
1593
|
+
if (chartAppearance.chartTooltip) configured.push("tooltip");
|
|
1594
|
+
if (chartAppearance.chartLabel) configured.push("labels");
|
|
1595
|
+
if (chartAppearance.chartMargin) configured.push("margins");
|
|
1596
|
+
if (chartAppearance.chartLegend) configured.push("legend");
|
|
1597
|
+
if (chartAppearance.verticalAxis) configured.push("vertical axis");
|
|
1598
|
+
if (chartAppearance.horizontalAxis) configured.push("horizontal axis");
|
|
1599
|
+
if (chartParams.chartColors) configured.push("chart colors");
|
|
1600
|
+
return toolSuccess(
|
|
1601
|
+
`Chart appearance updated for embed "${embedId}".
|
|
1602
|
+
|
|
1603
|
+
**Configured:** ${configured.join(", ")}
|
|
1604
|
+
|
|
1605
|
+
Use \`get_embed_details\` to view the full configuration.` + GUEST_TOKEN_REMINDER
|
|
1606
|
+
);
|
|
1607
|
+
} catch (err) {
|
|
1608
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
1609
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
// src/tools/embed/configure-embed-options.ts
|
|
1616
|
+
import { z as z23 } from "zod";
|
|
1617
|
+
function registerConfigureEmbedOptions(server, client) {
|
|
1618
|
+
server.tool(
|
|
1619
|
+
"configure_embed_options",
|
|
1620
|
+
"Configure display, permission, and behavior options for an embedded dashboard \u2014 control metric operations, layout, downloads, loaders, UI visibility, and more.",
|
|
1621
|
+
{
|
|
1622
|
+
embedId: z23.string().describe("ID of the embed to configure"),
|
|
1623
|
+
// Metric Operations
|
|
1624
|
+
disableMetricCreation: z23.boolean().optional().describe("Disable creating new metrics"),
|
|
1625
|
+
disableMetricUpdation: z23.boolean().optional().describe("Disable updating existing metrics"),
|
|
1626
|
+
disableMetricDeletion: z23.boolean().optional().describe("Disable deleting metrics"),
|
|
1627
|
+
disableManageMetrics: z23.boolean().optional().describe("Hide the manage metrics interface"),
|
|
1628
|
+
// Layout
|
|
1629
|
+
disableLayoutCustomization: z23.boolean().optional().describe("Prevent layout changes"),
|
|
1630
|
+
disableSaveLayout: z23.boolean().optional().describe("Prevent saving layout changes"),
|
|
1631
|
+
// Downloads & Export
|
|
1632
|
+
enableDownloadCsv: z23.boolean().optional().describe("Enable CSV download for individual metrics"),
|
|
1633
|
+
enableEmailCsv: z23.boolean().optional().describe("Enable emailing metric data as CSV"),
|
|
1634
|
+
disableDownloadPng: z23.boolean().optional().describe("Disable PNG download in fullscreen"),
|
|
1635
|
+
enableDownloadAllMetrics: z23.boolean().optional().describe("Enable bulk download of all metrics as CSV"),
|
|
1636
|
+
enableDownloadAllPdf: z23.boolean().optional().describe("Enable downloading entire dashboard as PDF"),
|
|
1637
|
+
disableDownloadDataNoFilters: z23.boolean().optional().describe("Prevent data download when no filters applied"),
|
|
1638
|
+
disableDownloadUnderlyingDataNoFilters: z23.boolean().optional().describe("Prevent underlying data download without filters"),
|
|
1639
|
+
// UI Visibility
|
|
1640
|
+
hideDashboardName: z23.boolean().optional().describe("Hide the dashboard title"),
|
|
1641
|
+
hideMetricCardShadow: z23.boolean().optional().describe("Remove card shadow effects"),
|
|
1642
|
+
disableMetricCardBorder: z23.boolean().optional().describe("Remove metric card borders"),
|
|
1643
|
+
showDashboardActions: z23.boolean().optional().describe("Show/hide dashboard action buttons"),
|
|
1644
|
+
disableFullscreen: z23.boolean().optional().describe("Disable fullscreen mode"),
|
|
1645
|
+
isHideTablePreview: z23.boolean().optional().describe("Hide table preview in fullscreen"),
|
|
1646
|
+
isHideChartSettings: z23.boolean().optional().describe("Hide chart settings in fullscreen"),
|
|
1647
|
+
shouldFitFullScreen: z23.boolean().optional().describe("Make fullscreen modal fit the component space"),
|
|
1648
|
+
// Loaders
|
|
1649
|
+
disableMainLoader: z23.boolean().optional().describe("Hide the main loading indicator"),
|
|
1650
|
+
disableMetricLoader: z23.boolean().optional().describe("Hide metric-level loading indicators"),
|
|
1651
|
+
// Data Access
|
|
1652
|
+
disableUnderlyingData: z23.boolean().optional().describe("Disable access to underlying data"),
|
|
1653
|
+
isShowNoDataFoundScreen: z23.boolean().optional().describe('Show a "no data" screen when data is unavailable'),
|
|
1654
|
+
// Scheduling
|
|
1655
|
+
disableScheduleEmailReports: z23.boolean().optional().describe("Disable email report scheduling"),
|
|
1656
|
+
// Filters & Interactions
|
|
1657
|
+
isStickyDashboardFilters: z23.boolean().optional().describe("Make filters sticky when scrolling"),
|
|
1658
|
+
enableMultiMetricFilters: z23.boolean().optional().describe("Enable cross-metric filter interactions"),
|
|
1659
|
+
optionsIcon: z23.enum(["kebab-menu-vertical", "download"]).optional().describe("Menu icon style"),
|
|
1660
|
+
hideDatePickerOptions: z23.array(z23.string()).optional().describe('Date picker options to hide (e.g. ["this month", "yesterday"])'),
|
|
1661
|
+
// Custom Messages
|
|
1662
|
+
tokenExpiryMessage: z23.string().optional().describe("Custom message when token expires"),
|
|
1663
|
+
tokenAbsentMessage: z23.string().optional().describe("Custom message when token is missing")
|
|
1664
|
+
},
|
|
1665
|
+
async (params) => {
|
|
1666
|
+
const { embedId, ...opts } = params;
|
|
1667
|
+
const options = {};
|
|
1668
|
+
if (opts.disableMetricCreation !== void 0) options.disableMetricCreation = opts.disableMetricCreation;
|
|
1669
|
+
if (opts.disableMetricUpdation !== void 0) options.disableMetricUpdation = opts.disableMetricUpdation;
|
|
1670
|
+
if (opts.disableMetricDeletion !== void 0) options.disableMetricDeletion = opts.disableMetricDeletion;
|
|
1671
|
+
if (opts.disableManageMetrics !== void 0) options.disableManageMetrics = opts.disableManageMetrics;
|
|
1672
|
+
if (opts.disableLayoutCustomization !== void 0) options.disableLayoutCustomization = opts.disableLayoutCustomization;
|
|
1673
|
+
if (opts.disableSaveLayout !== void 0) options.disableSaveLayout = opts.disableSaveLayout;
|
|
1674
|
+
if (opts.disableDownloadDataNoFilters !== void 0) options.disableDownloadDataNoFilters = opts.disableDownloadDataNoFilters;
|
|
1675
|
+
if (opts.disableDownloadUnderlyingDataNoFilters !== void 0) options.disableDownloadUnderlyingDataNoFilters = opts.disableDownloadUnderlyingDataNoFilters;
|
|
1676
|
+
if (opts.hideDashboardName !== void 0) options.hideDashboardName = opts.hideDashboardName;
|
|
1677
|
+
if (opts.hideMetricCardShadow !== void 0) options.hideMetricCardShadow = opts.hideMetricCardShadow;
|
|
1678
|
+
if (opts.disableMetricCardBorder !== void 0) options.disableMetricCardBorder = opts.disableMetricCardBorder;
|
|
1679
|
+
if (opts.showDashboardActions !== void 0) options.showDashboardActions = opts.showDashboardActions;
|
|
1680
|
+
if (opts.shouldFitFullScreen !== void 0) options.shouldFitFullScreen = opts.shouldFitFullScreen;
|
|
1681
|
+
if (opts.isShowNoDataFoundScreen !== void 0) options.isShowNoDataFoundScreen = opts.isShowNoDataFoundScreen;
|
|
1682
|
+
if (opts.disableMainLoader !== void 0) options.disableMainLoader = opts.disableMainLoader;
|
|
1683
|
+
if (opts.disableMetricLoader !== void 0) options.disableMetricLoader = opts.disableMetricLoader;
|
|
1684
|
+
if (opts.disableUnderlyingData !== void 0) options.disableUnderlyingData = opts.disableUnderlyingData;
|
|
1685
|
+
if (opts.disableScheduleEmailReports !== void 0) options.disableScheduleEmailReports = opts.disableScheduleEmailReports;
|
|
1686
|
+
if (opts.hideDatePickerOptions) options.hideDatePickerOptions = opts.hideDatePickerOptions;
|
|
1687
|
+
const attrs = {};
|
|
1688
|
+
if (opts.enableDownloadCsv !== void 0) attrs.enableDownloadCsv = opts.enableDownloadCsv;
|
|
1689
|
+
if (opts.enableEmailCsv !== void 0) attrs.enableEmailCsv = opts.enableEmailCsv;
|
|
1690
|
+
if (opts.disableDownloadPng !== void 0) attrs.disableDownloadPng = opts.disableDownloadPng;
|
|
1691
|
+
if (opts.enableDownloadAllMetrics !== void 0) attrs.enableDownloadAllMetrics = opts.enableDownloadAllMetrics;
|
|
1692
|
+
if (opts.enableDownloadAllPdf !== void 0) attrs.enableDownloadAllPdf = opts.enableDownloadAllPdf;
|
|
1693
|
+
if (opts.disableFullscreen !== void 0) attrs.disableFullscreen = opts.disableFullscreen;
|
|
1694
|
+
if (opts.isHideTablePreview !== void 0) attrs.isHideTablePreview = opts.isHideTablePreview;
|
|
1695
|
+
if (opts.isHideChartSettings !== void 0) attrs.isHideChartSettings = opts.isHideChartSettings;
|
|
1696
|
+
if (opts.isStickyDashboardFilters !== void 0) attrs.isStickyDashboardFilters = opts.isStickyDashboardFilters;
|
|
1697
|
+
if (opts.enableMultiMetricFilters !== void 0) attrs.enableMultiMetricFilters = opts.enableMultiMetricFilters;
|
|
1698
|
+
if (opts.optionsIcon) attrs.optionsIcon = opts.optionsIcon;
|
|
1699
|
+
const customMessages = {};
|
|
1700
|
+
if (opts.tokenExpiryMessage) customMessages.tokenExpiry = opts.tokenExpiryMessage;
|
|
1701
|
+
if (opts.tokenAbsentMessage) customMessages.tokenAbsent = opts.tokenAbsentMessage;
|
|
1702
|
+
if (Object.keys(customMessages).length) options.customMessages = customMessages;
|
|
1703
|
+
if (Object.keys(options).length === 0 && Object.keys(attrs).length === 0) {
|
|
1704
|
+
return toolError("No options provided. Specify at least one option to configure.");
|
|
1705
|
+
}
|
|
1706
|
+
try {
|
|
1707
|
+
const body = await getEmbedForUpdate(client, embedId);
|
|
1708
|
+
if (Object.keys(options).length) body.options = options;
|
|
1709
|
+
if (Object.keys(attrs).length) body.componentAttributes = attrs;
|
|
1710
|
+
const result = await client.request({
|
|
1711
|
+
method: "PUT",
|
|
1712
|
+
path: "/api/v2/data-app/embeds",
|
|
1713
|
+
body,
|
|
1714
|
+
auth: "api",
|
|
1715
|
+
invalidateCache: ["list_embeds"]
|
|
1716
|
+
});
|
|
1717
|
+
if (result.error) {
|
|
1718
|
+
return toolError(`Options update failed: ${stringify(result.error)}`);
|
|
1719
|
+
}
|
|
1720
|
+
const allKeys = [...Object.keys(options), ...Object.keys(attrs)];
|
|
1721
|
+
return toolSuccess(
|
|
1722
|
+
`Embed options updated for "${embedId}".
|
|
1723
|
+
|
|
1724
|
+
**Configured ${allKeys.length} option(s):** ${allKeys.join(", ")}
|
|
1725
|
+
|
|
1726
|
+
Use \`get_embed_details\` to view the full configuration.` + GUEST_TOKEN_REMINDER
|
|
1727
|
+
);
|
|
1728
|
+
} catch (err) {
|
|
1729
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
1730
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
);
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// src/tools/embed/configure-filters.ts
|
|
1737
|
+
import { z as z24 } from "zod";
|
|
1738
|
+
var stringFilterSchema = z24.object({
|
|
1739
|
+
filterName: z24.string().describe("Name of the filter as it appears in the dashboard"),
|
|
1740
|
+
options: z24.array(z24.object({
|
|
1741
|
+
value: z24.string(),
|
|
1742
|
+
label: z24.string()
|
|
1743
|
+
})).describe("Dropdown options"),
|
|
1744
|
+
defaultOption: z24.union([z24.string(), z24.array(z24.string())]).optional().describe("Default selected value (string or array for multi-select)")
|
|
1745
|
+
});
|
|
1746
|
+
var numberFilterSchema = z24.object({
|
|
1747
|
+
filterName: z24.string().describe("Name of the filter"),
|
|
1748
|
+
min: z24.number().describe("Minimum value"),
|
|
1749
|
+
max: z24.number().describe("Maximum value")
|
|
1750
|
+
});
|
|
1751
|
+
var datePresetSchema = z24.object({
|
|
1752
|
+
type: z24.string().optional().describe('Preset type (e.g. "Last", "This")'),
|
|
1753
|
+
interval: z24.string().optional().describe('Interval (e.g. "Days", "Months", "Years")'),
|
|
1754
|
+
timeGrain: z24.string().optional().describe("Time grain"),
|
|
1755
|
+
label: z24.string().describe('Display label (e.g. "Last 30 Days")'),
|
|
1756
|
+
startDate: z24.string().optional().describe("Start date (YYYY-MM-DD)"),
|
|
1757
|
+
endDate: z24.string().optional().describe("End date (YYYY-MM-DD)")
|
|
1758
|
+
});
|
|
1759
|
+
var dateFilterSchema = z24.object({
|
|
1760
|
+
filterName: z24.string().describe("Name of the filter"),
|
|
1761
|
+
startDate: z24.string().optional().describe("Default start date (YYYY-MM-DD)"),
|
|
1762
|
+
endDate: z24.string().optional().describe("Default end date (YYYY-MM-DD)"),
|
|
1763
|
+
datePresetOptions: z24.array(datePresetSchema).optional().describe("Custom date presets to show in the date picker")
|
|
1764
|
+
});
|
|
1765
|
+
var sqlFilterSchema = z24.object({
|
|
1766
|
+
filterName: z24.string().describe("Name of the filter"),
|
|
1767
|
+
sql: z24.string().describe('SQL query to fetch filter options (e.g. SELECT "name" FROM "table")'),
|
|
1768
|
+
columnName: z24.string().describe("Column name from the SQL result to use as option values")
|
|
1769
|
+
});
|
|
1770
|
+
function registerConfigureFilters(server, client) {
|
|
1771
|
+
server.tool(
|
|
1772
|
+
"configure_filters",
|
|
1773
|
+
"Configure dashboard and metric filters for an embed \u2014 set default values, dropdown options, date presets, SQL-based filters for large datasets, and hide specific filters.",
|
|
1774
|
+
{
|
|
1775
|
+
embedId: z24.string().describe("ID of the embed to configure"),
|
|
1776
|
+
// Global dashboard filters
|
|
1777
|
+
stringFilters: z24.array(stringFilterSchema).optional().describe("String filter configurations with dropdown options and defaults"),
|
|
1778
|
+
numberFilters: z24.array(numberFilterSchema).optional().describe("Number filter configurations with min/max defaults"),
|
|
1779
|
+
dateFilters: z24.array(dateFilterSchema).optional().describe("Date filter configurations with default ranges and presets"),
|
|
1780
|
+
sqlFilters: z24.array(sqlFilterSchema).optional().describe("SQL-based filter options for filters with large option sets"),
|
|
1781
|
+
// Filter visibility
|
|
1782
|
+
hideDashboardFilters: z24.array(z24.string()).optional().describe("Array of filter names to hide from the dashboard UI"),
|
|
1783
|
+
hideDatePickerOptions: z24.array(z24.string()).optional().describe('Date picker options to hide (e.g. ["this month", "yesterday"])'),
|
|
1784
|
+
// Metric-level filter config
|
|
1785
|
+
metricFilterPosition: z24.enum(["inside", "outside"]).optional().describe("Where to display metric-level filters"),
|
|
1786
|
+
enableMultiMetricFilters: z24.boolean().optional().describe("Allow multiple filters per metric")
|
|
1787
|
+
},
|
|
1788
|
+
async (params) => {
|
|
1789
|
+
const { embedId, ...filterParams } = params;
|
|
1790
|
+
const options = {};
|
|
1791
|
+
const attrs = {};
|
|
1792
|
+
const globalFilterOptions = {};
|
|
1793
|
+
if (filterParams.stringFilters) {
|
|
1794
|
+
for (const f of filterParams.stringFilters) {
|
|
1795
|
+
globalFilterOptions[f.filterName] = {
|
|
1796
|
+
options: f.options,
|
|
1797
|
+
...f.defaultOption !== void 0 && { defaultOption: f.defaultOption }
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
if (filterParams.numberFilters) {
|
|
1802
|
+
for (const f of filterParams.numberFilters) {
|
|
1803
|
+
globalFilterOptions[f.filterName] = {
|
|
1804
|
+
defaultOption: { min: f.min, max: f.max }
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
if (filterParams.dateFilters) {
|
|
1809
|
+
for (const f of filterParams.dateFilters) {
|
|
1810
|
+
const config = {};
|
|
1811
|
+
if (f.startDate || f.endDate) {
|
|
1812
|
+
config.defaultOption = {
|
|
1813
|
+
...f.startDate && { startDate: f.startDate },
|
|
1814
|
+
...f.endDate && { endDate: f.endDate }
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
if (f.datePresetOptions) {
|
|
1818
|
+
config.datePresetOptions = f.datePresetOptions;
|
|
1819
|
+
}
|
|
1820
|
+
globalFilterOptions[f.filterName] = config;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
if (filterParams.sqlFilters) {
|
|
1824
|
+
for (const f of filterParams.sqlFilters) {
|
|
1825
|
+
globalFilterOptions[f.filterName] = {
|
|
1826
|
+
sql: f.sql,
|
|
1827
|
+
columnName: f.columnName
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
if (Object.keys(globalFilterOptions).length) {
|
|
1832
|
+
options.globalFilterOptions = globalFilterOptions;
|
|
1833
|
+
}
|
|
1834
|
+
if (filterParams.hideDashboardFilters) {
|
|
1835
|
+
attrs.hideDashboardFilters = filterParams.hideDashboardFilters;
|
|
1836
|
+
}
|
|
1837
|
+
if (filterParams.hideDatePickerOptions) {
|
|
1838
|
+
options.hideDatePickerOptions = filterParams.hideDatePickerOptions;
|
|
1839
|
+
}
|
|
1840
|
+
if (filterParams.metricFilterPosition) {
|
|
1841
|
+
attrs.metricFilterPosition = filterParams.metricFilterPosition;
|
|
1842
|
+
}
|
|
1843
|
+
if (filterParams.enableMultiMetricFilters !== void 0) {
|
|
1844
|
+
attrs.enableMultiMetricFilters = filterParams.enableMultiMetricFilters;
|
|
1845
|
+
}
|
|
1846
|
+
if (Object.keys(options).length === 0 && Object.keys(attrs).length === 0) {
|
|
1847
|
+
return toolError("No filter configuration provided. Specify at least one filter option.");
|
|
1848
|
+
}
|
|
1849
|
+
try {
|
|
1850
|
+
const body = await getEmbedForUpdate(client, embedId);
|
|
1851
|
+
if (Object.keys(options).length) body.options = options;
|
|
1852
|
+
if (Object.keys(attrs).length) body.componentAttributes = attrs;
|
|
1853
|
+
const result = await client.request({
|
|
1854
|
+
method: "PUT",
|
|
1855
|
+
path: "/api/v2/data-app/embeds",
|
|
1856
|
+
body,
|
|
1857
|
+
auth: "api",
|
|
1858
|
+
invalidateCache: ["list_embeds"]
|
|
1859
|
+
});
|
|
1860
|
+
if (result.error) {
|
|
1861
|
+
return toolError(`Filter configuration failed: ${stringify(result.error)}`);
|
|
1862
|
+
}
|
|
1863
|
+
const configured = [];
|
|
1864
|
+
if (filterParams.stringFilters?.length) configured.push(`${filterParams.stringFilters.length} string filter(s)`);
|
|
1865
|
+
if (filterParams.numberFilters?.length) configured.push(`${filterParams.numberFilters.length} number filter(s)`);
|
|
1866
|
+
if (filterParams.dateFilters?.length) configured.push(`${filterParams.dateFilters.length} date filter(s)`);
|
|
1867
|
+
if (filterParams.sqlFilters?.length) configured.push(`${filterParams.sqlFilters.length} SQL filter(s)`);
|
|
1868
|
+
if (filterParams.hideDashboardFilters?.length) configured.push(`hiding ${filterParams.hideDashboardFilters.length} filter(s)`);
|
|
1869
|
+
if (filterParams.hideDatePickerOptions?.length) configured.push(`hiding ${filterParams.hideDatePickerOptions.length} date picker option(s)`);
|
|
1870
|
+
if (filterParams.metricFilterPosition) configured.push(`metric filter position: ${filterParams.metricFilterPosition}`);
|
|
1871
|
+
if (filterParams.enableMultiMetricFilters !== void 0) configured.push(`multi-metric filters: ${filterParams.enableMultiMetricFilters}`);
|
|
1872
|
+
return toolSuccess(
|
|
1873
|
+
`Filter configuration updated for embed "${embedId}".
|
|
1874
|
+
|
|
1875
|
+
**Configured:** ${configured.join(", ")}
|
|
1876
|
+
|
|
1877
|
+
Use \`get_embed_details\` to view the full configuration.` + GUEST_TOKEN_REMINDER
|
|
1878
|
+
);
|
|
1879
|
+
} catch (err) {
|
|
1880
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
1881
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
// src/tools/embed/configure-internationalization.ts
|
|
1888
|
+
import { z as z25 } from "zod";
|
|
1889
|
+
var SUPPORTED_LANGUAGES = [
|
|
1890
|
+
"en",
|
|
1891
|
+
"fr",
|
|
1892
|
+
"es",
|
|
1893
|
+
"de",
|
|
1894
|
+
"it",
|
|
1895
|
+
"pt",
|
|
1896
|
+
"zh",
|
|
1897
|
+
"ja",
|
|
1898
|
+
"ko",
|
|
1899
|
+
"hi",
|
|
1900
|
+
"ar",
|
|
1901
|
+
"ru"
|
|
1902
|
+
];
|
|
1903
|
+
function registerConfigureInternationalization(server, client) {
|
|
1904
|
+
server.tool(
|
|
1905
|
+
"configure_internationalization",
|
|
1906
|
+
"Configure language, translations, calendar type, and custom messages for an embedded dashboard. Supports 12 languages including English, French, Spanish, German, Italian, Portuguese, Chinese, Japanese, Korean, Hindi, Arabic, and Russian.",
|
|
933
1907
|
{
|
|
934
|
-
embedId:
|
|
935
|
-
|
|
936
|
-
|
|
1908
|
+
embedId: z25.string().describe("ID of the embed to configure"),
|
|
1909
|
+
// Language
|
|
1910
|
+
language: z25.enum(SUPPORTED_LANGUAGES).optional().describe('Primary language code (e.g. "fr" for French, "es" for Spanish, "ja" for Japanese)'),
|
|
1911
|
+
// Calendar
|
|
1912
|
+
calendarType: z25.enum(["default", "ind"]).optional().describe('Calendar system: "default" for standard, "ind" for Indian calendar'),
|
|
1913
|
+
// Translations
|
|
1914
|
+
translations: z25.array(z25.object({
|
|
1915
|
+
key: z25.string().describe("Original text to translate (must match exactly with text in dashboard)"),
|
|
1916
|
+
translations: z25.record(z25.string()).describe('Language code to translation mapping, e.g. { "fr": "Revenus", "es": "Ingresos" }')
|
|
1917
|
+
})).optional().describe("Translation dictionary entries. Keys must match dashboard text exactly. Define longer keys before shorter ones."),
|
|
1918
|
+
// Custom Messages
|
|
1919
|
+
tokenExpiryMessage: z25.string().optional().describe("Custom message shown when the authentication token expires"),
|
|
1920
|
+
tokenAbsentMessage: z25.string().optional().describe("Custom message shown when no token is provided")
|
|
937
1921
|
},
|
|
938
1922
|
async (params) => {
|
|
939
|
-
|
|
940
|
-
|
|
1923
|
+
const { embedId, ...i18nParams } = params;
|
|
1924
|
+
const attrs = {};
|
|
1925
|
+
const options = {};
|
|
1926
|
+
if (i18nParams.language) attrs.language = i18nParams.language;
|
|
1927
|
+
if (i18nParams.calendarType) attrs.calendarType = i18nParams.calendarType;
|
|
1928
|
+
if (i18nParams.translations?.length) {
|
|
1929
|
+
const dict = {};
|
|
1930
|
+
for (const entry of i18nParams.translations) {
|
|
1931
|
+
dict[entry.key] = entry.translations;
|
|
1932
|
+
}
|
|
1933
|
+
attrs.translationDictionary = dict;
|
|
1934
|
+
}
|
|
1935
|
+
const customMessages = {};
|
|
1936
|
+
if (i18nParams.tokenExpiryMessage) customMessages.tokenExpiry = i18nParams.tokenExpiryMessage;
|
|
1937
|
+
if (i18nParams.tokenAbsentMessage) customMessages.tokenAbsent = i18nParams.tokenAbsentMessage;
|
|
1938
|
+
if (Object.keys(customMessages).length) options.customMessages = customMessages;
|
|
1939
|
+
if (Object.keys(attrs).length === 0 && Object.keys(options).length === 0) {
|
|
1940
|
+
return toolError("No internationalization options provided. Specify at least one option.");
|
|
941
1941
|
}
|
|
942
1942
|
try {
|
|
943
|
-
const body =
|
|
944
|
-
if (
|
|
945
|
-
|
|
946
|
-
}
|
|
947
|
-
if (params.accessPreset) {
|
|
948
|
-
body.accessSettings = ACCESS_PRESETS[params.accessPreset];
|
|
949
|
-
}
|
|
1943
|
+
const body = await getEmbedForUpdate(client, embedId);
|
|
1944
|
+
if (Object.keys(attrs).length) body.componentAttributes = attrs;
|
|
1945
|
+
if (Object.keys(options).length) body.options = options;
|
|
950
1946
|
const result = await client.request({
|
|
951
1947
|
method: "PUT",
|
|
952
1948
|
path: "/api/v2/data-app/embeds",
|
|
@@ -955,25 +1951,19 @@ function registerApplyThemePreset(server, client) {
|
|
|
955
1951
|
invalidateCache: ["list_embeds"]
|
|
956
1952
|
});
|
|
957
1953
|
if (result.error) {
|
|
958
|
-
return toolError(`
|
|
959
|
-
}
|
|
960
|
-
const applied = [];
|
|
961
|
-
if (params.themePreset) applied.push(`theme: **${params.themePreset}**`);
|
|
962
|
-
if (params.accessPreset) applied.push(`access: **${params.accessPreset}**`);
|
|
963
|
-
let details = "";
|
|
964
|
-
if (params.accessPreset) {
|
|
965
|
-
const settings = ACCESS_PRESETS[params.accessPreset];
|
|
966
|
-
const enabled = Object.entries(settings).filter(([, v]) => v === true).map(([k]) => k).join(", ");
|
|
967
|
-
details = `
|
|
968
|
-
|
|
969
|
-
Enabled settings: ${enabled || "none"}`;
|
|
970
|
-
if (settings.metricCreationMode) {
|
|
971
|
-
details += `
|
|
972
|
-
Metric creation mode: ${settings.metricCreationMode}`;
|
|
973
|
-
}
|
|
1954
|
+
return toolError(`Internationalization update failed: ${stringify(result.error)}`);
|
|
974
1955
|
}
|
|
1956
|
+
const configured = [];
|
|
1957
|
+
if (i18nParams.language) configured.push(`language: ${i18nParams.language}`);
|
|
1958
|
+
if (i18nParams.calendarType) configured.push(`calendar: ${i18nParams.calendarType}`);
|
|
1959
|
+
if (i18nParams.translations?.length) configured.push(`${i18nParams.translations.length} translation(s)`);
|
|
1960
|
+
if (Object.keys(customMessages).length) configured.push("custom messages");
|
|
975
1961
|
return toolSuccess(
|
|
976
|
-
`
|
|
1962
|
+
`Internationalization configured for embed "${embedId}".
|
|
1963
|
+
|
|
1964
|
+
**Configured:** ${configured.join(", ")}
|
|
1965
|
+
|
|
1966
|
+
Use \`get_embed_details\` to view the full configuration.` + GUEST_TOKEN_REMINDER
|
|
977
1967
|
);
|
|
978
1968
|
} catch (err) {
|
|
979
1969
|
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
@@ -983,54 +1973,156 @@ Metric creation mode: ${settings.metricCreationMode}`;
|
|
|
983
1973
|
);
|
|
984
1974
|
}
|
|
985
1975
|
|
|
1976
|
+
// src/tools/embed/get-permission-schema.ts
|
|
1977
|
+
var PERMISSION_SCHEMA = {
|
|
1978
|
+
embedAccessSettings: {
|
|
1979
|
+
description: "Fields accepted in embed create/update (`accessSettings` in PUT /api/v2/data-app/embeds). Auth: API token. These are the embed-level defaults.",
|
|
1980
|
+
fields: {
|
|
1981
|
+
isAllowMetricCreation: { type: "boolean", default: false, note: "May require enabling via Databrain UI (Settings > Customize Layout) if API returns 500" },
|
|
1982
|
+
isAllowMetricDeletion: { type: "boolean", default: false },
|
|
1983
|
+
isAllowMetricEditing: { type: "boolean", default: false },
|
|
1984
|
+
isAllowMetricDrag: { type: "boolean", default: false },
|
|
1985
|
+
isAllowMetricResize: { type: "boolean", default: false },
|
|
1986
|
+
isAllowChangeLayout: { type: "boolean", default: false },
|
|
1987
|
+
isAllowFullscreen: { type: "boolean", default: true },
|
|
1988
|
+
isAllowTablePreview: { type: "boolean", default: true },
|
|
1989
|
+
isAllowChartSettings: { type: "boolean", default: true },
|
|
1990
|
+
isAllowDrillDown: { type: "boolean", default: false },
|
|
1991
|
+
isAllowCrossFilter: { type: "boolean", default: false },
|
|
1992
|
+
isAllowSorting: { type: "boolean", default: true },
|
|
1993
|
+
isAllowAiPilot: { type: "boolean", default: false },
|
|
1994
|
+
isAllowAiSummary: { type: "boolean", default: false },
|
|
1995
|
+
isAllowAiRecommendation: { type: "boolean", default: false },
|
|
1996
|
+
isAllowDownloadCsv: { type: "boolean", default: false },
|
|
1997
|
+
isAllowEmailCsv: { type: "boolean", default: false },
|
|
1998
|
+
isAllowDownloadAllMetrics: { type: "boolean", default: false },
|
|
1999
|
+
isAllowDownloadPdf: { type: "boolean", default: false },
|
|
2000
|
+
isAllowShareUrl: { type: "boolean", default: false },
|
|
2001
|
+
isAllowDashboardFilters: { type: "boolean", default: true },
|
|
2002
|
+
isAllowDateRangeFilter: { type: "boolean", default: true },
|
|
2003
|
+
isAllowCustomDateRange: { type: "boolean", default: false },
|
|
2004
|
+
metricCreationMode: { type: "enum", values: ["DRAG_DROP", "CHAT"], default: "DRAG_DROP" }
|
|
2005
|
+
}
|
|
2006
|
+
},
|
|
2007
|
+
guestTokenPermissions: {
|
|
2008
|
+
description: "Fields accepted in `permissions` when generating a guest token (POST /api/v2/guest-token/create). Auth: API token. These override embed-level defaults per session.",
|
|
2009
|
+
fields: {
|
|
2010
|
+
isEnableArchiveMetrics: { type: "boolean" },
|
|
2011
|
+
isEnableManageMetrics: { type: "boolean" },
|
|
2012
|
+
isEnableCreateDashboardView: { type: "boolean" },
|
|
2013
|
+
isEnableMetricUpdation: { type: "boolean" },
|
|
2014
|
+
isEnableCustomizeLayout: { type: "boolean" },
|
|
2015
|
+
isEnableUnderlyingData: { type: "boolean" },
|
|
2016
|
+
isEnableDownloadMetrics: { type: "boolean" },
|
|
2017
|
+
isShowSideBar: { type: "boolean" },
|
|
2018
|
+
isShowDashboardName: { type: "boolean" },
|
|
2019
|
+
isDisableMetricCreation: { type: "boolean" }
|
|
2020
|
+
},
|
|
2021
|
+
rejectedFields: ["accessSettings", "allowedEmbeds"]
|
|
2022
|
+
},
|
|
2023
|
+
guestTokenTopLevel: {
|
|
2024
|
+
description: "Top-level fields accepted in the guest token request body (POST /api/v2/guest-token/create).",
|
|
2025
|
+
required: ["clientId", "dataAppName"],
|
|
2026
|
+
optional: [
|
|
2027
|
+
"timezone",
|
|
2028
|
+
"appFilters",
|
|
2029
|
+
"dashboardAppFilters",
|
|
2030
|
+
"permissions",
|
|
2031
|
+
"theme",
|
|
2032
|
+
"hideDashboardFilters",
|
|
2033
|
+
"expiryTime",
|
|
2034
|
+
"datasourceName"
|
|
2035
|
+
],
|
|
2036
|
+
rejectedFields: ["allowedEmbeds", "accessSettings"]
|
|
2037
|
+
},
|
|
2038
|
+
precedence: {
|
|
2039
|
+
description: "When the same capability is set at multiple levels, highest precedence wins.",
|
|
2040
|
+
order: [
|
|
2041
|
+
"1. Web component `options` attribute (highest \u2014 client-side, not secure)",
|
|
2042
|
+
"2. Guest token `permissions` (server-side, per session)",
|
|
2043
|
+
"3. Embed `accessSettings` (server-side, default)"
|
|
2044
|
+
],
|
|
2045
|
+
note: "Embed accessSettings use `isAllow*` naming. Guest token permissions use `isEnable*`/`isShow*`/`isDisable*` naming. They are different schemas."
|
|
2046
|
+
},
|
|
2047
|
+
knownLimitations: [
|
|
2048
|
+
"isAllowMetricCreation in accessSettings may return 500 \u2014 enable via Databrain UI (Settings > Customize Layout) as a workaround.",
|
|
2049
|
+
"accessSettings and allowedEmbeds are rejected by the guest token endpoint \u2014 use permissions instead.",
|
|
2050
|
+
"After updating embed settings, existing guest tokens still carry old config. Regenerate guest tokens for changes to take effect."
|
|
2051
|
+
]
|
|
2052
|
+
};
|
|
2053
|
+
function registerGetPermissionSchema(server) {
|
|
2054
|
+
server.tool(
|
|
2055
|
+
"get_permission_schema",
|
|
2056
|
+
"Returns the machine-readable permission/capability schema showing which fields are accepted in embed accessSettings vs guest token permissions, precedence rules, rejected fields, and known limitations. Use this to validate payloads before calling embed or guest token APIs.",
|
|
2057
|
+
{},
|
|
2058
|
+
async () => {
|
|
2059
|
+
return toolSuccess(JSON.stringify(PERMISSION_SCHEMA, null, 2));
|
|
2060
|
+
}
|
|
2061
|
+
);
|
|
2062
|
+
}
|
|
2063
|
+
|
|
986
2064
|
// src/tools/frontend/generate-guest-token.ts
|
|
987
|
-
import { z as
|
|
2065
|
+
import { z as z26 } from "zod";
|
|
988
2066
|
function registerGenerateGuestToken(server, client) {
|
|
989
2067
|
server.tool(
|
|
990
2068
|
"generate_guest_token",
|
|
991
2069
|
"Generate a guest token for frontend embedding. This token authenticates end-users to view embedded dashboards.",
|
|
992
2070
|
{
|
|
993
|
-
clientId:
|
|
994
|
-
dataAppName:
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
2071
|
+
clientId: z26.string().describe('Unique end-user identifier for multi-tenant filtering. Use "None" if no tenancy.'),
|
|
2072
|
+
dataAppName: z26.string().describe("Name of the data app"),
|
|
2073
|
+
timezone: z26.string().optional().describe('Timezone, e.g. "America/New_York"'),
|
|
2074
|
+
appFilters: z26.array(z26.object({
|
|
2075
|
+
columnName: z26.string().describe("Column name in the database table"),
|
|
2076
|
+
tableName: z26.string().describe("Table name (must match a datamart table)"),
|
|
2077
|
+
operator: z26.string().describe("Comparison operator (=, IN, NOT IN, LIKE, >, <, >=, <=, BETWEEN, IS NULL, IS NOT NULL)"),
|
|
2078
|
+
value: z26.unknown().optional().describe("Filter value(s)"),
|
|
2079
|
+
type: z26.enum(["string", "number", "null"]).describe("Filter data type")
|
|
2080
|
+
})).optional().describe("Global filters applied to all metrics (server-side enforced, cannot be removed by end users)"),
|
|
2081
|
+
dashboardAppFilters: z26.array(z26.object({
|
|
2082
|
+
dashboardId: z26.string().describe("Dashboard ID to apply filters to"),
|
|
2083
|
+
filters: z26.array(z26.object({
|
|
2084
|
+
columnName: z26.string(),
|
|
2085
|
+
tableName: z26.string(),
|
|
2086
|
+
operator: z26.string(),
|
|
2087
|
+
value: z26.unknown().optional(),
|
|
2088
|
+
type: z26.enum(["string", "number", "null"])
|
|
2089
|
+
})).describe("Filters for this specific dashboard")
|
|
2090
|
+
})).optional().describe("Dashboard-specific filters (different filters per dashboard)"),
|
|
2091
|
+
permissions: z26.object({
|
|
2092
|
+
isEnableArchiveMetrics: z26.boolean().optional(),
|
|
2093
|
+
isEnableManageMetrics: z26.boolean().optional(),
|
|
2094
|
+
isEnableCreateDashboardView: z26.boolean().optional(),
|
|
2095
|
+
isEnableMetricUpdation: z26.boolean().optional(),
|
|
2096
|
+
isEnableCustomizeLayout: z26.boolean().optional(),
|
|
2097
|
+
isEnableUnderlyingData: z26.boolean().optional(),
|
|
2098
|
+
isEnableDownloadMetrics: z26.boolean().optional(),
|
|
2099
|
+
isShowSideBar: z26.boolean().optional(),
|
|
2100
|
+
isShowDashboardName: z26.boolean().optional(),
|
|
2101
|
+
isDisableMetricCreation: z26.boolean().optional()
|
|
2102
|
+
}).optional().describe("Runtime permission overrides (merge with and override embed-level settings)"),
|
|
2103
|
+
theme: z26.record(z26.unknown()).optional().describe("Runtime theme overrides (overrides embed-level theme)"),
|
|
2104
|
+
hideDashboardFilters: z26.array(z26.string()).optional().describe("Filter names to hide from the UI"),
|
|
2105
|
+
expiryTime: z26.number().optional().describe("Token TTL in milliseconds"),
|
|
2106
|
+
datasourceName: z26.string().optional().describe("Datasource name for multi-datasource routing")
|
|
1023
2107
|
},
|
|
1024
2108
|
async (params) => {
|
|
1025
2109
|
try {
|
|
2110
|
+
const body = {
|
|
2111
|
+
clientId: params.clientId,
|
|
2112
|
+
dataAppName: params.dataAppName
|
|
2113
|
+
};
|
|
2114
|
+
if (params.timezone) body.timezone = params.timezone;
|
|
2115
|
+
if (params.appFilters) body.appFilters = params.appFilters;
|
|
2116
|
+
if (params.dashboardAppFilters) body.dashboardAppFilters = params.dashboardAppFilters;
|
|
2117
|
+
if (params.permissions) body.permissions = params.permissions;
|
|
2118
|
+
if (params.theme) body.theme = params.theme;
|
|
2119
|
+
if (params.hideDashboardFilters) body.hideDashboardFilters = params.hideDashboardFilters;
|
|
2120
|
+
if (params.expiryTime) body.expiryTime = params.expiryTime;
|
|
2121
|
+
if (params.datasourceName) body.datasourceName = params.datasourceName;
|
|
1026
2122
|
const result = await client.request({
|
|
1027
2123
|
method: "POST",
|
|
1028
2124
|
path: "/api/v2/guest-token/create",
|
|
1029
|
-
body
|
|
1030
|
-
clientId: params.clientId,
|
|
1031
|
-
dataAppName: params.dataAppName,
|
|
1032
|
-
...params.params
|
|
1033
|
-
},
|
|
2125
|
+
body,
|
|
1034
2126
|
auth: "api"
|
|
1035
2127
|
});
|
|
1036
2128
|
return toolSuccess(
|
|
@@ -1051,7 +2143,7 @@ Next step: Use \`generate_embed_code\` to get copy-paste-ready frontend code.`
|
|
|
1051
2143
|
}
|
|
1052
2144
|
|
|
1053
2145
|
// src/tools/frontend/generate-embed-code.ts
|
|
1054
|
-
import { z as
|
|
2146
|
+
import { z as z27 } from "zod";
|
|
1055
2147
|
|
|
1056
2148
|
// src/templates/react.ts
|
|
1057
2149
|
function kebabCase(str) {
|
|
@@ -1863,22 +2955,22 @@ function registerGenerateEmbedCode(server, _client) {
|
|
|
1863
2955
|
"generate_embed_code",
|
|
1864
2956
|
"Generate framework-specific, copy-paste-ready code to embed a Databrain dashboard or metric. No API call \u2014 generates code locally.",
|
|
1865
2957
|
{
|
|
1866
|
-
embedId:
|
|
1867
|
-
framework:
|
|
1868
|
-
embedType:
|
|
1869
|
-
options:
|
|
1870
|
-
enableDownloadCsv:
|
|
1871
|
-
enableEmailCsv:
|
|
1872
|
-
enableDownloadAllMetrics:
|
|
1873
|
-
enableDownloadAllPdf:
|
|
1874
|
-
disableFullscreen:
|
|
1875
|
-
isHideTablePreview:
|
|
1876
|
-
isHideChartSettings:
|
|
1877
|
-
isStickyDashboardFilters:
|
|
1878
|
-
enableMultiMetricFilters:
|
|
1879
|
-
themeName:
|
|
1880
|
-
customTheme:
|
|
1881
|
-
optionsIcon:
|
|
2958
|
+
embedId: z27.string().describe("The embed ID to generate code for"),
|
|
2959
|
+
framework: z27.enum(FRAMEWORKS).describe("Frontend framework"),
|
|
2960
|
+
embedType: z27.enum(["dashboard", "metric"]).optional().default("dashboard"),
|
|
2961
|
+
options: z27.object({
|
|
2962
|
+
enableDownloadCsv: z27.boolean().optional(),
|
|
2963
|
+
enableEmailCsv: z27.boolean().optional(),
|
|
2964
|
+
enableDownloadAllMetrics: z27.boolean().optional(),
|
|
2965
|
+
enableDownloadAllPdf: z27.boolean().optional(),
|
|
2966
|
+
disableFullscreen: z27.boolean().optional(),
|
|
2967
|
+
isHideTablePreview: z27.boolean().optional(),
|
|
2968
|
+
isHideChartSettings: z27.boolean().optional(),
|
|
2969
|
+
isStickyDashboardFilters: z27.boolean().optional(),
|
|
2970
|
+
enableMultiMetricFilters: z27.boolean().optional(),
|
|
2971
|
+
themeName: z27.string().optional(),
|
|
2972
|
+
customTheme: z27.record(z27.unknown()).optional(),
|
|
2973
|
+
optionsIcon: z27.enum(["kebab-menu-vertical", "download"]).optional()
|
|
1882
2974
|
}).optional()
|
|
1883
2975
|
},
|
|
1884
2976
|
async (params) => {
|
|
@@ -1917,13 +3009,13 @@ ${generated.instructions}`;
|
|
|
1917
3009
|
}
|
|
1918
3010
|
|
|
1919
3011
|
// src/tools/analytics/list-dashboards.ts
|
|
1920
|
-
import { z as
|
|
3012
|
+
import { z as z28 } from "zod";
|
|
1921
3013
|
function registerListDashboards(server, client) {
|
|
1922
3014
|
server.tool(
|
|
1923
3015
|
"list_dashboards",
|
|
1924
3016
|
"List dashboards by data app. Requires an API token.",
|
|
1925
3017
|
{
|
|
1926
|
-
dataAppName:
|
|
3018
|
+
dataAppName: z28.string().optional().describe("Filter by data app name")
|
|
1927
3019
|
},
|
|
1928
3020
|
async (params) => {
|
|
1929
3021
|
try {
|
|
@@ -1957,18 +3049,19 @@ ${formatted}`);
|
|
|
1957
3049
|
}
|
|
1958
3050
|
|
|
1959
3051
|
// src/tools/analytics/list-metrics.ts
|
|
1960
|
-
import { z as
|
|
3052
|
+
import { z as z29 } from "zod";
|
|
1961
3053
|
function registerListMetrics(server, client) {
|
|
1962
3054
|
server.tool(
|
|
1963
3055
|
"list_metrics",
|
|
1964
3056
|
"List metrics available for embedding. Requires an API token.",
|
|
1965
3057
|
{
|
|
1966
|
-
dataAppName:
|
|
1967
|
-
|
|
3058
|
+
dataAppName: z29.string().describe("Name of the data app"),
|
|
3059
|
+
embedId: z29.string().describe("Embed ID"),
|
|
3060
|
+
clientId: z29.string().optional().describe("Client ID for tenant-scoped metrics")
|
|
1968
3061
|
},
|
|
1969
3062
|
async (params) => {
|
|
1970
3063
|
try {
|
|
1971
|
-
const query = { dataAppName: params.dataAppName };
|
|
3064
|
+
const query = { dataAppName: params.dataAppName, embedId: params.embedId };
|
|
1972
3065
|
if (params.clientId) query.clientId = params.clientId;
|
|
1973
3066
|
const result = await client.request({
|
|
1974
3067
|
method: "GET",
|
|
@@ -1996,18 +3089,18 @@ ${formatted}`);
|
|
|
1996
3089
|
}
|
|
1997
3090
|
|
|
1998
3091
|
// src/tools/analytics/query-metric-data.ts
|
|
1999
|
-
import { z as
|
|
3092
|
+
import { z as z30 } from "zod";
|
|
2000
3093
|
function registerQueryMetricData(server, client) {
|
|
2001
3094
|
server.tool(
|
|
2002
3095
|
"query_metric_data",
|
|
2003
3096
|
"Fetch data from a pre-defined metric. Requires an API token.",
|
|
2004
3097
|
{
|
|
2005
|
-
embedId:
|
|
2006
|
-
metricId:
|
|
2007
|
-
clientId:
|
|
2008
|
-
filters:
|
|
2009
|
-
limit:
|
|
2010
|
-
offset:
|
|
3098
|
+
embedId: z30.string().describe("Embed ID"),
|
|
3099
|
+
metricId: z30.string().describe("Metric ID to query"),
|
|
3100
|
+
clientId: z30.string().describe("Client ID for tenant filtering"),
|
|
3101
|
+
filters: z30.array(z30.record(z30.unknown())).optional().describe("Runtime filters"),
|
|
3102
|
+
limit: z30.number().optional().describe("Row limit"),
|
|
3103
|
+
offset: z30.number().optional().describe("Pagination offset")
|
|
2011
3104
|
},
|
|
2012
3105
|
async (params) => {
|
|
2013
3106
|
try {
|
|
@@ -2045,16 +3138,16 @@ function registerQueryMetricData(server, client) {
|
|
|
2045
3138
|
}
|
|
2046
3139
|
|
|
2047
3140
|
// src/tools/analytics/get-dashboard-data.ts
|
|
2048
|
-
import { z as
|
|
3141
|
+
import { z as z31 } from "zod";
|
|
2049
3142
|
function registerGetDashboardData(server, client) {
|
|
2050
3143
|
server.tool(
|
|
2051
3144
|
"get_dashboard_data",
|
|
2052
3145
|
"Fetch data for all metrics in a dashboard. Requires an API token. This is a composite operation that queries each metric.",
|
|
2053
3146
|
{
|
|
2054
|
-
dashboardId:
|
|
2055
|
-
clientId:
|
|
2056
|
-
embedId:
|
|
2057
|
-
filters:
|
|
3147
|
+
dashboardId: z31.string().describe("Dashboard ID"),
|
|
3148
|
+
clientId: z31.string().describe("Client ID for tenant filtering"),
|
|
3149
|
+
embedId: z31.string().describe("Embed ID"),
|
|
3150
|
+
filters: z31.array(z31.record(z31.unknown())).optional().describe("Runtime filters")
|
|
2058
3151
|
},
|
|
2059
3152
|
async (params) => {
|
|
2060
3153
|
try {
|
|
@@ -2062,7 +3155,7 @@ function registerGetDashboardData(server, client) {
|
|
|
2062
3155
|
method: "GET",
|
|
2063
3156
|
path: "/api/v2/data-app/metrics",
|
|
2064
3157
|
auth: "api",
|
|
2065
|
-
query: { dashboardId: params.dashboardId }
|
|
3158
|
+
query: { dashboardId: params.dashboardId, embedId: params.embedId, clientId: params.clientId }
|
|
2066
3159
|
});
|
|
2067
3160
|
if (!Array.isArray(metrics) || metrics.length === 0) {
|
|
2068
3161
|
return toolSuccess("No metrics found in this dashboard.");
|
|
@@ -2114,15 +3207,15 @@ Use \`query_metric_data\` to fetch detailed data for individual metrics.`
|
|
|
2114
3207
|
}
|
|
2115
3208
|
|
|
2116
3209
|
// src/tools/analytics/ask-ai-pilot.ts
|
|
2117
|
-
import { z as
|
|
3210
|
+
import { z as z32 } from "zod";
|
|
2118
3211
|
function registerAskAiPilot(server, client) {
|
|
2119
3212
|
server.tool(
|
|
2120
3213
|
"ask_ai_pilot",
|
|
2121
3214
|
"Send a natural language question to Databrain's AI Pilot. Requires the embed to have AI Pilot enabled and a semantic layer configured.",
|
|
2122
3215
|
{
|
|
2123
|
-
embedId:
|
|
2124
|
-
question:
|
|
2125
|
-
clientId:
|
|
3216
|
+
embedId: z32.string().describe("Embed ID with AI Pilot enabled"),
|
|
3217
|
+
question: z32.string().describe('Natural language question, e.g. "What was total revenue last month?"'),
|
|
3218
|
+
clientId: z32.string().describe("Client ID for tenant filtering")
|
|
2126
3219
|
},
|
|
2127
3220
|
async (params) => {
|
|
2128
3221
|
try {
|
|
@@ -2165,16 +3258,16 @@ ${result.sql}
|
|
|
2165
3258
|
}
|
|
2166
3259
|
|
|
2167
3260
|
// src/tools/analytics/download-metric-csv.ts
|
|
2168
|
-
import { z as
|
|
3261
|
+
import { z as z33 } from "zod";
|
|
2169
3262
|
function registerDownloadMetricCsv(server, client) {
|
|
2170
3263
|
server.tool(
|
|
2171
3264
|
"download_metric_csv",
|
|
2172
3265
|
"Download metric data as CSV. Returns CSV content or a download URL.",
|
|
2173
3266
|
{
|
|
2174
|
-
metricId:
|
|
2175
|
-
embedId:
|
|
2176
|
-
clientId:
|
|
2177
|
-
filters:
|
|
3267
|
+
metricId: z33.string().describe("Metric ID"),
|
|
3268
|
+
embedId: z33.string().describe("Embed ID"),
|
|
3269
|
+
clientId: z33.string().describe("Client ID for tenant filtering"),
|
|
3270
|
+
filters: z33.array(z33.record(z33.unknown())).optional().describe("Runtime filters")
|
|
2178
3271
|
},
|
|
2179
3272
|
async (params) => {
|
|
2180
3273
|
try {
|
|
@@ -2215,13 +3308,13 @@ ${preview}
|
|
|
2215
3308
|
}
|
|
2216
3309
|
|
|
2217
3310
|
// src/tools/analytics/list-scheduled-reports.ts
|
|
2218
|
-
import { z as
|
|
3311
|
+
import { z as z34 } from "zod";
|
|
2219
3312
|
function registerListScheduledReports(server, client) {
|
|
2220
3313
|
server.tool(
|
|
2221
3314
|
"list_scheduled_reports",
|
|
2222
3315
|
"List scheduled email reports for an embed.",
|
|
2223
3316
|
{
|
|
2224
|
-
embedId:
|
|
3317
|
+
embedId: z34.string().describe("Embed ID")
|
|
2225
3318
|
},
|
|
2226
3319
|
async (params) => {
|
|
2227
3320
|
try {
|
|
@@ -2249,13 +3342,13 @@ ${formatted}`);
|
|
|
2249
3342
|
}
|
|
2250
3343
|
|
|
2251
3344
|
// src/tools/analytics/export-dashboard.ts
|
|
2252
|
-
import { z as
|
|
3345
|
+
import { z as z35 } from "zod";
|
|
2253
3346
|
function registerExportDashboard(server, client) {
|
|
2254
3347
|
server.tool(
|
|
2255
3348
|
"export_dashboard",
|
|
2256
3349
|
"Export a dashboard as JSON. Useful for backup or migrating dashboards between environments.",
|
|
2257
3350
|
{
|
|
2258
|
-
dashboardId:
|
|
3351
|
+
dashboardId: z35.string().describe("ID of the dashboard to export")
|
|
2259
3352
|
},
|
|
2260
3353
|
async (params) => {
|
|
2261
3354
|
try {
|
|
@@ -2280,14 +3373,14 @@ function registerExportDashboard(server, client) {
|
|
|
2280
3373
|
}
|
|
2281
3374
|
|
|
2282
3375
|
// src/tools/analytics/import-dashboard.ts
|
|
2283
|
-
import { z as
|
|
3376
|
+
import { z as z36 } from "zod";
|
|
2284
3377
|
function registerImportDashboard(server, client) {
|
|
2285
3378
|
server.tool(
|
|
2286
3379
|
"import_dashboard",
|
|
2287
3380
|
"Import a dashboard from a JSON payload (previously exported via `export_dashboard`).",
|
|
2288
3381
|
{
|
|
2289
|
-
dashboardJson:
|
|
2290
|
-
dataAppName:
|
|
3382
|
+
dashboardJson: z36.string().describe("The full dashboard JSON string from export_dashboard"),
|
|
3383
|
+
dataAppName: z36.string().describe("Name of the target data app")
|
|
2291
3384
|
},
|
|
2292
3385
|
async (params) => {
|
|
2293
3386
|
try {
|
|
@@ -2322,7 +3415,7 @@ Next: Use \`create_embed\` to embed this dashboard.`
|
|
|
2322
3415
|
}
|
|
2323
3416
|
|
|
2324
3417
|
// src/tools/orchestration/setup-embed-interactive.ts
|
|
2325
|
-
import { z as
|
|
3418
|
+
import { z as z37 } from "zod";
|
|
2326
3419
|
function mapError(err) {
|
|
2327
3420
|
if (err instanceof DatabrainApiError) {
|
|
2328
3421
|
const mapped = ERROR_MAP.find((m) => m.match(err));
|
|
@@ -2358,14 +3451,14 @@ var ERROR_MAP = [
|
|
|
2358
3451
|
}
|
|
2359
3452
|
];
|
|
2360
3453
|
var STEPS = ["discover", "choose_app", "ensure_token", "list_embeds", "choose_embed", "validate", "complete"];
|
|
2361
|
-
var stateSchema =
|
|
2362
|
-
dataAppId:
|
|
2363
|
-
dataAppName:
|
|
2364
|
-
embedId:
|
|
2365
|
-
dashboardId:
|
|
2366
|
-
apiToken:
|
|
2367
|
-
clientId:
|
|
2368
|
-
workspaceName:
|
|
3454
|
+
var stateSchema = z37.object({
|
|
3455
|
+
dataAppId: z37.string().optional(),
|
|
3456
|
+
dataAppName: z37.string().optional(),
|
|
3457
|
+
embedId: z37.string().optional(),
|
|
3458
|
+
dashboardId: z37.string().optional(),
|
|
3459
|
+
apiToken: z37.string().optional(),
|
|
3460
|
+
clientId: z37.string().optional(),
|
|
3461
|
+
workspaceName: z37.string().optional()
|
|
2369
3462
|
}).describe("Accumulated setup state from previous steps");
|
|
2370
3463
|
function registerSetupEmbedInteractive(server, client) {
|
|
2371
3464
|
server.tool(
|
|
@@ -2381,9 +3474,9 @@ Steps: discover \u2192 choose_app \u2192 ensure_token \u2192 list_embeds \u2192
|
|
|
2381
3474
|
|
|
2382
3475
|
Pass the accumulated "state" object back on every call so the orchestrator can track progress.`,
|
|
2383
3476
|
{
|
|
2384
|
-
step:
|
|
3477
|
+
step: z37.enum(STEPS).describe("Which step to execute"),
|
|
2385
3478
|
state: stateSchema.optional().describe("State accumulated from previous steps \u2014 pass it back unchanged"),
|
|
2386
|
-
userChoice:
|
|
3479
|
+
userChoice: z37.string().optional().describe("User selection when a previous step returned CHOICE_REQUIRED")
|
|
2387
3480
|
},
|
|
2388
3481
|
async (params) => {
|
|
2389
3482
|
const state = params.state ?? {};
|
|
@@ -2480,10 +3573,38 @@ Next: call \`setup_embed_interactive\` with step \`"list_embeds"\` and state:
|
|
|
2480
3573
|
` + JSON.stringify({ ...state, apiToken: "[CONFIGURED]" }, null, 2) + "\n```"
|
|
2481
3574
|
);
|
|
2482
3575
|
}
|
|
3576
|
+
const existingTokens = await client.request({
|
|
3577
|
+
method: "GET",
|
|
3578
|
+
path: "/api/v2/data-app/api-tokens",
|
|
3579
|
+
auth: "service",
|
|
3580
|
+
query: { dataAppName: state.dataAppName }
|
|
3581
|
+
});
|
|
3582
|
+
if (Array.isArray(existingTokens) && existingTokens.length > 0) {
|
|
3583
|
+
const rotateResult = await client.request({
|
|
3584
|
+
method: "POST",
|
|
3585
|
+
path: "/api/v2/data-app/api-tokens/rotate",
|
|
3586
|
+
body: { dataAppName: state.dataAppName },
|
|
3587
|
+
auth: "service"
|
|
3588
|
+
});
|
|
3589
|
+
client.setRuntimeApiToken(rotateResult.key);
|
|
3590
|
+
return toolSuccess(
|
|
3591
|
+
`An API token existed for **${state.dataAppName}** but wasn't available in this session. Rotated to get a new one.
|
|
3592
|
+
|
|
3593
|
+
**New Token:** \`${rotateResult.key}\`
|
|
3594
|
+
|
|
3595
|
+
\u26A0\uFE0F The old token is now invalid. Save this new token securely \u2014 it will not be shown again.
|
|
3596
|
+
Store it as \`DATABRAIN_API_TOKEN\` in your backend environment.
|
|
3597
|
+
Update any services that used the old token.
|
|
3598
|
+
|
|
3599
|
+
Next: call \`setup_embed_interactive\` with step \`"list_embeds"\` and state:
|
|
3600
|
+
\`\`\`json
|
|
3601
|
+
` + JSON.stringify({ ...state, apiToken: "[REDACTED \u2014 stored in session]" }, null, 2) + "\n```"
|
|
3602
|
+
);
|
|
3603
|
+
}
|
|
2483
3604
|
const result = await client.request({
|
|
2484
3605
|
method: "POST",
|
|
2485
3606
|
path: "/api/v2/data-app/api-tokens",
|
|
2486
|
-
body: { dataAppName: state.dataAppName, name:
|
|
3607
|
+
body: { dataAppName: state.dataAppName, name: `${state.dataAppName}-api-token` },
|
|
2487
3608
|
auth: "service"
|
|
2488
3609
|
});
|
|
2489
3610
|
client.setRuntimeApiToken(result.key);
|
|
@@ -2492,7 +3613,9 @@ Next: call \`setup_embed_interactive\` with step \`"list_embeds"\` and state:
|
|
|
2492
3613
|
|
|
2493
3614
|
**Token:** \`${result.key}\`
|
|
2494
3615
|
|
|
2495
|
-
|
|
3616
|
+
\u26A0\uFE0F Save this token securely \u2014 it will not be shown again.
|
|
3617
|
+
Store it as \`DATABRAIN_API_TOKEN\` in your backend environment. Never expose it in frontend code.
|
|
3618
|
+
This is the **only** API token needed for this data app \u2014 reuse it across sessions.
|
|
2496
3619
|
|
|
2497
3620
|
Next: call \`setup_embed_interactive\` with step \`"list_embeds"\` and state:
|
|
2498
3621
|
\`\`\`json
|
|
@@ -2562,7 +3685,7 @@ Next: call \`setup_embed_interactive\` with step \`"choose_embed"\` and \`userCh
|
|
|
2562
3685
|
const embeds = await client.request({
|
|
2563
3686
|
method: "GET",
|
|
2564
3687
|
path: "/api/v2/data-app/embeds",
|
|
2565
|
-
auth: "
|
|
3688
|
+
auth: "service",
|
|
2566
3689
|
cacheKey: "list_embeds",
|
|
2567
3690
|
cacheTtlMs: 5 * 60 * 1e3
|
|
2568
3691
|
});
|
|
@@ -2672,13 +3795,18 @@ WARNING: The API token from step "create_token" was shown once. If you lost it,
|
|
|
2672
3795
|
// src/tools/registry.ts
|
|
2673
3796
|
function registerAllTools(server, client) {
|
|
2674
3797
|
registerListDatasources(server, client);
|
|
3798
|
+
registerCreateDatasource(server, client);
|
|
3799
|
+
registerSyncDatasource(server, client);
|
|
2675
3800
|
registerCreateDataApp(server, client);
|
|
2676
3801
|
registerListDataApps(server, client);
|
|
2677
3802
|
registerCreateApiToken(server, client);
|
|
2678
3803
|
registerListApiTokens(server, client);
|
|
2679
3804
|
registerRotateApiToken(server, client);
|
|
3805
|
+
registerSetApiToken(server, client);
|
|
2680
3806
|
registerListDatamarts(server, client);
|
|
3807
|
+
registerCreateDatamart(server, client);
|
|
2681
3808
|
registerGetSemanticLayerStatus(server, client);
|
|
3809
|
+
registerGenerateSemanticLayer(server, client);
|
|
2682
3810
|
registerCreateEmbed(server, client);
|
|
2683
3811
|
registerListEmbeds(server, client);
|
|
2684
3812
|
registerGetEmbedDetails(server, client);
|
|
@@ -2686,6 +3814,12 @@ function registerAllTools(server, client) {
|
|
|
2686
3814
|
registerDeleteEmbed(server, client);
|
|
2687
3815
|
registerRenameEmbed(server, client);
|
|
2688
3816
|
registerApplyThemePreset(server, client);
|
|
3817
|
+
registerCustomizeTheme(server, client);
|
|
3818
|
+
registerConfigureChartAppearance(server, client);
|
|
3819
|
+
registerConfigureEmbedOptions(server, client);
|
|
3820
|
+
registerConfigureFilters(server, client);
|
|
3821
|
+
registerConfigureInternationalization(server, client);
|
|
3822
|
+
registerGetPermissionSchema(server);
|
|
2689
3823
|
registerGenerateGuestToken(server, client);
|
|
2690
3824
|
registerGenerateEmbedCode(server, client);
|
|
2691
3825
|
registerListDashboards(server, client);
|
|
@@ -2848,16 +3982,16 @@ All API requests require authentication via one of two token types passed in the
|
|
|
2848
3982
|
\`Authorization\` header:
|
|
2849
3983
|
|
|
2850
3984
|
### Service Token
|
|
2851
|
-
- **Purpose**: Manage workspace-level resources (datasources, data apps, datamarts,
|
|
3985
|
+
- **Purpose**: Manage workspace-level resources (datasources, data apps, datamarts, API tokens).
|
|
2852
3986
|
- **Header**: \`Authorization: Bearer <service-token>\`
|
|
2853
3987
|
- **Scope**: Entire workspace. Found in workspace settings.
|
|
2854
|
-
- **Used for**:
|
|
3988
|
+
- **Used for**: CRUD on datasources, data apps, datamarts, and API token management.
|
|
2855
3989
|
|
|
2856
3990
|
### API Token
|
|
2857
|
-
- **Purpose**:
|
|
3991
|
+
- **Purpose**: Manage data-app-scoped resources and generate guest tokens.
|
|
2858
3992
|
- **Header**: \`Authorization: Bearer <api-token>\`
|
|
2859
|
-
- **Scope**: A single data app.
|
|
2860
|
-
- **Used for**:
|
|
3993
|
+
- **Scope**: A single data app. One API token per data app.
|
|
3994
|
+
- **Used for**: Embed CRUD, dashboard/metric listing, guest token generation, and data queries.
|
|
2861
3995
|
|
|
2862
3996
|
> **Important**: Service Tokens and API Tokens are **not interchangeable**. Use the
|
|
2863
3997
|
> correct token type for each endpoint.
|
|
@@ -2985,10 +4119,12 @@ available from synced datasources when using additional query parameters.
|
|
|
2985
4119
|
### Create Embed
|
|
2986
4120
|
\`\`\`
|
|
2987
4121
|
POST /api/v2/data-app/embeds
|
|
2988
|
-
Authorization: Bearer <
|
|
4122
|
+
Authorization: Bearer <api-token>
|
|
2989
4123
|
Content-Type: application/json
|
|
2990
4124
|
\`\`\`
|
|
2991
4125
|
|
|
4126
|
+
**Note**: Embed endpoints use an **API Token** (data-app-scoped), not a Service Token.
|
|
4127
|
+
|
|
2992
4128
|
Creates an embed configuration linking a dashboard to embeddable settings.
|
|
2993
4129
|
|
|
2994
4130
|
Key body fields:
|
|
@@ -3001,27 +4137,27 @@ Key body fields:
|
|
|
3001
4137
|
### List Embeds
|
|
3002
4138
|
\`\`\`
|
|
3003
4139
|
GET /api/v2/data-app/embeds?dataAppId=<id>
|
|
3004
|
-
Authorization: Bearer <
|
|
4140
|
+
Authorization: Bearer <api-token>
|
|
3005
4141
|
\`\`\`
|
|
3006
4142
|
|
|
3007
4143
|
Returns all embed configurations for a data app.
|
|
3008
4144
|
|
|
3009
4145
|
### Get Embed
|
|
3010
4146
|
\`\`\`
|
|
3011
|
-
GET /api/v2/data-app/embeds
|
|
3012
|
-
Authorization: Bearer <
|
|
4147
|
+
GET /api/v2/data-app/embeds?embedId=<embed-id>
|
|
4148
|
+
Authorization: Bearer <api-token>
|
|
3013
4149
|
\`\`\`
|
|
3014
4150
|
|
|
3015
4151
|
Returns a single embed configuration by ID.
|
|
3016
4152
|
|
|
3017
4153
|
### Update Embed
|
|
3018
4154
|
\`\`\`
|
|
3019
|
-
PUT /api/v2/data-app/embeds
|
|
3020
|
-
Authorization: Bearer <
|
|
4155
|
+
PUT /api/v2/data-app/embeds
|
|
4156
|
+
Authorization: Bearer <api-token>
|
|
3021
4157
|
Content-Type: application/json
|
|
3022
4158
|
\`\`\`
|
|
3023
4159
|
|
|
3024
|
-
|
|
4160
|
+
Body must include \`embedId\` and \`accessSettings\`. Fetch the current embed via GET first and merge your changes to avoid overwriting existing settings.
|
|
3025
4161
|
|
|
3026
4162
|
---
|
|
3027
4163
|
|
|
@@ -3037,12 +4173,17 @@ Content-Type: application/json
|
|
|
3037
4173
|
**Note**: This endpoint uses an **API Token**, not a Service Token.
|
|
3038
4174
|
|
|
3039
4175
|
Key body fields:
|
|
3040
|
-
- \`
|
|
3041
|
-
- \`
|
|
4176
|
+
- \`clientId\` (string, required) - Tenant identifier for multi-tenancy / row-level security
|
|
4177
|
+
- \`dataAppName\` (string, required) - Name of the data app
|
|
4178
|
+
- \`appFilters\` (array) - Global filters applied to all metrics (server-side enforced)
|
|
3042
4179
|
- \`dashboardAppFilters\` (array) - Dashboard-specific filters
|
|
3043
|
-
- \`
|
|
4180
|
+
- \`permissions\` (object) - Runtime permission overrides (e.g. \`isEnableDownloadMetrics\`)
|
|
3044
4181
|
- \`theme\` (object) - Runtime theme overrides
|
|
3045
|
-
- \`
|
|
4182
|
+
- \`hideDashboardFilters\` (array) - Filter names to hide
|
|
4183
|
+
- \`expiryTime\` (number) - Token TTL in milliseconds
|
|
4184
|
+
- \`datasourceName\` (string) - Multi-datasource routing
|
|
4185
|
+
|
|
4186
|
+
> **Rejected fields**: \`allowedEmbeds\` and \`accessSettings\` are NOT accepted and will return 400.
|
|
3046
4187
|
|
|
3047
4188
|
Returns: \`{ "token": "<guest-token>", "expiresAt": "<timestamp>" }\`
|
|
3048
4189
|
|
|
@@ -3074,7 +4215,7 @@ Returns the query result data as rows and columns.
|
|
|
3074
4215
|
### List Dashboards
|
|
3075
4216
|
\`\`\`
|
|
3076
4217
|
GET /api/v2/data-app/dashboards?dataAppId=<id>
|
|
3077
|
-
Authorization: Bearer <
|
|
4218
|
+
Authorization: Bearer <api-token>
|
|
3078
4219
|
\`\`\`
|
|
3079
4220
|
|
|
3080
4221
|
Returns all dashboards in a data app. Note: dashboards are **created in the Databrain
|
|
@@ -3083,7 +4224,7 @@ UI**, not via the API.
|
|
|
3083
4224
|
### List Metrics
|
|
3084
4225
|
\`\`\`
|
|
3085
4226
|
GET /api/v2/data-app/metrics?dashboardId=<id>
|
|
3086
|
-
Authorization: Bearer <
|
|
4227
|
+
Authorization: Bearer <api-token>
|
|
3087
4228
|
\`\`\`
|
|
3088
4229
|
|
|
3089
4230
|
Returns all metrics within a dashboard.
|
|
@@ -4408,23 +5549,40 @@ Set default access settings when creating or updating an embed:
|
|
|
4408
5549
|
|
|
4409
5550
|
### Runtime Overrides via Guest Token
|
|
4410
5551
|
|
|
4411
|
-
Override
|
|
4412
|
-
access where different users get different permissions
|
|
5552
|
+
Override permissions at guest token generation time using the \`permissions\` field.
|
|
5553
|
+
This is useful for role-based access where different users get different permissions.
|
|
5554
|
+
|
|
5555
|
+
> **Important**: The guest token API accepts \`permissions\`, NOT \`accessSettings\`.
|
|
5556
|
+
> Sending \`accessSettings\` or \`allowedEmbeds\` in a guest token request will return a 400 error.
|
|
4413
5557
|
|
|
4414
5558
|
\`\`\`json
|
|
4415
5559
|
{
|
|
4416
|
-
"embedId": "embed-789",
|
|
4417
5560
|
"clientId": "tenant-abc",
|
|
4418
|
-
"
|
|
4419
|
-
|
|
4420
|
-
"
|
|
4421
|
-
"
|
|
4422
|
-
"
|
|
5561
|
+
"dataAppName": "My App",
|
|
5562
|
+
"permissions": {
|
|
5563
|
+
"isEnableManageMetrics": true,
|
|
5564
|
+
"isEnableDownloadMetrics": true,
|
|
5565
|
+
"isEnableUnderlyingData": true,
|
|
5566
|
+
"isEnableCustomizeLayout": true
|
|
4423
5567
|
}
|
|
4424
5568
|
}
|
|
4425
5569
|
\`\`\`
|
|
4426
5570
|
|
|
4427
|
-
|
|
5571
|
+
**Accepted \`permissions\` fields:**
|
|
5572
|
+
| Field | Description |
|
|
5573
|
+
|---|---|
|
|
5574
|
+
| \`isEnableArchiveMetrics\` | Allow archiving metrics |
|
|
5575
|
+
| \`isEnableManageMetrics\` | Allow managing metrics |
|
|
5576
|
+
| \`isEnableCreateDashboardView\` | Allow creating dashboard views |
|
|
5577
|
+
| \`isEnableMetricUpdation\` | Allow editing metrics |
|
|
5578
|
+
| \`isEnableCustomizeLayout\` | Allow layout customization |
|
|
5579
|
+
| \`isEnableUnderlyingData\` | Allow viewing underlying data |
|
|
5580
|
+
| \`isEnableDownloadMetrics\` | Allow downloading metric data |
|
|
5581
|
+
| \`isShowSideBar\` | Show the sidebar |
|
|
5582
|
+
| \`isShowDashboardName\` | Show the dashboard name |
|
|
5583
|
+
| \`isDisableMetricCreation\` | Disable metric creation |
|
|
5584
|
+
|
|
5585
|
+
Guest token permissions **merge with and override** embed-level access settings. Only the
|
|
4428
5586
|
flags you specify are overridden; unspecified flags keep their embed-level values.
|
|
4429
5587
|
|
|
4430
5588
|
### Component-Level Overrides
|
|
@@ -4455,7 +5613,7 @@ Override access settings via the web component \`options\` attribute:
|
|
|
4455
5613
|
When the same flag is set at multiple levels, the highest precedence wins:
|
|
4456
5614
|
|
|
4457
5615
|
1. **Web component \`options\`** (highest - client-side)
|
|
4458
|
-
2. **Guest token \`
|
|
5616
|
+
2. **Guest token \`permissions\`** (server-side, per session)
|
|
4459
5617
|
3. **Embed configuration \`accessSettings\`** (server-side, default)
|
|
4460
5618
|
|
|
4461
5619
|
> **Best Practice**: Set restrictive defaults at the embed level. Use guest token
|
|
@@ -5150,55 +6308,517 @@ function registerAllResources(server) {
|
|
|
5150
6308
|
}
|
|
5151
6309
|
}
|
|
5152
6310
|
|
|
5153
|
-
// src/prompts/
|
|
5154
|
-
var
|
|
6311
|
+
// src/prompts/setup-from-scratch.ts
|
|
6312
|
+
var setupFromScratchPrompt = `You are guiding the user through a complete Databrain setup from scratch. Follow these steps in order, confirming each before moving on.
|
|
6313
|
+
|
|
6314
|
+
**Step 1 \u2014 Connect a Database**
|
|
6315
|
+
Ask the user for their database type (e.g. PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, etc.) and connection credentials (host, port, database name, username, password, or any provider-specific fields).
|
|
6316
|
+
Once you have the details, call \`create_datasource\` with the provided configuration.
|
|
5155
6317
|
|
|
5156
|
-
**Step
|
|
5157
|
-
|
|
5158
|
-
- \`list_datasources\` \u2014 connected databases
|
|
5159
|
-
- \`list_data_apps\` \u2014 workspace containers
|
|
5160
|
-
- \`list_datamarts\` \u2014 configured data models and table selections
|
|
5161
|
-
- \`list_dashboards\` \u2014 created dashboards
|
|
6318
|
+
**Step 2 \u2014 Sync the Schema**
|
|
6319
|
+
After the datasource is created, call \`sync_datasource\` to refresh the schema so Databrain can discover all available tables and columns.
|
|
5162
6320
|
|
|
5163
|
-
|
|
6321
|
+
**Step 3 \u2014 Create a Data App**
|
|
6322
|
+
Call \`create_data_app\` to create a workspace container that will hold dashboards and embeds. Ask the user for a name, or suggest a sensible default.
|
|
5164
6323
|
|
|
5165
|
-
**Step
|
|
5166
|
-
|
|
5167
|
-
- For each data app, list its datasources, datamarts, dashboards, and embeds.
|
|
5168
|
-
- Highlight key details: number of tables in each datamart, whether semantic layers are configured, whether embeds exist, and any multi-tenant settings.
|
|
5169
|
-
- Call out anything that looks incomplete (e.g. a datamart with no dashboard, a dashboard with no embed).
|
|
6324
|
+
**Step 4 \u2014 Ensure an API Token Exists**
|
|
6325
|
+
Call \`list_api_tokens\` for the data app. If a token already exists, ask the user to provide it and call \`set_api_token\`. Only call \`create_api_token\` if **no tokens exist** \u2014 there should be exactly one API token per data app. Warn the user to save the token securely as it will not be shown again.
|
|
5170
6326
|
|
|
5171
|
-
**Step
|
|
5172
|
-
|
|
6327
|
+
**Step 5 \u2014 Create a Datamart**
|
|
6328
|
+
List the synced tables and ask the user which ones they want to include in their datamart. Call \`create_datamart\` with the selected tables. Help the user define joins if multiple tables are selected.
|
|
5173
6329
|
|
|
5174
|
-
|
|
5175
|
-
-
|
|
6330
|
+
**Step 6 \u2014 (Optional) Generate Semantic Layer**
|
|
6331
|
+
Ask the user if they want to enable AI-powered chat on their data. If yes, call \`generate_semantic_layer\` for the datamart. Explain that this lets end-users ask natural-language questions about their data.
|
|
5176
6332
|
|
|
5177
|
-
|
|
5178
|
-
|
|
6333
|
+
**Step 7 \u2014 Create a Dashboard (Manual Step)**
|
|
6334
|
+
Tell the user: "Now open the Databrain UI to create a dashboard. Add metrics and charts using the datamart you just created. Let me know when you're done and I'll help you embed it."
|
|
6335
|
+
Wait for the user to confirm the dashboard is ready before proceeding.
|
|
5179
6336
|
|
|
5180
|
-
|
|
5181
|
-
|
|
6337
|
+
**Step 8 \u2014 Create an Embed**
|
|
6338
|
+
Once the user confirms the dashboard is ready, call \`create_embed\` with recommended settings:
|
|
6339
|
+
- Link it to the dashboard and data app created earlier
|
|
6340
|
+
- Enable reasonable defaults (e.g. theme settings, allowed interactivity)
|
|
5182
6341
|
|
|
5183
|
-
|
|
5184
|
-
|
|
6342
|
+
**Step 9 \u2014 Generate Embed Code**
|
|
6343
|
+
Ask the user which frontend framework they use (React, Next.js, Vue, Angular, vanilla HTML, etc.). Call \`generate_embed_code\` with the chosen framework to produce copy-paste-ready integration code.
|
|
5185
6344
|
|
|
5186
|
-
|
|
5187
|
-
- Suggest running the \`setup-ai-chat\` prompt to enable AI-powered data exploration.
|
|
6345
|
+
**Reference**: Use the \`databrain://getting-started\` resource for additional context on the Databrain entity model and how datasources, data apps, datamarts, dashboards, and embeds relate to each other.
|
|
5188
6346
|
|
|
5189
|
-
If
|
|
5190
|
-
- Confirm the workspace looks complete.
|
|
5191
|
-
- Suggest enabling multi-tenancy if not configured (reference the \`setup-multi-tenant\` prompt).
|
|
5192
|
-
- Suggest regenerating the semantic layer if the schema has changed recently.
|
|
6347
|
+
Walk through each step conversationally. Confirm success at each stage before moving to the next. If any step fails, help the user troubleshoot before continuing.`;
|
|
5193
6348
|
|
|
5194
|
-
|
|
6349
|
+
// src/prompts/setup-ai-chat.ts
|
|
6350
|
+
var setupAiChatPrompt = `You are helping the user enable AI-powered chat (AI Pilot) on their embedded Databrain dashboard. Follow these steps.
|
|
6351
|
+
|
|
6352
|
+
**Step 1 \u2014 Check Semantic Layer Status**
|
|
6353
|
+
Call \`list_datamarts\` to see all available datamarts. For each relevant datamart, check whether a semantic layer has already been generated.
|
|
6354
|
+
|
|
6355
|
+
**Step 2 \u2014 Generate Semantic Layer (if needed)**
|
|
6356
|
+
If no semantic layer exists for the target datamart:
|
|
6357
|
+
- Call \`generate_semantic_layer\` for that datamart.
|
|
6358
|
+
- Explain that the semantic layer teaches the AI about the data's structure, relationships, and business meaning so it can answer natural-language questions accurately.
|
|
6359
|
+
- Poll with \`get_semantic_layer_status\` until the generation is complete. Let the user know the progress.
|
|
6360
|
+
|
|
6361
|
+
If a semantic layer already exists, confirm this with the user and ask if they want to regenerate it (useful after schema changes).
|
|
6362
|
+
|
|
6363
|
+
**Step 3 \u2014 Enable AI Chat on the Embed**
|
|
6364
|
+
Call \`update_embed\` to configure the embed for AI chat mode:
|
|
6365
|
+
- Set \`isAllowAiPilot\` to true to enable the AI chat interface.
|
|
6366
|
+
- Set \`metricCreationMode\` to \`CHAT\` so end-users can ask questions and get chart responses.
|
|
6367
|
+
|
|
6368
|
+
Explain what these settings do:
|
|
6369
|
+
- \`isAllowAiPilot\`: Adds the AI chat icon/panel to the embedded dashboard.
|
|
6370
|
+
- \`metricCreationMode: CHAT\`: Allows the AI to create new metric visualizations in response to user questions.
|
|
6371
|
+
|
|
6372
|
+
**Step 4 \u2014 Generate Updated Frontend Code**
|
|
6373
|
+
Call \`generate_embed_code\` to produce updated integration code that includes the AI chat configuration. Make sure the user replaces their existing embed code with this updated version.
|
|
6374
|
+
|
|
6375
|
+
**Reference**: Use \`databrain://semantic-layer-guide\` for detailed information about how the semantic layer works, how to review and refine it, and best practices for getting accurate AI responses.
|
|
6376
|
+
|
|
6377
|
+
Walk the user through each step. If the semantic layer generation takes time, keep them informed of progress. After setup, suggest they test the AI chat by asking a sample question about their data.
|
|
6378
|
+
|
|
6379
|
+
**Important:** After enabling AI features on the embed, existing guest tokens still carry the old configuration. Remind the user to **regenerate guest tokens** (or reload their app) for changes to take effect.`;
|
|
6380
|
+
|
|
6381
|
+
// src/prompts/brand-my-embed.ts
|
|
6382
|
+
var brandMyEmbedPrompt = `You are helping the user brand and visually customize their embedded Databrain dashboard. Follow these steps.
|
|
6383
|
+
|
|
6384
|
+
**Step 1 \u2014 Identify the Embed**
|
|
6385
|
+
Call \`list_embeds\` to show available embeds. Ask the user which one they want to brand. Then call \`get_embed_details\` to see the current theme and configuration.
|
|
6386
|
+
|
|
6387
|
+
**Step 2 \u2014 Understand Brand Requirements**
|
|
6388
|
+
Ask the user about their brand. Gather as many of these as they can provide:
|
|
6389
|
+
- **Primary brand color** (e.g. "#4F46E5")
|
|
6390
|
+
- **Secondary/accent color**
|
|
6391
|
+
- **Font family** (e.g. Inter, Roboto, Open Sans, system default)
|
|
6392
|
+
- **Style preference** \u2014 modern/rounded vs corporate/sharp vs minimal/clean
|
|
6393
|
+
- **Dark or light mode?**
|
|
6394
|
+
- **Any specific chart colors they want?**
|
|
6395
|
+
|
|
6396
|
+
If they're unsure, suggest starting with a preset (\`light\`, \`dark\`, \`corporate\`, or \`minimal\`) using \`apply_embed_preset\` and refining from there.
|
|
6397
|
+
|
|
6398
|
+
**Step 3 \u2014 Apply Theme**
|
|
6399
|
+
Use \`customize_embed_theme\` with the gathered brand values. Map their preferences to:
|
|
6400
|
+
- \`primaryColor\`, \`backgroundColor\`, \`textColor\` \u2014 from their brand colors
|
|
6401
|
+
- \`fontFamily\` \u2014 from their font choice
|
|
6402
|
+
- \`cardBorderRadius\` \u2014 "12px" for modern/rounded, "4px" for corporate, "0px" for minimal
|
|
6403
|
+
- \`cardShadow\` \u2014 subtle shadow for modern, "none" for minimal
|
|
6404
|
+
- \`dashboardBackgroundColor\` \u2014 slightly off-white for light, dark gray for dark
|
|
6405
|
+
- \`chartColors\` \u2014 if they provided specific colors, use those
|
|
6406
|
+
|
|
6407
|
+
Also set component-level theme if they have button/checkbox color preferences:
|
|
6408
|
+
- \`buttonPrimaryColor\`, \`buttonPrimaryTextColor\`
|
|
6409
|
+
- \`checkboxCheckedColor\`, \`switchEnabledColor\`
|
|
6410
|
+
|
|
6411
|
+
**Step 4 \u2014 Configure Chart Appearance**
|
|
6412
|
+
Use \`configure_chart_appearance\` to fine-tune charts:
|
|
6413
|
+
- Set \`legendPosition\` \u2014 "bottom-center" is a safe default
|
|
6414
|
+
- Set \`legendShape\` \u2014 "circle" for modern, "rect" for corporate
|
|
6415
|
+
- Set \`legendLayout\` \u2014 "horizontal" usually works best
|
|
6416
|
+
- Set chart colors if not already done via theme
|
|
6417
|
+
|
|
6418
|
+
Ask if they want to customize tooltip fonts, axis styling, or label positions.
|
|
6419
|
+
|
|
6420
|
+
**Step 5 \u2014 Configure Display Options**
|
|
6421
|
+
Use \`configure_embed_options\` to clean up the UI:
|
|
6422
|
+
- Ask if they want to hide the dashboard name (\`hideDashboardName\`)
|
|
6423
|
+
- Ask about card shadows and borders
|
|
6424
|
+
- Ask about download/export options they want to show
|
|
6425
|
+
- Suggest disabling chart settings and table preview for a cleaner look for end-users
|
|
6426
|
+
|
|
6427
|
+
**Step 6 \u2014 Preview and Iterate**
|
|
6428
|
+
Tell the user to refresh their embed to see the changes. Ask:
|
|
6429
|
+
- "Does this match your brand?"
|
|
6430
|
+
- "Anything you'd like to adjust \u2014 colors, fonts, spacing, chart styling?"
|
|
6431
|
+
|
|
6432
|
+
Iterate by calling the relevant tools again with adjusted values. Remember: all customization tools merge with existing config, so they only need to send what changed.
|
|
6433
|
+
|
|
6434
|
+
**Important:** After any theme or styling changes, existing guest tokens still carry the old configuration. Remind the user to **regenerate guest tokens** (or reload their app) for changes to take effect.
|
|
6435
|
+
|
|
6436
|
+
**Reference**: Use \`databrain://theme-reference\` for the full theme schema and \`databrain://web-component-reference\` for all component attributes.`;
|
|
6437
|
+
|
|
6438
|
+
// src/prompts/localize-embed.ts
|
|
6439
|
+
var localizeEmbedPrompt = `You are helping the user set up multi-language support for their embedded Databrain dashboard. Follow these steps.
|
|
6440
|
+
|
|
6441
|
+
**Step 1 \u2014 Identify the Embed and Target Languages**
|
|
6442
|
+
Call \`list_embeds\` and ask the user which embed to localize. Then ask:
|
|
6443
|
+
- Which languages do they need? (Supported: English, French, Spanish, German, Italian, Portuguese, Chinese, Japanese, Korean, Hindi, Arabic, Russian)
|
|
6444
|
+
- What is the default/primary language?
|
|
6445
|
+
|
|
6446
|
+
**Step 2 \u2014 Set the Primary Language**
|
|
6447
|
+
Use \`configure_internationalization\` with the \`language\` parameter set to their primary language code (e.g. "fr" for French).
|
|
6448
|
+
|
|
6449
|
+
**Step 3 \u2014 Gather Translations**
|
|
6450
|
+
Call \`get_embed_details\` to understand what text is in the dashboard. Then help the user build translations:
|
|
6451
|
+
|
|
6452
|
+
Ask the user to list the key text elements in their dashboard:
|
|
6453
|
+
- Dashboard title
|
|
6454
|
+
- Metric titles and descriptions
|
|
6455
|
+
- Filter labels
|
|
6456
|
+
- Any footnotes or annotations
|
|
6457
|
+
|
|
6458
|
+
For each text element, ask for translations in their target languages. Build translation entries like:
|
|
6459
|
+
\`\`\`
|
|
6460
|
+
{ key: "Revenue", translations: { fr: "Revenus", es: "Ingresos", de: "Einnahmen" } }
|
|
6461
|
+
\`\`\`
|
|
6462
|
+
|
|
6463
|
+
**Important rules for translation keys:**
|
|
6464
|
+
- Keys must match the dashboard text EXACTLY (case-sensitive)
|
|
6465
|
+
- Define longer keys before shorter ones to avoid partial matches
|
|
6466
|
+
- If a translation is missing for a language, the original key text is shown as fallback
|
|
6467
|
+
|
|
6468
|
+
**Step 4 \u2014 Apply Translations**
|
|
6469
|
+
Use \`configure_internationalization\` with the \`translations\` array containing all entries.
|
|
6470
|
+
|
|
6471
|
+
**Step 5 \u2014 Configure Calendar (if needed)**
|
|
6472
|
+
Ask if they need a different calendar system:
|
|
6473
|
+
- \`"default"\` \u2014 standard Gregorian calendar
|
|
6474
|
+
- \`"ind"\` \u2014 Indian calendar format
|
|
6475
|
+
|
|
6476
|
+
Set via \`calendarType\` if needed.
|
|
6477
|
+
|
|
6478
|
+
**Step 6 \u2014 Set Custom Messages**
|
|
6479
|
+
Ask if they want to translate system messages:
|
|
6480
|
+
- Token expiry message (shown when session expires)
|
|
6481
|
+
- Token absent message (shown when no auth token)
|
|
6482
|
+
|
|
6483
|
+
Use \`configure_internationalization\` with \`tokenExpiryMessage\` and \`tokenAbsentMessage\` in their primary language.
|
|
6484
|
+
|
|
6485
|
+
**Step 7 \u2014 Test**
|
|
6486
|
+
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.
|
|
6487
|
+
|
|
6488
|
+
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.
|
|
6489
|
+
|
|
6490
|
+
**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.`;
|
|
6491
|
+
|
|
6492
|
+
// src/prompts/secure-my-embed.ts
|
|
6493
|
+
var secureMyEmbedPrompt = `You are helping the user secure and configure permissions for their embedded Databrain dashboard. Follow these steps.
|
|
6494
|
+
|
|
6495
|
+
**Step 1 \u2014 Understand User Roles**
|
|
6496
|
+
Ask the user about their end-user roles. Common patterns:
|
|
6497
|
+
- **Viewer** \u2014 can see data, maybe filter, but can't modify anything
|
|
6498
|
+
- **Analyst** \u2014 can export data, drill down, use filters
|
|
6499
|
+
- **Builder/Power User** \u2014 can create/edit metrics, change layouts
|
|
6500
|
+
- **Admin** \u2014 full access including AI features
|
|
6501
|
+
|
|
6502
|
+
Ask: "What roles do your end-users have? What should each role be allowed to do?"
|
|
6503
|
+
|
|
6504
|
+
**Step 2 \u2014 Lock Down the Embed Defaults**
|
|
6505
|
+
Use \`configure_embed_options\` to set restrictive defaults (principle of least privilege):
|
|
6506
|
+
|
|
6507
|
+
\`\`\`
|
|
6508
|
+
configure_embed_options(embedId,
|
|
6509
|
+
disableMetricCreation: true,
|
|
6510
|
+
disableMetricUpdation: true,
|
|
6511
|
+
disableMetricDeletion: true,
|
|
6512
|
+
disableManageMetrics: true,
|
|
6513
|
+
disableLayoutCustomization: true,
|
|
6514
|
+
disableSaveLayout: true,
|
|
6515
|
+
disableUnderlyingData: true,
|
|
6516
|
+
disableScheduleEmailReports: true,
|
|
6517
|
+
enableDownloadCsv: false,
|
|
6518
|
+
enableDownloadAllPdf: false)
|
|
6519
|
+
\`\`\`
|
|
6520
|
+
|
|
6521
|
+
Explain: "We start with everything locked down, then use guest token permissions to grant access per role."
|
|
6522
|
+
|
|
6523
|
+
**Step 3 \u2014 Create Role-Based Guest Token Examples**
|
|
6524
|
+
Show the user how to generate tokens for each role:
|
|
6525
|
+
|
|
6526
|
+
**Viewer (default \u2014 no overrides needed):**
|
|
6527
|
+
\`\`\`
|
|
6528
|
+
generate_guest_token(clientId, dataAppName)
|
|
6529
|
+
\`\`\`
|
|
6530
|
+
|
|
6531
|
+
**Analyst (can export and drill down):**
|
|
6532
|
+
\`\`\`
|
|
6533
|
+
generate_guest_token(clientId, dataAppName, {
|
|
6534
|
+
permissions: {
|
|
6535
|
+
isEnableDownloadMetrics: true,
|
|
6536
|
+
isEnableUnderlyingData: true
|
|
6537
|
+
}
|
|
6538
|
+
})
|
|
6539
|
+
\`\`\`
|
|
6540
|
+
|
|
6541
|
+
**Builder (can create and edit):**
|
|
6542
|
+
\`\`\`
|
|
6543
|
+
generate_guest_token(clientId, dataAppName, {
|
|
6544
|
+
permissions: {
|
|
6545
|
+
isEnableManageMetrics: true,
|
|
6546
|
+
isEnableMetricUpdation: true,
|
|
6547
|
+
isEnableCustomizeLayout: true
|
|
6548
|
+
}
|
|
6549
|
+
})
|
|
6550
|
+
\`\`\`
|
|
6551
|
+
|
|
6552
|
+
**Admin (full access with AI):**
|
|
6553
|
+
\`\`\`
|
|
6554
|
+
generate_guest_token(clientId, dataAppName, {
|
|
6555
|
+
permissions: {
|
|
6556
|
+
isEnableManageMetrics: true,
|
|
6557
|
+
isEnableMetricUpdation: true,
|
|
6558
|
+
isEnableCustomizeLayout: true,
|
|
6559
|
+
isEnableDownloadMetrics: true,
|
|
6560
|
+
isEnableUnderlyingData: true
|
|
6561
|
+
},
|
|
6562
|
+
accessSettings: {
|
|
6563
|
+
isAllowAiPilot: true,
|
|
6564
|
+
isAllowAiSummary: true
|
|
6565
|
+
}
|
|
6566
|
+
})
|
|
6567
|
+
\`\`\`
|
|
6568
|
+
|
|
6569
|
+
**Step 4 \u2014 Multi-Tenancy Check**
|
|
6570
|
+
Ask: "Do different customers/organizations see different data?"
|
|
6571
|
+
|
|
6572
|
+
If yes:
|
|
6573
|
+
- Explain that \`clientId\` in the guest token controls row-level security
|
|
6574
|
+
- The \`clientId\` must match values in the datamart's client column
|
|
6575
|
+
- Suggest the \`setup-multi-tenant\` prompt for full multi-tenancy setup
|
|
6576
|
+
|
|
6577
|
+
**Step 5 \u2014 Security Checklist**
|
|
6578
|
+
Walk through this checklist with the user:
|
|
6579
|
+
|
|
6580
|
+
1. \u2705 API token is stored server-side only (never in frontend code)
|
|
6581
|
+
2. \u2705 Guest tokens are generated server-side per request
|
|
6582
|
+
3. \u2705 Guest tokens have appropriate expiry time
|
|
6583
|
+
4. \u2705 Embed defaults are restrictive (locked down in Step 2)
|
|
6584
|
+
5. \u2705 Role-based permissions are granted via guest token overrides
|
|
6585
|
+
6. \u2705 Multi-tenancy is configured if serving multiple customers
|
|
6586
|
+
7. \u2705 Download restrictions are set (\`disableDownloadDataNoFilters\` if needed)
|
|
6587
|
+
|
|
6588
|
+
**Step 6 \u2014 Generate Secure Frontend Code**
|
|
6589
|
+
Call \`generate_embed_code\` for their framework. Emphasize that the generated code includes a backend route for token generation \u2014 this is the secure pattern.
|
|
6590
|
+
|
|
6591
|
+
**Important:** After changing embed permissions or access 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.
|
|
6592
|
+
|
|
6593
|
+
**Reference**: Use \`databrain://self-serve-reference\` for all permission flags and \`databrain://multi-tenancy-guide\` for row-level security details.`;
|
|
6594
|
+
|
|
6595
|
+
// src/prompts/setup-filters.ts
|
|
6596
|
+
var setupFiltersPrompt = `You are helping the user configure dashboard filters for their embedded Databrain dashboard. Follow these steps.
|
|
6597
|
+
|
|
6598
|
+
**Step 1 \u2014 Understand the Data**
|
|
6599
|
+
Call \`list_embeds\` and ask which embed to configure. Then call \`get_embed_details\` to see the current filter configuration.
|
|
6600
|
+
|
|
6601
|
+
Also call \`list_datamarts\` to understand what tables and columns are available \u2014 this helps suggest appropriate filters.
|
|
6602
|
+
|
|
6603
|
+
**Step 2 \u2014 Identify Filter Needs**
|
|
6604
|
+
Ask the user:
|
|
6605
|
+
- "What filters do your end-users need?" (e.g. date range, region, product category, status)
|
|
6606
|
+
- "Should any filters have default values?"
|
|
6607
|
+
- "Are there filters you want to hide from end-users?" (e.g. internal IDs)
|
|
6608
|
+
- "Do any filters have very large option lists?" (100+ options \u2014 these benefit from SQL-based filters)
|
|
6609
|
+
|
|
6610
|
+
**Step 3 \u2014 Configure String Filters**
|
|
6611
|
+
For text/category filters, use \`configure_filters\` with \`stringFilters\`:
|
|
6612
|
+
|
|
6613
|
+
\`\`\`
|
|
6614
|
+
stringFilters: [{
|
|
6615
|
+
filterName: "Region",
|
|
6616
|
+
options: [
|
|
6617
|
+
{ value: "US", label: "United States" },
|
|
6618
|
+
{ value: "EU", label: "Europe" },
|
|
6619
|
+
{ value: "APAC", label: "Asia Pacific" }
|
|
6620
|
+
],
|
|
6621
|
+
defaultOption: "US"
|
|
6622
|
+
}]
|
|
6623
|
+
\`\`\`
|
|
6624
|
+
|
|
6625
|
+
For multi-select defaults, use an array: \`defaultOption: ["US", "EU"]\`
|
|
6626
|
+
|
|
6627
|
+
**Step 4 \u2014 Configure Date Filters**
|
|
6628
|
+
For date range filters, use \`dateFilters\`:
|
|
6629
|
+
|
|
6630
|
+
\`\`\`
|
|
6631
|
+
dateFilters: [{
|
|
6632
|
+
filterName: "Date Range",
|
|
6633
|
+
startDate: "2024-01-01",
|
|
6634
|
+
endDate: "2024-12-31",
|
|
6635
|
+
datePresetOptions: [
|
|
6636
|
+
{ label: "Last 30 Days", type: "Last", interval: "Days" },
|
|
6637
|
+
{ label: "This Quarter", type: "This", interval: "Quarter" },
|
|
6638
|
+
{ label: "This Year", type: "This", interval: "Year" }
|
|
6639
|
+
]
|
|
6640
|
+
}]
|
|
6641
|
+
\`\`\`
|
|
6642
|
+
|
|
6643
|
+
**Step 5 \u2014 Configure Number Filters**
|
|
6644
|
+
For numeric range filters, use \`numberFilters\`:
|
|
6645
|
+
|
|
6646
|
+
\`\`\`
|
|
6647
|
+
numberFilters: [{
|
|
6648
|
+
filterName: "Revenue",
|
|
6649
|
+
min: 0,
|
|
6650
|
+
max: 1000000
|
|
6651
|
+
}]
|
|
6652
|
+
\`\`\`
|
|
6653
|
+
|
|
6654
|
+
**Step 6 \u2014 SQL-Based Filters (for large datasets)**
|
|
6655
|
+
If a filter has 100+ options (e.g. company names, product SKUs), use \`sqlFilters\`:
|
|
6656
|
+
|
|
6657
|
+
\`\`\`
|
|
6658
|
+
sqlFilters: [{
|
|
6659
|
+
filterName: "Company",
|
|
6660
|
+
sql: "SELECT DISTINCT \\"company_name\\" FROM \\"companies\\" ORDER BY \\"company_name\\"",
|
|
6661
|
+
columnName: "company_name"
|
|
6662
|
+
}]
|
|
6663
|
+
\`\`\`
|
|
6664
|
+
|
|
6665
|
+
Explain: "SQL filters dynamically fetch options from the database, so they're always up-to-date and don't bloat the embed configuration."
|
|
6666
|
+
|
|
6667
|
+
**Step 7 \u2014 Hide Unnecessary Filters**
|
|
6668
|
+
If the user has filters that should be hidden (e.g. internal IDs, tenant columns already handled by clientId):
|
|
6669
|
+
|
|
6670
|
+
\`\`\`
|
|
6671
|
+
hideDashboardFilters: ["internal_id", "tenant_id"]
|
|
6672
|
+
\`\`\`
|
|
6673
|
+
|
|
6674
|
+
**Step 8 \u2014 Configure Date Picker Options**
|
|
6675
|
+
Ask if they want to hide certain date picker presets:
|
|
6676
|
+
|
|
6677
|
+
\`\`\`
|
|
6678
|
+
hideDatePickerOptions: ["yesterday", "last week"]
|
|
6679
|
+
\`\`\`
|
|
6680
|
+
|
|
6681
|
+
**Step 9 \u2014 Metric Filter Position**
|
|
6682
|
+
Ask: "Do you want metric-level filters inside the card or outside?"
|
|
6683
|
+
- \`"inside"\` \u2014 compact, within each metric card (default)
|
|
6684
|
+
- \`"outside"\` \u2014 above the card, more prominent
|
|
6685
|
+
|
|
6686
|
+
Also ask if they want cross-metric filtering (\`enableMultiMetricFilters\`).
|
|
6687
|
+
|
|
6688
|
+
**Step 10 \u2014 Server-Side Filters (via Guest Token)**
|
|
6689
|
+
If the user needs filters that end-users cannot remove (e.g. for security):
|
|
6690
|
+
- Explain that \`appFilters\` in \`generate_guest_token\` are server-side enforced
|
|
6691
|
+
- These are invisible to the end-user and cannot be overridden
|
|
6692
|
+
- Perfect for tenant filtering, role-based data access, or compliance requirements
|
|
6693
|
+
|
|
6694
|
+
Show an example:
|
|
6695
|
+
\`\`\`
|
|
6696
|
+
generate_guest_token(clientId, dataAppName, {
|
|
6697
|
+
appFilters: [{
|
|
6698
|
+
columnName: "region",
|
|
6699
|
+
tableName: "sales",
|
|
6700
|
+
operator: "IN",
|
|
6701
|
+
value: ["US", "CA"],
|
|
6702
|
+
type: "string"
|
|
6703
|
+
}]
|
|
6704
|
+
})
|
|
6705
|
+
\`\`\`
|
|
6706
|
+
|
|
6707
|
+
**Important:** After changing filter 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.
|
|
6708
|
+
|
|
6709
|
+
**Reference**: Use \`databrain://filter-reference\` for all filter types, operators, and combination rules.`;
|
|
6710
|
+
|
|
6711
|
+
// src/prompts/manage-embeds.ts
|
|
6712
|
+
var manageEmbedsPrompt = `You are helping the user manage their Databrain embed configurations \u2014 list, compare, rename, delete, and maintain tokens. Follow these steps based on what the user needs.
|
|
6713
|
+
|
|
6714
|
+
**Step 1 \u2014 Understand the Task**
|
|
6715
|
+
Ask the user what they want to do:
|
|
6716
|
+
1. **List and compare embeds** \u2014 see all embeds and their configurations
|
|
6717
|
+
2. **Rename an embed** \u2014 change the display name
|
|
6718
|
+
3. **Delete an embed** \u2014 remove an embed configuration
|
|
6719
|
+
4. **Rotate an API token** \u2014 invalidate an old token and get a new one
|
|
6720
|
+
5. **Review embed settings** \u2014 audit theme, permissions, and options
|
|
6721
|
+
|
|
6722
|
+
**For Listing and Comparing Embeds:**
|
|
6723
|
+
1. Call \`list_embeds\` to show all embed configurations
|
|
6724
|
+
2. For detailed comparison, call \`get_embed_details\` on each embed the user wants to compare
|
|
6725
|
+
3. Present a comparison table showing:
|
|
6726
|
+
- Embed name and ID
|
|
6727
|
+
- Linked dashboard
|
|
6728
|
+
- Theme (preset or custom)
|
|
6729
|
+
- Key permissions (metric creation, downloads, AI pilot)
|
|
6730
|
+
- Multi-tenancy settings
|
|
6731
|
+
|
|
6732
|
+
**For Renaming an Embed:**
|
|
6733
|
+
1. Call \`list_embeds\` to show current names
|
|
6734
|
+
2. Ask which embed to rename and what the new name should be
|
|
6735
|
+
3. Call \`rename_embed\` with the embed ID and new name
|
|
6736
|
+
4. Confirm the rename succeeded
|
|
6737
|
+
|
|
6738
|
+
**For Deleting an Embed:**
|
|
6739
|
+
1. Call \`list_embeds\` to show embeds
|
|
6740
|
+
2. Ask which embed to delete
|
|
6741
|
+
3. \u26A0\uFE0F **Warn the user**: "Deleting an embed is permanent. Any frontend code using this embed ID will stop working. Guest tokens generated for this embed will be invalidated."
|
|
6742
|
+
4. Ask for explicit confirmation before proceeding
|
|
6743
|
+
5. Call \`delete_embed\` with the embed ID
|
|
6744
|
+
6. Confirm deletion
|
|
6745
|
+
|
|
6746
|
+
**For Rotating an API Token:**
|
|
6747
|
+
1. Call \`list_api_tokens\` to show existing tokens
|
|
6748
|
+
2. Explain: "Rotating a token invalidates the old one immediately and generates a new one. Any backend services using the old token will need to be updated."
|
|
6749
|
+
3. Ask for confirmation
|
|
6750
|
+
4. Call \`rotate_api_token\` with the token details
|
|
6751
|
+
5. Show the new token and warn: "Save this securely \u2014 it will not be shown again"
|
|
6752
|
+
6. Remind the user to update:
|
|
6753
|
+
- Their backend \`DATABRAIN_API_TOKEN\` env var
|
|
6754
|
+
- Any CI/CD secrets
|
|
6755
|
+
- Their MCP server configuration (if using env var)
|
|
6756
|
+
|
|
6757
|
+
**For Reviewing Embed Settings:**
|
|
6758
|
+
1. Call \`get_embed_details\` for the embed
|
|
6759
|
+
2. Present a clear summary organized by category:
|
|
6760
|
+
|
|
6761
|
+
**Theme:**
|
|
6762
|
+
- Current preset or custom theme values
|
|
6763
|
+
- Suggest \`customize_embed_theme\` if they want changes
|
|
6764
|
+
|
|
6765
|
+
**Permissions / Access Settings:**
|
|
6766
|
+
- What's enabled vs disabled
|
|
6767
|
+
- Suggest \`configure_embed_options\` for changes
|
|
6768
|
+
|
|
6769
|
+
**Filters:**
|
|
6770
|
+
- Current filter configuration
|
|
6771
|
+
- Suggest \`configure_filters\` for changes
|
|
6772
|
+
|
|
6773
|
+
**Internationalization:**
|
|
6774
|
+
- Current language and translations
|
|
6775
|
+
- Suggest \`configure_internationalization\` for changes
|
|
6776
|
+
|
|
6777
|
+
3. Ask: "Would you like to modify any of these settings?"
|
|
6778
|
+
|
|
6779
|
+
**Tips:**
|
|
6780
|
+
- Embed IDs are permanent \u2014 even if you rename an embed, the ID stays the same
|
|
6781
|
+
- Before deleting an embed, consider if you can reconfigure it instead
|
|
6782
|
+
- After rotating a token, use \`set_api_token\` to update the current MCP session
|
|
6783
|
+
- Regular token rotation is a security best practice \u2014 consider rotating every 90 days
|
|
6784
|
+
- After any embed setting changes, existing guest tokens still carry the old configuration \u2014 **regenerate guest tokens** for changes to take effect`;
|
|
5195
6785
|
|
|
5196
6786
|
// src/prompts/registry.ts
|
|
5197
6787
|
var prompts = [
|
|
5198
6788
|
{
|
|
5199
|
-
name: "
|
|
5200
|
-
description: "
|
|
5201
|
-
content:
|
|
6789
|
+
name: "setup-from-scratch",
|
|
6790
|
+
description: "Full guided setup: connect a datasource, create datamarts, build a dashboard, create an embed, and generate frontend code",
|
|
6791
|
+
content: setupFromScratchPrompt
|
|
6792
|
+
},
|
|
6793
|
+
{
|
|
6794
|
+
name: "brand-my-embed",
|
|
6795
|
+
description: "Customize colors, fonts, chart styles, and component theme to match your brand",
|
|
6796
|
+
content: brandMyEmbedPrompt
|
|
6797
|
+
},
|
|
6798
|
+
{
|
|
6799
|
+
name: "localize-embed",
|
|
6800
|
+
description: "Set up multi-language support with translations and locale settings",
|
|
6801
|
+
content: localizeEmbedPrompt
|
|
6802
|
+
},
|
|
6803
|
+
{
|
|
6804
|
+
name: "secure-my-embed",
|
|
6805
|
+
description: "Configure permissions, access controls, and role-based guest tokens",
|
|
6806
|
+
content: secureMyEmbedPrompt
|
|
6807
|
+
},
|
|
6808
|
+
{
|
|
6809
|
+
name: "setup-filters",
|
|
6810
|
+
description: "Configure dashboard filters: string, date, number, SQL-based, and server-side filters",
|
|
6811
|
+
content: setupFiltersPrompt
|
|
6812
|
+
},
|
|
6813
|
+
{
|
|
6814
|
+
name: "setup-ai-chat",
|
|
6815
|
+
description: "Enable AI Pilot (natural language Q&A) on your embedded dashboard",
|
|
6816
|
+
content: setupAiChatPrompt
|
|
6817
|
+
},
|
|
6818
|
+
{
|
|
6819
|
+
name: "manage-embeds",
|
|
6820
|
+
description: "List, compare, rename, delete embeds and rotate API tokens",
|
|
6821
|
+
content: manageEmbedsPrompt
|
|
5202
6822
|
}
|
|
5203
6823
|
];
|
|
5204
6824
|
function registerAllPrompts(server) {
|