@databrainhq/mcp-server 0.2.14 → 0.2.16
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 +5 -4
- package/dist/index.js +765 -401
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -326,7 +326,7 @@ var DatabrainClient = class {
|
|
|
326
326
|
}
|
|
327
327
|
const text = await response.text();
|
|
328
328
|
let data = text ? JSON.parse(text) : {};
|
|
329
|
-
if (data && typeof data === "object" && !Array.isArray(data) && "data" in data) {
|
|
329
|
+
if (!options.skipDataUnwrap && data && typeof data === "object" && !Array.isArray(data) && "data" in data) {
|
|
330
330
|
const inner = data.data;
|
|
331
331
|
if (inner !== null && inner !== void 0) {
|
|
332
332
|
data = inner;
|
|
@@ -550,14 +550,20 @@ var EmbedsApi = class {
|
|
|
550
550
|
invalidateCache: ["list_embeds"]
|
|
551
551
|
});
|
|
552
552
|
}
|
|
553
|
-
async listEmbeds(dataAppName) {
|
|
554
|
-
const query =
|
|
553
|
+
async listEmbeds(dataAppName, clientId) {
|
|
554
|
+
const query = {};
|
|
555
|
+
if (dataAppName && dataAppName.trim().length > 0) {
|
|
556
|
+
query.dataAppName = dataAppName.trim();
|
|
557
|
+
}
|
|
558
|
+
if (clientId && clientId.trim().length > 0) {
|
|
559
|
+
query.clientId = clientId.trim();
|
|
560
|
+
}
|
|
555
561
|
return this.client.request({
|
|
556
562
|
method: "GET",
|
|
557
563
|
path: "/api/v2/data-app/embeds",
|
|
558
564
|
auth: "api",
|
|
559
|
-
query,
|
|
560
|
-
cacheKey:
|
|
565
|
+
query: Object.keys(query).length > 0 ? query : void 0,
|
|
566
|
+
cacheKey: `list_embeds:${query.dataAppName || "*"}:${query.clientId || "*"}`,
|
|
561
567
|
cacheTtlMs: 5 * 60 * 1e3
|
|
562
568
|
});
|
|
563
569
|
}
|
|
@@ -708,6 +714,37 @@ var ScheduledReportsApi = class {
|
|
|
708
714
|
}
|
|
709
715
|
};
|
|
710
716
|
|
|
717
|
+
// src/api/tenants-api.ts
|
|
718
|
+
var TenantsApi = class {
|
|
719
|
+
constructor(client) {
|
|
720
|
+
this.client = client;
|
|
721
|
+
}
|
|
722
|
+
async listTenants(params) {
|
|
723
|
+
const input = {
|
|
724
|
+
workspaceId: params.workspaceId
|
|
725
|
+
};
|
|
726
|
+
if (params.limit !== void 0) input.limit = String(params.limit);
|
|
727
|
+
if (params.searchValue && params.searchValue.trim().length > 0) {
|
|
728
|
+
input.searchValue = params.searchValue.trim();
|
|
729
|
+
}
|
|
730
|
+
if (params.datasourceId && params.datasourceId.trim().length > 0) {
|
|
731
|
+
input.datasourceId = params.datasourceId.trim();
|
|
732
|
+
}
|
|
733
|
+
if (params.datamartId && params.datamartId.trim().length > 0) {
|
|
734
|
+
input.datamartId = params.datamartId.trim();
|
|
735
|
+
}
|
|
736
|
+
return this.client.request({
|
|
737
|
+
method: "POST",
|
|
738
|
+
path: "/api/mcp/getClientData",
|
|
739
|
+
auth: "service",
|
|
740
|
+
body: { input: { input } },
|
|
741
|
+
skipDataUnwrap: true,
|
|
742
|
+
cacheKey: `list_tenants:${JSON.stringify(input)}`,
|
|
743
|
+
cacheTtlMs: 5 * 60 * 1e3
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
|
|
711
748
|
// src/api/widgets-api.ts
|
|
712
749
|
var WidgetsApi = class {
|
|
713
750
|
constructor(client) {
|
|
@@ -899,8 +936,8 @@ var EmbedService = class {
|
|
|
899
936
|
this.embedsApi = embedsApi;
|
|
900
937
|
this.datamartsApi = datamartsApi;
|
|
901
938
|
}
|
|
902
|
-
async listEmbeds(dataAppName) {
|
|
903
|
-
return this.embedsApi.listEmbeds(dataAppName);
|
|
939
|
+
async listEmbeds(dataAppName, clientId) {
|
|
940
|
+
return this.embedsApi.listEmbeds(dataAppName, clientId);
|
|
904
941
|
}
|
|
905
942
|
async createEmbedStrict(body) {
|
|
906
943
|
const accessSettings = asRecord2(body.accessSettings);
|
|
@@ -1213,6 +1250,66 @@ var WidgetService = class {
|
|
|
1213
1250
|
error: `Multiple data apps exist (${normalized.map((a) => `"${a.name}"`).join(", ")}). Pass \`dataAppName\` explicitly, or pass \`embedId\` to auto-resolve.`
|
|
1214
1251
|
};
|
|
1215
1252
|
}
|
|
1253
|
+
async ensureTenantDashboardScope(params) {
|
|
1254
|
+
const clientId = params.clientId.trim();
|
|
1255
|
+
if (!clientId) {
|
|
1256
|
+
return {
|
|
1257
|
+
ok: false,
|
|
1258
|
+
error: "`clientId` cannot be empty. Pick a tenant/client first."
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
const ctx = await this.resolveEmbedContext(params.embedId);
|
|
1262
|
+
if (ctx.externalDashboardCreatedBy === clientId) {
|
|
1263
|
+
return {
|
|
1264
|
+
ok: true,
|
|
1265
|
+
value: {
|
|
1266
|
+
embedId: params.embedId,
|
|
1267
|
+
clientId,
|
|
1268
|
+
dataAppName: ctx.dataAppName
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
let tenantEmbeds;
|
|
1273
|
+
try {
|
|
1274
|
+
tenantEmbeds = await this.embedsApi.listEmbeds(params.dataAppName, clientId);
|
|
1275
|
+
} catch (err) {
|
|
1276
|
+
return {
|
|
1277
|
+
ok: false,
|
|
1278
|
+
error: `Could not verify tenant dashboard ownership for client "${clientId}": ${err.message}`
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
const target = params.embedId.trim();
|
|
1282
|
+
const matches = tenantEmbeds.map((embed) => ({
|
|
1283
|
+
raw: embed,
|
|
1284
|
+
normalized: this.discoveryService.normalizeEmbed(embed)
|
|
1285
|
+
})).filter(({ raw, normalized }) => {
|
|
1286
|
+
const ids = [
|
|
1287
|
+
raw.embedId,
|
|
1288
|
+
raw.id,
|
|
1289
|
+
raw._id,
|
|
1290
|
+
raw.dashboardId,
|
|
1291
|
+
normalized.embedId,
|
|
1292
|
+
normalized.internalId,
|
|
1293
|
+
normalized.dashboardId
|
|
1294
|
+
].filter(isNonEmptyString3);
|
|
1295
|
+
return ids.includes(target);
|
|
1296
|
+
});
|
|
1297
|
+
if (matches.length === 0) {
|
|
1298
|
+
const dashboardLabel = params.dashboardName || params.embedId;
|
|
1299
|
+
return {
|
|
1300
|
+
ok: false,
|
|
1301
|
+
error: `Refusing to touch dashboard "${dashboardLabel}" for client "${clientId}". The tenant-scoped embed lookup did not return this dashboard, which means the resolved target is likely the production/data-app dashboard rather than this client's dashboard copy. Create or select a multi-tenant dashboard embed for this client first (\`create_embed\` with \`clientId\`), then retry.`
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
return {
|
|
1305
|
+
ok: true,
|
|
1306
|
+
value: {
|
|
1307
|
+
embedId: params.embedId,
|
|
1308
|
+
clientId,
|
|
1309
|
+
dataAppName: matches[0].normalized.dataAppName
|
|
1310
|
+
}
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1216
1313
|
/**
|
|
1217
1314
|
* Resolve a datasource for a widget. Priority:
|
|
1218
1315
|
* 1. Explicit integrationId + integrationName (fast path).
|
|
@@ -1397,7 +1494,8 @@ Do not guess between the two \u2014 wait for the user to choose.`
|
|
|
1397
1494
|
dataAppId,
|
|
1398
1495
|
dataAppName,
|
|
1399
1496
|
externalDashboardId,
|
|
1400
|
-
dashboardId
|
|
1497
|
+
dashboardId,
|
|
1498
|
+
externalDashboardCreatedBy: (externalDashboard && isNonEmptyString3(externalDashboard.createdBy) ? externalDashboard.createdBy : null) || (isNonEmptyString3(exact.createdBy) ? exact.createdBy : null) || (metadata && isNonEmptyString3(metadata.createdBy) ? metadata.createdBy : null) || (isNonEmptyString3(exact.clientId) ? exact.clientId : null)
|
|
1401
1499
|
};
|
|
1402
1500
|
}
|
|
1403
1501
|
async mintGuestToken(clientId, dataAppName, allowedEmbeds) {
|
|
@@ -1482,6 +1580,7 @@ function createServiceBundle(client) {
|
|
|
1482
1580
|
const guestTokenApi = new GuestTokenApi(client);
|
|
1483
1581
|
const queryApi = new QueryApi(client);
|
|
1484
1582
|
const scheduledReportsApi = new ScheduledReportsApi(client);
|
|
1583
|
+
const tenantsApi = new TenantsApi(client);
|
|
1485
1584
|
const widgetsApi = new WidgetsApi(client);
|
|
1486
1585
|
const workspacesApi = new WorkspacesApi(client);
|
|
1487
1586
|
const discoveryService = new DiscoveryService();
|
|
@@ -1495,6 +1594,7 @@ function createServiceBundle(client) {
|
|
|
1495
1594
|
workspacesApi,
|
|
1496
1595
|
queryApi,
|
|
1497
1596
|
scheduledReportsApi,
|
|
1597
|
+
tenantsApi,
|
|
1498
1598
|
widgetsApi,
|
|
1499
1599
|
embedService: new EmbedService(embedsApi, datamartsApi),
|
|
1500
1600
|
discoveryService,
|
|
@@ -1572,16 +1672,153 @@ function registerListDataApps(server, client) {
|
|
|
1572
1672
|
);
|
|
1573
1673
|
}
|
|
1574
1674
|
|
|
1575
|
-
// src/tools/data-app/
|
|
1675
|
+
// src/tools/data-app/list-tenants.ts
|
|
1576
1676
|
import { z as z3 } from "zod";
|
|
1677
|
+
var isNonEmptyString4 = (value) => typeof value === "string" && value.trim().length > 0;
|
|
1678
|
+
function workspaceIdFrom(workspace) {
|
|
1679
|
+
return isNonEmptyString4(workspace.id) && workspace.id || isNonEmptyString4(workspace._id) && workspace._id || isNonEmptyString4(workspace.workspaceId) && workspace.workspaceId || null;
|
|
1680
|
+
}
|
|
1681
|
+
function tenantErrorMessage(result) {
|
|
1682
|
+
const message = result.message;
|
|
1683
|
+
if (isNonEmptyString4(message)) return message;
|
|
1684
|
+
const error = result.error;
|
|
1685
|
+
if (isNonEmptyString4(error)) return error;
|
|
1686
|
+
if (error !== null && error !== void 0) {
|
|
1687
|
+
try {
|
|
1688
|
+
return JSON.stringify(error);
|
|
1689
|
+
} catch {
|
|
1690
|
+
return String(error);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
return null;
|
|
1694
|
+
}
|
|
1695
|
+
function normalizeTenantList(result) {
|
|
1696
|
+
if (Array.isArray(result)) {
|
|
1697
|
+
return { tenants: result };
|
|
1698
|
+
}
|
|
1699
|
+
const errorMessage = tenantErrorMessage(result);
|
|
1700
|
+
if (errorMessage) {
|
|
1701
|
+
return { tenants: [], errorMessage };
|
|
1702
|
+
}
|
|
1703
|
+
return {
|
|
1704
|
+
tenants: Array.isArray(result.data) ? result.data : [],
|
|
1705
|
+
clientColumnType: isNonEmptyString4(result.clientColumnType) ? result.clientColumnType : void 0
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
function registerListTenants(server, client) {
|
|
1709
|
+
const services = createServiceBundle(client);
|
|
1710
|
+
server.tool(
|
|
1711
|
+
"list_tenants",
|
|
1712
|
+
"List available tenants/clients from the workspace tenancy database. Use this to pick a valid clientId before creating guest tokens or editing customer dashboards.",
|
|
1713
|
+
{
|
|
1714
|
+
workspaceName: z3.string().optional().describe("Workspace name. Preferred for non-technical callers; the tool resolves it to a workspaceId."),
|
|
1715
|
+
workspaceId: z3.string().optional().describe("Technical workspace UUID. Provide this instead of workspaceName if already known."),
|
|
1716
|
+
searchValue: z3.string().optional().describe("Optional case-insensitive search string for tenant/client labels."),
|
|
1717
|
+
limit: z3.number().int().positive().max(500).optional().describe("Maximum number of tenants to return. Backend may apply connector-specific limits."),
|
|
1718
|
+
datasourceId: z3.string().optional().describe("Optional datasource UUID override when the workspace has multiple datasource contexts."),
|
|
1719
|
+
datamartId: z3.string().optional().describe("Optional datamart UUID override when tenant lookup should use a specific datamart.")
|
|
1720
|
+
},
|
|
1721
|
+
READ_ONLY,
|
|
1722
|
+
async (params) => {
|
|
1723
|
+
try {
|
|
1724
|
+
let workspaceId = params.workspaceId?.trim() || "";
|
|
1725
|
+
let workspaceName = params.workspaceName?.trim() || "";
|
|
1726
|
+
if (!workspaceId && !workspaceName) {
|
|
1727
|
+
return toolError(
|
|
1728
|
+
"Provide `workspaceName` or `workspaceId` so the tenancy lookup can resolve the client table."
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
if (!workspaceId) {
|
|
1732
|
+
const workspaces = await services.workspacesApi.listWorkspaces();
|
|
1733
|
+
const matches = workspaces.filter(
|
|
1734
|
+
(workspace) => isNonEmptyString4(workspace.name) && workspace.name.trim().toLowerCase() === workspaceName.toLowerCase()
|
|
1735
|
+
);
|
|
1736
|
+
if (matches.length === 0) {
|
|
1737
|
+
const available = workspaces.map((workspace) => workspace.name).filter(isNonEmptyString4).map((name) => `"${name}"`).join(", ");
|
|
1738
|
+
return toolError(
|
|
1739
|
+
`No workspace named "${workspaceName}". ` + (available ? `Available workspaces: ${available}.` : "No workspaces were returned by list_workspaces.")
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
if (matches.length > 1) {
|
|
1743
|
+
return toolError(
|
|
1744
|
+
`Workspace name "${workspaceName}" is ambiguous. Retry with \`workspaceId\`.`
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
workspaceId = workspaceIdFrom(matches[0]) || "";
|
|
1748
|
+
if (!workspaceId) {
|
|
1749
|
+
return toolError(
|
|
1750
|
+
`Workspace "${workspaceName}" did not include an id in the backend response. Retry with \`workspaceId\`.`
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1753
|
+
workspaceName = matches[0].name || workspaceName;
|
|
1754
|
+
}
|
|
1755
|
+
if (!workspaceName) {
|
|
1756
|
+
try {
|
|
1757
|
+
const workspaces = await services.workspacesApi.listWorkspaces();
|
|
1758
|
+
const match = workspaces.find(
|
|
1759
|
+
(workspace) => workspaceIdFrom(workspace) === workspaceId
|
|
1760
|
+
);
|
|
1761
|
+
if (match?.name) workspaceName = match.name;
|
|
1762
|
+
} catch {
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
const result = await services.tenantsApi.listTenants({
|
|
1766
|
+
workspaceId,
|
|
1767
|
+
limit: params.limit,
|
|
1768
|
+
searchValue: params.searchValue,
|
|
1769
|
+
datasourceId: params.datasourceId,
|
|
1770
|
+
datamartId: params.datamartId
|
|
1771
|
+
});
|
|
1772
|
+
const { tenants, clientColumnType, errorMessage } = normalizeTenantList(result);
|
|
1773
|
+
if (errorMessage) {
|
|
1774
|
+
return toolError(`Failed to list tenants: ${errorMessage}`);
|
|
1775
|
+
}
|
|
1776
|
+
if (tenants.length === 0) {
|
|
1777
|
+
return toolSuccess(
|
|
1778
|
+
`No tenants found for workspace "${workspaceName || workspaceId}".`
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1781
|
+
return toolSuccess(
|
|
1782
|
+
JSON.stringify(
|
|
1783
|
+
{
|
|
1784
|
+
count: tenants.length,
|
|
1785
|
+
source: "tenancy_database",
|
|
1786
|
+
workspace: {
|
|
1787
|
+
id: workspaceId,
|
|
1788
|
+
name: workspaceName || null
|
|
1789
|
+
},
|
|
1790
|
+
clientColumnType: clientColumnType || "STRING",
|
|
1791
|
+
tenants: tenants.map((tenant) => {
|
|
1792
|
+
const clientId = isNonEmptyString4(tenant.value) ? tenant.value : null;
|
|
1793
|
+
const label = isNonEmptyString4(tenant.label) ? tenant.label : clientId;
|
|
1794
|
+
return {
|
|
1795
|
+
clientId,
|
|
1796
|
+
label
|
|
1797
|
+
};
|
|
1798
|
+
})
|
|
1799
|
+
},
|
|
1800
|
+
null,
|
|
1801
|
+
2
|
|
1802
|
+
)
|
|
1803
|
+
);
|
|
1804
|
+
} catch (err) {
|
|
1805
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
1806
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
);
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
// src/tools/data-app/create-data-app.ts
|
|
1813
|
+
import { z as z4 } from "zod";
|
|
1577
1814
|
function registerCreateDataApp(server, client) {
|
|
1578
1815
|
server.tool(
|
|
1579
1816
|
"create_data_app",
|
|
1580
1817
|
"Create a new data app.",
|
|
1581
1818
|
{
|
|
1582
|
-
name:
|
|
1583
|
-
workspaceName:
|
|
1584
|
-
body:
|
|
1819
|
+
name: z4.string().describe("Data app name"),
|
|
1820
|
+
workspaceName: z4.string().optional().describe("Optional workspace name if required by backend"),
|
|
1821
|
+
body: z4.record(z4.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/")
|
|
1585
1822
|
},
|
|
1586
1823
|
WRITE_CREATE,
|
|
1587
1824
|
async (params) => {
|
|
@@ -1615,7 +1852,7 @@ function registerCreateDataApp(server, client) {
|
|
|
1615
1852
|
}
|
|
1616
1853
|
|
|
1617
1854
|
// src/tools/data-app/list-api-tokens.ts
|
|
1618
|
-
import { z as
|
|
1855
|
+
import { z as z5 } from "zod";
|
|
1619
1856
|
|
|
1620
1857
|
// src/utils/resolve-resource.ts
|
|
1621
1858
|
function resolveResource(options) {
|
|
@@ -1684,7 +1921,7 @@ function registerListApiTokens(server, client) {
|
|
|
1684
1921
|
"list_api_tokens",
|
|
1685
1922
|
"List API tokens for a data app. Omit dataAppName to auto-resolve.",
|
|
1686
1923
|
{
|
|
1687
|
-
dataAppName:
|
|
1924
|
+
dataAppName: z5.string().optional().describe("Data app name. Omit to list available data apps.")
|
|
1688
1925
|
},
|
|
1689
1926
|
READ_ONLY,
|
|
1690
1927
|
async (params) => {
|
|
@@ -1729,16 +1966,16 @@ function registerListApiTokens(server, client) {
|
|
|
1729
1966
|
}
|
|
1730
1967
|
|
|
1731
1968
|
// src/tools/data-app/create-api-token.ts
|
|
1732
|
-
import { z as
|
|
1969
|
+
import { z as z6 } from "zod";
|
|
1733
1970
|
function registerCreateApiToken(server, client) {
|
|
1734
1971
|
const services = createServiceBundle(client);
|
|
1735
1972
|
server.tool(
|
|
1736
1973
|
"create_api_token",
|
|
1737
1974
|
"Create an API token for a data app and activate it for this session. Omit dataAppName to auto-resolve.",
|
|
1738
1975
|
{
|
|
1739
|
-
dataAppName:
|
|
1740
|
-
name:
|
|
1741
|
-
body:
|
|
1976
|
+
dataAppName: z6.string().optional().describe("Data app name. Omit to list available data apps."),
|
|
1977
|
+
name: z6.string().optional().describe("Optional token name"),
|
|
1978
|
+
body: z6.record(z6.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/api-tokens")
|
|
1742
1979
|
},
|
|
1743
1980
|
WRITE_CREATE,
|
|
1744
1981
|
async (params) => {
|
|
@@ -1790,16 +2027,16 @@ Save this in backend secrets immediately. This value may not be returned again.`
|
|
|
1790
2027
|
}
|
|
1791
2028
|
|
|
1792
2029
|
// src/tools/data-app/rotate-api-token.ts
|
|
1793
|
-
import { z as
|
|
2030
|
+
import { z as z7 } from "zod";
|
|
1794
2031
|
function registerRotateApiToken(server, client) {
|
|
1795
2032
|
const services = createServiceBundle(client);
|
|
1796
2033
|
server.tool(
|
|
1797
2034
|
"rotate_api_token",
|
|
1798
2035
|
"Rotate (invalidate and replace) an API token. Previous token becomes stale.",
|
|
1799
2036
|
{
|
|
1800
|
-
dataAppName:
|
|
1801
|
-
tokenId:
|
|
1802
|
-
body:
|
|
2037
|
+
dataAppName: z7.string().optional().describe("Data app name. Omit to list available data apps."),
|
|
2038
|
+
tokenId: z7.string().optional().describe("Optional token ID to rotate"),
|
|
2039
|
+
body: z7.record(z7.unknown()).optional().describe("Raw API body passthrough for rotate endpoint")
|
|
1803
2040
|
},
|
|
1804
2041
|
DESTRUCTIVE,
|
|
1805
2042
|
async (params) => {
|
|
@@ -1918,14 +2155,14 @@ function registerListDatasources(server, client) {
|
|
|
1918
2155
|
}
|
|
1919
2156
|
|
|
1920
2157
|
// src/tools/infrastructure/discover-datasource-schema.ts
|
|
1921
|
-
import { z as
|
|
2158
|
+
import { z as z8 } from "zod";
|
|
1922
2159
|
function registerDiscoverDatasourceSchema(server, client) {
|
|
1923
2160
|
const services = createServiceBundle(client);
|
|
1924
2161
|
server.tool(
|
|
1925
2162
|
"discover_datasource_schema",
|
|
1926
2163
|
"Inspect tables and columns in a datasource. Omit datasourceName to list available datasources.",
|
|
1927
2164
|
{
|
|
1928
|
-
datasourceName:
|
|
2165
|
+
datasourceName: z8.string().optional().describe(
|
|
1929
2166
|
"Name of the datasource to inspect. Omit to list available datasources."
|
|
1930
2167
|
)
|
|
1931
2168
|
},
|
|
@@ -2008,13 +2245,13 @@ Fix the connection or add a new database in the **Databrain UI**.`
|
|
|
2008
2245
|
}
|
|
2009
2246
|
|
|
2010
2247
|
// src/tools/infrastructure/list-datamarts.ts
|
|
2011
|
-
import { z as
|
|
2248
|
+
import { z as z9 } from "zod";
|
|
2012
2249
|
function registerListDatamarts(server, client) {
|
|
2013
2250
|
server.tool(
|
|
2014
2251
|
"list_datamarts",
|
|
2015
2252
|
"List datamarts with table counts and semantic layer scores.",
|
|
2016
2253
|
{
|
|
2017
|
-
dataAppName:
|
|
2254
|
+
dataAppName: z9.string().optional().describe("Filter by data app name")
|
|
2018
2255
|
},
|
|
2019
2256
|
READ_ONLY,
|
|
2020
2257
|
async (params) => {
|
|
@@ -2103,7 +2340,7 @@ function registerListWorkspaces(server, client) {
|
|
|
2103
2340
|
}
|
|
2104
2341
|
|
|
2105
2342
|
// src/tools/infrastructure/create-datamart.ts
|
|
2106
|
-
import { z as
|
|
2343
|
+
import { z as z10 } from "zod";
|
|
2107
2344
|
|
|
2108
2345
|
// src/tools/widgets/production-guards.ts
|
|
2109
2346
|
var APPLY_TO_PRODUCTION_CONFIRMATION = "APPLY_TO_PRODUCTION";
|
|
@@ -2121,29 +2358,29 @@ function genericProductionSafetyNote() {
|
|
|
2121
2358
|
}
|
|
2122
2359
|
|
|
2123
2360
|
// src/tools/infrastructure/create-datamart.ts
|
|
2124
|
-
var columnSchema =
|
|
2125
|
-
name:
|
|
2126
|
-
alias:
|
|
2127
|
-
label:
|
|
2128
|
-
isHide:
|
|
2129
|
-
isCustomColumn:
|
|
2130
|
-
sql:
|
|
2361
|
+
var columnSchema = z10.object({
|
|
2362
|
+
name: z10.string(),
|
|
2363
|
+
alias: z10.string().optional(),
|
|
2364
|
+
label: z10.string().optional(),
|
|
2365
|
+
isHide: z10.boolean().optional(),
|
|
2366
|
+
isCustomColumn: z10.boolean().optional(),
|
|
2367
|
+
sql: z10.string().optional()
|
|
2131
2368
|
});
|
|
2132
|
-
var tableSchema =
|
|
2133
|
-
name:
|
|
2134
|
-
schemaName:
|
|
2135
|
-
clientColumn:
|
|
2136
|
-
label:
|
|
2137
|
-
columns:
|
|
2369
|
+
var tableSchema = z10.object({
|
|
2370
|
+
name: z10.string(),
|
|
2371
|
+
schemaName: z10.string().optional(),
|
|
2372
|
+
clientColumn: z10.string().optional(),
|
|
2373
|
+
label: z10.string().optional(),
|
|
2374
|
+
columns: z10.array(columnSchema)
|
|
2138
2375
|
});
|
|
2139
|
-
var relationshipSchema =
|
|
2140
|
-
parentTableName:
|
|
2141
|
-
parentColumnName:
|
|
2142
|
-
childTableName:
|
|
2143
|
-
childColumnName:
|
|
2144
|
-
cardinality:
|
|
2145
|
-
join:
|
|
2146
|
-
relationshipName:
|
|
2376
|
+
var relationshipSchema = z10.object({
|
|
2377
|
+
parentTableName: z10.string(),
|
|
2378
|
+
parentColumnName: z10.string(),
|
|
2379
|
+
childTableName: z10.string(),
|
|
2380
|
+
childColumnName: z10.string(),
|
|
2381
|
+
cardinality: z10.enum(["OneToOne", "OneToMany", "ManyToOne", "ManyToMany"]),
|
|
2382
|
+
join: z10.enum(["INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN"]),
|
|
2383
|
+
relationshipName: z10.string()
|
|
2147
2384
|
});
|
|
2148
2385
|
function datamartSuccessMessage(name, result) {
|
|
2149
2386
|
return `Datamart "${name}" created successfully (ID: ${result.id || result._id || "UNKNOWN"}).
|
|
@@ -2164,21 +2401,21 @@ function registerCreateDatamart(server, client) {
|
|
|
2164
2401
|
"create_datamart",
|
|
2165
2402
|
"Create a production datamart from selected tables and relationships. Defaults to dry-run; apply requires explicit confirmation.",
|
|
2166
2403
|
{
|
|
2167
|
-
mode:
|
|
2168
|
-
confirm:
|
|
2169
|
-
name:
|
|
2170
|
-
datasourceName:
|
|
2171
|
-
tableList:
|
|
2172
|
-
relationships:
|
|
2173
|
-
tenancySettings:
|
|
2174
|
-
tenancyLevel:
|
|
2175
|
-
clientColumnType:
|
|
2176
|
-
schemaName:
|
|
2177
|
-
tableName:
|
|
2178
|
-
tableClientNameColumn:
|
|
2179
|
-
tablePrimaryKeyColumn:
|
|
2404
|
+
mode: z10.enum(["dry_run", "apply"]).default("dry_run").describe("dry_run returns the plan only; apply creates the datamart"),
|
|
2405
|
+
confirm: z10.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
|
|
2406
|
+
name: z10.string().describe("Unique datamart name"),
|
|
2407
|
+
datasourceName: z10.string().optional().describe("Name of the datasource to use. Omit to list available datasources."),
|
|
2408
|
+
tableList: z10.array(tableSchema).optional().describe("Array of tables with their columns"),
|
|
2409
|
+
relationships: z10.array(relationshipSchema).optional().describe("Join definitions between tables"),
|
|
2410
|
+
tenancySettings: z10.object({
|
|
2411
|
+
tenancyLevel: z10.enum(["TABLE", "DATABASE"]).optional(),
|
|
2412
|
+
clientColumnType: z10.string().optional(),
|
|
2413
|
+
schemaName: z10.string().optional(),
|
|
2414
|
+
tableName: z10.string().optional(),
|
|
2415
|
+
tableClientNameColumn: z10.string().optional(),
|
|
2416
|
+
tablePrimaryKeyColumn: z10.string().optional()
|
|
2180
2417
|
}).optional().describe("Multi-tenant configuration"),
|
|
2181
|
-
body:
|
|
2418
|
+
body: z10.record(z10.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/data-app/datamarts")
|
|
2182
2419
|
},
|
|
2183
2420
|
WRITE_CREATE,
|
|
2184
2421
|
async (params) => {
|
|
@@ -2273,14 +2510,14 @@ function registerCreateDatamart(server, client) {
|
|
|
2273
2510
|
}
|
|
2274
2511
|
|
|
2275
2512
|
// src/tools/infrastructure/sync-datasource.ts
|
|
2276
|
-
import { z as
|
|
2513
|
+
import { z as z11 } from "zod";
|
|
2277
2514
|
function registerSyncDatasource(server, client) {
|
|
2278
2515
|
const datasourcesApi = new DatasourcesApi(client);
|
|
2279
2516
|
server.tool(
|
|
2280
2517
|
"sync_datasource",
|
|
2281
2518
|
"Trigger a datasource schema sync to refresh table and column metadata. If datasourceName is omitted and there are multiple datasources, returns the list so the user can choose.",
|
|
2282
2519
|
{
|
|
2283
|
-
datasourceName:
|
|
2520
|
+
datasourceName: z11.string().optional().describe("Name of the datasource to sync. Omit to list available datasources.")
|
|
2284
2521
|
},
|
|
2285
2522
|
WRITE_IDEMPOTENT,
|
|
2286
2523
|
async (params) => {
|
|
@@ -2329,7 +2566,7 @@ The schema metadata will be refreshed. You can now use \`discover_datasource_sch
|
|
|
2329
2566
|
}
|
|
2330
2567
|
|
|
2331
2568
|
// src/tools/embed/create-embed.ts
|
|
2332
|
-
import { z as
|
|
2569
|
+
import { z as z12 } from "zod";
|
|
2333
2570
|
var DEFAULT_ACCESS_SETTINGS2 = {
|
|
2334
2571
|
isAllowMetricCreation: false,
|
|
2335
2572
|
isAllowMetricDeletion: false,
|
|
@@ -2342,18 +2579,18 @@ var DEFAULT_ACCESS_SETTINGS2 = {
|
|
|
2342
2579
|
isAllowCreateDashboardView: false,
|
|
2343
2580
|
metricCreationMode: "DRAG_DROP"
|
|
2344
2581
|
};
|
|
2345
|
-
var accessSettingsSchema =
|
|
2346
|
-
datamartName:
|
|
2347
|
-
isAllowMetricCreation:
|
|
2348
|
-
metricCreationMode:
|
|
2349
|
-
isAllowMetricDeletion:
|
|
2350
|
-
isAllowMetricUpdate:
|
|
2351
|
-
isAllowMetricLayoutChange:
|
|
2352
|
-
isAllowManageMetrics:
|
|
2353
|
-
isAllowUnderlyingData:
|
|
2354
|
-
isAllowAiPilot:
|
|
2355
|
-
isAllowEmailReports:
|
|
2356
|
-
isAllowCreateDashboardView:
|
|
2582
|
+
var accessSettingsSchema = z12.object({
|
|
2583
|
+
datamartName: z12.string().describe("Name of the datamart (required)"),
|
|
2584
|
+
isAllowMetricCreation: z12.boolean().optional().describe("Allow end-users to create metrics"),
|
|
2585
|
+
metricCreationMode: z12.enum(["DRAG_DROP", "CHAT"]).optional().describe("Metric creation mode"),
|
|
2586
|
+
isAllowMetricDeletion: z12.boolean().optional().describe("Allow metric deletion"),
|
|
2587
|
+
isAllowMetricUpdate: z12.boolean().optional().describe("Allow metric editing"),
|
|
2588
|
+
isAllowMetricLayoutChange: z12.boolean().optional().describe("Allow layout changes"),
|
|
2589
|
+
isAllowManageMetrics: z12.boolean().optional().describe("Show manage metrics UI"),
|
|
2590
|
+
isAllowUnderlyingData: z12.boolean().optional().describe("Allow viewing underlying data"),
|
|
2591
|
+
isAllowAiPilot: z12.boolean().optional().describe("Enable AI pilot features"),
|
|
2592
|
+
isAllowEmailReports: z12.boolean().optional().describe("Allow email report scheduling"),
|
|
2593
|
+
isAllowCreateDashboardView: z12.boolean().optional().describe("Allow creating dashboard views")
|
|
2357
2594
|
}).describe("Embed access settings. Only datamartName is required \u2014 all flags default to false.");
|
|
2358
2595
|
function registerCreateEmbed(server, client) {
|
|
2359
2596
|
const services = createServiceBundle(client);
|
|
@@ -2361,23 +2598,23 @@ function registerCreateEmbed(server, client) {
|
|
|
2361
2598
|
"create_embed",
|
|
2362
2599
|
"Create an embed. Provide dashboardId, workspaceName, accessSettings.datamartName. Add clientId for multi-tenant per-client dashboards. All isAllow* flags default to false.",
|
|
2363
2600
|
{
|
|
2364
|
-
body:
|
|
2601
|
+
body: z12.record(z12.unknown()).optional().describe(
|
|
2365
2602
|
"Raw API body passthrough. If provided, all other fields are ignored."
|
|
2366
2603
|
),
|
|
2367
|
-
dashboardId:
|
|
2368
|
-
workspaceName:
|
|
2369
|
-
embedType:
|
|
2370
|
-
metricId:
|
|
2371
|
-
name:
|
|
2604
|
+
dashboardId: z12.string().optional().describe("Dashboard identifier for embed creation"),
|
|
2605
|
+
workspaceName: z12.string().optional().describe("Workspace name. Omit to auto-resolve if only one exists."),
|
|
2606
|
+
embedType: z12.enum(["dashboard", "metric"]).optional().describe("Embed type"),
|
|
2607
|
+
metricId: z12.string().optional().describe("Required for metric embed type"),
|
|
2608
|
+
name: z12.string().optional().describe("Human-readable embed config name"),
|
|
2372
2609
|
accessSettings: accessSettingsSchema.optional(),
|
|
2373
2610
|
// Multi-tenant (dashboard embed) params
|
|
2374
|
-
clientId:
|
|
2611
|
+
clientId: z12.string().optional().describe(
|
|
2375
2612
|
"Client identifier for multi-tenant isolation. When provided, creates a dashboard embed (each client gets their own dashboard copy)."
|
|
2376
2613
|
),
|
|
2377
|
-
templateDashboardId:
|
|
2378
|
-
isImportMetrics:
|
|
2379
|
-
metricImportMode:
|
|
2380
|
-
isRenameDashboard:
|
|
2614
|
+
templateDashboardId: z12.string().optional().describe("Template dashboard ID to clone metrics from"),
|
|
2615
|
+
isImportMetrics: z12.boolean().optional().describe("Import metrics from template dashboard"),
|
|
2616
|
+
metricImportMode: z12.enum(["COPY", "LINK"]).optional().describe("COPY duplicates metrics, LINK shares them"),
|
|
2617
|
+
isRenameDashboard: z12.boolean().optional().describe("Rename the dashboard for this client")
|
|
2381
2618
|
},
|
|
2382
2619
|
WRITE_CREATE,
|
|
2383
2620
|
async (params) => {
|
|
@@ -2479,14 +2716,14 @@ Next: run \`get_embed_details\` to verify the embed configuration.`
|
|
|
2479
2716
|
}
|
|
2480
2717
|
|
|
2481
2718
|
// src/tools/embed/list-embeds.ts
|
|
2482
|
-
import { z as
|
|
2719
|
+
import { z as z13 } from "zod";
|
|
2483
2720
|
function registerListEmbeds(server, client) {
|
|
2484
2721
|
const services = createServiceBundle(client);
|
|
2485
2722
|
server.tool(
|
|
2486
2723
|
"list_embeds",
|
|
2487
|
-
"List embed configurations. Requires an API token (use create_api_token first).
|
|
2724
|
+
"List embed configurations. Requires an API token (use create_api_token first). Use `list_tenants` for authoritative client discovery from the tenancy database.",
|
|
2488
2725
|
{
|
|
2489
|
-
dataAppName:
|
|
2726
|
+
dataAppName: z13.string().optional().describe("Filter by data app name")
|
|
2490
2727
|
},
|
|
2491
2728
|
READ_ONLY,
|
|
2492
2729
|
async (params) => {
|
|
@@ -2499,7 +2736,15 @@ function registerListEmbeds(server, client) {
|
|
|
2499
2736
|
}
|
|
2500
2737
|
const embeds = result.map((e) => {
|
|
2501
2738
|
const n = services.discoveryService.normalizeEmbed(e);
|
|
2502
|
-
const
|
|
2739
|
+
const externalDashboard = e.externalDashboard && typeof e.externalDashboard === "object" ? e.externalDashboard : {};
|
|
2740
|
+
let clientId = null;
|
|
2741
|
+
if (typeof e.clientId === "string" && e.clientId.trim().length > 0) {
|
|
2742
|
+
clientId = e.clientId;
|
|
2743
|
+
} else if (typeof e.createdBy === "string" && e.createdBy.trim().length > 0) {
|
|
2744
|
+
clientId = e.createdBy;
|
|
2745
|
+
} else if (typeof externalDashboard.createdBy === "string" && externalDashboard.createdBy.trim().length > 0) {
|
|
2746
|
+
clientId = externalDashboard.createdBy;
|
|
2747
|
+
}
|
|
2503
2748
|
return {
|
|
2504
2749
|
embedId: n.embedId || null,
|
|
2505
2750
|
internalId: !n.embedId && n.internalId ? n.internalId : void 0,
|
|
@@ -2529,14 +2774,14 @@ function registerListEmbeds(server, client) {
|
|
|
2529
2774
|
}
|
|
2530
2775
|
|
|
2531
2776
|
// src/tools/embed/get-embed-details.ts
|
|
2532
|
-
import { z as
|
|
2777
|
+
import { z as z14 } from "zod";
|
|
2533
2778
|
function registerGetEmbedDetails(server, client) {
|
|
2534
2779
|
const services = createServiceBundle(client);
|
|
2535
2780
|
server.tool(
|
|
2536
2781
|
"get_embed_details",
|
|
2537
2782
|
"Get full embed configuration including access settings and theme.",
|
|
2538
2783
|
{
|
|
2539
|
-
embedId:
|
|
2784
|
+
embedId: z14.string().describe("ID of the embed")
|
|
2540
2785
|
},
|
|
2541
2786
|
READ_ONLY,
|
|
2542
2787
|
async (params) => {
|
|
@@ -2552,7 +2797,7 @@ function registerGetEmbedDetails(server, client) {
|
|
|
2552
2797
|
}
|
|
2553
2798
|
|
|
2554
2799
|
// src/tools/embed/update-embed.ts
|
|
2555
|
-
import { z as
|
|
2800
|
+
import { z as z15 } from "zod";
|
|
2556
2801
|
|
|
2557
2802
|
// src/tools/embed/helpers.ts
|
|
2558
2803
|
var GUEST_TOKEN_REMINDER = "\n\n\u26A0\uFE0F **Guest tokens must be regenerated** for these changes to take effect. Existing guest tokens still carry the old settings. Call `generate_guest_token` to create a new token with the updated configuration.";
|
|
@@ -2590,15 +2835,15 @@ function registerUpdateEmbed(server, client) {
|
|
|
2590
2835
|
"update_embed",
|
|
2591
2836
|
"Update embed access settings, permissions, theme, or name.",
|
|
2592
2837
|
{
|
|
2593
|
-
embedId:
|
|
2594
|
-
body:
|
|
2838
|
+
embedId: z15.string().describe("ID of the embed to update"),
|
|
2839
|
+
body: z15.record(z15.unknown()).optional().describe(
|
|
2595
2840
|
"Raw API body for PUT /api/v2/data-app/embeds. If provided, it is sent directly (passthrough mode)."
|
|
2596
2841
|
),
|
|
2597
|
-
name:
|
|
2598
|
-
accessSettings:
|
|
2842
|
+
name: z15.string().optional().describe("New name for the embed"),
|
|
2843
|
+
accessSettings: z15.record(z15.unknown()).optional().describe(
|
|
2599
2844
|
"Partial update of access settings. See `get_embed_details` for current settings."
|
|
2600
2845
|
),
|
|
2601
|
-
theme:
|
|
2846
|
+
theme: z15.record(z15.unknown()).optional().describe(
|
|
2602
2847
|
"Theme configuration object. See databrain://theme-reference for the full schema."
|
|
2603
2848
|
)
|
|
2604
2849
|
},
|
|
@@ -2644,7 +2889,7 @@ function registerUpdateEmbed(server, client) {
|
|
|
2644
2889
|
}
|
|
2645
2890
|
|
|
2646
2891
|
// src/tools/embed/customize-theme.ts
|
|
2647
|
-
import { z as
|
|
2892
|
+
import { z as z16 } from "zod";
|
|
2648
2893
|
var THEME_PRESETS = {
|
|
2649
2894
|
light: {
|
|
2650
2895
|
general: {
|
|
@@ -2778,7 +3023,7 @@ var ACCESS_PRESETS = {
|
|
|
2778
3023
|
metricCreationMode: "CHAT"
|
|
2779
3024
|
}
|
|
2780
3025
|
};
|
|
2781
|
-
var fontFamilyEnum =
|
|
3026
|
+
var fontFamilyEnum = z16.enum([
|
|
2782
3027
|
"Inter",
|
|
2783
3028
|
"Roboto",
|
|
2784
3029
|
"Open Sans",
|
|
@@ -2790,17 +3035,17 @@ var fontFamilyEnum = z15.enum([
|
|
|
2790
3035
|
"Nunito",
|
|
2791
3036
|
"system-ui"
|
|
2792
3037
|
]).describe("Font family");
|
|
2793
|
-
var tooltipFontSchema =
|
|
2794
|
-
fontFamily:
|
|
2795
|
-
fontSize:
|
|
2796
|
-
fontWeight:
|
|
2797
|
-
fontColor:
|
|
3038
|
+
var tooltipFontSchema = z16.object({
|
|
3039
|
+
fontFamily: z16.string().optional(),
|
|
3040
|
+
fontSize: z16.number().optional(),
|
|
3041
|
+
fontWeight: z16.number().optional(),
|
|
3042
|
+
fontColor: z16.string().optional()
|
|
2798
3043
|
}).optional();
|
|
2799
|
-
var fontConfigInputSchema =
|
|
2800
|
-
fontSize:
|
|
2801
|
-
fontFamily:
|
|
2802
|
-
fontWeight:
|
|
2803
|
-
color:
|
|
3044
|
+
var fontConfigInputSchema = z16.object({
|
|
3045
|
+
fontSize: z16.number().optional(),
|
|
3046
|
+
fontFamily: z16.string().optional(),
|
|
3047
|
+
fontWeight: z16.number().optional(),
|
|
3048
|
+
color: z16.string().optional()
|
|
2804
3049
|
}).optional();
|
|
2805
3050
|
function mapToFrontendFontConfig(input) {
|
|
2806
3051
|
if (!input) return void 0;
|
|
@@ -2811,23 +3056,23 @@ function mapToFrontendFontConfig(input) {
|
|
|
2811
3056
|
...input.color && { color: input.color }
|
|
2812
3057
|
};
|
|
2813
3058
|
}
|
|
2814
|
-
var axisSchema =
|
|
2815
|
-
hideAxisLines:
|
|
2816
|
-
hideSplitLines:
|
|
2817
|
-
hideAxisLabels:
|
|
2818
|
-
hideAxisTicks:
|
|
2819
|
-
axisName:
|
|
2820
|
-
axisNameOffset:
|
|
2821
|
-
axisLabelMargin:
|
|
2822
|
-
fontSize:
|
|
2823
|
-
fontFamily:
|
|
2824
|
-
fontWeight:
|
|
2825
|
-
color:
|
|
2826
|
-
axisColor:
|
|
2827
|
-
axisNameFontConfig:
|
|
2828
|
-
fontFamily:
|
|
2829
|
-
fontSize:
|
|
2830
|
-
fontWeight:
|
|
3059
|
+
var axisSchema = z16.object({
|
|
3060
|
+
hideAxisLines: z16.boolean().optional().describe("Hide axis lines"),
|
|
3061
|
+
hideSplitLines: z16.boolean().optional().describe("Hide grid/split lines"),
|
|
3062
|
+
hideAxisLabels: z16.boolean().optional().describe("Hide axis labels"),
|
|
3063
|
+
hideAxisTicks: z16.boolean().optional().describe("Hide axis tick marks"),
|
|
3064
|
+
axisName: z16.string().optional().describe("Axis title text"),
|
|
3065
|
+
axisNameOffset: z16.number().optional().describe("Axis title offset from axis"),
|
|
3066
|
+
axisLabelMargin: z16.number().optional().describe("Margin between axis labels and chart area in pixels"),
|
|
3067
|
+
fontSize: z16.number().optional().describe("Axis label font size"),
|
|
3068
|
+
fontFamily: z16.string().optional().describe("Axis label font family"),
|
|
3069
|
+
fontWeight: z16.number().optional().describe("Axis label font weight"),
|
|
3070
|
+
color: z16.string().optional().describe("Axis label color (hex)"),
|
|
3071
|
+
axisColor: z16.string().optional().describe("Axis line color (hex)"),
|
|
3072
|
+
axisNameFontConfig: z16.object({
|
|
3073
|
+
fontFamily: z16.string().optional(),
|
|
3074
|
+
fontSize: z16.number().optional(),
|
|
3075
|
+
fontWeight: z16.number().optional()
|
|
2831
3076
|
}).optional().describe("Axis title font configuration")
|
|
2832
3077
|
}).optional();
|
|
2833
3078
|
function buildAdminTheme(p, presetBase) {
|
|
@@ -2983,94 +3228,94 @@ function registerCustomizeTheme(server, client) {
|
|
|
2983
3228
|
"customize_embed_theme",
|
|
2984
3229
|
`Theme an embed: presets (${Object.keys(THEME_PRESETS).join("/")}), colors, fonts, cards, chart appearance, responsive layout, or access presets (${Object.keys(ACCESS_PRESETS).join("/")}).`,
|
|
2985
3230
|
{
|
|
2986
|
-
embedId:
|
|
2987
|
-
body:
|
|
3231
|
+
embedId: z16.string().describe("ID of the embed to theme"),
|
|
3232
|
+
body: z16.record(z16.unknown()).optional().describe(
|
|
2988
3233
|
"Raw API body for PUT /api/v2/data-app/embeds. If provided, sent directly and all convenience fields are ignored."
|
|
2989
3234
|
),
|
|
2990
3235
|
// --- Presets ---
|
|
2991
|
-
themePreset:
|
|
2992
|
-
accessPreset:
|
|
3236
|
+
themePreset: z16.enum(["light", "dark", "corporate", "minimal"]).optional().describe("Apply a theme preset as the base. Granular params below override preset values."),
|
|
3237
|
+
accessPreset: z16.enum(["view-only", "power-user", "export-only", "ai-enabled"]).optional().describe("Apply an access settings preset"),
|
|
2993
3238
|
// --- General ---
|
|
2994
3239
|
fontFamily: fontFamilyEnum.optional().describe("Global font family"),
|
|
2995
|
-
datePickerFormat:
|
|
3240
|
+
datePickerFormat: z16.string().optional().describe("Date picker display format"),
|
|
2996
3241
|
// --- Colors ---
|
|
2997
|
-
primaryColor:
|
|
2998
|
-
backgroundColor:
|
|
2999
|
-
textColor:
|
|
3000
|
-
ctaColor:
|
|
3001
|
-
ctaTextColor:
|
|
3242
|
+
primaryColor: z16.string().optional().describe("Primary accent color (hex)"),
|
|
3243
|
+
backgroundColor: z16.string().optional().describe("Overall background color (hex)"),
|
|
3244
|
+
textColor: z16.string().optional().describe("Default text color (hex)"),
|
|
3245
|
+
ctaColor: z16.string().optional().describe("Call-to-action button color (hex)"),
|
|
3246
|
+
ctaTextColor: z16.string().optional().describe("CTA button text color (hex)"),
|
|
3002
3247
|
// --- Dashboard ---
|
|
3003
|
-
dashboardBackgroundColor:
|
|
3004
|
-
selectBoxSize:
|
|
3005
|
-
selectBoxVariant:
|
|
3006
|
-
selectBoxBorderRadius:
|
|
3007
|
-
selectBoxTextColor:
|
|
3008
|
-
metricCardColor:
|
|
3248
|
+
dashboardBackgroundColor: z16.string().optional().describe("Dashboard container background (hex)"),
|
|
3249
|
+
selectBoxSize: z16.enum(["small", "medium", "large"]).optional().describe("Filter select box size"),
|
|
3250
|
+
selectBoxVariant: z16.enum(["floating", "outlined", "standard"]).optional().describe("Filter select box style"),
|
|
3251
|
+
selectBoxBorderRadius: z16.string().optional().describe('Select box border radius (e.g. "20px")'),
|
|
3252
|
+
selectBoxTextColor: z16.string().optional().describe("Select box text color (hex)"),
|
|
3253
|
+
metricCardColor: z16.string().optional().describe("Metric card background color (hex)"),
|
|
3009
3254
|
// --- Card Title ---
|
|
3010
|
-
cardTitleFontSize:
|
|
3011
|
-
cardTitleFontWeight:
|
|
3012
|
-
cardTitleColor:
|
|
3255
|
+
cardTitleFontSize: z16.string().optional().describe('Card title font size (e.g. "16px")'),
|
|
3256
|
+
cardTitleFontWeight: z16.string().optional().describe('Card title font weight (e.g. "600")'),
|
|
3257
|
+
cardTitleColor: z16.string().optional().describe("Card title text color (hex)"),
|
|
3013
3258
|
// --- Card Description ---
|
|
3014
|
-
cardDescriptionFontSize:
|
|
3015
|
-
cardDescriptionFontWeight:
|
|
3016
|
-
cardDescriptionColor:
|
|
3259
|
+
cardDescriptionFontSize: z16.string().optional().describe("Card description font size"),
|
|
3260
|
+
cardDescriptionFontWeight: z16.string().optional().describe("Card description font weight"),
|
|
3261
|
+
cardDescriptionColor: z16.string().optional().describe("Card description text color (hex)"),
|
|
3017
3262
|
// --- Card Customization ---
|
|
3018
|
-
cardPadding:
|
|
3019
|
-
cardBorderRadius:
|
|
3020
|
-
cardShadow:
|
|
3021
|
-
disableShadowOnHover:
|
|
3022
|
-
disableCardStroke:
|
|
3023
|
-
cardStrokeColor:
|
|
3263
|
+
cardPadding: z16.string().optional().describe("Card internal padding"),
|
|
3264
|
+
cardBorderRadius: z16.string().optional().describe("Card border radius"),
|
|
3265
|
+
cardShadow: z16.string().optional().describe("Card box-shadow CSS value"),
|
|
3266
|
+
disableShadowOnHover: z16.boolean().optional().describe("Disable shadow on card hover"),
|
|
3267
|
+
disableCardStroke: z16.boolean().optional().describe("Remove card border stroke"),
|
|
3268
|
+
cardStrokeColor: z16.string().optional().describe("Card border stroke color (hex)"),
|
|
3024
3269
|
// --- Chart Colors (admin theme level) ---
|
|
3025
|
-
chartFontFamily:
|
|
3026
|
-
chartFontColor:
|
|
3027
|
-
chartColors:
|
|
3028
|
-
chartPaletteName:
|
|
3029
|
-
chartPaletteColors:
|
|
3270
|
+
chartFontFamily: z16.string().optional().describe("Font family for chart text"),
|
|
3271
|
+
chartFontColor: z16.string().optional().describe("Font color for chart text (hex)"),
|
|
3272
|
+
chartColors: z16.array(z16.string()).optional().describe("Array of chart series colors (hex)"),
|
|
3273
|
+
chartPaletteName: z16.string().optional().describe("Name for a custom palette"),
|
|
3274
|
+
chartPaletteColors: z16.array(z16.string()).optional().describe("9 colors for a custom palette"),
|
|
3030
3275
|
// --- Chart Tooltip (admin theme level) ---
|
|
3031
3276
|
tooltipHeader: tooltipFontSchema.describe("Tooltip header font config"),
|
|
3032
3277
|
tooltipLabel: tooltipFontSchema.describe("Tooltip label font config"),
|
|
3033
3278
|
tooltipValue: tooltipFontSchema.describe("Tooltip value font config"),
|
|
3034
3279
|
// --- Component Theme ---
|
|
3035
|
-
buttonPrimaryColor:
|
|
3036
|
-
buttonPrimaryTextColor:
|
|
3037
|
-
buttonSecondaryColor:
|
|
3038
|
-
buttonSecondaryTextColor:
|
|
3039
|
-
checkboxCheckedColor:
|
|
3040
|
-
checkboxUncheckedColor:
|
|
3041
|
-
switchEnabledColor:
|
|
3042
|
-
switchDisabledColor:
|
|
3043
|
-
breadcrumbFontFamily:
|
|
3044
|
-
breadcrumbFontColor:
|
|
3045
|
-
breadcrumbActiveColor:
|
|
3046
|
-
filterBadgeColor:
|
|
3047
|
-
filterBadgeTextColor:
|
|
3048
|
-
datePickerColor:
|
|
3280
|
+
buttonPrimaryColor: z16.string().optional().describe("Primary button background (hex)"),
|
|
3281
|
+
buttonPrimaryTextColor: z16.string().optional().describe("Primary button text (hex)"),
|
|
3282
|
+
buttonSecondaryColor: z16.string().optional().describe("Secondary button background (hex)"),
|
|
3283
|
+
buttonSecondaryTextColor: z16.string().optional().describe("Secondary button text (hex)"),
|
|
3284
|
+
checkboxCheckedColor: z16.string().optional().describe("Checked checkbox color (hex)"),
|
|
3285
|
+
checkboxUncheckedColor: z16.string().optional().describe("Unchecked checkbox color (hex)"),
|
|
3286
|
+
switchEnabledColor: z16.string().optional().describe("Enabled switch color (hex)"),
|
|
3287
|
+
switchDisabledColor: z16.string().optional().describe("Disabled switch color (hex)"),
|
|
3288
|
+
breadcrumbFontFamily: z16.string().optional().describe("Drill breadcrumb font family"),
|
|
3289
|
+
breadcrumbFontColor: z16.string().optional().describe("Drill breadcrumb font color (hex)"),
|
|
3290
|
+
breadcrumbActiveColor: z16.string().optional().describe("Active breadcrumb color (hex)"),
|
|
3291
|
+
filterBadgeColor: z16.string().optional().describe("Multi-select filter badge color (hex)"),
|
|
3292
|
+
filterBadgeTextColor: z16.string().optional().describe("Filter badge text color (hex)"),
|
|
3293
|
+
datePickerColor: z16.string().optional().describe("Date picker accent color (hex)"),
|
|
3049
3294
|
// --- Responsive Breakpoints ---
|
|
3050
|
-
breakpointXl:
|
|
3051
|
-
breakpointLg:
|
|
3052
|
-
breakpointMd:
|
|
3053
|
-
breakpointSm:
|
|
3054
|
-
breakpointXs:
|
|
3295
|
+
breakpointXl: z16.number().optional().describe("XL breakpoint width in px"),
|
|
3296
|
+
breakpointLg: z16.number().optional().describe("LG breakpoint width in px"),
|
|
3297
|
+
breakpointMd: z16.number().optional().describe("MD breakpoint width in px"),
|
|
3298
|
+
breakpointSm: z16.number().optional().describe("SM breakpoint width in px"),
|
|
3299
|
+
breakpointXs: z16.number().optional().describe("XS breakpoint width in px"),
|
|
3055
3300
|
// --- Metric Layout Columns ---
|
|
3056
|
-
layoutColsXl:
|
|
3057
|
-
layoutColsLg:
|
|
3058
|
-
layoutColsMd:
|
|
3059
|
-
layoutColsSm:
|
|
3060
|
-
layoutColsXs:
|
|
3061
|
-
layoutColsXxs:
|
|
3301
|
+
layoutColsXl: z16.number().optional().describe("Grid columns at XL breakpoint"),
|
|
3302
|
+
layoutColsLg: z16.number().optional().describe("Grid columns at LG breakpoint"),
|
|
3303
|
+
layoutColsMd: z16.number().optional().describe("Grid columns at MD breakpoint"),
|
|
3304
|
+
layoutColsSm: z16.number().optional().describe("Grid columns at SM breakpoint"),
|
|
3305
|
+
layoutColsXs: z16.number().optional().describe("Grid columns at XS breakpoint"),
|
|
3306
|
+
layoutColsXxs: z16.number().optional().describe("Grid columns at XXS breakpoint"),
|
|
3062
3307
|
// --- Chart Appearance (detailed) ---
|
|
3063
3308
|
tooltipHeaderStyle: fontConfigInputSchema.describe("Tooltip header: fontSize, fontFamily, fontWeight, color"),
|
|
3064
3309
|
tooltipLabelStyle: fontConfigInputSchema.describe("Tooltip label: fontSize, fontFamily, fontWeight, color"),
|
|
3065
3310
|
tooltipValueStyle: fontConfigInputSchema.describe("Tooltip value: fontSize, fontFamily, fontWeight, color"),
|
|
3066
|
-
labelPosition:
|
|
3067
|
-
radialLabelPosition:
|
|
3068
|
-
marginTop:
|
|
3069
|
-
marginLeft:
|
|
3070
|
-
marginRight:
|
|
3071
|
-
marginBottom:
|
|
3072
|
-
legendShow:
|
|
3073
|
-
legendPosition:
|
|
3311
|
+
labelPosition: z16.enum(["hidden", "top", "left", "right", "bottom", "inside"]).optional().describe("Data label position on charts"),
|
|
3312
|
+
radialLabelPosition: z16.enum(["outside", "inside"]).optional().describe("Label position for radial/pie charts"),
|
|
3313
|
+
marginTop: z16.number().optional().describe("Chart top margin in pixels"),
|
|
3314
|
+
marginLeft: z16.number().optional().describe("Chart left margin in pixels"),
|
|
3315
|
+
marginRight: z16.number().optional().describe("Chart right margin in pixels"),
|
|
3316
|
+
marginBottom: z16.number().optional().describe("Chart bottom margin in pixels"),
|
|
3317
|
+
legendShow: z16.boolean().optional().describe("Show or hide chart legend"),
|
|
3318
|
+
legendPosition: z16.enum([
|
|
3074
3319
|
"top-left",
|
|
3075
3320
|
"top-center",
|
|
3076
3321
|
"top-right",
|
|
@@ -3080,17 +3325,17 @@ function registerCustomizeTheme(server, client) {
|
|
|
3080
3325
|
"bottom-center",
|
|
3081
3326
|
"bottom-right"
|
|
3082
3327
|
]).optional().describe("Legend position"),
|
|
3083
|
-
legendLayout:
|
|
3084
|
-
legendShape:
|
|
3085
|
-
legendFontSize:
|
|
3086
|
-
legendFontWeight:
|
|
3087
|
-
legendFontFamily:
|
|
3088
|
-
legendColor:
|
|
3089
|
-
legendTruncateLength:
|
|
3090
|
-
disableLegendScrolling:
|
|
3091
|
-
enableVariableLegendPosition:
|
|
3092
|
-
legendTop:
|
|
3093
|
-
legendLeft:
|
|
3328
|
+
legendLayout: z16.enum(["horizontal", "vertical"]).optional().describe("Legend layout direction"),
|
|
3329
|
+
legendShape: z16.enum(["circle", "rect", "roundRect", "triangle", "diamond", "arrow", "none"]).optional().describe("Legend icon shape"),
|
|
3330
|
+
legendFontSize: z16.number().optional().describe("Legend font size"),
|
|
3331
|
+
legendFontWeight: z16.number().optional().describe("Legend font weight"),
|
|
3332
|
+
legendFontFamily: z16.string().optional().describe("Legend font family"),
|
|
3333
|
+
legendColor: z16.string().optional().describe("Legend text color (hex)"),
|
|
3334
|
+
legendTruncateLength: z16.number().optional().describe("Max characters before truncating legend"),
|
|
3335
|
+
disableLegendScrolling: z16.boolean().optional().describe("Disable scrolling in legend"),
|
|
3336
|
+
enableVariableLegendPosition: z16.boolean().optional().describe("Allow user to drag legend"),
|
|
3337
|
+
legendTop: z16.number().optional().describe("Custom legend top offset"),
|
|
3338
|
+
legendLeft: z16.number().optional().describe("Custom legend left offset"),
|
|
3094
3339
|
verticalAxis: axisSchema.describe("Vertical (Y) axis configuration"),
|
|
3095
3340
|
horizontalAxis: axisSchema.describe("Horizontal (X) axis configuration")
|
|
3096
3341
|
},
|
|
@@ -3165,7 +3410,7 @@ Use \`get_embed_details\` to view the full current theme configuration.` + GUEST
|
|
|
3165
3410
|
}
|
|
3166
3411
|
|
|
3167
3412
|
// src/tools/embed/get-demo-link.ts
|
|
3168
|
-
import { z as
|
|
3413
|
+
import { z as z17 } from "zod";
|
|
3169
3414
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
3170
3415
|
var DEFAULT_EXPIRY_HOURS = 24;
|
|
3171
3416
|
var MAX_EXPIRY_HOURS = 24 * 30;
|
|
@@ -3175,11 +3420,11 @@ function registerGetDemoLink(server, client) {
|
|
|
3175
3420
|
"get_demo_link",
|
|
3176
3421
|
`Get a shareable demo URL for a dashboard embed scoped to a specific clientId \u2014 same link the UI's "Share Demo Link" generates. Loads the dashboard as that end-customer sees it. Use to verify ops-built widgets without a test app.`,
|
|
3177
3422
|
{
|
|
3178
|
-
clientId:
|
|
3179
|
-
dashboardName:
|
|
3180
|
-
workspaceName:
|
|
3181
|
-
embedId:
|
|
3182
|
-
expiryHours:
|
|
3423
|
+
clientId: z17.string().describe("End-customer identifier to scope the preview to."),
|
|
3424
|
+
dashboardName: z17.string().optional().describe("Dashboard name (non-technical). Preferred \u2014 pass with optional `workspaceName`."),
|
|
3425
|
+
workspaceName: z17.string().optional().describe("Workspace name. Only needed if `dashboardName` is ambiguous."),
|
|
3426
|
+
embedId: z17.string().optional().describe("Technical alternative to `dashboardName`."),
|
|
3427
|
+
expiryHours: z17.number().min(1).max(MAX_EXPIRY_HOURS).optional().describe(`Token TTL in hours. Defaults to ${DEFAULT_EXPIRY_HOURS}. Max ${MAX_EXPIRY_HOURS} (30 days).`)
|
|
3183
3428
|
},
|
|
3184
3429
|
WRITE_CREATE,
|
|
3185
3430
|
async (params) => {
|
|
@@ -3235,70 +3480,70 @@ function registerGetDemoLink(server, client) {
|
|
|
3235
3480
|
}
|
|
3236
3481
|
|
|
3237
3482
|
// src/tools/frontend/generate-guest-token.ts
|
|
3238
|
-
import { z as
|
|
3483
|
+
import { z as z18 } from "zod";
|
|
3239
3484
|
function registerGenerateGuestToken(server, client) {
|
|
3240
3485
|
const services = createServiceBundle(client);
|
|
3241
3486
|
server.tool(
|
|
3242
3487
|
"generate_guest_token",
|
|
3243
3488
|
"Generate a guest token for frontend embedding. Requires clientId. Omit dataAppName to auto-resolve.",
|
|
3244
3489
|
{
|
|
3245
|
-
clientId:
|
|
3246
|
-
dataAppName:
|
|
3247
|
-
body:
|
|
3490
|
+
clientId: z18.string().describe('Unique end-user identifier for multi-tenant filtering. Use "None" if no tenancy.'),
|
|
3491
|
+
dataAppName: z18.string().optional().describe("Name of the data app. Omit to list available data apps."),
|
|
3492
|
+
body: z18.record(z18.unknown()).optional().describe(
|
|
3248
3493
|
"Raw API body for POST /api/v2/guest-token/create. If provided, sent directly and convenience fields are ignored."
|
|
3249
3494
|
),
|
|
3250
|
-
params:
|
|
3251
|
-
allowedEmbeds:
|
|
3252
|
-
rlsSettings:
|
|
3253
|
-
metricId:
|
|
3254
|
-
values:
|
|
3495
|
+
params: z18.object({
|
|
3496
|
+
allowedEmbeds: z18.array(z18.string()).optional().describe("Restrict token to specific embed IDs"),
|
|
3497
|
+
rlsSettings: z18.array(z18.object({
|
|
3498
|
+
metricId: z18.string().describe("Metric ID"),
|
|
3499
|
+
values: z18.record(z18.unknown()).describe("RLS values by column name")
|
|
3255
3500
|
})).optional().describe("Legacy alias used in docs/examples; forwarded as params.rlsSettings"),
|
|
3256
|
-
appFilters:
|
|
3257
|
-
metricId:
|
|
3258
|
-
values:
|
|
3501
|
+
appFilters: z18.array(z18.object({
|
|
3502
|
+
metricId: z18.string().describe("Metric ID"),
|
|
3503
|
+
values: z18.record(z18.unknown()).describe("Filter values by column name")
|
|
3259
3504
|
})).optional().describe("Metric-level app filters"),
|
|
3260
|
-
dashboardAppFilters:
|
|
3261
|
-
dashboardId:
|
|
3262
|
-
values:
|
|
3263
|
-
isShowOnUrl:
|
|
3505
|
+
dashboardAppFilters: z18.array(z18.object({
|
|
3506
|
+
dashboardId: z18.string().describe("Dashboard ID"),
|
|
3507
|
+
values: z18.record(z18.unknown()).describe("Filter values by dashboard filter name"),
|
|
3508
|
+
isShowOnUrl: z18.boolean().optional().describe("Whether filters are shown in URL params")
|
|
3264
3509
|
})).optional().describe("Dashboard-level app filters"),
|
|
3265
|
-
hideDashboardFilters:
|
|
3266
|
-
userIdentifier:
|
|
3267
|
-
timezone:
|
|
3510
|
+
hideDashboardFilters: z18.array(z18.string()).optional().describe("Dashboard filter names to hide"),
|
|
3511
|
+
userIdentifier: z18.string().optional().describe("End-user identifier for private metrics support"),
|
|
3512
|
+
timezone: z18.string().optional().describe('IANA timezone, e.g. "America/New_York"')
|
|
3268
3513
|
}).optional().describe("Advanced token params"),
|
|
3269
3514
|
// Legacy convenience fields (auto-mapped into params for backward compatibility)
|
|
3270
|
-
timezone:
|
|
3271
|
-
allowedEmbeds:
|
|
3272
|
-
rlsSettings:
|
|
3273
|
-
metricId:
|
|
3274
|
-
values:
|
|
3515
|
+
timezone: z18.string().optional().describe("Legacy: moved under params.timezone"),
|
|
3516
|
+
allowedEmbeds: z18.array(z18.string()).optional().describe("Legacy: moved under params.allowedEmbeds"),
|
|
3517
|
+
rlsSettings: z18.array(z18.object({
|
|
3518
|
+
metricId: z18.string().describe("Metric ID"),
|
|
3519
|
+
values: z18.record(z18.unknown()).describe("RLS values by column name")
|
|
3275
3520
|
})).optional().describe("Legacy: moved under params.rlsSettings"),
|
|
3276
|
-
appFilters:
|
|
3277
|
-
metricId:
|
|
3278
|
-
values:
|
|
3521
|
+
appFilters: z18.array(z18.object({
|
|
3522
|
+
metricId: z18.string().describe("Metric ID"),
|
|
3523
|
+
values: z18.record(z18.unknown()).describe("Filter values by column name")
|
|
3279
3524
|
})).optional().describe("Legacy: moved under params.appFilters"),
|
|
3280
|
-
dashboardAppFilters:
|
|
3281
|
-
dashboardId:
|
|
3282
|
-
values:
|
|
3283
|
-
isShowOnUrl:
|
|
3525
|
+
dashboardAppFilters: z18.array(z18.object({
|
|
3526
|
+
dashboardId: z18.string().describe("Dashboard ID to apply filters to"),
|
|
3527
|
+
values: z18.record(z18.unknown()).describe("Filter values for this dashboard"),
|
|
3528
|
+
isShowOnUrl: z18.boolean().optional().describe("Whether filters are shown in URL params")
|
|
3284
3529
|
})).optional().describe("Legacy: moved under params.dashboardAppFilters"),
|
|
3285
|
-
permissions:
|
|
3286
|
-
isEnableArchiveMetrics:
|
|
3287
|
-
isEnableManageMetrics:
|
|
3288
|
-
isEnableCreateDashboardView:
|
|
3289
|
-
isEnableMetricUpdation:
|
|
3290
|
-
isEnableCustomizeLayout:
|
|
3291
|
-
isEnableUnderlyingData:
|
|
3292
|
-
isEnableDownloadMetrics:
|
|
3293
|
-
isShowSideBar:
|
|
3294
|
-
isShowDashboardName:
|
|
3295
|
-
isDisableMetricCreation:
|
|
3530
|
+
permissions: z18.object({
|
|
3531
|
+
isEnableArchiveMetrics: z18.boolean().optional(),
|
|
3532
|
+
isEnableManageMetrics: z18.boolean().optional(),
|
|
3533
|
+
isEnableCreateDashboardView: z18.boolean().optional(),
|
|
3534
|
+
isEnableMetricUpdation: z18.boolean().optional(),
|
|
3535
|
+
isEnableCustomizeLayout: z18.boolean().optional(),
|
|
3536
|
+
isEnableUnderlyingData: z18.boolean().optional(),
|
|
3537
|
+
isEnableDownloadMetrics: z18.boolean().optional(),
|
|
3538
|
+
isShowSideBar: z18.boolean().optional(),
|
|
3539
|
+
isShowDashboardName: z18.boolean().optional(),
|
|
3540
|
+
isDisableMetricCreation: z18.boolean().optional()
|
|
3296
3541
|
}).optional().describe("Runtime permission overrides (merge with and override embed-level settings)"),
|
|
3297
|
-
theme:
|
|
3298
|
-
hideDashboardFilters:
|
|
3299
|
-
userIdentifier:
|
|
3300
|
-
expiryTime:
|
|
3301
|
-
datasourceName:
|
|
3542
|
+
theme: z18.record(z18.unknown()).optional().describe("Runtime theme overrides (overrides embed-level theme)"),
|
|
3543
|
+
hideDashboardFilters: z18.array(z18.string()).optional().describe("Legacy: moved under params.hideDashboardFilters"),
|
|
3544
|
+
userIdentifier: z18.string().optional().describe("Legacy: moved under params.userIdentifier"),
|
|
3545
|
+
expiryTime: z18.number().optional().describe("Token TTL in milliseconds"),
|
|
3546
|
+
datasourceName: z18.string().optional().describe("Datasource name for multi-datasource routing")
|
|
3302
3547
|
},
|
|
3303
3548
|
WRITE_CREATE,
|
|
3304
3549
|
async (params) => {
|
|
@@ -3351,7 +3596,7 @@ Next step: Use \`generate_embed_code\` to get copy-paste-ready frontend code.`
|
|
|
3351
3596
|
}
|
|
3352
3597
|
|
|
3353
3598
|
// src/tools/frontend/generate-embed-code.ts
|
|
3354
|
-
import { z as
|
|
3599
|
+
import { z as z19 } from "zod";
|
|
3355
3600
|
|
|
3356
3601
|
// src/templates/react.ts
|
|
3357
3602
|
function kebabCase(str) {
|
|
@@ -4347,22 +4592,22 @@ function registerGenerateEmbedCode(server, _client) {
|
|
|
4347
4592
|
"generate_embed_code",
|
|
4348
4593
|
"Generate framework-specific embed code (React, Next.js, Vue, Angular, Svelte, SolidJS, vanilla HTML).",
|
|
4349
4594
|
{
|
|
4350
|
-
embedId:
|
|
4351
|
-
framework:
|
|
4352
|
-
embedType:
|
|
4353
|
-
options:
|
|
4354
|
-
enableDownloadCsv:
|
|
4355
|
-
enableEmailCsv:
|
|
4356
|
-
enableDownloadAllMetrics:
|
|
4357
|
-
enableDownloadAllPdf:
|
|
4358
|
-
disableFullscreen:
|
|
4359
|
-
isHideTablePreview:
|
|
4360
|
-
isHideChartSettings:
|
|
4361
|
-
isStickyDashboardFilters:
|
|
4362
|
-
enableMultiMetricFilters:
|
|
4363
|
-
themeName:
|
|
4364
|
-
customTheme:
|
|
4365
|
-
optionsIcon:
|
|
4595
|
+
embedId: z19.string().describe("The embed ID to generate code for"),
|
|
4596
|
+
framework: z19.enum(FRAMEWORKS).describe("Frontend framework"),
|
|
4597
|
+
embedType: z19.enum(["dashboard", "metric"]).optional().default("dashboard"),
|
|
4598
|
+
options: z19.object({
|
|
4599
|
+
enableDownloadCsv: z19.boolean().optional(),
|
|
4600
|
+
enableEmailCsv: z19.boolean().optional(),
|
|
4601
|
+
enableDownloadAllMetrics: z19.boolean().optional(),
|
|
4602
|
+
enableDownloadAllPdf: z19.boolean().optional(),
|
|
4603
|
+
disableFullscreen: z19.boolean().optional(),
|
|
4604
|
+
isHideTablePreview: z19.boolean().optional(),
|
|
4605
|
+
isHideChartSettings: z19.boolean().optional(),
|
|
4606
|
+
isStickyDashboardFilters: z19.boolean().optional(),
|
|
4607
|
+
enableMultiMetricFilters: z19.boolean().optional(),
|
|
4608
|
+
themeName: z19.string().optional(),
|
|
4609
|
+
customTheme: z19.record(z19.unknown()).optional(),
|
|
4610
|
+
optionsIcon: z19.enum(["kebab-menu-vertical", "download"]).optional()
|
|
4366
4611
|
}).optional()
|
|
4367
4612
|
},
|
|
4368
4613
|
READ_ONLY,
|
|
@@ -4402,17 +4647,17 @@ ${generated.instructions}`;
|
|
|
4402
4647
|
}
|
|
4403
4648
|
|
|
4404
4649
|
// src/tools/analytics/list-dashboards.ts
|
|
4405
|
-
import { z as
|
|
4650
|
+
import { z as z20 } from "zod";
|
|
4406
4651
|
function registerListDashboards(server, client) {
|
|
4407
4652
|
const services = createServiceBundle(client);
|
|
4408
4653
|
server.tool(
|
|
4409
4654
|
"list_dashboards",
|
|
4410
4655
|
"List dashboards globally or for a specific data app.",
|
|
4411
4656
|
{
|
|
4412
|
-
scope:
|
|
4657
|
+
scope: z20.enum(["auto", "global", "data_app"]).optional().describe(
|
|
4413
4658
|
"Dashboard listing scope. auto: use data_app when dataAppName + API token are available, otherwise global."
|
|
4414
4659
|
),
|
|
4415
|
-
dataAppName:
|
|
4660
|
+
dataAppName: z20.string().optional().describe("Data app name for data_app scope. Ignored for global scope.")
|
|
4416
4661
|
},
|
|
4417
4662
|
READ_ONLY,
|
|
4418
4663
|
async (params) => {
|
|
@@ -4447,7 +4692,7 @@ function registerListDashboards(server, client) {
|
|
|
4447
4692
|
}
|
|
4448
4693
|
|
|
4449
4694
|
// src/tools/query/ask-question.ts
|
|
4450
|
-
import { z as
|
|
4695
|
+
import { z as z21 } from "zod";
|
|
4451
4696
|
var MAX_DISPLAY_ROWS = 50;
|
|
4452
4697
|
function formatDataTable(data, columns) {
|
|
4453
4698
|
if (!Array.isArray(data) || data.length === 0) return "_No data returned._";
|
|
@@ -4477,16 +4722,16 @@ function registerAskQuestion(server, client) {
|
|
|
4477
4722
|
"ask_question",
|
|
4478
4723
|
"Ask a natural language question \u2014 converts to SQL, returns data + chart suggestion. Requires datamartName with a populated semantic layer.",
|
|
4479
4724
|
{
|
|
4480
|
-
question:
|
|
4725
|
+
question: z21.string().describe(
|
|
4481
4726
|
'The natural language question to ask (e.g. "What was total revenue last month?")'
|
|
4482
4727
|
),
|
|
4483
|
-
datamartName:
|
|
4728
|
+
datamartName: z21.string().optional().describe(
|
|
4484
4729
|
"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."
|
|
4485
4730
|
),
|
|
4486
|
-
sessionId:
|
|
4731
|
+
sessionId: z21.string().optional().describe(
|
|
4487
4732
|
"Session ID from a previous ask_question response, for follow-up questions."
|
|
4488
4733
|
),
|
|
4489
|
-
timezone:
|
|
4734
|
+
timezone: z21.string().optional().describe('IANA timezone (e.g. "America/New_York"). Defaults to UTC.')
|
|
4490
4735
|
},
|
|
4491
4736
|
WRITE_CREATE,
|
|
4492
4737
|
async (params) => {
|
|
@@ -4662,14 +4907,14 @@ To fix this:
|
|
|
4662
4907
|
}
|
|
4663
4908
|
|
|
4664
4909
|
// src/tools/query/get-semantic-layer.ts
|
|
4665
|
-
import { z as
|
|
4910
|
+
import { z as z22 } from "zod";
|
|
4666
4911
|
function registerGetSemanticLayer(server, client) {
|
|
4667
4912
|
const services = createServiceBundle(client);
|
|
4668
4913
|
server.tool(
|
|
4669
4914
|
"get_semantic_layer",
|
|
4670
4915
|
"Get semantic layer metadata for a datamart: table/column descriptions, synonyms, completion score. Omit datamartName to auto-resolve.",
|
|
4671
4916
|
{
|
|
4672
|
-
datamartName:
|
|
4917
|
+
datamartName: z22.string().optional().describe(
|
|
4673
4918
|
"Name of the datamart to inspect. Omit to list available datamarts."
|
|
4674
4919
|
)
|
|
4675
4920
|
},
|
|
@@ -4779,15 +5024,15 @@ ${result.feedback}`);
|
|
|
4779
5024
|
}
|
|
4780
5025
|
|
|
4781
5026
|
// src/tools/query/update-semantic-layer.ts
|
|
4782
|
-
import { z as
|
|
4783
|
-
var columnSchema2 =
|
|
4784
|
-
name:
|
|
4785
|
-
description:
|
|
4786
|
-
synonyms:
|
|
5027
|
+
import { z as z23 } from "zod";
|
|
5028
|
+
var columnSchema2 = z23.object({
|
|
5029
|
+
name: z23.string().describe("Column name (must exist in the datamart)"),
|
|
5030
|
+
description: z23.string().optional().describe("Business-friendly description of what this column represents"),
|
|
5031
|
+
synonyms: z23.array(z23.string()).optional().describe(
|
|
4787
5032
|
'Alternative names users might use for this column (e.g. ["revenue", "sales amount"])'
|
|
4788
5033
|
),
|
|
4789
|
-
miscellaneousInfo:
|
|
4790
|
-
columnType:
|
|
5034
|
+
miscellaneousInfo: z23.string().optional().describe("Additional context such as valid values, units, or caveats"),
|
|
5035
|
+
columnType: z23.enum([
|
|
4791
5036
|
"String",
|
|
4792
5037
|
"Long String",
|
|
4793
5038
|
"String (Custom)",
|
|
@@ -4800,16 +5045,16 @@ var columnSchema2 = z22.object({
|
|
|
4800
5045
|
"JSON"
|
|
4801
5046
|
]).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
5047
|
});
|
|
4803
|
-
var tableSchema2 =
|
|
4804
|
-
name:
|
|
4805
|
-
description:
|
|
5048
|
+
var tableSchema2 = z23.object({
|
|
5049
|
+
name: z23.string().describe("Table name (must exist in the datamart)"),
|
|
5050
|
+
description: z23.string().optional().describe(
|
|
4806
5051
|
"Business-friendly description of what this table represents"
|
|
4807
5052
|
),
|
|
4808
|
-
synonyms:
|
|
4809
|
-
miscellaneousInfo:
|
|
5053
|
+
synonyms: z23.array(z23.string()).optional().describe("Alternative names for this table"),
|
|
5054
|
+
miscellaneousInfo: z23.string().optional().describe(
|
|
4810
5055
|
"Additional context such as how the table relates to other tables"
|
|
4811
5056
|
),
|
|
4812
|
-
columns:
|
|
5057
|
+
columns: z23.array(columnSchema2).optional().describe("Column-level semantic definitions to update")
|
|
4813
5058
|
});
|
|
4814
5059
|
function columnKey(tableName, columnName) {
|
|
4815
5060
|
return `${tableName.toLowerCase()}\0${columnName.toLowerCase()}`;
|
|
@@ -4876,13 +5121,13 @@ function registerUpdateSemanticLayer(server, client) {
|
|
|
4876
5121
|
"update_semantic_layer",
|
|
4877
5122
|
"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.",
|
|
4878
5123
|
{
|
|
4879
|
-
datamartName:
|
|
5124
|
+
datamartName: z23.string().optional().describe(
|
|
4880
5125
|
"Name of the datamart to update. Omit to list available datamarts."
|
|
4881
5126
|
),
|
|
4882
|
-
tables:
|
|
5127
|
+
tables: z23.array(tableSchema2).optional().describe(
|
|
4883
5128
|
"Table and column definitions to update. Only include tables/columns you want to change."
|
|
4884
5129
|
),
|
|
4885
|
-
feedback:
|
|
5130
|
+
feedback: z23.string().optional().describe(
|
|
4886
5131
|
'General feedback or context about the datamart (e.g. "This datamart contains e-commerce data for the US market").'
|
|
4887
5132
|
)
|
|
4888
5133
|
},
|
|
@@ -4944,7 +5189,7 @@ You can now:
|
|
|
4944
5189
|
}
|
|
4945
5190
|
|
|
4946
5191
|
// src/tools/query/semantic-generation.ts
|
|
4947
|
-
import { z as
|
|
5192
|
+
import { z as z24 } from "zod";
|
|
4948
5193
|
function nonEmptyString(value) {
|
|
4949
5194
|
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
4950
5195
|
}
|
|
@@ -5099,9 +5344,9 @@ function registerStartSemanticLayerGeneration(server, client) {
|
|
|
5099
5344
|
"start_semantic_layer_generation",
|
|
5100
5345
|
"Start backend semantic-layer auto-generation for a datamart. Returns an async job id to poll.",
|
|
5101
5346
|
{
|
|
5102
|
-
datamartName:
|
|
5103
|
-
workspaceName:
|
|
5104
|
-
datamartId:
|
|
5347
|
+
datamartName: z24.string().optional().describe("Datamart name. Omit to list available datamarts"),
|
|
5348
|
+
workspaceName: z24.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
|
|
5349
|
+
datamartId: z24.string().optional().describe("Datamart UUID. Use only when already known.")
|
|
5105
5350
|
},
|
|
5106
5351
|
WRITE_CREATE,
|
|
5107
5352
|
async (params) => {
|
|
@@ -5143,9 +5388,9 @@ function registerGetSemanticLayerGenerationStatus(server, client) {
|
|
|
5143
5388
|
"get_semantic_layer_generation_status",
|
|
5144
5389
|
"Get semantic-layer auto-generation job status for a datamart.",
|
|
5145
5390
|
{
|
|
5146
|
-
datamartName:
|
|
5147
|
-
workspaceName:
|
|
5148
|
-
datamartId:
|
|
5391
|
+
datamartName: z24.string().optional().describe("Datamart name. Omit to list available datamarts"),
|
|
5392
|
+
workspaceName: z24.string().optional().describe("Workspace linked to the datamart. Use when datamartName resolution needs a dataMartId bridge."),
|
|
5393
|
+
datamartId: z24.string().optional().describe("Datamart UUID. Use only when already known.")
|
|
5149
5394
|
},
|
|
5150
5395
|
READ_ONLY,
|
|
5151
5396
|
async (params) => {
|
|
@@ -5182,17 +5427,17 @@ function registerGetSemanticLayerGenerationStatus(server, client) {
|
|
|
5182
5427
|
}
|
|
5183
5428
|
|
|
5184
5429
|
// src/tools/widgets/list-widgets.ts
|
|
5185
|
-
import { z as
|
|
5430
|
+
import { z as z25 } from "zod";
|
|
5186
5431
|
function registerListWidgets(server, client) {
|
|
5187
5432
|
const services = createServiceBundle(client);
|
|
5188
5433
|
server.tool(
|
|
5189
5434
|
"list_widgets",
|
|
5190
5435
|
"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.",
|
|
5191
5436
|
{
|
|
5192
|
-
clientId:
|
|
5193
|
-
dashboardName:
|
|
5194
|
-
workspaceName:
|
|
5195
|
-
embedId:
|
|
5437
|
+
clientId: z25.string().describe("Client/tenant identifier for this dashboard"),
|
|
5438
|
+
dashboardName: z25.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
|
|
5439
|
+
workspaceName: z25.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
|
|
5440
|
+
embedId: z25.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier.")
|
|
5196
5441
|
},
|
|
5197
5442
|
READ_ONLY,
|
|
5198
5443
|
async (params) => {
|
|
@@ -5238,28 +5483,28 @@ function registerListWidgets(server, client) {
|
|
|
5238
5483
|
}
|
|
5239
5484
|
|
|
5240
5485
|
// src/tools/widgets/add-widget.ts
|
|
5241
|
-
import { z as
|
|
5486
|
+
import { z as z26 } from "zod";
|
|
5242
5487
|
function registerAddWidget(server, client) {
|
|
5243
5488
|
const services = createServiceBundle(client);
|
|
5244
5489
|
server.tool(
|
|
5245
5490
|
"add_widget",
|
|
5246
5491
|
"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.",
|
|
5247
5492
|
{
|
|
5248
|
-
clientId:
|
|
5249
|
-
dashboardName:
|
|
5250
|
-
workspaceName:
|
|
5251
|
-
embedId:
|
|
5252
|
-
dataAppName:
|
|
5253
|
-
templateMetricId:
|
|
5254
|
-
name:
|
|
5255
|
-
metricId:
|
|
5256
|
-
query:
|
|
5257
|
-
chartOptions:
|
|
5258
|
-
description:
|
|
5259
|
-
companyIntegrationId:
|
|
5260
|
-
integrationName:
|
|
5261
|
-
isPublished:
|
|
5262
|
-
body:
|
|
5493
|
+
clientId: z26.string().describe("Client/tenant identifier (widget is created in their scope)"),
|
|
5494
|
+
dashboardName: z26.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
|
|
5495
|
+
workspaceName: z26.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
|
|
5496
|
+
embedId: z26.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
|
|
5497
|
+
dataAppName: z26.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
|
|
5498
|
+
templateMetricId: z26.string().optional().describe("Source widget metricId to clone from. Copies query, chartOptions, and datasource fields; caller can override via other params."),
|
|
5499
|
+
name: z26.string().optional().describe("Widget name (overrides template)"),
|
|
5500
|
+
metricId: z26.string().optional().describe("Unique slug for the new widget (auto-generated if omitted)"),
|
|
5501
|
+
query: z26.string().optional().describe("SQL query body (overrides template)"),
|
|
5502
|
+
chartOptions: z26.record(z26.unknown()).optional().describe("Chart config JSON (chart type, colors, legend, axes). Overrides template."),
|
|
5503
|
+
description: z26.string().optional(),
|
|
5504
|
+
companyIntegrationId: z26.string().optional().describe("Datasource UUID override. Auto-resolved from `embedId` or sole active datasource; leave blank unless the user explicitly picked one by ID."),
|
|
5505
|
+
integrationName: z26.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.'),
|
|
5506
|
+
isPublished: z26.boolean().optional().describe("Publish immediately (default true)"),
|
|
5507
|
+
body: z26.record(z26.unknown()).optional().describe("Raw createMetric body passthrough. If provided, used as-is (after defaults). Overrides all other params.")
|
|
5263
5508
|
},
|
|
5264
5509
|
WRITE_CREATE,
|
|
5265
5510
|
async (params) => {
|
|
@@ -5283,6 +5528,13 @@ function registerAddWidget(server, client) {
|
|
|
5283
5528
|
embedId
|
|
5284
5529
|
);
|
|
5285
5530
|
if (!dataAppName.ok) return toolError(dataAppName.error);
|
|
5531
|
+
const tenantScope = await services.widgetService.ensureTenantDashboardScope({
|
|
5532
|
+
embedId,
|
|
5533
|
+
clientId: params.clientId,
|
|
5534
|
+
dataAppName: dataAppName.value,
|
|
5535
|
+
dashboardName: embed.value.resolvedFrom?.dashboardName || params.dashboardName || null
|
|
5536
|
+
});
|
|
5537
|
+
if (!tenantScope.ok) return toolError(tenantScope.error);
|
|
5286
5538
|
const guestToken = await services.widgetService.mintGuestToken(
|
|
5287
5539
|
params.clientId,
|
|
5288
5540
|
dataAppName.value,
|
|
@@ -5369,20 +5621,20 @@ Use \`list_widgets\` to verify, or \`update_widget\`/\`remove_widget\` to adjust
|
|
|
5369
5621
|
}
|
|
5370
5622
|
|
|
5371
5623
|
// src/tools/widgets/update-widget.ts
|
|
5372
|
-
import { z as
|
|
5624
|
+
import { z as z27 } from "zod";
|
|
5373
5625
|
function registerUpdateWidget(server, client) {
|
|
5374
5626
|
const services = createServiceBundle(client);
|
|
5375
5627
|
server.tool(
|
|
5376
5628
|
"update_widget",
|
|
5377
5629
|
"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.",
|
|
5378
5630
|
{
|
|
5379
|
-
externalMetricId:
|
|
5380
|
-
clientId:
|
|
5381
|
-
dashboardName:
|
|
5382
|
-
workspaceName:
|
|
5383
|
-
embedId:
|
|
5384
|
-
dataAppName:
|
|
5385
|
-
set:
|
|
5631
|
+
externalMetricId: z27.string().describe("Internal uuid of the widget (externalMetrics.id). Not the user-facing metricId slug."),
|
|
5632
|
+
clientId: z27.string().describe("Client/tenant identifier"),
|
|
5633
|
+
dashboardName: z27.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
|
|
5634
|
+
workspaceName: z27.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
|
|
5635
|
+
embedId: z27.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
|
|
5636
|
+
dataAppName: z27.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank."),
|
|
5637
|
+
set: z27.record(z27.unknown()).describe("Partial patch. Keys can include: name, query, chartOptions, description, isPublished, isLive, selectedGroupBy, drillDownSettings, limit, etc.")
|
|
5386
5638
|
},
|
|
5387
5639
|
WRITE_IDEMPOTENT,
|
|
5388
5640
|
async (params) => {
|
|
@@ -5393,19 +5645,27 @@ function registerUpdateWidget(server, client) {
|
|
|
5393
5645
|
const embed = await services.widgetService.resolveEmbedId({
|
|
5394
5646
|
embedId: params.embedId,
|
|
5395
5647
|
dashboardName: params.dashboardName,
|
|
5396
|
-
workspaceName: params.workspaceName
|
|
5648
|
+
workspaceName: params.workspaceName,
|
|
5649
|
+
required: true
|
|
5397
5650
|
});
|
|
5398
5651
|
if (!embed.ok) return toolError(embed.error);
|
|
5399
5652
|
const embedId = embed.value.embedId;
|
|
5400
5653
|
const dataAppName = await services.widgetService.resolveDataAppName(
|
|
5401
5654
|
params.dataAppName,
|
|
5402
|
-
embedId
|
|
5655
|
+
embedId
|
|
5403
5656
|
);
|
|
5404
5657
|
if (!dataAppName.ok) return toolError(dataAppName.error);
|
|
5658
|
+
const tenantScope = await services.widgetService.ensureTenantDashboardScope({
|
|
5659
|
+
embedId,
|
|
5660
|
+
clientId: params.clientId,
|
|
5661
|
+
dataAppName: dataAppName.value,
|
|
5662
|
+
dashboardName: embed.value.resolvedFrom?.dashboardName || params.dashboardName || null
|
|
5663
|
+
});
|
|
5664
|
+
if (!tenantScope.ok) return toolError(tenantScope.error);
|
|
5405
5665
|
const guestToken = await services.widgetService.mintGuestToken(
|
|
5406
5666
|
params.clientId,
|
|
5407
5667
|
dataAppName.value,
|
|
5408
|
-
|
|
5668
|
+
[embedId]
|
|
5409
5669
|
);
|
|
5410
5670
|
const result = await services.widgetsApi.updateMetric(guestToken, {
|
|
5411
5671
|
externalMetricId: params.externalMetricId,
|
|
@@ -5437,19 +5697,19 @@ Use \`list_widgets\` to verify.`
|
|
|
5437
5697
|
}
|
|
5438
5698
|
|
|
5439
5699
|
// src/tools/widgets/remove-widget.ts
|
|
5440
|
-
import { z as
|
|
5700
|
+
import { z as z28 } from "zod";
|
|
5441
5701
|
function registerRemoveWidget(server, client) {
|
|
5442
5702
|
const services = createServiceBundle(client);
|
|
5443
5703
|
server.tool(
|
|
5444
5704
|
"remove_widget",
|
|
5445
5705
|
"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.",
|
|
5446
5706
|
{
|
|
5447
|
-
externalMetricId:
|
|
5448
|
-
clientId:
|
|
5449
|
-
dashboardName:
|
|
5450
|
-
workspaceName:
|
|
5451
|
-
embedId:
|
|
5452
|
-
dataAppName:
|
|
5707
|
+
externalMetricId: z28.string().describe("Internal uuid of the widget (externalMetrics.id)"),
|
|
5708
|
+
clientId: z28.string().describe("Client/tenant identifier \u2014 the widget will be hidden for this client only"),
|
|
5709
|
+
dashboardName: z28.string().optional().describe("Dashboard name (non-technical). Preferred input \u2014 pass this with optional `workspaceName` instead of `embedId`."),
|
|
5710
|
+
workspaceName: z28.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
|
|
5711
|
+
embedId: z28.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
|
|
5712
|
+
dataAppName: z28.string().optional().describe("Technical override. Auto-resolved from the dashboard; non-technical callers should leave this blank.")
|
|
5453
5713
|
},
|
|
5454
5714
|
DESTRUCTIVE,
|
|
5455
5715
|
async (params) => {
|
|
@@ -5457,19 +5717,27 @@ function registerRemoveWidget(server, client) {
|
|
|
5457
5717
|
const embed = await services.widgetService.resolveEmbedId({
|
|
5458
5718
|
embedId: params.embedId,
|
|
5459
5719
|
dashboardName: params.dashboardName,
|
|
5460
|
-
workspaceName: params.workspaceName
|
|
5720
|
+
workspaceName: params.workspaceName,
|
|
5721
|
+
required: true
|
|
5461
5722
|
});
|
|
5462
5723
|
if (!embed.ok) return toolError(embed.error);
|
|
5463
5724
|
const embedId = embed.value.embedId;
|
|
5464
5725
|
const dataAppName = await services.widgetService.resolveDataAppName(
|
|
5465
5726
|
params.dataAppName,
|
|
5466
|
-
embedId
|
|
5727
|
+
embedId
|
|
5467
5728
|
);
|
|
5468
5729
|
if (!dataAppName.ok) return toolError(dataAppName.error);
|
|
5730
|
+
const tenantScope = await services.widgetService.ensureTenantDashboardScope({
|
|
5731
|
+
embedId,
|
|
5732
|
+
clientId: params.clientId,
|
|
5733
|
+
dataAppName: dataAppName.value,
|
|
5734
|
+
dashboardName: embed.value.resolvedFrom?.dashboardName || params.dashboardName || null
|
|
5735
|
+
});
|
|
5736
|
+
if (!tenantScope.ok) return toolError(tenantScope.error);
|
|
5469
5737
|
const guestToken = await services.widgetService.mintGuestToken(
|
|
5470
5738
|
params.clientId,
|
|
5471
5739
|
dataAppName.value,
|
|
5472
|
-
|
|
5740
|
+
[embedId]
|
|
5473
5741
|
);
|
|
5474
5742
|
const result = await services.widgetsApi.markArchived(guestToken, {
|
|
5475
5743
|
id: params.externalMetricId,
|
|
@@ -5497,21 +5765,21 @@ Note: this is a per-client soft delete. The widget remains available for other c
|
|
|
5497
5765
|
}
|
|
5498
5766
|
|
|
5499
5767
|
// src/tools/widgets/generate-widget.ts
|
|
5500
|
-
import { z as
|
|
5768
|
+
import { z as z29 } from "zod";
|
|
5501
5769
|
function registerGenerateWidget(server, client) {
|
|
5502
5770
|
const services = createServiceBundle(client);
|
|
5503
5771
|
server.tool(
|
|
5504
5772
|
"generate_widget",
|
|
5505
5773
|
"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.",
|
|
5506
5774
|
{
|
|
5507
|
-
clientId:
|
|
5508
|
-
userInputs:
|
|
5509
|
-
dashboardName:
|
|
5510
|
-
workspaceName:
|
|
5511
|
-
embedId:
|
|
5512
|
-
dataAppName:
|
|
5513
|
-
integrationId:
|
|
5514
|
-
integrationName:
|
|
5775
|
+
clientId: z29.string().describe("Client/tenant identifier"),
|
|
5776
|
+
userInputs: z29.record(z29.unknown()).describe('Prompt + context object. Typical shape: { prompt: "calls per day over last 30 days", schema: {...}, examples: [...] }. Backend decides which keys it honors.'),
|
|
5777
|
+
dashboardName: z29.string().optional().describe("Dashboard name (non-technical). Preferred input for CS/CX flows \u2014 pass this with optional `workspaceName` instead of `embedId`."),
|
|
5778
|
+
workspaceName: z29.string().optional().describe("Workspace name (non-technical). Only needed if `dashboardName` is ambiguous across workspaces."),
|
|
5779
|
+
embedId: z29.string().optional().describe("Technical alternative to `dashboardName`. Pass only if you already have the embed identifier."),
|
|
5780
|
+
dataAppName: z29.string().optional().describe("Technical override. Auto-resolved from the embed; non-technical callers should leave this blank."),
|
|
5781
|
+
integrationId: z29.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."),
|
|
5782
|
+
integrationName: z29.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.")
|
|
5515
5783
|
},
|
|
5516
5784
|
READ_ONLY,
|
|
5517
5785
|
async (params) => {
|
|
@@ -5529,6 +5797,13 @@ function registerGenerateWidget(server, client) {
|
|
|
5529
5797
|
embedId
|
|
5530
5798
|
);
|
|
5531
5799
|
if (!dataAppName.ok) return toolError(dataAppName.error);
|
|
5800
|
+
const tenantScope = await services.widgetService.ensureTenantDashboardScope({
|
|
5801
|
+
embedId,
|
|
5802
|
+
clientId: params.clientId,
|
|
5803
|
+
dataAppName: dataAppName.value,
|
|
5804
|
+
dashboardName: embed.value.resolvedFrom?.dashboardName || params.dashboardName || null
|
|
5805
|
+
});
|
|
5806
|
+
if (!tenantScope.ok) return toolError(tenantScope.error);
|
|
5532
5807
|
const guestToken = await services.widgetService.mintGuestToken(
|
|
5533
5808
|
params.clientId,
|
|
5534
5809
|
dataAppName.value,
|
|
@@ -5577,9 +5852,9 @@ function registerGenerateWidget(server, client) {
|
|
|
5577
5852
|
}
|
|
5578
5853
|
|
|
5579
5854
|
// src/tools/widgets/create-or-update-metric-from-query.ts
|
|
5580
|
-
import { z as
|
|
5581
|
-
var modeSchema =
|
|
5582
|
-
var conflictStrategySchema =
|
|
5855
|
+
import { z as z30 } from "zod";
|
|
5856
|
+
var modeSchema = z30.enum(["dry_run", "apply"]).default("dry_run");
|
|
5857
|
+
var conflictStrategySchema = z30.enum(["fail", "update", "create_new"]).default("fail");
|
|
5583
5858
|
function makePlanText(plan) {
|
|
5584
5859
|
return JSON.stringify(plan, null, 2);
|
|
5585
5860
|
}
|
|
@@ -5596,22 +5871,22 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
|
|
|
5596
5871
|
"Create or update a production metric/widget from validated SQL. Defaults to dry-run; apply requires explicit confirmation.",
|
|
5597
5872
|
{
|
|
5598
5873
|
mode: modeSchema.describe("dry_run returns the plan only; apply executes after confirmation"),
|
|
5599
|
-
confirm:
|
|
5874
|
+
confirm: z30.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
|
|
5600
5875
|
conflictStrategy: conflictStrategySchema.describe("What to do if metricId already exists on the target dashboard"),
|
|
5601
|
-
clientId:
|
|
5602
|
-
workspaceName:
|
|
5603
|
-
dashboardName:
|
|
5604
|
-
embedId:
|
|
5605
|
-
dataAppName:
|
|
5606
|
-
name:
|
|
5607
|
-
metricId:
|
|
5608
|
-
query:
|
|
5609
|
-
description:
|
|
5610
|
-
chartOptions:
|
|
5611
|
-
companyIntegrationId:
|
|
5612
|
-
integrationName:
|
|
5613
|
-
isPublished:
|
|
5614
|
-
requestedBy:
|
|
5876
|
+
clientId: z30.string().describe("Client/tenant identifier for the dashboard widget scope"),
|
|
5877
|
+
workspaceName: z30.string().describe("Target workspace name"),
|
|
5878
|
+
dashboardName: z30.string().optional().describe("Dashboard name to attach to"),
|
|
5879
|
+
embedId: z30.string().optional().describe("Technical embed id alternative to dashboardName"),
|
|
5880
|
+
dataAppName: z30.string().optional().describe("Optional data app override"),
|
|
5881
|
+
name: z30.string().describe("Metric/widget display name"),
|
|
5882
|
+
metricId: z30.string().describe("Stable metric slug to create or update"),
|
|
5883
|
+
query: z30.string().describe("SQL query already validated against the warehouse"),
|
|
5884
|
+
description: z30.string().optional().describe("Metric description"),
|
|
5885
|
+
chartOptions: z30.record(z30.unknown()).optional().describe("Chart configuration JSON"),
|
|
5886
|
+
companyIntegrationId: z30.string().optional().describe("Datasource UUID override"),
|
|
5887
|
+
integrationName: z30.string().optional().describe("Datasource display name override"),
|
|
5888
|
+
isPublished: z30.boolean().optional().describe("Publish after apply. Defaults to false for production safety"),
|
|
5889
|
+
requestedBy: z30.string().optional().describe("Human operator/request context to store on the metric when supported")
|
|
5615
5890
|
},
|
|
5616
5891
|
WRITE_IDEMPOTENT,
|
|
5617
5892
|
async (params) => {
|
|
@@ -5640,6 +5915,13 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
|
|
|
5640
5915
|
embedId
|
|
5641
5916
|
);
|
|
5642
5917
|
if (!dataAppName.ok) return toolError(dataAppName.error);
|
|
5918
|
+
const tenantScope = await services.widgetService.ensureTenantDashboardScope({
|
|
5919
|
+
embedId,
|
|
5920
|
+
clientId: params.clientId,
|
|
5921
|
+
dataAppName: dataAppName.value,
|
|
5922
|
+
dashboardName: embed.value.resolvedFrom?.dashboardName || params.dashboardName || null
|
|
5923
|
+
});
|
|
5924
|
+
if (!tenantScope.ok) return toolError(tenantScope.error);
|
|
5643
5925
|
const guestToken = await services.widgetService.mintGuestToken(
|
|
5644
5926
|
params.clientId,
|
|
5645
5927
|
dataAppName.value,
|
|
@@ -5702,6 +5984,7 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
|
|
|
5702
5984
|
dataAppName: dataAppName.value,
|
|
5703
5985
|
externalDashboardId: ctx.externalDashboardId,
|
|
5704
5986
|
clientId: params.clientId,
|
|
5987
|
+
tenantScoped: true,
|
|
5705
5988
|
datasource: datasource.value
|
|
5706
5989
|
},
|
|
5707
5990
|
conflict: existing ? {
|
|
@@ -5762,26 +6045,97 @@ function registerCreateOrUpdateMetricFromQuery(server, client) {
|
|
|
5762
6045
|
);
|
|
5763
6046
|
}
|
|
5764
6047
|
|
|
6048
|
+
// src/tools/widgets/create-workspace-metric.ts
|
|
6049
|
+
import { z as z31 } from "zod";
|
|
6050
|
+
var modeSchema2 = z31.enum(["dry_run", "apply"]).default("dry_run");
|
|
6051
|
+
function registerCreateWorkspaceMetric(server, client) {
|
|
6052
|
+
server.tool(
|
|
6053
|
+
"create_workspace_metric",
|
|
6054
|
+
"Create a workspace-level metric from validated SQL using the org service token. Scoped to the workspace, unpublished, and NOT attached to any dashboard. Defaults to dry-run; apply requires explicit confirmation.",
|
|
6055
|
+
{
|
|
6056
|
+
mode: modeSchema2.describe(
|
|
6057
|
+
"dry_run validates the SQL only (no write); apply creates the metric after confirmation"
|
|
6058
|
+
),
|
|
6059
|
+
confirm: z31.string().optional().describe(`Required for apply: ${APPLY_TO_PRODUCTION_CONFIRMATION}`),
|
|
6060
|
+
workspaceName: z31.string().describe("Target workspace name"),
|
|
6061
|
+
name: z31.string().describe("Metric display name"),
|
|
6062
|
+
query: z31.string().describe("SQL query to validate and store"),
|
|
6063
|
+
metricId: z31.string().optional().describe("Stable metric slug; generated from the name if omitted"),
|
|
6064
|
+
description: z31.string().optional().describe("Metric description"),
|
|
6065
|
+
datasourceId: z31.string().optional().describe("Datasource UUID override; defaults to the workspace datasource"),
|
|
6066
|
+
isPublished: z31.boolean().optional().describe("Publish after apply. Defaults to false for safety")
|
|
6067
|
+
},
|
|
6068
|
+
WRITE_CREATE,
|
|
6069
|
+
async (params) => {
|
|
6070
|
+
try {
|
|
6071
|
+
const mode = params.mode || "dry_run";
|
|
6072
|
+
if (!params.name.trim()) return toolError("`name` cannot be empty.");
|
|
6073
|
+
if (!params.query.trim()) return toolError("`query` cannot be empty.");
|
|
6074
|
+
const isApply = mode === "apply";
|
|
6075
|
+
if (isApply) {
|
|
6076
|
+
const guardError = validateProductionApply({
|
|
6077
|
+
workspaceName: params.workspaceName,
|
|
6078
|
+
confirm: params.confirm
|
|
6079
|
+
});
|
|
6080
|
+
if (guardError) return toolError(guardError);
|
|
6081
|
+
}
|
|
6082
|
+
const body = {
|
|
6083
|
+
workspaceName: params.workspaceName,
|
|
6084
|
+
name: params.name,
|
|
6085
|
+
query: params.query,
|
|
6086
|
+
metricId: params.metricId,
|
|
6087
|
+
description: params.description,
|
|
6088
|
+
datasourceId: params.datasourceId,
|
|
6089
|
+
isPublished: params.isPublished ?? false,
|
|
6090
|
+
validateOnly: !isApply
|
|
6091
|
+
};
|
|
6092
|
+
const result = await client.request({
|
|
6093
|
+
method: "POST",
|
|
6094
|
+
// MCP-only control-plane route (not the public /api/v2 surface).
|
|
6095
|
+
path: "/api/mcp/metrics/create",
|
|
6096
|
+
auth: "service",
|
|
6097
|
+
body
|
|
6098
|
+
});
|
|
6099
|
+
return toolSuccess(
|
|
6100
|
+
JSON.stringify(
|
|
6101
|
+
{
|
|
6102
|
+
mode,
|
|
6103
|
+
safety: productionSafetyNote(params.workspaceName),
|
|
6104
|
+
result,
|
|
6105
|
+
nextStep: isApply ? "Metric created unpublished and unattached. Publish/attach it to a dashboard as a separate step when ready." : 'Dry-run only \u2014 no metric written. Re-run with mode "apply" and confirm to create it.'
|
|
6106
|
+
},
|
|
6107
|
+
null,
|
|
6108
|
+
2
|
|
6109
|
+
)
|
|
6110
|
+
);
|
|
6111
|
+
} catch (err) {
|
|
6112
|
+
if (err instanceof DatabrainApiError) return err.toToolResponse();
|
|
6113
|
+
return toolError(`Unexpected error: ${err.message}`);
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6116
|
+
);
|
|
6117
|
+
}
|
|
6118
|
+
|
|
5765
6119
|
// src/tools/widgets/validate-metric-spec.ts
|
|
5766
|
-
import { z as
|
|
6120
|
+
import { z as z32 } from "zod";
|
|
5767
6121
|
function registerValidateMetricSpec(server, client) {
|
|
5768
6122
|
const services = createServiceBundle(client);
|
|
5769
6123
|
server.tool(
|
|
5770
6124
|
"validate_metric_spec",
|
|
5771
6125
|
"Validate a production metric spec from SQL without saving it. Resolves dashboard, data app, datasource, conflicts, and required fields.",
|
|
5772
6126
|
{
|
|
5773
|
-
clientId:
|
|
5774
|
-
workspaceName:
|
|
5775
|
-
dashboardName:
|
|
5776
|
-
embedId:
|
|
5777
|
-
dataAppName:
|
|
5778
|
-
name:
|
|
5779
|
-
metricId:
|
|
5780
|
-
query:
|
|
5781
|
-
description:
|
|
5782
|
-
chartOptions:
|
|
5783
|
-
companyIntegrationId:
|
|
5784
|
-
integrationName:
|
|
6127
|
+
clientId: z32.string().describe("Client/tenant identifier used to validate dashboard widget scope"),
|
|
6128
|
+
workspaceName: z32.string().describe("Target production-backed workspace name"),
|
|
6129
|
+
dashboardName: z32.string().optional().describe("Dashboard name to attach to"),
|
|
6130
|
+
embedId: z32.string().optional().describe("Technical embed id alternative to dashboardName"),
|
|
6131
|
+
dataAppName: z32.string().optional().describe("Optional data app override"),
|
|
6132
|
+
name: z32.string().describe("Metric/widget display name"),
|
|
6133
|
+
metricId: z32.string().describe("Stable metric slug to create or update"),
|
|
6134
|
+
query: z32.string().describe("SQL query already validated against the warehouse"),
|
|
6135
|
+
description: z32.string().optional().describe("Metric description"),
|
|
6136
|
+
chartOptions: z32.record(z32.unknown()).optional().describe("Chart configuration JSON"),
|
|
6137
|
+
companyIntegrationId: z32.string().optional().describe("Datasource UUID override"),
|
|
6138
|
+
integrationName: z32.string().optional().describe("Datasource display name override")
|
|
5785
6139
|
},
|
|
5786
6140
|
READ_ONLY,
|
|
5787
6141
|
async (params) => {
|
|
@@ -5808,6 +6162,13 @@ function registerValidateMetricSpec(server, client) {
|
|
|
5808
6162
|
embedId
|
|
5809
6163
|
);
|
|
5810
6164
|
if (!dataAppName.ok) return toolError(dataAppName.error);
|
|
6165
|
+
const tenantScope = await services.widgetService.ensureTenantDashboardScope({
|
|
6166
|
+
embedId,
|
|
6167
|
+
clientId: params.clientId,
|
|
6168
|
+
dataAppName: dataAppName.value,
|
|
6169
|
+
dashboardName: embed.value.resolvedFrom?.dashboardName || params.dashboardName || null
|
|
6170
|
+
});
|
|
6171
|
+
if (!tenantScope.ok) return toolError(tenantScope.error);
|
|
5811
6172
|
const guestToken = await services.widgetService.mintGuestToken(
|
|
5812
6173
|
params.clientId,
|
|
5813
6174
|
dataAppName.value,
|
|
@@ -5833,6 +6194,7 @@ function registerValidateMetricSpec(server, client) {
|
|
|
5833
6194
|
embedId,
|
|
5834
6195
|
dataAppName: dataAppName.value,
|
|
5835
6196
|
externalDashboardId: ctx.externalDashboardId,
|
|
6197
|
+
tenantScoped: true,
|
|
5836
6198
|
datasource
|
|
5837
6199
|
},
|
|
5838
6200
|
conflict: existing ? {
|
|
@@ -5864,20 +6226,20 @@ function registerValidateMetricSpec(server, client) {
|
|
|
5864
6226
|
}
|
|
5865
6227
|
|
|
5866
6228
|
// src/tools/reports/list-scheduled-reports.ts
|
|
5867
|
-
import { z as
|
|
6229
|
+
import { z as z33 } from "zod";
|
|
5868
6230
|
function registerListScheduledReports(server, client) {
|
|
5869
6231
|
const services = createServiceBundle(client);
|
|
5870
6232
|
server.tool(
|
|
5871
6233
|
"list_scheduled_reports",
|
|
5872
6234
|
"List configured scheduled reports for a specific client/dashboard embed. Shows schedule metadata, not delivery success history.",
|
|
5873
6235
|
{
|
|
5874
|
-
clientId:
|
|
5875
|
-
dashboardName:
|
|
5876
|
-
workspaceName:
|
|
5877
|
-
embedId:
|
|
5878
|
-
userIdentifier:
|
|
5879
|
-
pageNumber:
|
|
5880
|
-
isPagination:
|
|
6236
|
+
clientId: z33.string().describe("Client/tenant identifier. Required by the current backend API for non-empty results"),
|
|
6237
|
+
dashboardName: z33.string().optional().describe("Dashboard name to inspect"),
|
|
6238
|
+
workspaceName: z33.string().optional().describe("Workspace name if dashboardName is ambiguous"),
|
|
6239
|
+
embedId: z33.string().optional().describe("Technical embed id alternative to dashboardName"),
|
|
6240
|
+
userIdentifier: z33.string().optional().describe("Optional creator/user filter"),
|
|
6241
|
+
pageNumber: z33.number().int().positive().optional().describe("Page number when pagination is enabled"),
|
|
6242
|
+
isPagination: z33.boolean().optional().describe("Enable backend pagination")
|
|
5881
6243
|
},
|
|
5882
6244
|
READ_ONLY,
|
|
5883
6245
|
async (params) => {
|
|
@@ -5988,6 +6350,7 @@ function registerAllTools(server, client) {
|
|
|
5988
6350
|
registerSyncDatasource(server, client);
|
|
5989
6351
|
registerCreateDatamart(server, client);
|
|
5990
6352
|
registerListDataApps(server, client);
|
|
6353
|
+
registerListTenants(server, client);
|
|
5991
6354
|
registerCreateDataApp(server, client);
|
|
5992
6355
|
registerListApiTokens(server, client);
|
|
5993
6356
|
registerCreateApiToken(server, client);
|
|
@@ -6009,6 +6372,7 @@ function registerAllTools(server, client) {
|
|
|
6009
6372
|
registerListWidgets(server, client);
|
|
6010
6373
|
registerValidateMetricSpec(server, client);
|
|
6011
6374
|
registerCreateOrUpdateMetricFromQuery(server, client);
|
|
6375
|
+
registerCreateWorkspaceMetric(server, client);
|
|
6012
6376
|
registerAddWidget(server, client);
|
|
6013
6377
|
registerUpdateWidget(server, client);
|
|
6014
6378
|
registerRemoveWidget(server, client);
|
|
@@ -9277,7 +9641,7 @@ function createServer(config) {
|
|
|
9277
9641
|
},
|
|
9278
9642
|
{
|
|
9279
9643
|
instructions: operatorBlurb + `Databrain uses two token types \u2014 never confuse them:
|
|
9280
|
-
\u2022 SERVICE TOKEN (DATABRAIN_SERVICE_TOKEN): org-level, pre-configured in MCP server config. Used for: list_datasources, discover_datasource_schema, sync_datasource, list_datamarts, create_datamart, list_workspaces, list_data_apps, create_data_app, list_api_tokens, create_api_token, rotate_api_token, list_dashboards (workspace scope), ask_question, get_semantic_layer, update_semantic_layer, start_semantic_layer_generation, get_semantic_layer_generation_status. ${serviceTokenStatus}
|
|
9644
|
+
\u2022 SERVICE TOKEN (DATABRAIN_SERVICE_TOKEN): org-level, pre-configured in MCP server config. Used for: list_datasources, discover_datasource_schema, sync_datasource, list_datamarts, create_datamart, list_workspaces, list_data_apps, list_tenants, create_data_app, list_api_tokens, create_api_token, rotate_api_token, list_dashboards (workspace scope), ask_question, get_semantic_layer, update_semantic_layer, start_semantic_layer_generation, get_semantic_layer_generation_status. ${serviceTokenStatus}
|
|
9281
9645
|
\u2022 API TOKEN (per data app): scoped to one data app, used for embedding operations (list_embeds, get_embed_details, create_embed, update_embed, generate_guest_token), data-app scoped dashboard listing, widget CRUD/metric creation through guest-token APIs, and list_scheduled_reports. ${apiTokenStatus}
|
|
9282
9646
|
|
|
9283
9647
|
4 core use cases \u2014 match the user's scenario to the right prompt:
|
|
@@ -9286,7 +9650,7 @@ function createServer(config) {
|
|
|
9286
9650
|
3. Explore & query data \u2192 prompt: query-data (list_datamarts \u2192 get_semantic_layer \u2192 if empty, auto-generate descriptions with update_semantic_layer or use populate-semantic-layer prompt \u2192 ask_question)
|
|
9287
9651
|
4. SQL-to-metric migration \u2192 prompt: sql-to-metric-migration (validate_metric_spec \u2192 create_or_update_metric_from_query dry_run \u2192 apply only with explicit production confirmation)
|
|
9288
9652
|
|
|
9289
|
-
Production safety: OAuth may not be available yet. In service-token mode, writes are actor=system. For create_datamart and create_or_update_metric_from_query, dry_run is the default. Apply requires confirm="APPLY_TO_PRODUCTION" after the user reviews the plan. Do not apply from a vague request.
|
|
9653
|
+
Production safety: OAuth may not be available yet. In service-token mode, writes are actor=system. For create_datamart and create_or_update_metric_from_query, dry_run is the default. Apply requires confirm="APPLY_TO_PRODUCTION" after the user reviews the plan. Widget validation and mutation tools must verify the resolved embed is tenant-scoped for the requested clientId; if not, they must refuse to touch it as a likely production/data-app dashboard. Do not apply from a vague request.
|
|
9290
9654
|
|
|
9291
9655
|
Infrastructure creation is intentionally narrow in MCP: datasource and workspace creation still happen outside this workflow; sync_datasource and dry-run/apply create_datamart are available. The semantic layer is populated and maintained via MCP tools (get_semantic_layer, update_semantic_layer, start_semantic_layer_generation, get_semantic_layer_generation_status). When the semantic layer is empty, use these tools \u2014 do NOT send the user to the UI for semantic metadata.
|
|
9292
9656
|
|