@databrainhq/mcp-server 0.3.0-rc.7 → 0.3.0-rc.9

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.
@@ -18,7 +18,7 @@ import {
18
18
  toolError,
19
19
  toolSuccess,
20
20
  validateBundle
21
- } from "./chunk-ETXH5HTT.js";
21
+ } from "./chunk-H3SI2MAU.js";
22
22
 
23
23
  // src/apps/contract.ts
24
24
  import { z } from "zod";
@@ -129,6 +129,8 @@ var TOOL_METADATA = {
129
129
  sync_datasource: { category: "infrastructure", workflowIntent: "setup_data", effect: "commit" },
130
130
  // dry_run is the default, but mode:"apply" performs a real POST that creates the datamart.
131
131
  create_datamart: { category: "infrastructure", workflowIntent: "setup_data", effect: "commit" },
132
+ // Hosted only (an "All workspaces" sign-in); stdio does not register it.
133
+ create_workspace: { category: "infrastructure", workflowIntent: "setup_data", effect: "commit" },
132
134
  // Embedding — data apps & tokens
133
135
  list_data_apps: { category: "embedding", workflowIntent: "embed_dashboard", effect: "read" },
134
136
  list_tenants: { category: "embedding", workflowIntent: "embed_dashboard", effect: "read" },
@@ -152,6 +154,8 @@ var TOOL_METADATA = {
152
154
  list_dashboards: { category: "embedding", workflowIntent: "embedded_widget", effect: "read" },
153
155
  create_dashboard: { category: "embedding", workflowIntent: "author_dashboard", effect: "commit" },
154
156
  get_dashboard: { category: "embedding", workflowIntent: "author_dashboard", effect: "read" },
157
+ // Persists nothing the user sees, but the backend mints (and then deletes) an internal guest token to render it.
158
+ dashboard_snapshot: { category: "internal_analytics", workflowIntent: "query_data", effect: "commit" },
155
159
  // Does not persist a dashboard, but mints an internal guest token to render tile previews.
156
160
  generate_dashboard_tiles: { category: "embedding", workflowIntent: "author_dashboard", effect: "commit" },
157
161
  list_widgets: { category: "embedding", workflowIntent: "embedded_widget", effect: "read" },
@@ -183,7 +187,6 @@ var TOOL_METADATA = {
183
187
  propose_semantic_change: { category: "semantic_layer", workflowIntent: "model_data", effect: "read" },
184
188
  apply_semantic_change: { category: "semantic_layer", workflowIntent: "model_data", effect: "commit" },
185
189
  eval_semantic_model: { category: "semantic_layer", workflowIntent: "query_data", effect: "read" },
186
- semantic_readiness: { category: "semantic_layer", workflowIntent: "model_data", effect: "read" },
187
190
  get_semantic_layer: { category: "semantic_layer", workflowIntent: "query_data", effect: "read" },
188
191
  update_semantic_layer: { category: "semantic_layer", workflowIntent: "query_data", effect: "commit" },
189
192
  start_semantic_layer_generation: { category: "semantic_layer", workflowIntent: "query_data", effect: "commit" },
@@ -617,9 +620,6 @@ var SemanticModelApi = class {
617
620
  noRetryOnTimeout: true
618
621
  });
619
622
  }
620
- readiness(id2) {
621
- return this.client.request({ method: "GET", path: this.path(id2, "readiness"), auth: "service" });
622
- }
623
623
  };
624
624
 
625
625
  // src/api/tenants-api.ts
@@ -1972,8 +1972,9 @@ function registerCreateApiToken(server, client) {
1972
1972
  );
1973
1973
  }
1974
1974
  client.setRuntimeApiToken(key);
1975
+ const used = client.hasApiToken() ? "API token created and set for this MCP session." : "API token created. This session keeps acting as the signed-in user; the token is for your backend.";
1975
1976
  return toolSuccess(
1976
- `API token created and set for this MCP session.
1977
+ `${used}
1977
1978
 
1978
1979
  **Token:** \`${key}\`
1979
1980
 
@@ -4865,20 +4866,75 @@ function registerGetDashboard(server, client) {
4865
4866
  );
4866
4867
  }
4867
4868
 
4869
+ // src/tools/dashboard/dashboard-snapshot.ts
4870
+ import { z as z24 } from "zod";
4871
+ var SNAPSHOT_REQUEST_MS = 45e3;
4872
+ function registerDashboardSnapshot(server, client) {
4873
+ server.tool(
4874
+ "dashboard_snapshot",
4875
+ "Get a PNG picture of a dashboard, or of one chart on it (metricId), as the signed-in user sees it in Databrain. Light or dark, 480 to 1600 px wide. Needs a Databrain sign-in (OAuth), not a service token.",
4876
+ {
4877
+ workspaceName: workspaceNameSchema.optional(),
4878
+ dashboardId: z24.string().min(1).max(200).optional().describe("Dashboard id from list_dashboards. Needed unless metricId is given."),
4879
+ metricId: z24.string().min(1).max(200).optional().describe("One chart (metric) id: draw only that chart, from a dashboard it is on."),
4880
+ theme: z24.enum(["light", "dark"]).default("light").describe("Picture colours"),
4881
+ width: z24.number().int().min(480).max(1600).default(1200).describe("Picture width in pixels")
4882
+ },
4883
+ READ_ONLY,
4884
+ async (params) => {
4885
+ if (!params.dashboardId && !params.metricId) {
4886
+ return toolError("Name a dashboardId (from list_dashboards), or a metricId for one chart.");
4887
+ }
4888
+ try {
4889
+ const result = await client.request({
4890
+ method: "POST",
4891
+ path: "/api/v2/workspace/dashboard/snapshot",
4892
+ auth: "service",
4893
+ timeoutMs: SNAPSHOT_REQUEST_MS,
4894
+ body: {
4895
+ ...params.workspaceName ? { workspaceName: params.workspaceName } : {},
4896
+ ...params.dashboardId ? { dashboardId: params.dashboardId } : {},
4897
+ ...params.metricId ? { metricId: params.metricId } : {},
4898
+ theme: params.theme,
4899
+ width: params.width
4900
+ }
4901
+ });
4902
+ if (result?.mimeType !== "image/png" || typeof result.image !== "string" || !result.image) {
4903
+ return toolError("The snapshot answer had no picture. Try again.");
4904
+ }
4905
+ const what = result.metric?.name ? `Chart "${result.metric.name}" on dashboard "${result.dashboard?.name ?? ""}"` : `Dashboard "${result.dashboard?.name ?? ""}"`;
4906
+ const text2 = [
4907
+ `${what} in workspace "${result.workspace?.name ?? ""}", ${result.theme ?? params.theme}, ${result.width}x${result.height} px.`,
4908
+ result.summary ?? ""
4909
+ ].filter(Boolean).join("\n");
4910
+ return {
4911
+ content: [
4912
+ { type: "text", text: text2 },
4913
+ { type: "image", data: result.image, mimeType: "image/png" }
4914
+ ]
4915
+ };
4916
+ } catch (err) {
4917
+ if (err instanceof DatabrainApiError) return err.toToolResponse();
4918
+ return toolError(`Unexpected error: ${err.message}`);
4919
+ }
4920
+ }
4921
+ );
4922
+ }
4923
+
4868
4924
  // src/apps/preview.ts
4869
4925
  import { createHash } from "crypto";
4870
- import { z as z24 } from "zod";
4871
- var draftSchema = z24.object({
4872
- question: z24.string().min(1).max(2e3),
4873
- sql: z24.string().min(1).max(2e4),
4874
- datamartName: z24.string().min(1).max(256),
4875
- dimensions: z24.array(z24.string().max(128)).max(20).default([]),
4876
- measures: z24.array(z24.string().max(128)).max(20).default([]),
4877
- chartType: z24.string().min(1).max(80),
4878
- title: z24.string().min(1).max(200),
4879
- sessionId: z24.string().max(256).optional(),
4926
+ import { z as z25 } from "zod";
4927
+ var draftSchema = z25.object({
4928
+ question: z25.string().min(1).max(2e3),
4929
+ sql: z25.string().min(1).max(2e4),
4930
+ datamartName: z25.string().min(1).max(256),
4931
+ dimensions: z25.array(z25.string().max(128)).max(20).default([]),
4932
+ measures: z25.array(z25.string().max(128)).max(20).default([]),
4933
+ chartType: z25.string().min(1).max(80),
4934
+ title: z25.string().min(1).max(200),
4935
+ sessionId: z25.string().max(256).optional(),
4880
4936
  /** Workspaces built on this datamart. One means Save needs no choice. */
4881
- workspaces: z24.array(z24.string().max(256)).max(50).default([])
4937
+ workspaces: z25.array(z25.string().max(256)).max(50).default([])
4882
4938
  }).strict();
4883
4939
  var PREVIEW_PATH = "/api/v2/data-app/chart-preview";
4884
4940
  var METRIC_PATH = "/api/mcp/metrics/create";
@@ -5005,7 +5061,7 @@ async function saveDraftAsDashboard(client, draft, input) {
5005
5061
  }
5006
5062
 
5007
5063
  // src/tools/query/ask-question.ts
5008
- import { z as z25 } from "zod";
5064
+ import { z as z26 } from "zod";
5009
5065
 
5010
5066
  // src/tools/query/answer-format.ts
5011
5067
  function formatConfidence(value) {
@@ -5062,16 +5118,16 @@ function registerAskQuestion(server, client) {
5062
5118
  "ask_question",
5063
5119
  "Ask a natural language question \u2014 converts to SQL, returns data + chart suggestion. Requires datamartName with a populated semantic layer.",
5064
5120
  {
5065
- question: z25.string().describe(
5121
+ question: z26.string().describe(
5066
5122
  'The natural language question to ask (e.g. "What was total revenue last month?")'
5067
5123
  ),
5068
- datamartName: z25.string().optional().describe(
5124
+ datamartName: z26.string().optional().describe(
5069
5125
  "Name of the datamart to query. Use `list_datamarts` to find available names. REQUIRED unless exactly one datamart exists (auto-resolved). Do NOT pass a workspace name here."
5070
5126
  ),
5071
- sessionId: z25.string().optional().describe(
5127
+ sessionId: z26.string().optional().describe(
5072
5128
  "Session ID from a previous ask_question response, for follow-up questions."
5073
5129
  ),
5074
- timezone: z25.string().optional().describe('IANA timezone (e.g. "America/New_York"). Defaults to UTC.')
5130
+ timezone: z26.string().optional().describe('IANA timezone (e.g. "America/New_York"). Defaults to UTC.')
5075
5131
  },
5076
5132
  WRITE_CREATE,
5077
5133
  async (params) => {
@@ -5267,14 +5323,14 @@ To fix this:
5267
5323
  }
5268
5324
 
5269
5325
  // src/tools/query/get-semantic-layer.ts
5270
- import { z as z26 } from "zod";
5326
+ import { z as z27 } from "zod";
5271
5327
  function registerGetSemanticLayer(server, client) {
5272
5328
  const services = createServiceBundle(client);
5273
5329
  server.tool(
5274
5330
  "get_semantic_layer",
5275
5331
  "Get semantic layer metadata for a datamart: table/column descriptions, synonyms, completion score. Omit datamartName to auto-resolve.",
5276
5332
  {
5277
- datamartName: z26.string().optional().describe(
5333
+ datamartName: z27.string().optional().describe(
5278
5334
  "Name of the datamart to inspect. Omit to list available datamarts."
5279
5335
  )
5280
5336
  },
@@ -5384,7 +5440,7 @@ ${result.feedback}`);
5384
5440
  }
5385
5441
 
5386
5442
  // src/tools/query/semantic-model.ts
5387
- import { z as z27 } from "zod";
5443
+ import { z as z28 } from "zod";
5388
5444
 
5389
5445
  // src/services/semantic-source.ts
5390
5446
  import { parseDocument, stringify as stringify2 } from "yaml";
@@ -5584,21 +5640,21 @@ function retrieveSemanticSource(source2, question, k) {
5584
5640
  }
5585
5641
 
5586
5642
  // src/tools/query/semantic-model.ts
5587
- var datamartId = z27.string().trim().min(1).max(256).describe("Datamart ID from list_datamarts.");
5588
- var source = z27.string().min(1).max(2e6).describe("Complete proposed semantic model as YAML.");
5589
- var entityKind = z27.enum(["table", "column", "measure", "metric", "join", "entity", "glossary", "saved_query", "instruction", "access_rule"]);
5643
+ var datamartId = z28.string().trim().min(1).max(256).describe("Datamart ID from list_datamarts.");
5644
+ var source = z28.string().min(1).max(2e6).describe("Complete proposed semantic model as YAML.");
5645
+ var entityKind = z28.enum(["table", "column", "measure", "metric", "join", "entity", "glossary", "saved_query", "instruction", "access_rule"]);
5590
5646
  var changeInputs = {
5591
5647
  source: source.optional(),
5592
- yaml_patch: z27.string().min(1).max(2e6).optional().describe("YAML mapping merged into the pulled model. Named lists merge by name. Other lists replace. Null removes a field. An empty list clears it."),
5593
- change: z27.object({
5594
- operation: z27.enum(["upsert", "remove"]),
5648
+ yaml_patch: z28.string().min(1).max(2e6).optional().describe("YAML mapping merged into the pulled model. Named lists merge by name. Other lists replace. Null removes a field. An empty list clears it."),
5649
+ change: z28.object({
5650
+ operation: z28.enum(["upsert", "remove"]),
5595
5651
  kind: entityKind,
5596
- name: z27.string().trim().min(1),
5597
- table: z27.string().trim().min(1).optional().describe("Required for columns. Use the table name and an unqualified column name."),
5598
- value: z27.record(z27.unknown()).optional().describe("Fields to merge for upsert. Omit for remove.")
5652
+ name: z28.string().trim().min(1),
5653
+ table: z28.string().trim().min(1).optional().describe("Required for columns. Use the table name and an unqualified column name."),
5654
+ value: z28.record(z28.unknown()).optional().describe("Fields to merge for upsert. Omit for remove.")
5599
5655
  }).strict().optional().describe("One named entity change. Supply exactly one of source, yaml_patch or change.")
5600
5656
  };
5601
- var expectedHash = z27.string().trim().min(1).nullable().optional().describe("Stored content_hash from the reviewed proposal. Null explicitly means the first apply. Required when dry_run is false.");
5657
+ var expectedHash = z28.string().trim().min(1).nullable().optional().describe("Stored content_hash from the reviewed proposal. Null explicitly means the first apply. Required when dry_run is false.");
5602
5658
  function errorCode(error) {
5603
5659
  const nested = isObject(error.details) && isObject(error.details.error) ? error.details.error : void 0;
5604
5660
  return typeof nested?.code === "string" ? nested.code : error.code;
@@ -5640,15 +5696,24 @@ async function candidate(api, id2, input) {
5640
5696
  async function proposal(api, id2, input) {
5641
5697
  const next = await candidate(api, id2, input);
5642
5698
  const validation = await api.validate(id2, next.source);
5643
- const plan = validation.ok ? await api.plan(id2, next.source, next.expected_content_hash) : null;
5644
- return { ...next, validation, errors: validation.errors, plan, dry_run: true };
5699
+ let plan = null;
5700
+ let plan_note;
5701
+ if (validation.ok) {
5702
+ try {
5703
+ plan = await api.plan(id2, next.source, next.expected_content_hash);
5704
+ } catch (error) {
5705
+ if (!(error instanceof DatabrainApiError && [404, 405, 501].includes(error.status))) throw error;
5706
+ plan_note = "This Databrain API has no plan step. The validation above is the server check; apply runs it again.";
5707
+ }
5708
+ }
5709
+ return { ...next, validation, errors: validation.errors, plan, ...plan_note ? { plan_note } : {}, dry_run: true };
5645
5710
  }
5646
5711
  function registerSearchSemanticModel(server, client) {
5647
5712
  const api = createServiceBundle(client).semanticModelApi;
5648
5713
  server.tool(
5649
5714
  "search_semantic_model",
5650
5715
  "Find semantic model entities relevant to a question. Ranks names, aliases, synonyms and descriptions, then includes related entities. Uses the stored model and never writes.",
5651
- { datamartId, question: z27.string().trim().min(1).max(1e4), k: z27.number().int().min(1).max(100).default(10) },
5716
+ { datamartId, question: z28.string().trim().min(1).max(1e4), k: z28.number().int().min(1).max(100).default(10) },
5652
5717
  READ_ONLY,
5653
5718
  async ({ datamartId: datamartId2, question, k }) => {
5654
5719
  try {
@@ -5671,7 +5736,7 @@ function registerGetSemanticEntity(server, client) {
5671
5736
  server.tool(
5672
5737
  "get_semantic_entity",
5673
5738
  "Read one entity by kind and exact name from the stored semantic model. For a column, use table.column. Returns the stored content hash for change review.",
5674
- { datamartId, kind: entityKind, name: z27.string().trim().min(1) },
5739
+ { datamartId, kind: entityKind, name: z28.string().trim().min(1) },
5675
5740
  READ_ONLY,
5676
5741
  async ({ datamartId: datamartId2, kind, name }) => {
5677
5742
  try {
@@ -5709,9 +5774,9 @@ function registerApplySemanticChange(server, client) {
5709
5774
  {
5710
5775
  datamartId,
5711
5776
  ...changeInputs,
5712
- dry_run: z27.boolean().default(true),
5777
+ dry_run: z28.boolean().default(true),
5713
5778
  expected_content_hash: expectedHash,
5714
- confirm: z27.string().optional().describe("User supplied APPLY_TO_PRODUCTION after reviewing the plan. Never invent confirmation.")
5779
+ confirm: z28.string().optional().describe("User supplied APPLY_TO_PRODUCTION after reviewing the plan. Never invent confirmation.")
5715
5780
  },
5716
5781
  DESTRUCTIVE,
5717
5782
  async (params) => {
@@ -5735,12 +5800,12 @@ function registerEvalSemanticModel(server, client) {
5735
5800
  server.tool(
5736
5801
  "eval_semantic_model",
5737
5802
  "Evaluate saved queries and optional questions against a proposed or stored semantic model. Returns the pass rate and each verdict with its reason. Runs warehouse queries without changing the model.",
5738
- { datamartId, source: source.optional(), questions: z27.array(z27.union([z27.string().trim().min(1), z27.object({
5739
- name: z27.string().optional(),
5740
- question: z27.string().min(1),
5741
- sql: z27.string().optional(),
5742
- expected_columns: z27.array(z27.string()).optional(),
5743
- expected_empty: z27.boolean().optional()
5803
+ { datamartId, source: source.optional(), questions: z28.array(z28.union([z28.string().trim().min(1), z28.object({
5804
+ name: z28.string().optional(),
5805
+ question: z28.string().min(1),
5806
+ sql: z28.string().optional(),
5807
+ expected_columns: z28.array(z28.string()).optional(),
5808
+ expected_empty: z28.boolean().optional()
5744
5809
  }).strict()])).max(100).optional() },
5745
5810
  READ_ONLY,
5746
5811
  async ({ datamartId: datamartId2, source: source2, questions }) => {
@@ -5753,33 +5818,17 @@ function registerEvalSemanticModel(server, client) {
5753
5818
  }
5754
5819
  );
5755
5820
  }
5756
- function registerSemanticReadiness(server, client) {
5757
- const api = createServiceBundle(client).semanticModelApi;
5758
- server.tool(
5759
- "semantic_readiness",
5760
- "Read the semantic readiness number, verdict and open checks for a stored model. Uses the server result without changing the model.",
5761
- { datamartId },
5762
- READ_ONLY,
5763
- async ({ datamartId: datamartId2 }) => {
5764
- try {
5765
- return toolSuccess(stringify(await api.readiness(datamartId2)));
5766
- } catch (error) {
5767
- return failure(error);
5768
- }
5769
- }
5770
- );
5771
- }
5772
5821
 
5773
5822
  // src/tools/query/update-semantic-layer.ts
5774
- import { z as z28 } from "zod";
5775
- var columnSchema2 = z28.object({
5776
- name: z28.string().describe("Column name (must exist in the datamart)"),
5777
- description: z28.string().optional().describe("Business-friendly description of what this column represents"),
5778
- synonyms: z28.array(z28.string()).optional().describe(
5823
+ import { z as z29 } from "zod";
5824
+ var columnSchema2 = z29.object({
5825
+ name: z29.string().describe("Column name (must exist in the datamart)"),
5826
+ description: z29.string().optional().describe("Business-friendly description of what this column represents"),
5827
+ synonyms: z29.array(z29.string()).optional().describe(
5779
5828
  'Alternative names users might use for this column (e.g. ["revenue", "sales amount"])'
5780
5829
  ),
5781
- miscellaneousInfo: z28.string().optional().describe("Additional context such as valid values, units, or caveats"),
5782
- columnType: z28.enum([
5830
+ miscellaneousInfo: z29.string().optional().describe("Additional context such as valid values, units, or caveats"),
5831
+ columnType: z29.enum([
5783
5832
  "String",
5784
5833
  "Long String",
5785
5834
  "String (Custom)",
@@ -5794,21 +5843,21 @@ var columnSchema2 = z28.object({
5794
5843
  // The public PUT accepts these three as well (docs: Update Semantic Layer).
5795
5844
  // They were missing here, and zod strips unknown keys, so a caller who set
5796
5845
  // `isIdentifier: true` got a success message and a row with `false`.
5797
- columnTypeConfig: z28.union([z28.record(z28.string(), z28.string()), z28.string(), z28.object({ lowerLimit: z28.number(), upperLimit: z28.number() }), z28.null()]).optional().describe("Shape depends on columnType: value-to-description object for String / String (Custom) / ENUM / Mapper, { lowerLimit, upperLimit } for Range, a string for Expression and JSON. Omit for Identifier, Number and Long String."),
5798
- isIdentifier: z28.boolean().optional().describe("Mark the column as an identifier (primary or foreign key)."),
5799
- isNotIndexed: z28.boolean().optional().describe("Exclude the column from AI indexing.")
5846
+ columnTypeConfig: z29.union([z29.record(z29.string(), z29.string()), z29.string(), z29.object({ lowerLimit: z29.number(), upperLimit: z29.number() }), z29.null()]).optional().describe("Shape depends on columnType: value-to-description object for String / String (Custom) / ENUM / Mapper, { lowerLimit, upperLimit } for Range, a string for Expression and JSON. Omit for Identifier, Number and Long String."),
5847
+ isIdentifier: z29.boolean().optional().describe("Mark the column as an identifier (primary or foreign key)."),
5848
+ isNotIndexed: z29.boolean().optional().describe("Exclude the column from AI indexing.")
5800
5849
  });
5801
- var tableSchema2 = z28.object({
5802
- name: z28.string().describe("Table name (must exist in the datamart)"),
5803
- schemaName: z28.string().optional().describe("Schema the table lives in, when the datamart has the same table name in more than one schema."),
5804
- description: z28.string().optional().describe(
5850
+ var tableSchema2 = z29.object({
5851
+ name: z29.string().describe("Table name (must exist in the datamart)"),
5852
+ schemaName: z29.string().optional().describe("Schema the table lives in, when the datamart has the same table name in more than one schema."),
5853
+ description: z29.string().optional().describe(
5805
5854
  "Business-friendly description of what this table represents"
5806
5855
  ),
5807
- synonyms: z28.array(z28.string()).optional().describe("Alternative names for this table"),
5808
- miscellaneousInfo: z28.string().optional().describe(
5856
+ synonyms: z29.array(z29.string()).optional().describe("Alternative names for this table"),
5857
+ miscellaneousInfo: z29.string().optional().describe(
5809
5858
  "Additional context such as how the table relates to other tables"
5810
5859
  ),
5811
- columns: z28.array(columnSchema2).optional().describe("Column-level semantic definitions to update")
5860
+ columns: z29.array(columnSchema2).optional().describe("Column-level semantic definitions to update")
5812
5861
  });
5813
5862
  function columnKey(tableName, columnName) {
5814
5863
  return `${tableName.toLowerCase()}\0${columnName.toLowerCase()}`;
@@ -5885,13 +5934,13 @@ function registerUpdateSemanticLayer(server, client) {
5885
5934
  "update_semantic_layer",
5886
5935
  "Populate or update the semantic layer for a datamart. Add table/column descriptions, synonyms, context. Required before ask_question works. Auto-generate descriptions from column names when empty.",
5887
5936
  {
5888
- datamartName: z28.string().optional().describe(
5937
+ datamartName: z29.string().optional().describe(
5889
5938
  "Name of the datamart to update. Omit to list available datamarts."
5890
5939
  ),
5891
- tables: z28.array(tableSchema2).optional().describe(
5940
+ tables: z29.array(tableSchema2).optional().describe(
5892
5941
  "Table and column definitions to update. Only include tables/columns you want to change."
5893
5942
  ),
5894
- feedback: z28.string().optional().describe(
5943
+ feedback: z29.string().optional().describe(
5895
5944
  'General feedback or context about the datamart (e.g. "This datamart contains e-commerce data for the US market").'
5896
5945
  )
5897
5946
  },
@@ -5953,7 +6002,7 @@ You can now:
5953
6002
  }
5954
6003
 
5955
6004
  // src/tools/query/semantic-generation.ts
5956
- import { z as z29 } from "zod";
6005
+ import { z as z30 } from "zod";
5957
6006
  function nonEmptyString(value) {
5958
6007
  return typeof value === "string" && value.trim().length > 0 ? value : null;
5959
6008
  }
@@ -6059,9 +6108,9 @@ function registerStartSemanticLayerGeneration(server, client) {
6059
6108
  "start_semantic_layer_generation",
6060
6109
  "Start backend semantic-layer auto-generation for a datamart. Returns an async job id to poll.",
6061
6110
  {
6062
- datamartName: z29.string().optional().describe("Datamart name. Omit to list available datamarts"),
6063
- workspaceName: z29.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
6064
- datamartId: z29.string().optional().describe("Datamart UUID. Use only when already known.")
6111
+ datamartName: z30.string().optional().describe("Datamart name. Omit to list available datamarts"),
6112
+ workspaceName: z30.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
6113
+ datamartId: z30.string().optional().describe("Datamart UUID. Use only when already known.")
6065
6114
  },
6066
6115
  WRITE_CREATE,
6067
6116
  async (params) => {
@@ -6103,9 +6152,9 @@ function registerGetSemanticLayerGenerationStatus(server, client) {
6103
6152
  "get_semantic_layer_generation_status",
6104
6153
  "Get semantic-layer auto-generation job status for a datamart.",
6105
6154
  {
6106
- datamartName: z29.string().optional().describe("Datamart name. Omit to list available datamarts"),
6107
- workspaceName: z29.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
6108
- datamartId: z29.string().optional().describe("Datamart UUID. Use only when already known.")
6155
+ datamartName: z30.string().optional().describe("Datamart name. Omit to list available datamarts"),
6156
+ workspaceName: z30.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
6157
+ datamartId: z30.string().optional().describe("Datamart UUID. Use only when already known.")
6109
6158
  },
6110
6159
  READ_ONLY,
6111
6160
  async (params) => {
@@ -6142,7 +6191,7 @@ function registerGetSemanticLayerGenerationStatus(server, client) {
6142
6191
  }
6143
6192
 
6144
6193
  // src/tools/widgets/list-widgets.ts
6145
- import { z as z30 } from "zod";
6194
+ import { z as z31 } from "zod";
6146
6195
  function widgetReferenceId(widget) {
6147
6196
  for (const candidate2 of [widget.externalMetricId, widget.id, widget._id, widget.metricId]) {
6148
6197
  if (typeof candidate2 === "string" && candidate2.trim().length > 0) return candidate2.trim();
@@ -6155,10 +6204,10 @@ function registerListWidgets(server, client) {
6155
6204
  "list_widgets",
6156
6205
  "List widgets on a customer dashboard. Non-technical flow: pass `clientId` + `dashboardName` (+ optional `workspaceName` if ambiguous). Returns each widget's externalMetricId (uuid, used for update/remove), metricId (slug), name, published state, and creator identifier.",
6157
6206
  {
6158
- clientId: z30.string().describe("Client/tenant identifier for this dashboard"),
6159
- dashboardName: z30.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6160
- workspaceName: z30.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6161
- embedId: z30.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier.")
6207
+ clientId: z31.string().describe("Client/tenant identifier for this dashboard"),
6208
+ dashboardName: z31.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6209
+ workspaceName: z31.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6210
+ embedId: z31.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier.")
6162
6211
  },
6163
6212
  READ_ONLY,
6164
6213
  async (params) => {
@@ -6222,29 +6271,29 @@ function registerListWidgets(server, client) {
6222
6271
  }
6223
6272
 
6224
6273
  // src/tools/widgets/add-widget.ts
6225
- import { z as z31 } from "zod";
6274
+ import { z as z32 } from "zod";
6226
6275
  function registerAddWidget(server, client) {
6227
6276
  const services = createServiceBundle(client);
6228
6277
  server.tool(
6229
6278
  "add_widget",
6230
6279
  "Add a widget to a customer dashboard. Non-technical flow: pass `clientId` + `dashboardName` (+ optional `workspaceName` if ambiguous). Cloning via `templateMetricId` is the recommended path \u2014 query, chart config, and datasource are all inherited. Data app and datasource auto-resolve; never ask the user for them.",
6231
6280
  {
6232
- clientId: z31.string().describe("Client/tenant identifier (widget is created in their scope)"),
6233
- dashboardName: z31.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6234
- workspaceName: z31.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6235
- embedId: z31.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6236
- dataAppName: z31.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
6237
- templateMetricId: z31.string().optional().describe("Source widget metricId to clone from. Copies query, chartOptions, and datasource fields; caller can override via other params."),
6238
- name: z31.string().optional().describe("Widget name (overrides template)"),
6239
- metricId: z31.string().optional().describe("Unique slug for the new widget (auto-generated if omitted)"),
6240
- query: z31.string().optional().describe("SQL query body (overrides template)"),
6241
- chartOptions: z31.record(z31.unknown()).optional().describe("Chart config JSON (chart type, colors, legend, axes). Overrides template."),
6242
- description: z31.string().optional(),
6243
- companyIntegrationId: z31.string().optional().describe("Datasource UUID override. Auto-resolved from `embedId` or sole active datasource; leave blank unless the user explicitly picked one by ID."),
6244
- integrationName: z31.string().optional().describe('Datasource display name (e.g. "Snowflake"). Use this when the user picks a datasource by name \u2014 the id is looked up automatically.'),
6245
- isPublished: z31.boolean().optional().describe("Publish immediately (default true)"),
6246
- body: z31.record(z31.unknown()).optional().describe("Raw createMetric body passthrough. If provided, used as-is (after defaults). Overrides all other params."),
6247
- widgets: z31.array(z31.record(z31.unknown())).min(1).max(50).optional().describe("Batch of raw createMetric bodies. Dashboard attachment is added to each body.")
6281
+ clientId: z32.string().describe("Client/tenant identifier (widget is created in their scope)"),
6282
+ dashboardName: z32.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6283
+ workspaceName: z32.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6284
+ embedId: z32.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6285
+ dataAppName: z32.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
6286
+ templateMetricId: z32.string().optional().describe("Source widget metricId to clone from. Copies query, chartOptions, and datasource fields; caller can override via other params."),
6287
+ name: z32.string().optional().describe("Widget name (overrides template)"),
6288
+ metricId: z32.string().optional().describe("Unique slug for the new widget (auto-generated if omitted)"),
6289
+ query: z32.string().optional().describe("SQL query body (overrides template)"),
6290
+ chartOptions: z32.record(z32.unknown()).optional().describe("Chart config JSON (chart type, colors, legend, axes). Overrides template."),
6291
+ description: z32.string().optional(),
6292
+ companyIntegrationId: z32.string().optional().describe("Datasource UUID override. Auto-resolved from `embedId` or sole active datasource; leave blank unless the user explicitly picked one by ID."),
6293
+ integrationName: z32.string().optional().describe('Datasource display name (e.g. "Snowflake"). Use this when the user picks a datasource by name \u2014 the id is looked up automatically.'),
6294
+ isPublished: z32.boolean().optional().describe("Publish immediately (default true)"),
6295
+ body: z32.record(z32.unknown()).optional().describe("Raw createMetric body passthrough. If provided, used as-is (after defaults). Overrides all other params."),
6296
+ widgets: z32.array(z32.record(z32.unknown())).min(1).max(50).optional().describe("Batch of raw createMetric bodies. Dashboard attachment is added to each body.")
6248
6297
  },
6249
6298
  WRITE_CREATE,
6250
6299
  async (params) => {
@@ -6392,20 +6441,20 @@ Use \`list_widgets\` to verify, or \`update_widget\`/\`remove_widget\` to adjust
6392
6441
  }
6393
6442
 
6394
6443
  // src/tools/widgets/update-widget.ts
6395
- import { z as z32 } from "zod";
6444
+ import { z as z33 } from "zod";
6396
6445
  function registerUpdateWidget(server, client) {
6397
6446
  const services = createServiceBundle(client);
6398
6447
  server.tool(
6399
6448
  "update_widget",
6400
6449
  "Update a widget on a customer dashboard. Non-technical flow: pass `clientId` + `dashboardName` (+ optional `workspaceName` if ambiguous) + `externalMetricId` + `set`. Data app auto-resolves from the dashboard.",
6401
6450
  {
6402
- externalMetricId: z32.string().describe("Internal uuid of the widget (externalMetrics.id). Not the user-facing metricId slug."),
6403
- clientId: z32.string().describe("Client/tenant identifier"),
6404
- dashboardName: z32.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6405
- workspaceName: z32.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6406
- embedId: z32.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6407
- dataAppName: z32.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank."),
6408
- set: z32.record(z32.unknown()).describe("Partial patch. Keys can include: name, query, chartOptions, description, isPublished, isLive, selectedGroupBy, drillDownSettings, limit, etc.")
6451
+ externalMetricId: z33.string().describe("Internal uuid of the widget (externalMetrics.id). Not the user-facing metricId slug."),
6452
+ clientId: z33.string().describe("Client/tenant identifier"),
6453
+ dashboardName: z33.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6454
+ workspaceName: z33.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6455
+ embedId: z33.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6456
+ dataAppName: z33.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank."),
6457
+ set: z33.record(z33.unknown()).describe("Partial patch. Keys can include: name, query, chartOptions, description, isPublished, isLive, selectedGroupBy, drillDownSettings, limit, etc.")
6409
6458
  },
6410
6459
  WRITE_IDEMPOTENT,
6411
6460
  async (params) => {
@@ -6468,19 +6517,19 @@ Use \`list_widgets\` to verify.`
6468
6517
  }
6469
6518
 
6470
6519
  // src/tools/widgets/remove-widget.ts
6471
- import { z as z33 } from "zod";
6520
+ import { z as z34 } from "zod";
6472
6521
  function registerRemoveWidget(server, client) {
6473
6522
  const services = createServiceBundle(client);
6474
6523
  server.tool(
6475
6524
  "remove_widget",
6476
6525
  "Remove a widget from a customer dashboard. Non-technical flow: pass `clientId` + `dashboardName` (+ optional `workspaceName` if ambiguous) + `externalMetricId`. Per-client soft delete \u2014 the widget stays on the template dashboard and on other customers' dashboards.",
6477
6526
  {
6478
- externalMetricId: z33.string().describe("Internal uuid of the widget (externalMetrics.id)"),
6479
- clientId: z33.string().describe("Client/tenant identifier \u2014 the widget will be hidden for this client only"),
6480
- dashboardName: z33.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6481
- workspaceName: z33.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6482
- embedId: z33.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6483
- dataAppName: z33.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank.")
6527
+ externalMetricId: z34.string().describe("Internal uuid of the widget (externalMetrics.id)"),
6528
+ clientId: z34.string().describe("Client/tenant identifier \u2014 the widget will be hidden for this client only"),
6529
+ dashboardName: z34.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6530
+ workspaceName: z34.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6531
+ embedId: z34.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6532
+ dataAppName: z34.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank.")
6484
6533
  },
6485
6534
  DESTRUCTIVE,
6486
6535
  async (params) => {
@@ -6536,21 +6585,21 @@ Note: this is a per-client soft delete. The widget remains available for other c
6536
6585
  }
6537
6586
 
6538
6587
  // src/tools/widgets/generate-widget.ts
6539
- import { z as z34 } from "zod";
6588
+ import { z as z35 } from "zod";
6540
6589
  function registerGenerateWidget(server, client) {
6541
6590
  const services = createServiceBundle(client);
6542
6591
  const tool = server.tool(
6543
6592
  "generate_widget",
6544
6593
  "AI-generate a widget draft from a natural-language prompt. Does NOT persist \u2014 returns a draft to pass to `add_widget`. Non-technical flow: `clientId` + `dashboardName` (+ `workspaceName` if ambiguous); data app and datasource auto-resolve. Never ask the user for data app, datasource, or integration.",
6545
6594
  {
6546
- clientId: z34.string().describe("Client/tenant identifier"),
6547
- userInputs: z34.record(z34.unknown()).describe('Prompt + context object. Typical shape: { prompt: "calls per day over last 30 days", schema: {...}, examples: [...] }. Backend decides which keys it honors.'),
6548
- dashboardName: z34.string().optional().describe("Dashboard name (non-technical). Preferred input for CS/CX flows \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6549
- workspaceName: z34.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6550
- embedId: z34.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6551
- dataAppName: z34.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
6552
- integrationId: z34.string().optional().describe("Technical override \u2014 the datasource UUID. Non-technical callers must leave this blank; the datasource is inherited from the dashboard's existing widgets."),
6553
- integrationName: z34.string().optional().describe("Technical override \u2014 datasource display name. Non-technical callers must leave this blank; the datasource is inherited from the dashboard's existing widgets.")
6595
+ clientId: z35.string().describe("Client/tenant identifier"),
6596
+ userInputs: z35.record(z35.unknown()).describe('Prompt + context object. Typical shape: { prompt: "calls per day over last 30 days", schema: {...}, examples: [...] }. Backend decides which keys it honors.'),
6597
+ dashboardName: z35.string().optional().describe("Dashboard name (non-technical). Preferred input for CS/CX flows \u2014 pass this with optional `workspaceName` instead of `embedId`."),
6598
+ workspaceName: z35.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
6599
+ embedId: z35.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
6600
+ dataAppName: z35.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
6601
+ integrationId: z35.string().optional().describe("Technical override \u2014 the datasource UUID. Non-technical callers must leave this blank; the datasource is inherited from the dashboard's existing widgets."),
6602
+ integrationName: z35.string().optional().describe("Technical override \u2014 datasource display name. Non-technical callers must leave this blank; the datasource is inherited from the dashboard's existing widgets.")
6554
6603
  },
6555
6604
  READ_ONLY,
6556
6605
  async (params) => {
@@ -6635,9 +6684,9 @@ function registerGenerateWidget(server, client) {
6635
6684
  }
6636
6685
 
6637
6686
  // src/tools/widgets/create-or-update-metric-from-query.ts
6638
- import { z as z35 } from "zod";
6639
- var modeSchema = z35.enum(["dry_run", "apply"]).default("dry_run");
6640
- var conflictStrategySchema = z35.enum(["fail", "update", "create_new"]).default("fail");
6687
+ import { z as z36 } from "zod";
6688
+ var modeSchema = z36.enum(["dry_run", "apply"]).default("dry_run");
6689
+ var conflictStrategySchema = z36.enum(["fail", "update", "create_new"]).default("fail");
6641
6690
  function makePlanText(plan) {
6642
6691
  return JSON.stringify(plan, null, 2);
6643
6692
  }
@@ -6654,22 +6703,22 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
6654
6703
  "[EMBEDDING] Create or update a production metric/widget on a customer dashboard from validated SQL. Requires clientId (multi-tenant embed). Defaults to dry-run; apply requires explicit confirmation.",
6655
6704
  {
6656
6705
  mode: modeSchema.describe("dry_run returns the plan only; apply executes after confirmation"),
6657
- confirm: z35.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
6706
+ confirm: z36.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
6658
6707
  conflictStrategy: conflictStrategySchema.describe("What to do if metricId already exists on the target dashboard"),
6659
- clientId: z35.string().describe("Client/tenant identifier for the dashboard widget scope"),
6660
- workspaceName: z35.string().describe("Target workspace name"),
6661
- dashboardName: z35.string().optional().describe("Dashboard name to attach to"),
6662
- embedId: z35.string().optional().describe("Technical embed id alternative to dashboardName"),
6663
- dataAppName: z35.string().optional().describe("Optional data app override"),
6664
- name: z35.string().describe("Metric/widget display name"),
6665
- metricId: z35.string().describe("Stable metric slug to create or update"),
6666
- query: z35.string().describe("SQL query already validated against the warehouse"),
6667
- description: z35.string().optional().describe("Metric description"),
6668
- chartOptions: z35.record(z35.unknown()).optional().describe("Chart configuration JSON"),
6669
- companyIntegrationId: z35.string().optional().describe("Datasource UUID override"),
6670
- integrationName: z35.string().optional().describe("Datasource display name override"),
6671
- isPublished: z35.boolean().optional().describe("Publish after apply. Defaults to false for production safety"),
6672
- requestedBy: z35.string().optional().describe("Human operator/request context to store on the metric when supported")
6708
+ clientId: z36.string().describe("Client/tenant identifier for the dashboard widget scope"),
6709
+ workspaceName: z36.string().describe("Target workspace name"),
6710
+ dashboardName: z36.string().optional().describe("Dashboard name to attach to"),
6711
+ embedId: z36.string().optional().describe("Technical embed id alternative to dashboardName"),
6712
+ dataAppName: z36.string().optional().describe("Optional data app override"),
6713
+ name: z36.string().describe("Metric/widget display name"),
6714
+ metricId: z36.string().describe("Stable metric slug to create or update"),
6715
+ query: z36.string().describe("SQL query already validated against the warehouse"),
6716
+ description: z36.string().optional().describe("Metric description"),
6717
+ chartOptions: z36.record(z36.unknown()).optional().describe("Chart configuration JSON"),
6718
+ companyIntegrationId: z36.string().optional().describe("Datasource UUID override"),
6719
+ integrationName: z36.string().optional().describe("Datasource display name override"),
6720
+ isPublished: z36.boolean().optional().describe("Publish after apply. Defaults to false for production safety"),
6721
+ requestedBy: z36.string().optional().describe("Human operator/request context to store on the metric when supported")
6673
6722
  },
6674
6723
  WRITE_IDEMPOTENT,
6675
6724
  async (params) => {
@@ -6829,8 +6878,8 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
6829
6878
  }
6830
6879
 
6831
6880
  // src/tools/widgets/create-workspace-metric.ts
6832
- import { z as z36 } from "zod";
6833
- var modeSchema2 = z36.enum(["dry_run", "apply"]).default("dry_run");
6881
+ import { z as z37 } from "zod";
6882
+ var modeSchema2 = z37.enum(["dry_run", "apply"]).default("dry_run");
6834
6883
  function registerCreateWorkspaceMetric(server, client) {
6835
6884
  server.tool(
6836
6885
  "create_workspace_metric",
@@ -6839,18 +6888,18 @@ function registerCreateWorkspaceMetric(server, client) {
6839
6888
  mode: modeSchema2.describe(
6840
6889
  "dry_run validates the SQL only (no write); apply creates or updates the metric after confirmation"
6841
6890
  ),
6842
- confirm: z36.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
6843
- workspaceName: z36.string().describe("Target workspace name"),
6844
- name: z36.string().describe("Metric display name"),
6845
- query: z36.string().describe(
6891
+ confirm: z37.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
6892
+ workspaceName: z37.string().describe("Target workspace name"),
6893
+ name: z37.string().describe("Metric display name"),
6894
+ query: z37.string().describe(
6846
6895
  "SQL query to validate and store. In a TABLE-tenancy workspace never compare the client column to a fixed client value; use `client_id_variable` so each client sees only its own rows (the backend refuses it with TENANT_KEY_LITERAL)."
6847
6896
  ),
6848
- metricId: z36.string().optional().describe(
6897
+ metricId: z37.string().optional().describe(
6849
6898
  "Stable metric slug. Pass an existing slug to UPDATE that metric in place. Omit to create \u2014 the slug is derived deterministically from the name (no random/uniqueness suffix), so re-running with the same name upserts the same metric rather than creating a duplicate."
6850
6899
  ),
6851
- description: z36.string().optional().describe("Metric description"),
6852
- datasourceId: z36.string().optional().describe("Datasource UUID override; defaults to the workspace datasource"),
6853
- isPublished: z36.boolean().optional().describe(
6900
+ description: z37.string().optional().describe("Metric description"),
6901
+ datasourceId: z37.string().optional().describe("Datasource UUID override; defaults to the workspace datasource"),
6902
+ isPublished: z37.boolean().optional().describe(
6854
6903
  "Publish after apply. Defaults to false. Applies to creates; on updates the existing publish state is preserved."
6855
6904
  )
6856
6905
  },
@@ -6906,25 +6955,25 @@ function registerCreateWorkspaceMetric(server, client) {
6906
6955
  }
6907
6956
 
6908
6957
  // src/tools/widgets/validate-metric-spec.ts
6909
- import { z as z37 } from "zod";
6958
+ import { z as z38 } from "zod";
6910
6959
  function registerValidateMetricSpec(server, client) {
6911
6960
  const services = createServiceBundle(client);
6912
6961
  server.tool(
6913
6962
  "validate_metric_spec",
6914
6963
  "Validate a production metric spec from SQL without saving it. Resolves dashboard, data app, datasource, conflicts, and required fields.",
6915
6964
  {
6916
- clientId: z37.string().describe("Client/tenant identifier used to validate dashboard widget scope"),
6917
- workspaceName: z37.string().describe("Target production-backed workspace name"),
6918
- dashboardName: z37.string().optional().describe("Dashboard name to attach to"),
6919
- embedId: z37.string().optional().describe("Technical embed id alternative to dashboardName"),
6920
- dataAppName: z37.string().optional().describe("Optional data app override"),
6921
- name: z37.string().describe("Metric/widget display name"),
6922
- metricId: z37.string().describe("Stable metric slug to create or update"),
6923
- query: z37.string().describe("SQL query already validated against the warehouse"),
6924
- description: z37.string().optional().describe("Metric description"),
6925
- chartOptions: z37.record(z37.unknown()).optional().describe("Chart configuration JSON"),
6926
- companyIntegrationId: z37.string().optional().describe("Datasource UUID override"),
6927
- integrationName: z37.string().optional().describe("Datasource display name override")
6965
+ clientId: z38.string().describe("Client/tenant identifier used to validate dashboard widget scope"),
6966
+ workspaceName: z38.string().describe("Target production-backed workspace name"),
6967
+ dashboardName: z38.string().optional().describe("Dashboard name to attach to"),
6968
+ embedId: z38.string().optional().describe("Technical embed id alternative to dashboardName"),
6969
+ dataAppName: z38.string().optional().describe("Optional data app override"),
6970
+ name: z38.string().describe("Metric/widget display name"),
6971
+ metricId: z38.string().describe("Stable metric slug to create or update"),
6972
+ query: z38.string().describe("SQL query already validated against the warehouse"),
6973
+ description: z38.string().optional().describe("Metric description"),
6974
+ chartOptions: z38.record(z38.unknown()).optional().describe("Chart configuration JSON"),
6975
+ companyIntegrationId: z38.string().optional().describe("Datasource UUID override"),
6976
+ integrationName: z38.string().optional().describe("Datasource display name override")
6928
6977
  },
6929
6978
  READ_ONLY,
6930
6979
  async (params) => {
@@ -7015,20 +7064,20 @@ function registerValidateMetricSpec(server, client) {
7015
7064
  }
7016
7065
 
7017
7066
  // src/tools/reports/list-scheduled-reports.ts
7018
- import { z as z38 } from "zod";
7067
+ import { z as z39 } from "zod";
7019
7068
  function registerListScheduledReports(server, client) {
7020
7069
  const services = createServiceBundle(client);
7021
7070
  server.tool(
7022
7071
  "list_scheduled_reports",
7023
7072
  "List configured scheduled reports for a specific client/dashboard embed. Shows schedule metadata, not delivery success history.",
7024
7073
  {
7025
- clientId: z38.string().describe("Client/tenant identifier. Required by the current backend API for non-empty results"),
7026
- dashboardName: z38.string().optional().describe("Dashboard name to inspect"),
7027
- workspaceName: z38.string().optional().describe("Workspace name if dashboardName is ambiguous"),
7028
- embedId: z38.string().optional().describe("Technical embed id alternative to dashboardName"),
7029
- userIdentifier: z38.string().optional().describe("Optional creator/user filter"),
7030
- pageNumber: z38.number().int().positive().optional().describe("Page number when pagination is enabled"),
7031
- isPagination: z38.boolean().optional().describe("Enable backend pagination")
7074
+ clientId: z39.string().describe("Client/tenant identifier. Required by the current backend API for non-empty results"),
7075
+ dashboardName: z39.string().optional().describe("Dashboard name to inspect"),
7076
+ workspaceName: z39.string().optional().describe("Workspace name if dashboardName is ambiguous"),
7077
+ embedId: z39.string().optional().describe("Technical embed id alternative to dashboardName"),
7078
+ userIdentifier: z39.string().optional().describe("Optional creator/user filter"),
7079
+ pageNumber: z39.number().int().positive().optional().describe("Page number when pagination is enabled"),
7080
+ isPagination: z39.boolean().optional().describe("Enable backend pagination")
7032
7081
  },
7033
7082
  READ_ONLY,
7034
7083
  async (params) => {
@@ -7102,7 +7151,7 @@ function registerWhoami(server, client) {
7102
7151
  status: "authenticated",
7103
7152
  activeBearer: "service-token",
7104
7153
  ...auth.oauthFallback ? { oauthFallback: true } : {},
7105
- note: auth.oauthFallback ? "OAuth token acquisition failed; the configured static service token is in use. Mutations use the static org-level principal, with actor=system. OAuth will be retried after a short cooldown." : "Org-level service token (DATABRAIN_SERVICE_TOKEN). Per-user identity is NOT bound \u2014 mutations are audit-logged as actor=system. Per-user binding will be available once the OAuth login flow ships."
7154
+ note: auth.oauthFallback ? "OAuth token acquisition failed; the configured static service token is in use. Mutations use the static org-level principal, with actor=system. OAuth will be retried after a short cooldown." : "Org-level service token (DATABRAIN_SERVICE_TOKEN). Per-user identity is NOT bound \u2014 mutations are audit-logged as actor=system. For per-user access, connect an AI app to the hosted server (it signs in with your Databrain account)."
7106
7155
  },
7107
7156
  null,
7108
7157
  2
@@ -7138,19 +7187,19 @@ function registerWhoami(server, client) {
7138
7187
  }
7139
7188
 
7140
7189
  // src/tools/custom-chart/scaffold.ts
7141
- import { z as z40 } from "zod";
7190
+ import { z as z41 } from "zod";
7142
7191
 
7143
7192
  // src/tools/custom-chart/shared.ts
7144
- import { z as z39 } from "zod";
7193
+ import { z as z40 } from "zod";
7145
7194
  var slotInputSchema = slotSchema;
7146
7195
  var optionInputSchema = optionSchema;
7147
- var manifestInputSchema = z39.record(z39.unknown()).describe("manifest.json as an object (schemaVersion 2). Validated with the same rules as the server.");
7196
+ var manifestInputSchema = z40.record(z40.unknown()).describe("manifest.json as an object (schemaVersion 2). Validated with the same rules as the server.");
7148
7197
  var bundleInputShape = {
7149
7198
  manifest: manifestInputSchema.optional(),
7150
- bundleJs: z39.string().optional().describe("dist/index.js from `databrain custom-chart build` (the shim plus the chart module)."),
7151
- bundleCss: z39.string().optional().describe("The raw chart.css, kept for provenance."),
7152
- iconSvg: z39.string().optional().describe("icon.svg, at most 16 KiB, no script, no foreignObject, no external href."),
7153
- projectDir: z39.string().optional().describe("Local only: a chart project folder. The tool reads it instead of manifest/bundleJs/iconSvg.")
7199
+ bundleJs: z40.string().optional().describe("dist/index.js from `databrain custom-chart build` (the shim plus the chart module)."),
7200
+ bundleCss: z40.string().optional().describe("The raw chart.css, kept for provenance."),
7201
+ iconSvg: z40.string().optional().describe("icon.svg, at most 16 KiB, no script, no foreignObject, no external href."),
7202
+ projectDir: z40.string().optional().describe("Local only: a chart project folder. The tool reads it instead of manifest/bundleJs/iconSvg.")
7154
7203
  };
7155
7204
  function formatProblems(problems) {
7156
7205
  if (problems.length === 0) return "No problems.";
@@ -7217,11 +7266,11 @@ function registerCustomChartScaffold(server, _client) {
7217
7266
  "custom_chart_scaffold",
7218
7267
  "Return the files of a new custom chart project (manifest.json, chart.ts, chart.css, icon.svg, sample data, guide). Nothing is written or persisted; write the files yourself.",
7219
7268
  {
7220
- key: z40.string().describe("Chart key, becomes chart type custom:<key>. Lowercase letters, digits, _ and -, 64 max."),
7221
- name: z40.string().describe("Display name shown in the chart picker, 1 to 256 characters."),
7222
- description: z40.string().optional().describe("Optional description, up to 500 characters."),
7223
- slots: z40.array(slotInputSchema).optional().describe("Data slots. Defaults to category (dimension), value (measure), target (optional measure)."),
7224
- options: z40.array(optionInputSchema).optional().describe("Chart options. Defaults to the template options (labels, subtitle, radius, colour, sort).")
7269
+ key: z41.string().describe("Chart key, becomes chart type custom:<key>. Lowercase letters, digits, _ and -, 64 max."),
7270
+ name: z41.string().describe("Display name shown in the chart picker, 1 to 256 characters."),
7271
+ description: z41.string().optional().describe("Optional description, up to 500 characters."),
7272
+ slots: z41.array(slotInputSchema).optional().describe("Data slots. Defaults to category (dimension), value (measure), target (optional measure)."),
7273
+ options: z41.array(optionInputSchema).optional().describe("Chart options. Defaults to the template options (labels, subtitle, radius, colour, sort).")
7225
7274
  },
7226
7275
  READ_ONLY,
7227
7276
  async (params) => {
@@ -7313,18 +7362,18 @@ function registerCustomChartValidate(server, client) {
7313
7362
  }
7314
7363
 
7315
7364
  // src/tools/custom-chart/upload.ts
7316
- import { z as z41 } from "zod";
7365
+ import { z as z42 } from "zod";
7317
7366
  function registerCustomChartUpload(server, client) {
7318
7367
  const api = createCustomChartsApi(client);
7319
7368
  server.tool(
7320
7369
  "custom_chart_upload",
7321
7370
  "Upload a built custom chart bundle to the company. Creates the chart (private, version 1) or a new preview version. Defaults to dry_run; apply requires explicit confirmation and can publish in the same call.",
7322
7371
  {
7323
- mode: z41.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run validates and returns the plan; apply uploads."),
7324
- confirm: z41.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
7325
- publish: z41.boolean().default(false).describe("After a successful apply, publish the uploaded version so every builder can use it."),
7326
- name: z41.string().optional().describe("Display name override. Defaults to manifest.name."),
7327
- description: z41.string().optional().describe("Description override. Defaults to manifest.description."),
7372
+ mode: z42.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run validates and returns the plan; apply uploads."),
7373
+ confirm: z42.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
7374
+ publish: z42.boolean().default(false).describe("After a successful apply, publish the uploaded version so every builder can use it."),
7375
+ name: z42.string().optional().describe("Display name override. Defaults to manifest.name."),
7376
+ description: z42.string().optional().describe("Description override. Defaults to manifest.description."),
7328
7377
  ...bundleInputShape
7329
7378
  },
7330
7379
  WRITE_CREATE,
@@ -7465,17 +7514,17 @@ function registerListCustomCharts(server, client) {
7465
7514
  }
7466
7515
 
7467
7516
  // src/tools/custom-chart/publish.ts
7468
- import { z as z42 } from "zod";
7517
+ import { z as z43 } from "zod";
7469
7518
  function registerPublishCustomChart(server, client) {
7470
7519
  const api = createCustomChartsApi(client);
7471
7520
  server.tool(
7472
7521
  "publish_custom_chart",
7473
7522
  "Publish a custom chart version so every builder in the company can use chart type custom:<key>. Defaults to the preview version. Defaults to dry_run; apply requires explicit confirmation.",
7474
7523
  {
7475
- key: z42.string().describe("The chart key (manifest.id), for example revenue-bars."),
7476
- versionId: z42.string().optional().describe("A specific version id to publish. Defaults to the preview version."),
7477
- mode: z42.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run shows what would be published; apply publishes."),
7478
- confirm: z42.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`)
7524
+ key: z43.string().describe("The chart key (manifest.id), for example revenue-bars."),
7525
+ versionId: z43.string().optional().describe("A specific version id to publish. Defaults to the preview version."),
7526
+ mode: z43.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run shows what would be published; apply publishes."),
7527
+ confirm: z43.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`)
7479
7528
  },
7480
7529
  WRITE_IDEMPOTENT,
7481
7530
  async (params) => {
@@ -7531,6 +7580,66 @@ function registerPublishCustomChart(server, client) {
7531
7580
  );
7532
7581
  }
7533
7582
 
7583
+ // src/tools/tool-titles.ts
7584
+ var TOOL_TITLES = {
7585
+ whoami: "Show active credential",
7586
+ list_datasources: "List data sources",
7587
+ discover_datasource_schema: "Discover data source schema",
7588
+ list_datamarts: "List datamarts",
7589
+ list_workspaces: "List workspaces",
7590
+ sync_datasource: "Sync data source",
7591
+ create_datamart: "Create datamart",
7592
+ create_workspace: "Create workspace",
7593
+ list_data_apps: "List data apps",
7594
+ list_tenants: "List tenants",
7595
+ create_data_app: "Create data app",
7596
+ list_api_tokens: "List API tokens",
7597
+ create_api_token: "Create API token",
7598
+ rotate_api_token: "Rotate API token",
7599
+ create_embed: "Create embed",
7600
+ list_embeds: "List embeds",
7601
+ get_embed_details: "Get embed details",
7602
+ update_embed: "Update embed",
7603
+ customize_embed_theme: "Customize embed theme",
7604
+ get_demo_link: "Get demo link",
7605
+ generate_guest_token: "Generate guest token",
7606
+ generate_embed_code: "Generate embed code",
7607
+ list_dashboards: "List dashboards",
7608
+ create_dashboard: "Create dashboard",
7609
+ get_dashboard: "Get dashboard",
7610
+ dashboard_snapshot: "Dashboard snapshot (picture)",
7611
+ generate_dashboard_tiles: "Generate dashboard tiles",
7612
+ ask_question: "Ask a data question",
7613
+ get_semantic_layer: "Get semantic layer",
7614
+ update_semantic_layer: "Update semantic layer",
7615
+ start_semantic_layer_generation: "Start semantic layer generation",
7616
+ get_semantic_layer_generation_status: "Get semantic layer generation status",
7617
+ search_semantic_model: "Search semantic model",
7618
+ get_semantic_entity: "Get semantic entity",
7619
+ propose_semantic_change: "Propose semantic model change",
7620
+ apply_semantic_change: "Apply semantic model change",
7621
+ eval_semantic_model: "Evaluate semantic model",
7622
+ list_widgets: "List widgets",
7623
+ validate_metric_spec: "Validate metric spec",
7624
+ create_or_update_metric_from_query: "Create or update metric from SQL",
7625
+ create_workspace_metric: "Create workspace metric",
7626
+ add_widget: "Add widget",
7627
+ update_widget: "Update widget",
7628
+ remove_widget: "Remove widget",
7629
+ generate_widget: "Generate widget",
7630
+ list_scheduled_reports: "List scheduled reports",
7631
+ custom_chart_scaffold: "Scaffold custom chart",
7632
+ custom_chart_validate: "Validate custom chart",
7633
+ custom_chart_upload: "Upload custom chart",
7634
+ list_custom_charts: "List custom charts",
7635
+ publish_custom_chart: "Publish custom chart"
7636
+ };
7637
+ function toolTitle(name) {
7638
+ if (TOOL_TITLES[name]) return TOOL_TITLES[name];
7639
+ const words = name.split("_").filter(Boolean);
7640
+ return words.map((word, index) => index === 0 ? word[0].toUpperCase() + word.slice(1) : word).join(" ");
7641
+ }
7642
+
7534
7643
  // src/tools/registry.ts
7535
7644
  function instrumentToolRegistration(server) {
7536
7645
  const original = server.tool.bind(server);
@@ -7551,7 +7660,15 @@ function instrumentToolRegistration(server) {
7551
7660
  () => handler(...handlerArgs)
7552
7661
  );
7553
7662
  }
7554
- return original(...args);
7663
+ const title = toolTitle(name);
7664
+ const annotationIndex = cbIndex - 1;
7665
+ const annotations = args[annotationIndex];
7666
+ if (annotationIndex > 1 && annotations && typeof annotations === "object" && ("readOnlyHint" in annotations || "destructiveHint" in annotations) && !("title" in annotations)) {
7667
+ args[annotationIndex] = { title, ...annotations };
7668
+ }
7669
+ const registered = original(...args);
7670
+ if (registered && typeof registered === "object" && !registered.title) registered.title = title;
7671
+ return registered;
7555
7672
  };
7556
7673
  }
7557
7674
  function registerAllTools(server, client) {
@@ -7580,6 +7697,7 @@ function registerAllTools(server, client) {
7580
7697
  registerListDashboards(server, client);
7581
7698
  registerCreateDashboard(server, client);
7582
7699
  registerGetDashboard(server, client);
7700
+ registerDashboardSnapshot(server, client);
7583
7701
  registerGenerateDashboardTiles(server, client);
7584
7702
  registerAskQuestion(server, client);
7585
7703
  registerGetSemanticLayer(server, client);
@@ -7591,7 +7709,6 @@ function registerAllTools(server, client) {
7591
7709
  registerProposeSemanticChange(server, client);
7592
7710
  registerApplySemanticChange(server, client);
7593
7711
  registerEvalSemanticModel(server, client);
7594
- registerSemanticReadiness(server, client);
7595
7712
  registerListWidgets(server, client);
7596
7713
  registerValidateMetricSpec(server, client);
7597
7714
  registerCreateOrUpdateMetricFromQuery(server, client);
@@ -9493,7 +9610,7 @@ The v2 source is one YAML document. Use the configured service credential and a
9493
9610
 
9494
9611
  1. \`search_semantic_model\` finds entities by question. \`get_semantic_entity\` reads one entity by kind and name. Column names use table.column.
9495
9612
  2. \`propose_semantic_change\` takes complete source YAML, a YAML patch or one structured change. It returns candidate source, validation errors, a plan and the stored expected hash without writing.
9496
- 3. \`eval_semantic_model\` evaluates the proposed source. \`semantic_readiness\` returns the server score, verdict and open checks.
9613
+ 3. \`eval_semantic_model\` evaluates the proposed source.
9497
9614
  4. \`apply_semantic_change\` defaults to a dry run. Live apply requires the exact reviewed source, \`expected_content_hash\`, \`dry_run: false\` and user supplied \`confirm: "APPLY_TO_PRODUCTION"\`. Never supply confirmation yourself. Apply changes the live model for all consumers. On a conflict, prepare a fresh proposal and review it again.
9498
9615
 
9499
9616
  YAML patches merge mappings and named lists by name. Other lists replace. Empty lists clear; null removes a field. Structured changes use operation upsert or remove, kind, name, and value for upsert. Column changes also require table. Patch output normalizes formatting and comments, so review the returned source before apply.
@@ -10487,7 +10604,7 @@ If **no datamarts exist**, tell the user they need to create one in the **Databr
10487
10604
  If **no datasources exist either**, the user must first connect their database in the **Databrain UI**.
10488
10605
 
10489
10606
  **For a v2 semantic model**
10490
- If the datamart uses v2, inspect the question's entities with \`search_semantic_model\` and \`get_semantic_entity\`. Read \`semantic_readiness\` for open checks, then continue to Step 2. If metadata needs correction, use \`propose_semantic_change\`, evaluate its source with \`eval_semantic_model\`, and show the plan. Only apply the reviewed source and expected hash through \`apply_semantic_change\` after the user supplies \`APPLY_TO_PRODUCTION\`. Never use the legacy \`update_semantic_layer\` steps below for v2.
10607
+ If the datamart uses v2, inspect the question's entities with \`search_semantic_model\` and \`get_semantic_entity\`. Then continue to Step 2. If metadata needs correction, use \`propose_semantic_change\`, evaluate its source with \`eval_semantic_model\`, and show the plan. Only apply the reviewed source and expected hash through \`apply_semantic_change\` after the user supplies \`APPLY_TO_PRODUCTION\`. Never use the legacy \`update_semantic_layer\` steps below for v2.
10491
10608
 
10492
10609
  **Step 1b \u2014 Verify the Semantic Layer**
10493
10610
  After confirming a datamart exists, use \`get_semantic_layer\` to check its tables. The semantic layer is built on the **datamart** (not the raw datasource) \u2014 do NOT use \`discover_datasource_schema\` to populate it.
@@ -10577,7 +10694,7 @@ If \`list_datamarts\` reports \`semanticLayerVersion: "v2"\`, or the selected da
10577
10694
  3. Call \`eval_semantic_model\` with the returned source. Show the verdicts and let the user review the candidate.
10578
10695
  4. Stop for the user's explicit \`APPLY_TO_PRODUCTION\` confirmation. Never supply it yourself.
10579
10696
  5. Call \`apply_semantic_change\` with the exact reviewed source, its \`expected_content_hash\`, \`dry_run: false\` and the user's confirmation. A conflict requires a new proposal and review.
10580
- 6. Call \`semantic_readiness\` and inspect the changed entity. Do not call \`update_semantic_layer\` for a v2 model.
10697
+ 6. Call \`get_semantic_entity\` and inspect the changed entity. Do not call \`update_semantic_layer\` for a v2 model.
10581
10698
 
10582
10699
  **Why this matters:** Without a populated semantic layer, \`ask_question\` cannot generate SQL \u2014 every query will fail with "out of context." Populating the semantic layer is the critical step between having a datamart and being able to query it.
10583
10700
 
@@ -10955,6 +11072,7 @@ export {
10955
11072
  enableChartApps,
10956
11073
  chartResult,
10957
11074
  draftSchema,
11075
+ PREVIEW_PATH,
10958
11076
  RENDER_TYPES,
10959
11077
  draftPage,
10960
11078
  workspacesForDatamart,
@@ -10963,4 +11081,4 @@ export {
10963
11081
  registerAllResources,
10964
11082
  registerAllPrompts
10965
11083
  };
10966
- //# sourceMappingURL=chunk-JSYG7ULR.js.map
11084
+ //# sourceMappingURL=chunk-VK6XPPBT.js.map