@ffschrattenecker/tm1-mcp-server 7.0.2 → 8.1.0
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/CHANGELOG.md +101 -1
- package/README.md +9 -6
- package/dist/config.d.ts +10 -1
- package/dist/config.js +40 -22
- package/dist/connections.d.ts +30 -2
- package/dist/connections.js +94 -21
- package/dist/http-transport.js +51 -4
- package/dist/index.js +6 -0
- package/dist/lib/callgraph/referenceIndex.js +2 -1
- package/dist/lib/callgraph/rulesLinter.d.ts +0 -19
- package/dist/lib/callgraph/rulesLinter.js +0 -590
- package/dist/lib/callgraph/tiParser.js +10 -5
- package/dist/lib/callgraph/tm1-adapter.d.ts +5 -1
- package/dist/lib/callgraph/tm1-adapter.js +41 -9
- package/dist/lib/callgraph/variableEnv.js +3 -2
- package/dist/lib/cell-address.d.ts +1 -1
- package/dist/lib/cell-address.js +2 -2
- package/dist/lib/complexity/antipatterns.js +3 -1
- package/dist/lib/complexity/comment-classifier.js +2 -1
- package/dist/lib/feeders/element-type-cache.js +4 -6
- package/dist/lib/naming/odata-filter.js +2 -1
- package/dist/lib/pro-parser.js +13 -4
- package/dist/lib/safe-regex.d.ts +0 -13
- package/dist/lib/safe-regex.js +40 -13
- package/dist/lib/sample-cells.js +1 -1
- package/dist/lib/ti-identifier.d.ts +11 -0
- package/dist/lib/ti-identifier.js +11 -0
- package/dist/lib/tm1-name.d.ts +9 -0
- package/dist/lib/tm1-name.js +9 -0
- package/dist/lib/v12-compat/deprecated-ti.js +2 -2
- package/dist/secrets-cli.d.ts +10 -0
- package/dist/secrets-cli.js +218 -0
- package/dist/secrets.d.ts +39 -0
- package/dist/secrets.js +128 -0
- package/dist/session-manager.d.ts +1 -0
- package/dist/session-manager.js +24 -0
- package/dist/tm1-client/connection/profile.js +2 -4
- package/dist/tm1-client/dispatcher.d.ts +1 -1
- package/dist/tm1-client/dispatcher.js +18 -6
- package/dist/tm1-client/http.d.ts +21 -0
- package/dist/tm1-client/http.js +39 -30
- package/dist/tm1-client/services/cell-service.d.ts +9 -7
- package/dist/tm1-client/services/cell-service.js +51 -25
- package/dist/tm1-client/services/chore-service.js +8 -9
- package/dist/tm1-client/services/cube-service.d.ts +14 -18
- package/dist/tm1-client/services/cube-service.js +51 -45
- package/dist/tm1-client/services/dimension-order.d.ts +13 -0
- package/dist/tm1-client/services/dimension-order.js +42 -0
- package/dist/tm1-client/services/dimension-service.js +4 -6
- package/dist/tm1-client/services/element-service.d.ts +35 -11
- package/dist/tm1-client/services/element-service.js +124 -60
- package/dist/tm1-client/services/file-service.d.ts +42 -6
- package/dist/tm1-client/services/file-service.js +225 -28
- package/dist/tm1-client/services/hierarchy-service.d.ts +19 -9
- package/dist/tm1-client/services/hierarchy-service.js +118 -12
- package/dist/tm1-client/services/monitoring-service.js +2 -5
- package/dist/tm1-client/services/odata-page.d.ts +6 -0
- package/dist/tm1-client/services/odata-page.js +8 -0
- package/dist/tm1-client/services/process-service.d.ts +1 -1
- package/dist/tm1-client/services/process-service.js +26 -26
- package/dist/tm1-client/services/security-service.js +8 -9
- package/dist/tm1-client/services/server-service.js +14 -15
- package/dist/tm1-client/services/subset-service.d.ts +23 -10
- package/dist/tm1-client/services/subset-service.js +82 -26
- package/dist/tm1-client/services/view-service.js +12 -13
- package/dist/tm1-client.js +5 -2
- package/dist/tools/analysis/check-v12-readiness.js +3 -3
- package/dist/tools/celldata/check-feeders.js +6 -6
- package/dist/tools/celldata/check-writable-coords.js +33 -22
- package/dist/tools/celldata/execute-mdx.js +10 -9
- package/dist/tools/celldata/get-view.js +10 -9
- package/dist/tools/celldata/member-ref.d.ts +15 -0
- package/dist/tools/celldata/member-ref.js +67 -0
- package/dist/tools/celldata/trace-cell-calculation.js +6 -6
- package/dist/tools/celldata/trace-feeders.js +6 -6
- package/dist/tools/celldata/write-cells.js +62 -12
- package/dist/tools/dimension-management/create-element-attribute.js +1 -1
- package/dist/tools/dimension-management/delete-hierarchy.js +3 -4
- package/dist/tools/dimension-management/get-element-attribute-values.js +7 -2
- package/dist/tools/dimension-management/list-element-attributes.js +1 -1
- package/dist/tools/dimension-management/update-element-attribute-value.js +8 -3
- package/dist/tools/dimension-management/update-element.js +14 -7
- package/dist/tools/fileops/container.d.ts +8 -0
- package/dist/tools/fileops/container.js +11 -0
- package/dist/tools/fileops/delete-file.js +4 -2
- package/dist/tools/fileops/get-file-content.js +45 -7
- package/dist/tools/fileops/list-files.js +5 -2
- package/dist/tools/fileops/search-files.js +4 -1
- package/dist/tools/fileops/upload-file.js +5 -2
- package/dist/tools/index.js +0 -2
- package/dist/tools/model-building/check-cube-rule.js +3 -2
- package/dist/tools/model-building/clear-cube.js +16 -27
- package/dist/tools/model-building/set-cube-rules.js +13 -20
- package/dist/tools/model-building/unload-cube.js +1 -1
- package/dist/tools/operations/get-cube-stats.js +1 -1
- package/dist/tools/operations/get-transaction-log.js +1 -1
- package/dist/tools/operations/list-error-logs.js +27 -2
- package/dist/tools/schemas/items-fileops.d.ts +4 -0
- package/dist/tools/schemas/items-fileops.js +1 -0
- package/dist/tools/security/list-clients.js +5 -5
- package/dist/tools/security/list-groups.js +1 -1
- package/dist/tools/subsets/create-subset.js +9 -5
- package/dist/tools/subsets/delete-subset.js +8 -3
- package/dist/tools/subsets/update-subset.js +9 -9
- package/dist/tools/ti-development/diff-process-with-file.js +4 -29
- package/dist/tools/ti-development/diff-processes.js +9 -0
- package/dist/tools/ti-development/export-process-to-git.js +9 -7
- package/dist/tools/ti-development/export-process-to-pro.js +15 -8
- package/dist/tools/ti-development/import-pro-file.js +10 -21
- package/dist/tools/ti-development/import-process-from-git.js +8 -18
- package/dist/tools/ti-development/install-pro-bundle.js +2 -2
- package/dist/tools/ti-development/upsert-process.js +26 -5
- package/npm-shrinkwrap.json +245 -9
- package/package.json +10 -5
- package/dist/tools/dimension-management/move-element.d.ts +0 -2
- package/dist/tools/dimension-management/move-element.js +0 -28
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
import { TM1Error, TM1ErrorCode } from "../../types.js";
|
|
9
9
|
import { freeCellset, transformCellsetResponse } from "./cellset-transform.js";
|
|
10
10
|
import { rethrowIfSystemicOrDenied } from "./fallback.js";
|
|
11
|
-
|
|
12
|
-
const enc = (s) => encodeURIComponent(String(s).replace(/'/g, "''"));
|
|
11
|
+
import { odataKey } from "./odata-page.js";
|
|
13
12
|
export class ViewService {
|
|
14
13
|
http;
|
|
15
14
|
constructor(http) {
|
|
@@ -31,7 +30,7 @@ export class ViewService {
|
|
|
31
30
|
// not an optimisation.
|
|
32
31
|
const fetchScope = async (collection, isPrivate) => {
|
|
33
32
|
try {
|
|
34
|
-
const res = await this.http.request("GET", `/api/v1/Cubes('${
|
|
33
|
+
const res = await this.http.request("GET", `/api/v1/Cubes('${odataKey(cubeName)}')/${collection}?$select=Name,MDX`);
|
|
35
34
|
return res.value.map((v) => ({
|
|
36
35
|
name: v.Name,
|
|
37
36
|
mdx: v.MDX,
|
|
@@ -70,7 +69,7 @@ export class ViewService {
|
|
|
70
69
|
cellsExpand += `;$skip=${skip}`;
|
|
71
70
|
cellsExpand += ")";
|
|
72
71
|
const axesExpand = "Axes($expand=Tuples($expand=Members($select=Name;$expand=Hierarchy($select=Name))))";
|
|
73
|
-
const path = `/api/v1/Cubes('${
|
|
72
|
+
const path = `/api/v1/Cubes('${odataKey(cubeName)}')/Views('${odataKey(viewName)}')/tm1.Execute?$expand=${cellsExpand},${axesExpand}`;
|
|
74
73
|
const response = await this.http.request("POST", path, undefined, opts);
|
|
75
74
|
try {
|
|
76
75
|
const mdxResult = transformCellsetResponse(response);
|
|
@@ -97,7 +96,7 @@ export class ViewService {
|
|
|
97
96
|
*/
|
|
98
97
|
async getDefinition(cubeName, viewName, isPrivate) {
|
|
99
98
|
const fetchBase = async (segment) => {
|
|
100
|
-
const path = `/api/v1/Cubes('${
|
|
99
|
+
const path = `/api/v1/Cubes('${odataKey(cubeName)}')/${segment}('${odataKey(viewName)}')?$select=Name,MDX`;
|
|
101
100
|
return this.http.request("GET", path);
|
|
102
101
|
};
|
|
103
102
|
const order = isPrivate === true
|
|
@@ -155,7 +154,7 @@ export class ViewService {
|
|
|
155
154
|
`Columns/${subsetExpand}`,
|
|
156
155
|
`Rows/${subsetExpand}`,
|
|
157
156
|
].join(",");
|
|
158
|
-
const nativePath = `/api/v1/Cubes('${
|
|
157
|
+
const nativePath = `/api/v1/Cubes('${odataKey(cubeName)}')/${resolvedSeg}('${odataKey(viewName)}')/tm1.NativeView?$expand=${nativeExpand}`;
|
|
159
158
|
let native;
|
|
160
159
|
try {
|
|
161
160
|
native = await this.http.request("GET", nativePath);
|
|
@@ -202,7 +201,7 @@ export class ViewService {
|
|
|
202
201
|
* POST /api/v1/Cubes('{c}')/Views with @odata.type = #ibm.tm1.api.v1.MDXView
|
|
203
202
|
*/
|
|
204
203
|
async createMdx(cubeName, viewName, mdx) {
|
|
205
|
-
await this.http.request("POST", `/api/v1/Cubes('${
|
|
204
|
+
await this.http.request("POST", `/api/v1/Cubes('${odataKey(cubeName)}')/Views`, {
|
|
206
205
|
"@odata.type": "#ibm.tm1.api.v1.MDXView",
|
|
207
206
|
Name: viewName,
|
|
208
207
|
MDX: mdx,
|
|
@@ -216,7 +215,7 @@ export class ViewService {
|
|
|
216
215
|
* POST /api/v1/Cubes('{c}')/Views with @odata.type = #ibm.tm1.api.v1.NativeView
|
|
217
216
|
*/
|
|
218
217
|
async createNative(cubeName, viewName, spec) {
|
|
219
|
-
const hierPath = (a) => `Dimensions('${
|
|
218
|
+
const hierPath = (a) => `Dimensions('${odataKey(a.dimension)}')/Hierarchies('${odataKey(a.hierarchy ?? a.dimension)}')`;
|
|
220
219
|
const mapAxis = (a) => {
|
|
221
220
|
const sources = [a.subset, a.expression, a.elements].filter((s) => s !== undefined);
|
|
222
221
|
if (sources.length !== 1) {
|
|
@@ -229,7 +228,7 @@ export class ViewService {
|
|
|
229
228
|
}
|
|
230
229
|
if (a.subset !== undefined) {
|
|
231
230
|
return {
|
|
232
|
-
"Subset@odata.bind": `${hierPath(a)}/Subsets('${
|
|
231
|
+
"Subset@odata.bind": `${hierPath(a)}/Subsets('${odataKey(a.subset)}')`,
|
|
233
232
|
};
|
|
234
233
|
}
|
|
235
234
|
if (a.expression !== undefined) {
|
|
@@ -243,7 +242,7 @@ export class ViewService {
|
|
|
243
242
|
return {
|
|
244
243
|
Subset: {
|
|
245
244
|
"Hierarchy@odata.bind": hierPath(a),
|
|
246
|
-
"Elements@odata.bind": (a.elements ?? []).map((e) => `${hierPath(a)}/Elements('${
|
|
245
|
+
"Elements@odata.bind": (a.elements ?? []).map((e) => `${hierPath(a)}/Elements('${odataKey(e)}')`),
|
|
247
246
|
},
|
|
248
247
|
};
|
|
249
248
|
};
|
|
@@ -260,7 +259,7 @@ export class ViewService {
|
|
|
260
259
|
}
|
|
261
260
|
const axis = mapAxis(t);
|
|
262
261
|
axis["Selected@odata.bind"] =
|
|
263
|
-
`${hierPath(t)}/Elements('${
|
|
262
|
+
`${hierPath(t)}/Elements('${odataKey(t.selected)}')`;
|
|
264
263
|
return axis;
|
|
265
264
|
};
|
|
266
265
|
const body = {
|
|
@@ -274,14 +273,14 @@ export class ViewService {
|
|
|
274
273
|
};
|
|
275
274
|
if (spec.formatString !== undefined)
|
|
276
275
|
body.FormatString = spec.formatString;
|
|
277
|
-
await this.http.request("POST", `/api/v1/Cubes('${
|
|
276
|
+
await this.http.request("POST", `/api/v1/Cubes('${odataKey(cubeName)}')/Views`, body);
|
|
278
277
|
}
|
|
279
278
|
/**
|
|
280
279
|
* Delete a public view from a cube.
|
|
281
280
|
* DELETE /api/v1/Cubes('{c}')/Views('{v}')
|
|
282
281
|
*/
|
|
283
282
|
async delete(cubeName, viewName) {
|
|
284
|
-
await this.http.request("DELETE", `/api/v1/Cubes('${
|
|
283
|
+
await this.http.request("DELETE", `/api/v1/Cubes('${odataKey(cubeName)}')/Views('${odataKey(viewName)}')`);
|
|
285
284
|
}
|
|
286
285
|
}
|
|
287
286
|
//# sourceMappingURL=view-service.js.map
|
package/dist/tm1-client.js
CHANGED
|
@@ -6,6 +6,7 @@ import { DimensionService } from "./tm1-client/services/dimension-service.js";
|
|
|
6
6
|
import { HierarchyService } from "./tm1-client/services/hierarchy-service.js";
|
|
7
7
|
import { ElementService } from "./tm1-client/services/element-service.js";
|
|
8
8
|
import { CellService } from "./tm1-client/services/cell-service.js";
|
|
9
|
+
import { DimensionOrderCache } from "./tm1-client/services/dimension-order.js";
|
|
9
10
|
import { ViewService } from "./tm1-client/services/view-service.js";
|
|
10
11
|
import { SubsetService } from "./tm1-client/services/subset-service.js";
|
|
11
12
|
import { ProcessService } from "./tm1-client/services/process-service.js";
|
|
@@ -53,10 +54,12 @@ export class TM1Client {
|
|
|
53
54
|
// http.ts — now the coupling is a visible, idempotent call at construction.
|
|
54
55
|
registerCallgraphCacheInvalidation();
|
|
55
56
|
this.batch = new BatchService(this.http);
|
|
56
|
-
|
|
57
|
+
// One dimension-order cache per connection, shared by cubes and cells.
|
|
58
|
+
const dimOrder = new DimensionOrderCache(this.http);
|
|
59
|
+
this.cubes = new CubeService(this.http, dimOrder);
|
|
57
60
|
this.dimensions = new DimensionService(this.http);
|
|
58
61
|
this.hierarchies = new HierarchyService(this.http);
|
|
59
|
-
this.cells = new CellService(this.http);
|
|
62
|
+
this.cells = new CellService(this.http, dimOrder);
|
|
60
63
|
this.views = new ViewService(this.http);
|
|
61
64
|
this.subsets = new SubsetService(this.http);
|
|
62
65
|
// ElementService depends on CellService and BatchService — both MUST be
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { scanForDeprecatedTi, } from "../../lib/v12-compat/scanner.js";
|
|
3
|
-
import { READ_ONLY
|
|
3
|
+
import { READ_ONLY } from "../annotations.js";
|
|
4
4
|
import { V12ReadinessResultSchema } from "../schemas/items.js";
|
|
5
5
|
import { defineTool } from "../define-tool.js";
|
|
6
6
|
const RULESET_SOURCE = "vscode-tm1-ti@cf73b93 / tiSignatures.ts (synced 2026-05-12)";
|
|
@@ -23,9 +23,9 @@ export const registerCheckV12Readiness = defineTool({
|
|
|
23
23
|
description: [
|
|
24
24
|
"Static gap-analysis against the TM1 / Planning Analytics v12 (Cloud Native) deprecation list.",
|
|
25
25
|
"Scans every TI process section (prolog/metadata/data/epilog) and every cube rule for functions removed in v12, returning findings with severity, location, and migration hint per occurrence.",
|
|
26
|
-
"Read-only, two
|
|
26
|
+
"Read-only: one bulk request per scope, so two for the default (processes and cube rules) and one when scope names only one of them. Not exhaustive: covers syntactic deprecations only (runtime/semantic differences require manual review).",
|
|
27
27
|
],
|
|
28
|
-
annotations:
|
|
28
|
+
annotations: READ_ONLY,
|
|
29
29
|
output: V12ReadinessResultSchema,
|
|
30
30
|
input: {
|
|
31
31
|
scope: z
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { withToolHint } from "../error-format.js";
|
|
3
3
|
import { CheckFeedersResultSchema } from "../schemas/items.js";
|
|
4
|
-
import { READ_ONLY
|
|
4
|
+
import { READ_ONLY } from "../annotations.js";
|
|
5
5
|
import { defineTool } from "../define-tool.js";
|
|
6
6
|
export const registerCheckFeeders = defineTool({
|
|
7
7
|
name: "tm1_check_feeders",
|
|
8
8
|
description: [
|
|
9
9
|
"Check the feeders of a cell: verifies feeder coverage for the cells underlying this cell and returns the problematic ones with a fed flag — fed=false marks a broken or missing feeder (the classic cause of empty consolidated/rule cells). An empty result means no feeder problems were detected (live-verified on 11.8: fully-fed areas return []).",
|
|
10
10
|
"Per-cell runtime check; complements tm1_audit_feeders (static rule analysis).",
|
|
11
|
-
"Elements
|
|
12
|
-
"
|
|
11
|
+
"Elements are given in cube dimension order (discover with tm1_list_cubes). Write Hierarchy:Element for an alternate hierarchy; the first colon splits, so in dimension Region 'Region:A:B' is element 'A:B' of the default one.",
|
|
12
|
+
"Related: tm1_trace_feeders (statements involved), tm1_trace_cell_calculation (why has this cell value X).",
|
|
13
13
|
],
|
|
14
|
-
annotations:
|
|
14
|
+
annotations: READ_ONLY,
|
|
15
15
|
output: CheckFeedersResultSchema,
|
|
16
16
|
input: {
|
|
17
17
|
cubeName: z.string().describe("Name of the TM1 cube"),
|
|
18
18
|
elements: z
|
|
19
19
|
.array(z.string())
|
|
20
|
-
.describe("Element names for each dimension of the cube, in cube dimension order"),
|
|
20
|
+
.describe("Element names for each dimension of the cube, in cube dimension order. Prefix with a hierarchy to leave the default one: 'AltHier:Elem'."),
|
|
21
21
|
timeoutMs: z
|
|
22
22
|
.number()
|
|
23
23
|
.int()
|
|
@@ -30,7 +30,7 @@ export const registerCheckFeeders = defineTool({
|
|
|
30
30
|
const fedCells = await withToolHint(tm1Client.cells.checkFeeders(cubeName, elements, {
|
|
31
31
|
signal: extra?.signal,
|
|
32
32
|
...(timeoutMs ? { timeoutMs } : {}),
|
|
33
|
-
}), `CheckFeeders failed for cube '${cubeName}'. Verify dimension order/elements via tm1_list_cubes
|
|
33
|
+
}), `CheckFeeders failed for cube '${cubeName}'. Verify dimension order/elements via tm1_list_cubes.`);
|
|
34
34
|
return {
|
|
35
35
|
content: [
|
|
36
36
|
{
|
|
@@ -4,41 +4,48 @@ import { rethrowIfSystemic } from "../../tm1-client/services/fallback.js";
|
|
|
4
4
|
import { READ_ONLY } from "../annotations.js";
|
|
5
5
|
import { WritableCoordsResultSchema } from "../schemas/items.js";
|
|
6
6
|
import { defineTool } from "../define-tool.js";
|
|
7
|
+
import { memberRef } from "./member-ref.js";
|
|
7
8
|
import { dimensionCountMismatch } from "../../lib/coordinate-error.js";
|
|
8
9
|
import { bindLeftOutSandbox, resolveCellAddress, } from "../../lib/cell-address.js";
|
|
9
10
|
export const registerCheckWritableCoords = defineTool({
|
|
10
11
|
name: "tm1_check_writable_coords",
|
|
11
|
-
description: "Pre-flight check before CellPutN/CellPutS. Verifies (1) every coord element exists, (2) every element is N-Level (
|
|
12
|
+
description: "Pre-flight check before CellPutN/CellPutS. Verifies (1) every coord element exists, (2) every element is N-Level (whether the server accepts a write to a Consolidated element depends on the account's rights; tm1_write_cells refuses one either way), and (3) whether the target cube has rules that may overlap the coord. Returns per-coord status + a rule-overlap warning. Use before writing cells in a TI process or via tm1_write_cells; pass the same dimensions list as the write to check exactly the cell it will address.",
|
|
12
13
|
annotations: READ_ONLY,
|
|
13
14
|
output: WritableCoordsResultSchema,
|
|
14
15
|
input: {
|
|
15
16
|
cubeName: z.string().describe("Target cube name"),
|
|
16
17
|
coords: z
|
|
17
18
|
.array(z.string())
|
|
18
|
-
.describe("Element
|
|
19
|
+
.describe("Element per dimension, in cube dimension order — or in the order of `dimensions` when given. Accepts the same forms as tm1_write_cells: a bare name (default hierarchy) or [Dimension].[Hierarchy].[Element]. Sandboxes may be left out (bound to Base)."),
|
|
19
20
|
dimensions: z
|
|
20
21
|
.array(z.string())
|
|
21
22
|
.optional()
|
|
22
|
-
.describe("
|
|
23
|
+
.describe("Optional: the same dimension list tm1_write_cells takes (any order, Sandboxes may be left out and is then bound to Base), so this checks exactly the cell the write will address."),
|
|
23
24
|
},
|
|
24
|
-
handler: async ({ cubeName, coords:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
handler: async ({ cubeName, coords: givenCoords, dimensions }, tm1Client) => {
|
|
26
|
+
// Dimension order only — cached per connection — instead of listing
|
|
27
|
+
// every cube with its dimensions to find this one.
|
|
28
|
+
let dims;
|
|
29
|
+
try {
|
|
30
|
+
dims = await tm1Client.cubes.getDimensionNames(cubeName);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
if (e instanceof TM1Error && e.code === TM1ErrorCode.NOT_FOUND) {
|
|
34
|
+
throw new TM1Error({
|
|
35
|
+
code: TM1ErrorCode.NOT_FOUND,
|
|
36
|
+
message: `Cube '${cubeName}' not found`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
throw e;
|
|
32
40
|
}
|
|
33
|
-
|
|
34
|
-
let coords = given;
|
|
41
|
+
let coords = givenCoords;
|
|
35
42
|
let sandboxDefaulted;
|
|
36
43
|
if (dimensions !== undefined) {
|
|
37
|
-
if (
|
|
38
|
-
throw dimensionCountMismatch(cubeName, dimensions,
|
|
44
|
+
if (givenCoords.length !== dimensions.length) {
|
|
45
|
+
throw dimensionCountMismatch(cubeName, dimensions, givenCoords);
|
|
39
46
|
}
|
|
40
|
-
const address = resolveCellAddress(cubeName, dims, dimensions);
|
|
41
|
-
coords = address.toCubeOrder(
|
|
47
|
+
const address = resolveCellAddress(cubeName, dims, dimensions, "Nothing was checked.");
|
|
48
|
+
coords = address.toCubeOrder(givenCoords);
|
|
42
49
|
sandboxDefaulted = address.sandboxDefaulted;
|
|
43
50
|
}
|
|
44
51
|
else {
|
|
@@ -54,9 +61,12 @@ export const registerCheckWritableCoords = defineTool({
|
|
|
54
61
|
const checks = await Promise.all(dims.map(async (dim, idx) => {
|
|
55
62
|
// coords.length === dims.length is guarded above
|
|
56
63
|
const element = coords[idx];
|
|
64
|
+
// Same reading of `[Dim].[Hier].[Elem]` as tm1_write_cells, so the
|
|
65
|
+
// check probes the hierarchy the write would hit. One keyed lookup
|
|
66
|
+
// per coordinate instead of loading every hierarchy of the cube.
|
|
67
|
+
const ref = memberRef(dim, element);
|
|
57
68
|
try {
|
|
58
|
-
const
|
|
59
|
-
const el = hier.elements.find((e) => e.name.toLowerCase() === element.toLowerCase());
|
|
69
|
+
const el = await tm1Client.elements.getType(ref.dimension, ref.hierarchy, ref.element);
|
|
60
70
|
if (!el) {
|
|
61
71
|
return {
|
|
62
72
|
dimension: dim,
|
|
@@ -66,12 +76,13 @@ export const registerCheckWritableCoords = defineTool({
|
|
|
66
76
|
isNLevel: false,
|
|
67
77
|
};
|
|
68
78
|
}
|
|
79
|
+
const type = el.type;
|
|
69
80
|
return {
|
|
70
81
|
dimension: dim,
|
|
71
|
-
element: el.name,
|
|
82
|
+
element: element === ref.element ? el.name : element,
|
|
72
83
|
exists: true,
|
|
73
|
-
type
|
|
74
|
-
isNLevel:
|
|
84
|
+
type,
|
|
85
|
+
isNLevel: type !== "Consolidated",
|
|
75
86
|
};
|
|
76
87
|
}
|
|
77
88
|
catch (e) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { PAGINATION_SCHEMA } from "../pagination.js";
|
|
2
|
+
import { PAGINATION_SCHEMA, UNBOUNDED_MAX_ITEMS } from "../pagination.js";
|
|
3
3
|
import { FORMAT_SCHEMA, mdEscape, payloadResponse } from "../format.js";
|
|
4
4
|
import { withToolHint } from "../error-format.js";
|
|
5
5
|
import { clipAxesToWindow } from "../../tm1-client/services/cellset-transform.js";
|
|
@@ -71,7 +71,7 @@ export const registerExecuteMdx = defineTool({
|
|
|
71
71
|
name: "tm1_execute_mdx",
|
|
72
72
|
description: [
|
|
73
73
|
"Execute an MDX query against the TM1 server and return structured cell data with axes (page-envelope shape consistent with list_*).",
|
|
74
|
-
"format='markdown' renders a pivot grid (2 axes
|
|
74
|
+
"format='markdown' renders a pivot grid (2 axes; set fetchAll=true to get up to 5000 cells in one grid) or a flat coordinate table; 'json' (default) returns the structured envelope.",
|
|
75
75
|
"Related: tm1_create_mdx_view to persist a query as a public view, tm1_sample_cells for cheap sparsity probe, tm1_get_cell_value for a single coordinate.",
|
|
76
76
|
],
|
|
77
77
|
annotations: READ_ONLY,
|
|
@@ -90,8 +90,11 @@ export const registerExecuteMdx = defineTool({
|
|
|
90
90
|
},
|
|
91
91
|
handler: async ({ mdx, limit, offset, fetchAll, format, timeoutMs }, tm1Client, extra) => {
|
|
92
92
|
const all = fetchAll === true || limit === 0;
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
// "All" is the documented first slab of UNBOUNDED_MAX_ITEMS, pushed down
|
|
94
|
+
// to TM1 as $top — not the whole cellset. has_more/next_offset then say
|
|
95
|
+
// where to resume, exactly as for any other page.
|
|
96
|
+
const top = all ? UNBOUNDED_MAX_ITEMS : limit;
|
|
97
|
+
const skip = all ? 0 : offset;
|
|
95
98
|
const result = await withToolHint(tm1Client.cells.executeMdx(mdx, top, skip, {
|
|
96
99
|
signal: extra?.signal,
|
|
97
100
|
...(timeoutMs ? { timeoutMs } : {}),
|
|
@@ -99,12 +102,10 @@ export const registerExecuteMdx = defineTool({
|
|
|
99
102
|
const total = result.totalCellCount;
|
|
100
103
|
const count = result.cells.length;
|
|
101
104
|
const off = all ? 0 : offset;
|
|
102
|
-
const has_more =
|
|
105
|
+
const has_more = off + count < total;
|
|
103
106
|
// Clip axes to the returned cell page so a capped read over a huge view
|
|
104
|
-
// doesn't ship the full tuple list.
|
|
105
|
-
const { axes, clipped } =
|
|
106
|
-
? { axes: result.axes, clipped: false }
|
|
107
|
-
: clipAxesToWindow(result.axes, count, off);
|
|
107
|
+
// doesn't ship the full tuple list.
|
|
108
|
+
const { axes, clipped } = clipAxesToWindow(result.axes, count, off);
|
|
108
109
|
const envelope = {
|
|
109
110
|
axes,
|
|
110
111
|
total,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { PAGINATION_SCHEMA } from "../pagination.js";
|
|
2
|
+
import { PAGINATION_SCHEMA, UNBOUNDED_MAX_ITEMS } from "../pagination.js";
|
|
3
3
|
import { FORMAT_SCHEMA, payloadResponse } from "../format.js";
|
|
4
4
|
import { renderMdxMarkdown } from "./execute-mdx.js";
|
|
5
5
|
import { clipAxesToWindow } from "../../tm1-client/services/cellset-transform.js";
|
|
@@ -10,7 +10,7 @@ export const registerGetView = defineTool({
|
|
|
10
10
|
name: "tm1_get_view",
|
|
11
11
|
description: [
|
|
12
12
|
"Execute a named cube view and return structured cell data with axes (page-envelope shape consistent with tm1_execute_mdx).",
|
|
13
|
-
"Cells paginate by default so wide/tall views don't flood context; fetchAll=true
|
|
13
|
+
"Cells paginate by default so wide/tall views don't flood context; fetchAll=true returns up to 5000 cells in one call; has_more/next_offset tell where to resume.",
|
|
14
14
|
"format='markdown' renders a pivot grid (2 axes, full result) or a flat coordinate table; 'json' (default) returns the structured envelope.",
|
|
15
15
|
],
|
|
16
16
|
annotations: READ_ONLY,
|
|
@@ -23,20 +23,21 @@ export const registerGetView = defineTool({
|
|
|
23
23
|
},
|
|
24
24
|
handler: async ({ cubeName, viewName, limit, offset, fetchAll, format }, tm1Client, extra) => {
|
|
25
25
|
const all = fetchAll === true || limit === 0;
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// "All" is the documented first slab of UNBOUNDED_MAX_ITEMS, pushed down
|
|
27
|
+
// to TM1 as $top — not the whole cellset. has_more/next_offset then say
|
|
28
|
+
// where to resume, exactly as for any other page.
|
|
29
|
+
const top = all ? UNBOUNDED_MAX_ITEMS : limit;
|
|
30
|
+
const skip = all ? 0 : offset;
|
|
28
31
|
const result = await tm1Client.views.getView(cubeName, viewName, top, skip, {
|
|
29
32
|
signal: extra?.signal,
|
|
30
33
|
});
|
|
31
34
|
const total = result.totalCellCount;
|
|
32
35
|
const count = result.cells.length;
|
|
33
36
|
const off = all ? 0 : offset;
|
|
34
|
-
const has_more =
|
|
37
|
+
const has_more = off + count < total;
|
|
35
38
|
// Clip axes to the returned cell page so a capped read over a tall view
|
|
36
|
-
// doesn't ship the full tuple list.
|
|
37
|
-
const { axes, clipped } =
|
|
38
|
-
? { axes: result.axes, clipped: false }
|
|
39
|
-
: clipAxesToWindow(result.axes, count, off);
|
|
39
|
+
// doesn't ship the full tuple list.
|
|
40
|
+
const { axes, clipped } = clipAxesToWindow(result.axes, count, off);
|
|
40
41
|
const envelope = {
|
|
41
42
|
cubeName,
|
|
42
43
|
viewName,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an element reference points, in the four shapes cell-service's
|
|
3
|
+
* `qualifyWriteMember` accepts: a bare name, `[Element]`,
|
|
4
|
+
* `[Dimension].[Element]` (default hierarchy) and the fully qualified
|
|
5
|
+
* `[Dimension].[Hierarchy].[Element]`.
|
|
6
|
+
*
|
|
7
|
+
* Both sides must read a reference the same way. Where this function and the
|
|
8
|
+
* writer disagree, the guard probes one coordinate and the write hits another.
|
|
9
|
+
*/
|
|
10
|
+
export declare function memberRef(dimension: string, element: string): {
|
|
11
|
+
dimension: string;
|
|
12
|
+
hierarchy: string;
|
|
13
|
+
element: string;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=member-ref.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split a bracketed MDX reference into its parts, `]]` unescaped back to `]`.
|
|
3
|
+
*
|
|
4
|
+
* Hand-parsed rather than matched: a regex over `[^\]]+` cannot see past an
|
|
5
|
+
* escaped `]]`, so every name containing a `]` would fall out of the guard
|
|
6
|
+
* and be written unchecked.
|
|
7
|
+
*
|
|
8
|
+
* Returns null for anything that is not a well-formed bracketed reference —
|
|
9
|
+
* the caller then treats the string as a bare element name, which is what the
|
|
10
|
+
* writer does with it too.
|
|
11
|
+
*/
|
|
12
|
+
function mdxParts(ref) {
|
|
13
|
+
const parts = [];
|
|
14
|
+
let i = 0;
|
|
15
|
+
while (i < ref.length) {
|
|
16
|
+
if (ref[i] !== "[")
|
|
17
|
+
return null;
|
|
18
|
+
i++;
|
|
19
|
+
let name = "";
|
|
20
|
+
for (;;) {
|
|
21
|
+
if (i >= ref.length)
|
|
22
|
+
return null;
|
|
23
|
+
if (ref[i] === "]") {
|
|
24
|
+
if (ref[i + 1] === "]") {
|
|
25
|
+
name += "]";
|
|
26
|
+
i += 2;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
i++;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
name += ref[i];
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
parts.push(name);
|
|
36
|
+
if (i === ref.length)
|
|
37
|
+
return parts;
|
|
38
|
+
if (ref[i] !== ".")
|
|
39
|
+
return null;
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Where an element reference points, in the four shapes cell-service's
|
|
46
|
+
* `qualifyWriteMember` accepts: a bare name, `[Element]`,
|
|
47
|
+
* `[Dimension].[Element]` (default hierarchy) and the fully qualified
|
|
48
|
+
* `[Dimension].[Hierarchy].[Element]`.
|
|
49
|
+
*
|
|
50
|
+
* Both sides must read a reference the same way. Where this function and the
|
|
51
|
+
* writer disagree, the guard probes one coordinate and the write hits another.
|
|
52
|
+
*/
|
|
53
|
+
export function memberRef(dimension, element) {
|
|
54
|
+
const parts = element.startsWith("[") ? mdxParts(element) : null;
|
|
55
|
+
if (parts?.length === 3) {
|
|
56
|
+
return { dimension: parts[0], hierarchy: parts[1], element: parts[2] };
|
|
57
|
+
}
|
|
58
|
+
if (parts?.length === 2) {
|
|
59
|
+
return { dimension: parts[0], hierarchy: parts[0], element: parts[1] };
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
dimension,
|
|
63
|
+
hierarchy: dimension,
|
|
64
|
+
element: parts?.length === 1 ? parts[0] : element,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=member-ref.js.map
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { withToolHint } from "../error-format.js";
|
|
3
3
|
import { CalculationTraceResultSchema } from "../schemas/items.js";
|
|
4
|
-
import { READ_ONLY
|
|
4
|
+
import { READ_ONLY } from "../annotations.js";
|
|
5
5
|
import { defineTool } from "../define-tool.js";
|
|
6
6
|
export const registerTraceCellCalculation = defineTool({
|
|
7
7
|
name: "tm1_trace_cell_calculation",
|
|
8
8
|
description: [
|
|
9
9
|
"Trace how a cell value is calculated: recursive component tree with per-component type (Consolidation/Rule/Simple), status (Null/Data/Error), value, and the rule statements that populate it — answers 'why is this cell X / empty?'.",
|
|
10
10
|
"The tree is truncated client-side via maxDepth/maxComponents; truncated=true marks cut branches (re-run with the branch tuple as new start cell to drill deeper).",
|
|
11
|
-
"Elements
|
|
12
|
-
"
|
|
11
|
+
"Elements are given in cube dimension order (discover with tm1_list_cubes). Write Hierarchy:Element for an alternate hierarchy; the first colon splits, so in dimension Region 'Region:A:B' is element 'A:B' of the default one.",
|
|
12
|
+
"Related: tm1_check_feeders / tm1_trace_feeders for feeder issues, tm1_get_cube_rules for the full rule text.",
|
|
13
13
|
],
|
|
14
|
-
annotations:
|
|
14
|
+
annotations: READ_ONLY,
|
|
15
15
|
output: CalculationTraceResultSchema,
|
|
16
16
|
input: {
|
|
17
17
|
cubeName: z.string().describe("Name of the TM1 cube"),
|
|
18
18
|
elements: z
|
|
19
19
|
.array(z.string())
|
|
20
|
-
.describe("Element names for each dimension of the cube, in cube dimension order"),
|
|
20
|
+
.describe("Element names for each dimension of the cube, in cube dimension order. Prefix with a hierarchy to leave the default one: 'AltHier:Elem'."),
|
|
21
21
|
maxDepth: z
|
|
22
22
|
.number()
|
|
23
23
|
.int()
|
|
@@ -44,7 +44,7 @@ export const registerTraceCellCalculation = defineTool({
|
|
|
44
44
|
const tree = await withToolHint(tm1Client.cells.traceCellCalculation(cubeName, elements, maxDepth, maxComponents, {
|
|
45
45
|
signal: extra?.signal,
|
|
46
46
|
...(timeoutMs ? { timeoutMs } : {}),
|
|
47
|
-
}), `TraceCellCalculation failed for cube '${cubeName}'. Verify dimension order/elements via tm1_list_cubes
|
|
47
|
+
}), `TraceCellCalculation failed for cube '${cubeName}'. Verify dimension order/elements via tm1_list_cubes.`);
|
|
48
48
|
return {
|
|
49
49
|
content: [{ type: "text", text: JSON.stringify(tree) }],
|
|
50
50
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { withToolHint } from "../error-format.js";
|
|
3
|
-
import { READ_ONLY
|
|
3
|
+
import { READ_ONLY } from "../annotations.js";
|
|
4
4
|
import { TraceFeedersResultSchema } from "../schemas/items.js";
|
|
5
5
|
import { defineTool } from "../define-tool.js";
|
|
6
6
|
export const registerTraceFeeders = defineTool({
|
|
@@ -8,16 +8,16 @@ export const registerTraceFeeders = defineTool({
|
|
|
8
8
|
description: [
|
|
9
9
|
"Trace the feeders of a cell: returns the cells this cell feeds plus the feeder statements involved — answers 'which feeder statement fires from this cell, and where to'.",
|
|
10
10
|
"Use when a rule cell stays empty under SKIPCHECK: trace the source cell to see whether its feeder reaches the target.",
|
|
11
|
-
"Elements
|
|
12
|
-
"
|
|
11
|
+
"Elements are given in cube dimension order (discover with tm1_list_cubes). Write Hierarchy:Element for an alternate hierarchy; the first colon splits, so in dimension Region 'Region:A:B' is element 'A:B' of the default one.",
|
|
12
|
+
"Related: tm1_check_feeders (fed/unfed flags), tm1_audit_feeders (static analysis).",
|
|
13
13
|
],
|
|
14
|
-
annotations:
|
|
14
|
+
annotations: READ_ONLY,
|
|
15
15
|
output: TraceFeedersResultSchema,
|
|
16
16
|
input: {
|
|
17
17
|
cubeName: z.string().describe("Name of the TM1 cube"),
|
|
18
18
|
elements: z
|
|
19
19
|
.array(z.string())
|
|
20
|
-
.describe("Element names for each dimension of the cube, in cube dimension order"),
|
|
20
|
+
.describe("Element names for each dimension of the cube, in cube dimension order. Prefix with a hierarchy to leave the default one: 'AltHier:Elem'."),
|
|
21
21
|
timeoutMs: z
|
|
22
22
|
.number()
|
|
23
23
|
.int()
|
|
@@ -30,7 +30,7 @@ export const registerTraceFeeders = defineTool({
|
|
|
30
30
|
const result = await withToolHint(tm1Client.cells.traceFeeders(cubeName, elements, {
|
|
31
31
|
signal: extra?.signal,
|
|
32
32
|
...(timeoutMs ? { timeoutMs } : {}),
|
|
33
|
-
}), `TraceFeeders failed for cube '${cubeName}'. Verify dimension order/elements via tm1_list_cubes
|
|
33
|
+
}), `TraceFeeders failed for cube '${cubeName}'. Verify dimension order/elements via tm1_list_cubes.`);
|
|
34
34
|
return {
|
|
35
35
|
content: [
|
|
36
36
|
{
|