@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/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 the `create_api_token` tool to generate one." : " Set it in your MCP server environment variables."}`
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/data-app/create-data-app.ts
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: z2.string().describe("Unique alphanumeric name for the data app")
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 z3 } from "zod";
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: z3.string().optional().describe("Filter by workspace name")
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 z4 } from "zod";
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. The token is shown once \u2014 save it securely. The server will use it for subsequent embed/guest-token operations in this session.",
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: z4.string().describe("Name of the data app to scope the token to"),
374
- name: z4.string().describe('Descriptive label for the token (e.g., "Production API Key")')
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. You can also set it as the \`DATABRAIN_API_TOKEN\` environment variable for future sessions.
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 z5 } from "zod";
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: z5.string().describe("Name of the data app")
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 z6 } from "zod";
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: z6.string().describe("Name of the data app")
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 z7 } from "zod";
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: z7.string().optional().describe("Filter by data app name")
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 z8 } from "zod";
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: z8.string().describe("ID of the datamart")
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 z9 } from "zod";
557
- var accessSettingsSchema = z9.object({
558
- datamartName: z9.string(),
559
- isAllowAiPilot: z9.boolean().optional().default(false),
560
- isAllowEmailReports: z9.boolean(),
561
- isAllowManageMetrics: z9.boolean(),
562
- isAllowCreateDashboardView: z9.boolean(),
563
- isAllowMetricCreation: z9.boolean(),
564
- isAllowMetricDeletion: z9.boolean(),
565
- isAllowMetricLayoutChange: z9.boolean(),
566
- isAllowMetricUpdate: z9.boolean(),
567
- isAllowUnderlyingData: z9.boolean(),
568
- metricCreationMode: z9.enum(["DRAG_DROP", "CHAT"]),
569
- joinModel: z9.enum(["single", "multi"]).optional().default("multi"),
570
- tableTenancySettings: z9.array(z9.object({
571
- name: z9.string(),
572
- clientColumn: z9.string()
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: z9.string().describe("ID of the dashboard to embed"),
581
- embedType: z9.enum(["dashboard", "metric"]).describe("Type of embed"),
582
- workspaceName: z9.string().describe("Workspace name"),
583
- metricId: z9.string().optional().describe('Required if embedType is "metric"'),
584
- name: z9.string().optional().describe("Human-readable name for the embed"),
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 z10 } from "zod";
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: z10.string().optional().describe("Filter by data app name")
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 z11 } from "zod";
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: z11.string().describe("ID of the embed")
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 z12 } from "zod";
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: z12.string().describe("ID of the embed to update"),
697
- name: z12.string().optional().describe("New name for the embed"),
698
- accessSettings: z12.record(z12.unknown()).optional().describe(
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: z12.record(z12.unknown()).optional().describe(
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 = { embedId: params.embedId };
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 z13 } from "zod";
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: z13.string().describe("ID of the embed to delete")
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 z14 } from "zod";
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: z14.string().describe("ID of the embed to rename"),
764
- name: z14.string().describe("New name for the embed")
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 z15 } from "zod";
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: z15.string().describe("ID of the embed to update"),
935
- themePreset: z15.enum(["light", "dark", "corporate", "minimal"]).optional().describe("Theme preset to apply"),
936
- accessPreset: z15.enum(["view-only", "power-user", "export-only", "ai-enabled"]).optional().describe("Access settings preset to apply")
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
- if (!params.themePreset && !params.accessPreset) {
940
- return toolError("Provide at least one of themePreset or accessPreset.");
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 = { embedId: params.embedId };
944
- if (params.themePreset) {
945
- body.theme = THEME_PRESETS[params.themePreset];
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(`Failed to apply preset: ${stringify(result.error)}`);
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
- `Preset applied to embed "${params.embedId}": ${applied.join(", ")}.${details}`
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 z16 } from "zod";
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: z16.string().describe("Unique end-user identifier for multi-tenant filtering"),
994
- dataAppName: z16.string().describe("Name of the data app"),
995
- params: z16.object({
996
- allowedEmbeds: z16.array(z16.string()).optional().describe("Array of embed IDs to whitelist"),
997
- timezone: z16.string().optional().describe('Timezone, e.g. "America/New_York"'),
998
- appFilters: z16.array(z16.object({
999
- metricId: z16.string(),
1000
- values: z16.array(z16.unknown())
1001
- })).optional(),
1002
- dashboardAppFilters: z16.array(z16.object({
1003
- dashboardId: z16.string(),
1004
- values: z16.array(z16.unknown())
1005
- })).optional(),
1006
- permissions: z16.object({
1007
- isEnableArchiveMetrics: z16.boolean().optional(),
1008
- isEnableManageMetrics: z16.boolean().optional(),
1009
- isEnableCreateDashboardView: z16.boolean().optional(),
1010
- isEnableMetricUpdation: z16.boolean().optional(),
1011
- isEnableCustomizeLayout: z16.boolean().optional(),
1012
- isEnableUnderlyingData: z16.boolean().optional(),
1013
- isEnableDownloadMetrics: z16.boolean().optional(),
1014
- isShowSideBar: z16.boolean().optional(),
1015
- isShowDashboardName: z16.boolean().optional(),
1016
- isDisableMetricCreation: z16.boolean().optional()
1017
- }).optional(),
1018
- hideDashboardFilters: z16.array(z16.string()).optional(),
1019
- expiryTime: z16.number().optional().describe("Token TTL in milliseconds"),
1020
- datasourceName: z16.string().optional(),
1021
- userIdentifier: z16.string().optional()
1022
- }).optional().describe("Optional token parameters")
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 z17 } from "zod";
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: z17.string().describe("The embed ID to generate code for"),
1867
- framework: z17.enum(FRAMEWORKS).describe("Frontend framework"),
1868
- embedType: z17.enum(["dashboard", "metric"]).optional().default("dashboard"),
1869
- options: z17.object({
1870
- enableDownloadCsv: z17.boolean().optional(),
1871
- enableEmailCsv: z17.boolean().optional(),
1872
- enableDownloadAllMetrics: z17.boolean().optional(),
1873
- enableDownloadAllPdf: z17.boolean().optional(),
1874
- disableFullscreen: z17.boolean().optional(),
1875
- isHideTablePreview: z17.boolean().optional(),
1876
- isHideChartSettings: z17.boolean().optional(),
1877
- isStickyDashboardFilters: z17.boolean().optional(),
1878
- enableMultiMetricFilters: z17.boolean().optional(),
1879
- themeName: z17.string().optional(),
1880
- customTheme: z17.record(z17.unknown()).optional(),
1881
- optionsIcon: z17.enum(["kebab-menu-vertical", "download"]).optional()
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 z18 } from "zod";
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: z18.string().optional().describe("Filter by data app name")
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 z19 } from "zod";
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: z19.string().describe("Name of the data app"),
1967
- clientId: z19.string().optional().describe("Client ID for tenant-scoped metrics")
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 z20 } from "zod";
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: z20.string().describe("Embed ID"),
2006
- metricId: z20.string().describe("Metric ID to query"),
2007
- clientId: z20.string().describe("Client ID for tenant filtering"),
2008
- filters: z20.array(z20.record(z20.unknown())).optional().describe("Runtime filters"),
2009
- limit: z20.number().optional().describe("Row limit"),
2010
- offset: z20.number().optional().describe("Pagination 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 z21 } from "zod";
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: z21.string().describe("Dashboard ID"),
2055
- clientId: z21.string().describe("Client ID for tenant filtering"),
2056
- embedId: z21.string().describe("Embed ID"),
2057
- filters: z21.array(z21.record(z21.unknown())).optional().describe("Runtime 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 z22 } from "zod";
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: z22.string().describe("Embed ID with AI Pilot enabled"),
2124
- question: z22.string().describe('Natural language question, e.g. "What was total revenue last month?"'),
2125
- clientId: z22.string().describe("Client ID for tenant filtering")
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 z23 } from "zod";
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: z23.string().describe("Metric ID"),
2175
- embedId: z23.string().describe("Embed ID"),
2176
- clientId: z23.string().describe("Client ID for tenant filtering"),
2177
- filters: z23.array(z23.record(z23.unknown())).optional().describe("Runtime 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 z24 } from "zod";
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: z24.string().describe("Embed ID")
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 z25 } from "zod";
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: z25.string().describe("ID of the dashboard to export")
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 z26 } from "zod";
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: z26.string().describe("The full dashboard JSON string from export_dashboard"),
2290
- dataAppName: z26.string().describe("Name of the target data app")
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 z27 } from "zod";
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 = z27.object({
2362
- dataAppId: z27.string().optional(),
2363
- dataAppName: z27.string().optional(),
2364
- embedId: z27.string().optional(),
2365
- dashboardId: z27.string().optional(),
2366
- apiToken: z27.string().optional(),
2367
- clientId: z27.string().optional(),
2368
- workspaceName: z27.string().optional()
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: z27.enum(STEPS).describe("Which step to execute"),
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: z27.string().optional().describe("User selection when a previous step returned CHOICE_REQUIRED")
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: `mcp-embed-setup-${Date.now()}` },
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
- WARNING: This token is shown once. Store it backend-side only. Never expose it in frontend code or logs.
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: "api",
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, embeds).
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**: All CRUD operations on datasources, data apps, datamarts, embeds, and dashboards listing.
3988
+ - **Used for**: CRUD on datasources, data apps, datamarts, and API token management.
2855
3989
 
2856
3990
  ### API Token
2857
- - **Purpose**: Generate guest tokens for end-user embedding.
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**: Guest token generation and metric data queries.
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 <service-token>
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 <service-token>
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/<embed-id>
3012
- Authorization: Bearer <service-token>
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/<embed-id>
3020
- Authorization: Bearer <service-token>
4155
+ PUT /api/v2/data-app/embeds
4156
+ Authorization: Bearer <api-token>
3021
4157
  Content-Type: application/json
3022
4158
  \`\`\`
3023
4159
 
3024
- Updates an existing embed configuration (theme, access settings, etc.).
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
- - \`embedId\` (string) - The embed configuration ID
3041
- - \`appFilters\` (array) - Global filters applied to all metrics
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
- - \`clientId\` (string) - Tenant identifier for multi-tenancy / row-level security
4180
+ - \`permissions\` (object) - Runtime permission overrides (e.g. \`isEnableDownloadMetrics\`)
3044
4181
  - \`theme\` (object) - Runtime theme overrides
3045
- - \`accessSettings\` (object) - Runtime permission overrides
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 <service-token>
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 <service-token>
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 access settings at guest token generation time. This is useful for role-based
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
- "accessSettings": {
4419
- "isAllowMetricCreation": true,
4420
- "isAllowMetricEditing": true,
4421
- "isAllowAiPilot": true,
4422
- "metricCreationMode": "CHAT"
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
- Guest token access settings **merge with and override** embed-level settings. Only the
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 \`accessSettings\`** (server-side, per session)
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/explore-data.ts
5154
- var exploreDataPrompt = `You are helping the user explore what data and resources are available in their Databrain workspace. Gather a complete picture and provide actionable recommendations.
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 1 \u2014 Inventory All Resources**
5157
- If the user has an API token configured, call all of these to build a full picture:
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
- If any call fails due to missing authentication, note which resources could not be retrieved and advise the user to set up an API token first.
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 2 \u2014 Summarize the Data Landscape**
5166
- Present a clear summary organized by data app:
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 3 \u2014 Suggest Next Steps**
5172
- Based on what you find, recommend the most relevant next actions:
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
- If **no resources exist at all**:
5175
- - Suggest running the \`setup-from-scratch\` prompt to get started with the full onboarding funnel.
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
- If **datasources exist but no datamarts**:
5178
- - Suggest creating datamarts to model the data for dashboarding.
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
- If **datamarts exist but no dashboards**:
5181
- - Suggest the user create dashboards in the Databrain UI.
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
- If **dashboards exist but no embeds**:
5184
- - Suggest running the \`embed-dashboard\` prompt to embed a dashboard.
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
- If **embeds exist but AI chat is not enabled**:
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 **everything is set up**:
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
- Be concise but thorough. The goal is to give the user a clear understanding of their current state and a clear path forward.`;
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: "explore-data",
5200
- description: "Explore what data and dashboards are available in your Databrain workspace",
5201
- content: exploreDataPrompt
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) {