@databrainhq/mcp-server 0.2.12 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # @databrainhq/mcp-server
2
2
 
3
- Add embedded analytics to your app using natural language. Connect this MCP server to any AI assistant and describe what you want — it handles the Databrain API for you.
3
+ Add embedded analytics to your app using natural language. Connect this MCP server to any AI assistant and describe what you want — it handles the setup steps for you.
4
4
 
5
5
  ```
6
6
  "What was total revenue last month?"
7
7
  "Embed my sales dashboard in my React app"
8
8
  ```
9
9
 
10
- Works with Claude, Cursor, Windsurf, and any MCP-compatible client.
10
+ Works with Codex, Claude, Cursor, Windsurf, and any MCP-compatible client.
11
11
 
12
12
  ---
13
13
 
@@ -23,6 +23,24 @@ Works with Claude, Cursor, Windsurf, and any MCP-compatible client.
23
23
 
24
24
  > **Self-hosted?** Also set `DATABRAIN_API_URL` in the `env` block — see [Self-hosted Databrain](#self-hosted-databrain) below.
25
25
 
26
+ <details>
27
+ <summary><b>Codex</b></summary>
28
+
29
+ Add the MCP server with your service token:
30
+
31
+ ```bash
32
+ codex mcp add databrain --env DATABRAIN_SERVICE_TOKEN=YOUR_SERVICE_TOKEN -- npx @databrainhq/mcp-server
33
+ ```
34
+
35
+ Verify the server is configured:
36
+
37
+ ```bash
38
+ codex mcp list
39
+ ```
40
+
41
+ Start a new Codex session after adding the server.
42
+ </details>
43
+
26
44
  <details>
27
45
  <summary><b>Cursor</b></summary>
28
46
 
@@ -119,7 +137,8 @@ Running Databrain on your own infrastructure? Point the MCP server at your insta
119
137
  "args": ["@databrainhq/mcp-server"],
120
138
  "env": {
121
139
  "DATABRAIN_SERVICE_TOKEN": "<your-service-token>",
122
- "DATABRAIN_API_URL": "https://api.your-databrain.example.com"
140
+ "DATABRAIN_API_URL": "https://api.your-databrain.example.com",
141
+ "DATABRAIN_DEMO_DOMAIN": "https://demo.your-databrain.example.com"
123
142
  }
124
143
  }
125
144
  }
@@ -129,7 +148,7 @@ Running Databrain on your own infrastructure? Point the MCP server at your insta
129
148
  Checklist:
130
149
  - `DATABRAIN_API_URL` must be the **API origin** for your self-hosted instance (not the dashboard URL). Typically `https://api.<your-domain>`.
131
150
  - The token must be minted from that same self-hosted instance — tokens are not portable across instances.
132
- - Use `DATABRAIN_SERVICE_TOKEN` (Settings → Service Tokens) for org-level access. Per-user identity binding will arrive with the OAuth-based login flow now in development.
151
+ - Use `DATABRAIN_SERVICE_TOKEN` (Settings → Service Tokens) for org-level access.
133
152
  - The MCP client process must be able to reach the URL (VPN, firewall, self-signed certs all apply here).
134
153
  - Scaffolded frontend code (React, Next.js, Vue, etc.) reads the same `DATABRAIN_API_URL` for server-side guest-token generation, so the variable flows through to your app.
135
154
 
@@ -229,11 +248,11 @@ Let end-users build their own metrics with drag-and-drop or AI-powered chat.
229
248
 
230
249
  ## Environment Variables
231
250
 
232
- At least one of `DATABRAIN_SERVICE_TOKEN` or `DATABRAIN_API_TOKEN` must be set. Per-user identity binding (OAuth-based login) is in active development — until it ships, sessions authenticate at the org / data-app level.
251
+ At least one of `DATABRAIN_SERVICE_TOKEN` or `DATABRAIN_API_TOKEN` must be set. Use a service token for full setup and management flows; use an API token only when you are operating inside one data app.
233
252
 
234
253
  | Variable | Required | Default | Description |
235
254
  |----------|----------|---------|-------------|
236
- | `DATABRAIN_SERVICE_TOKEN` | One of two† | — | Org-level token from Settings → Service Tokens. Powers all discovery and setup operations. Audit logs record actions as `actor=system` until per-user OAuth login ships. |
255
+ | `DATABRAIN_SERVICE_TOKEN` | One of two† | — | Org-level token from Settings → Service Tokens. Powers discovery, setup, data app management, semantic layer, and production-safe datamart operations. |
237
256
  | `DATABRAIN_API_TOKEN` | One of two† | — | Per-data-app token. Created automatically during setup via `create_api_token`, or set manually if you already have one. Sufficient for embedding operations on a single data app. |
238
257
  | `DATABRAIN_API_URL` | **Required for self-hosted** | `https://api.usedatabrain.com` | API base URL. Set to your instance's API origin (e.g. `https://api.your-databrain.example.com`). |
239
258
  | `DATABRAIN_DEMO_DOMAIN` | Optional | `https://demo.usedatabrain.com` | Base host for `get_demo_link` URLs. Override for self-hosted instances or custom preview domains (e.g. `https://demo.your-databrain.example.com`). |
@@ -297,9 +316,9 @@ Prompts are guided workflows your AI assistant can follow. Ask for them by name
297
316
  | `create_embed` | Create an embed (standard or multi-tenant with `clientId`) |
298
317
  | `list_embeds` | List embed configurations (includes `distinctClientIds` for multi-tenant client discovery) |
299
318
  | `get_embed_details` | Get full embed details by ID |
300
- | `update_embed` | Update embed config — access settings, options, filters, i18n (via `body` passthrough) |
319
+ | `update_embed` | Update embed config — access settings, options, filters, i18n, and advanced embed options |
301
320
  | `customize_embed_theme` | Presets, colors, fonts, cards, chart appearance, responsive layout |
302
- | `get_demo_link` | Get a shareable demo URL for a `clientId` — same link the UI's "Share Demo Link" generates. Use to preview ops-built widgets without a test app |
321
+ | `get_demo_link` | Get a shareable demo URL for a `clientId` — same link the UI's "Share Demo Link" generates |
303
322
  </details>
304
323
 
305
324
  <details>
@@ -319,7 +338,7 @@ Prompts are guided workflows your AI assistant can follow. Ask for them by name
319
338
  | `ask_question` | Ask a natural language question — gets converted to SQL, returns data + chart suggestion |
320
339
  | `get_semantic_layer` | Inspect semantic layer metadata (table/column descriptions, synonyms, completion score) |
321
340
  | `update_semantic_layer` | Improve semantic layer quality (add descriptions, synonyms, example questions) |
322
- | `start_semantic_layer_generation` | Start backend semantic-layer generation for a datamart |
341
+ | `start_semantic_layer_generation` | Start semantic-layer generation for a datamart |
323
342
  | `get_semantic_layer_generation_status` | Check semantic-layer generation progress |
324
343
  </details>
325
344
 
@@ -332,12 +351,12 @@ Prompts are guided workflows your AI assistant can follow. Ask for them by name
332
351
  | `list_widgets` | List widgets on an embed for a specific client |
333
352
  | `validate_metric_spec` | Validate a SQL-backed metric target without saving |
334
353
  | `create_or_update_metric_from_query` | Dry-run or apply a SQL-backed metric create/update |
335
- | `add_widget` | Add a widget — clone from a template (`templateMetricId`), supply a full spec, or pass a raw body from `generate_widget` |
354
+ | `add_widget` | Add a widget — clone from a template (`templateMetricId`), supply a metric configuration, or use a generated draft |
336
355
  | `update_widget` | Patch a widget's fields (name, query, chartOptions, isPublished, etc.) |
337
356
  | `remove_widget` | Per-client soft delete — hides the widget for one client only |
338
357
  | `generate_widget` | AI-generate a widget draft from a natural-language prompt. Returns a draft; pass to `add_widget` to persist |
339
358
 
340
- Widget mutation tools mint a guest token internally for `(clientId, dataAppName)`. Provide `embedId` + `clientId` — the tools resolve the rest.
359
+ Provide `clientId` and `dashboardName`; include `workspaceName` only if dashboard names are ambiguous. The tools resolve the required resources automatically.
341
360
  Production writes through `create_or_update_metric_from_query` default to `dry_run`; `apply` requires `confirm: "APPLY_TO_PRODUCTION"`.
342
361
  </details>
343
362
 
@@ -346,7 +365,7 @@ Production writes through `create_or_update_metric_from_query` default to `dry_r
346
365
 
347
366
  | Tool | Description |
348
367
  |------|-------------|
349
- | `list_scheduled_reports` | List configured scheduled reports for a dashboard/client scope. Delivery history is not part of this MCP/API surface yet |
368
+ | `list_scheduled_reports` | List configured scheduled reports for a dashboard/client scope |
350
369
  </details>
351
370
 
352
371
  ---
@@ -407,13 +426,11 @@ node --version # Should be v18.0.0 or higher
407
426
  ## How it works
408
427
 
409
428
  ```
410
- Your AI Client ←→ MCP Server ←→ Databrain API
411
- (Claude, (this (your data,
412
- Cursor, package) dashboards,
413
- etc.) embeds)
429
+ Your AI Client MCP Server Databrain API
430
+ (Codex, Claude, Cursor, etc.) ←→ (this package) ←→ (your data, dashboards, embeds)
414
431
  ```
415
432
 
416
- The MCP server translates natural language requests into Databrain API calls. It manages authentication (service tokens, API tokens, guest tokens), validates inputs, and provides guided workflows so you don't need to learn the Databrain API directly.
433
+ The MCP server translates natural language requests into Databrain actions. It manages authentication (service tokens, API tokens, guest tokens), validates inputs, and provides guided workflows so you don't need to learn the Databrain API directly.
417
434
 
418
435
  All tokens are managed automatically. You provide your service token once in the config — everything else is created and handled by the server during setup.
419
436
 
package/dist/index.js CHANGED
@@ -646,7 +646,7 @@ var QueryApi = class {
646
646
  async startSemanticLayerGeneration(datamartId) {
647
647
  return this.client.request({
648
648
  method: "POST",
649
- path: `/api/schema/datamart/${encodeURIComponent(datamartId)}/semantic-layer/auto-generate`,
649
+ path: `/schema/datamart/${encodeURIComponent(datamartId)}/semantic-layer/auto-generate`,
650
650
  auth: "service",
651
651
  invalidateCache: ["semantic_layer_"]
652
652
  });
@@ -654,7 +654,7 @@ var QueryApi = class {
654
654
  async getSemanticLayerGenerationStatus(datamartId) {
655
655
  return this.client.request({
656
656
  method: "GET",
657
- path: `/api/schema/datamart/${encodeURIComponent(datamartId)}/semantic-layer/generation-status`,
657
+ path: `/schema/datamart/${encodeURIComponent(datamartId)}/semantic-layer/generation-status`,
658
658
  auth: "service"
659
659
  });
660
660
  }
@@ -4798,7 +4798,7 @@ var columnSchema2 = z22.object({
4798
4798
  "Identifier",
4799
4799
  "Number",
4800
4800
  "JSON"
4801
- ]).nullable().optional().describe('Semantic column type. Use "Identifier" for IDs/foreign keys, "Number" for numeric values, "String" for text. Omit for date columns.')
4801
+ ]).nullable().optional().describe('Semantic column type. Use "Identifier" for IDs/foreign keys, "Number" for numeric values, "String" for text. Omit for date and boolean columns.')
4802
4802
  });
4803
4803
  var tableSchema2 = z22.object({
4804
4804
  name: z22.string().describe("Table name (must exist in the datamart)"),
@@ -4811,7 +4811,33 @@ var tableSchema2 = z22.object({
4811
4811
  ),
4812
4812
  columns: z22.array(columnSchema2).optional().describe("Column-level semantic definitions to update")
4813
4813
  });
4814
- function normalizeTablesForApi(tables) {
4814
+ function columnKey(tableName, columnName) {
4815
+ return `${tableName.toLowerCase()}\0${columnName.toLowerCase()}`;
4816
+ }
4817
+ function buildColumnDatatypeMap(semanticLayer) {
4818
+ const datatypes = /* @__PURE__ */ new Map();
4819
+ for (const table of semanticLayer.tables ?? []) {
4820
+ for (const column of table.columns ?? []) {
4821
+ if (column.datatype) {
4822
+ datatypes.set(columnKey(table.name, column.name), column.datatype);
4823
+ }
4824
+ }
4825
+ }
4826
+ return datatypes;
4827
+ }
4828
+ function hasExplicitColumnType(tables) {
4829
+ return tables?.some(
4830
+ (table) => table.columns?.some(
4831
+ (column) => column.columnType !== void 0 && column.columnType !== null
4832
+ )
4833
+ ) ?? false;
4834
+ }
4835
+ function shouldOmitColumnTypeForDatatype(datatype) {
4836
+ if (!datatype) return false;
4837
+ const normalized = datatype.trim().toLowerCase();
4838
+ return normalized === "boolean" || normalized === "bool" || normalized === "date" || normalized.startsWith("date(") || normalized.startsWith("datetime") || normalized.startsWith("timestamp") || normalized === "time" || normalized.startsWith("time(") || normalized.startsWith("time ");
4839
+ }
4840
+ function normalizeTablesForApi(tables, datatypesByColumn) {
4815
4841
  if (!tables) return void 0;
4816
4842
  return tables.map((table) => {
4817
4843
  const normalized = { name: table.name };
@@ -4831,7 +4857,12 @@ function normalizeTablesForApi(tables) {
4831
4857
  normalizedColumn.miscellaneousInfo = column.miscellaneousInfo;
4832
4858
  }
4833
4859
  if (column.columnType !== void 0 && column.columnType !== null) {
4834
- normalizedColumn.columnType = column.columnType;
4860
+ const datatype = datatypesByColumn?.get(
4861
+ columnKey(table.name, column.name)
4862
+ );
4863
+ if (!shouldOmitColumnTypeForDatatype(datatype)) {
4864
+ normalizedColumn.columnType = column.columnType;
4865
+ }
4835
4866
  }
4836
4867
  return normalizedColumn;
4837
4868
  });
@@ -4874,7 +4905,10 @@ function registerUpdateSemanticLayer(server, client) {
4874
4905
  });
4875
4906
  if (resolution.kind !== "resolved") return resolution.response;
4876
4907
  const datamartName = resolution.name;
4877
- const tables = normalizeTablesForApi(params.tables);
4908
+ const datatypesByColumn = hasExplicitColumnType(params.tables) ? buildColumnDatatypeMap(
4909
+ await services.queryApi.getSemanticLayer(datamartName)
4910
+ ) : void 0;
4911
+ const tables = normalizeTablesForApi(params.tables, datatypesByColumn);
4878
4912
  await services.queryApi.updateSemanticLayer({
4879
4913
  datamartName,
4880
4914
  tables,
@@ -4911,10 +4945,108 @@ You can now:
4911
4945
 
4912
4946
  // src/tools/query/semantic-generation.ts
4913
4947
  import { z as z23 } from "zod";
4948
+ function nonEmptyString(value) {
4949
+ return typeof value === "string" && value.trim().length > 0 ? value : null;
4950
+ }
4914
4951
  function datamartIdFrom(datamart) {
4915
- return typeof datamart.id === "string" && datamart.id || typeof datamart._id === "string" && datamart._id || typeof datamart.datamartId === "string" && datamart.datamartId || null;
4952
+ return nonEmptyString(datamart.id) || nonEmptyString(datamart._id) || nonEmptyString(datamart.datamartId) || nonEmptyString(datamart.dataMartId) || null;
4953
+ }
4954
+ function nestedDatamartFromWorkspace(workspace) {
4955
+ const nested = workspace.datamart;
4956
+ return nested && typeof nested === "object" && !Array.isArray(nested) ? nested : null;
4957
+ }
4958
+ function workspaceDatamartIdFrom(workspace) {
4959
+ const nested = nestedDatamartFromWorkspace(workspace);
4960
+ return nonEmptyString(workspace.dataMartId) || nonEmptyString(workspace.datamartId) || (nested ? datamartIdFrom(nested) : null);
4961
+ }
4962
+ function workspaceDatamartNameFrom(workspace) {
4963
+ const nested = nestedDatamartFromWorkspace(workspace);
4964
+ return (nested ? nonEmptyString(nested.name) || nonEmptyString(nested.datamartName) : null) || nonEmptyString(workspace.datamartName) || null;
4965
+ }
4966
+ async function resolveDatamartFromWorkspace(params) {
4967
+ const services = createServiceBundle(params.client);
4968
+ const workspaces = await services.workspacesApi.listWorkspaces();
4969
+ const resolution = resolveResource({
4970
+ items: workspaces,
4971
+ name: params.workspaceName,
4972
+ getName: (ws) => ws.name,
4973
+ formatItem: formatWorkspace,
4974
+ resourceType: "workspace",
4975
+ toolName: params.toolName
4976
+ });
4977
+ if (resolution.kind !== "resolved") return { ok: false, response: resolution.response };
4978
+ const selected = resolution.selected;
4979
+ const datamartId = workspaceDatamartIdFrom(selected);
4980
+ const workspaceDatamartName = workspaceDatamartNameFrom(selected);
4981
+ if (!datamartId) {
4982
+ return {
4983
+ ok: false,
4984
+ response: toolError(
4985
+ `Workspace "${resolution.name}" does not expose a linked dataMartId/datamart.id. Pass datamartId directly or choose a workspace linked to a datamart.`
4986
+ )
4987
+ };
4988
+ }
4989
+ if (params.datamartName && workspaceDatamartName && workspaceDatamartName.toLowerCase() !== params.datamartName.toLowerCase()) {
4990
+ return {
4991
+ ok: false,
4992
+ response: toolError(
4993
+ `Workspace "${resolution.name}" is linked to datamart "${workspaceDatamartName}", not "${params.datamartName}". Use the linked datamart name or choose another workspace.`
4994
+ )
4995
+ };
4996
+ }
4997
+ return {
4998
+ ok: true,
4999
+ datamartName: workspaceDatamartName || params.datamartName || datamartId,
5000
+ datamartId,
5001
+ workspaceName: resolution.name
5002
+ };
5003
+ }
5004
+ async function findDatamartIdInLinkedWorkspaces(params) {
5005
+ const services = createServiceBundle(params.client);
5006
+ const workspaces = await services.workspacesApi.listWorkspaces();
5007
+ const matches = workspaces.map((workspace) => {
5008
+ const record = workspace;
5009
+ return {
5010
+ name: nonEmptyString(record.name),
5011
+ datamartName: workspaceDatamartNameFrom(record),
5012
+ datamartId: workspaceDatamartIdFrom(record)
5013
+ };
5014
+ }).filter(
5015
+ (workspace) => workspace.datamartName?.toLowerCase() === params.datamartName.toLowerCase() && workspace.datamartId
5016
+ );
5017
+ const uniqueIds = [...new Set(matches.map((workspace) => workspace.datamartId))];
5018
+ if (uniqueIds.length === 1 && uniqueIds[0]) {
5019
+ return {
5020
+ kind: "resolved",
5021
+ datamartId: uniqueIds[0],
5022
+ workspaceName: matches.find((workspace) => workspace.datamartId === uniqueIds[0])?.name || void 0
5023
+ };
5024
+ }
5025
+ if (uniqueIds.length > 1) {
5026
+ return {
5027
+ kind: "ambiguous",
5028
+ workspaceNames: matches.map((workspace) => workspace.name).filter((name) => !!name)
5029
+ };
5030
+ }
5031
+ return { kind: "none" };
4916
5032
  }
4917
5033
  async function resolveDatamart(params) {
5034
+ if (params.datamartId) {
5035
+ return {
5036
+ ok: true,
5037
+ datamartName: params.datamartName || params.datamartId,
5038
+ datamartId: params.datamartId,
5039
+ workspaceName: params.workspaceName
5040
+ };
5041
+ }
5042
+ if (params.workspaceName) {
5043
+ return resolveDatamartFromWorkspace({
5044
+ client: params.client,
5045
+ workspaceName: params.workspaceName,
5046
+ datamartName: params.datamartName,
5047
+ toolName: params.toolName
5048
+ });
5049
+ }
4918
5050
  const services = createServiceBundle(params.client);
4919
5051
  const datamarts = await services.datamartsApi.listDatamarts();
4920
5052
  const resolution = resolveResource({
@@ -4928,10 +5060,30 @@ async function resolveDatamart(params) {
4928
5060
  if (resolution.kind !== "resolved") return { ok: false, response: resolution.response };
4929
5061
  const datamartId = datamartIdFrom(resolution.selected);
4930
5062
  if (!datamartId) {
5063
+ const workspaceMatch = await findDatamartIdInLinkedWorkspaces({
5064
+ client: params.client,
5065
+ datamartName: resolution.name
5066
+ });
5067
+ if (workspaceMatch.kind === "resolved") {
5068
+ return {
5069
+ ok: true,
5070
+ datamartName: resolution.name,
5071
+ datamartId: workspaceMatch.datamartId,
5072
+ workspaceName: workspaceMatch.workspaceName
5073
+ };
5074
+ }
5075
+ if (workspaceMatch.kind === "ambiguous") {
5076
+ return {
5077
+ ok: false,
5078
+ response: toolError(
5079
+ `Datamart "${resolution.name}" is linked from multiple workspaces with different IDs. Call ${params.toolName} again with workspaceName. Matching workspaces: ${workspaceMatch.workspaceNames.join(", ")}`
5080
+ )
5081
+ };
5082
+ }
4931
5083
  return {
4932
5084
  ok: false,
4933
5085
  response: toolError(
4934
- `Datamart "${resolution.name}" resolved by name but no id/_id/datamartId was returned. Use list_datamarts to verify the API response includes IDs.`
5086
+ `Datamart "${resolution.name}" resolved by name but no id/_id/datamartId/dataMartId was returned and no linked workspace exposed its ID. Pass workspaceName for a workspace linked to this datamart, pass datamartId directly, or update the datamart list API to include IDs.`
4935
5087
  )
4936
5088
  };
4937
5089
  }
@@ -4947,14 +5099,18 @@ function registerStartSemanticLayerGeneration(server, client) {
4947
5099
  "start_semantic_layer_generation",
4948
5100
  "Start backend semantic-layer auto-generation for a datamart. Returns an async job id to poll.",
4949
5101
  {
4950
- datamartName: z23.string().optional().describe("Datamart name. Omit to list available datamarts")
5102
+ datamartName: z23.string().optional().describe("Datamart name. Omit to list available datamarts"),
5103
+ workspaceName: z23.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
5104
+ datamartId: z23.string().optional().describe("Datamart UUID. Use only when already known.")
4951
5105
  },
4952
5106
  WRITE_CREATE,
4953
5107
  async (params) => {
4954
5108
  try {
4955
5109
  const resolved = await resolveDatamart({
4956
5110
  client,
5111
+ datamartId: params.datamartId,
4957
5112
  datamartName: params.datamartName,
5113
+ workspaceName: params.workspaceName,
4958
5114
  toolName: "start_semantic_layer_generation"
4959
5115
  });
4960
5116
  if (!resolved.ok) return resolved.response;
@@ -4966,8 +5122,9 @@ function registerStartSemanticLayerGeneration(server, client) {
4966
5122
  {
4967
5123
  datamartName: resolved.datamartName,
4968
5124
  datamartId: resolved.datamartId,
5125
+ workspaceName: resolved.workspaceName,
4969
5126
  ...result,
4970
- nextStep: "Call get_semantic_layer_generation_status with the same datamartName until status is complete or failed."
5127
+ nextStep: "Call get_semantic_layer_generation_status with the same datamartName or workspaceName until status is complete or failed."
4971
5128
  },
4972
5129
  null,
4973
5130
  2
@@ -4986,14 +5143,18 @@ function registerGetSemanticLayerGenerationStatus(server, client) {
4986
5143
  "get_semantic_layer_generation_status",
4987
5144
  "Get semantic-layer auto-generation job status for a datamart.",
4988
5145
  {
4989
- datamartName: z23.string().optional().describe("Datamart name. Omit to list available datamarts")
5146
+ datamartName: z23.string().optional().describe("Datamart name. Omit to list available datamarts"),
5147
+ workspaceName: z23.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
5148
+ datamartId: z23.string().optional().describe("Datamart UUID. Use only when already known.")
4990
5149
  },
4991
5150
  READ_ONLY,
4992
5151
  async (params) => {
4993
5152
  try {
4994
5153
  const resolved = await resolveDatamart({
4995
5154
  client,
5155
+ datamartId: params.datamartId,
4996
5156
  datamartName: params.datamartName,
5157
+ workspaceName: params.workspaceName,
4997
5158
  toolName: "get_semantic_layer_generation_status"
4998
5159
  });
4999
5160
  if (!resolved.ok) return resolved.response;
@@ -5005,6 +5166,7 @@ function registerGetSemanticLayerGenerationStatus(server, client) {
5005
5166
  {
5006
5167
  datamartName: resolved.datamartName,
5007
5168
  datamartId: resolved.datamartId,
5169
+ workspaceName: resolved.workspaceName,
5008
5170
  ...result
5009
5171
  },
5010
5172
  null,
@@ -8819,7 +8981,7 @@ Guidelines for generating descriptions:
8819
8981
  - **Table descriptions:** Explain what the table contains in 1-2 sentences. Example: \`orders\` \u2192 "Contains customer purchase orders with line items, totals, and fulfillment status."
8820
8982
  - **Column descriptions:** Explain the business meaning. Example: \`total_amount\` \u2192 "Total order value in the customer's local currency, including tax and shipping."
8821
8983
  - **Synonyms:** Add common alternative names users might say. Example: \`total_amount\` \u2192 ["revenue", "order total", "sales amount"]
8822
- - **Column types:** Set semantic types when obvious: "Date", "Number", "Identifier", "Category", "Currency", "Email", "Name", etc.
8984
+ - **Column types:** Only use supported semantic types: "String", "Long String", "ENUM", "Mapper", "Range", "Expression", "Identifier", "Number", or "JSON". Omit \`columnType\` for date and boolean database columns.
8823
8985
 
8824
8986
  Ask the user if they want to review and refine the descriptions before applying, or if you should go ahead and apply them directly.
8825
8987