@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.
Files changed (116) hide show
  1. package/CHANGELOG.md +101 -1
  2. package/README.md +9 -6
  3. package/dist/config.d.ts +10 -1
  4. package/dist/config.js +40 -22
  5. package/dist/connections.d.ts +30 -2
  6. package/dist/connections.js +94 -21
  7. package/dist/http-transport.js +51 -4
  8. package/dist/index.js +6 -0
  9. package/dist/lib/callgraph/referenceIndex.js +2 -1
  10. package/dist/lib/callgraph/rulesLinter.d.ts +0 -19
  11. package/dist/lib/callgraph/rulesLinter.js +0 -590
  12. package/dist/lib/callgraph/tiParser.js +10 -5
  13. package/dist/lib/callgraph/tm1-adapter.d.ts +5 -1
  14. package/dist/lib/callgraph/tm1-adapter.js +41 -9
  15. package/dist/lib/callgraph/variableEnv.js +3 -2
  16. package/dist/lib/cell-address.d.ts +1 -1
  17. package/dist/lib/cell-address.js +2 -2
  18. package/dist/lib/complexity/antipatterns.js +3 -1
  19. package/dist/lib/complexity/comment-classifier.js +2 -1
  20. package/dist/lib/feeders/element-type-cache.js +4 -6
  21. package/dist/lib/naming/odata-filter.js +2 -1
  22. package/dist/lib/pro-parser.js +13 -4
  23. package/dist/lib/safe-regex.d.ts +0 -13
  24. package/dist/lib/safe-regex.js +40 -13
  25. package/dist/lib/sample-cells.js +1 -1
  26. package/dist/lib/ti-identifier.d.ts +11 -0
  27. package/dist/lib/ti-identifier.js +11 -0
  28. package/dist/lib/tm1-name.d.ts +9 -0
  29. package/dist/lib/tm1-name.js +9 -0
  30. package/dist/lib/v12-compat/deprecated-ti.js +2 -2
  31. package/dist/secrets-cli.d.ts +10 -0
  32. package/dist/secrets-cli.js +218 -0
  33. package/dist/secrets.d.ts +39 -0
  34. package/dist/secrets.js +128 -0
  35. package/dist/session-manager.d.ts +1 -0
  36. package/dist/session-manager.js +24 -0
  37. package/dist/tm1-client/connection/profile.js +2 -4
  38. package/dist/tm1-client/dispatcher.d.ts +1 -1
  39. package/dist/tm1-client/dispatcher.js +18 -6
  40. package/dist/tm1-client/http.d.ts +21 -0
  41. package/dist/tm1-client/http.js +39 -30
  42. package/dist/tm1-client/services/cell-service.d.ts +9 -7
  43. package/dist/tm1-client/services/cell-service.js +51 -25
  44. package/dist/tm1-client/services/chore-service.js +8 -9
  45. package/dist/tm1-client/services/cube-service.d.ts +14 -18
  46. package/dist/tm1-client/services/cube-service.js +51 -45
  47. package/dist/tm1-client/services/dimension-order.d.ts +13 -0
  48. package/dist/tm1-client/services/dimension-order.js +42 -0
  49. package/dist/tm1-client/services/dimension-service.js +4 -6
  50. package/dist/tm1-client/services/element-service.d.ts +35 -11
  51. package/dist/tm1-client/services/element-service.js +124 -60
  52. package/dist/tm1-client/services/file-service.d.ts +42 -6
  53. package/dist/tm1-client/services/file-service.js +225 -28
  54. package/dist/tm1-client/services/hierarchy-service.d.ts +19 -9
  55. package/dist/tm1-client/services/hierarchy-service.js +118 -12
  56. package/dist/tm1-client/services/monitoring-service.js +2 -5
  57. package/dist/tm1-client/services/odata-page.d.ts +6 -0
  58. package/dist/tm1-client/services/odata-page.js +8 -0
  59. package/dist/tm1-client/services/process-service.d.ts +1 -1
  60. package/dist/tm1-client/services/process-service.js +26 -26
  61. package/dist/tm1-client/services/security-service.js +8 -9
  62. package/dist/tm1-client/services/server-service.js +14 -15
  63. package/dist/tm1-client/services/subset-service.d.ts +23 -10
  64. package/dist/tm1-client/services/subset-service.js +82 -26
  65. package/dist/tm1-client/services/view-service.js +12 -13
  66. package/dist/tm1-client.js +5 -2
  67. package/dist/tools/analysis/check-v12-readiness.js +3 -3
  68. package/dist/tools/celldata/check-feeders.js +6 -6
  69. package/dist/tools/celldata/check-writable-coords.js +33 -22
  70. package/dist/tools/celldata/execute-mdx.js +10 -9
  71. package/dist/tools/celldata/get-view.js +10 -9
  72. package/dist/tools/celldata/member-ref.d.ts +15 -0
  73. package/dist/tools/celldata/member-ref.js +67 -0
  74. package/dist/tools/celldata/trace-cell-calculation.js +6 -6
  75. package/dist/tools/celldata/trace-feeders.js +6 -6
  76. package/dist/tools/celldata/write-cells.js +62 -12
  77. package/dist/tools/dimension-management/create-element-attribute.js +1 -1
  78. package/dist/tools/dimension-management/delete-hierarchy.js +3 -4
  79. package/dist/tools/dimension-management/get-element-attribute-values.js +7 -2
  80. package/dist/tools/dimension-management/list-element-attributes.js +1 -1
  81. package/dist/tools/dimension-management/update-element-attribute-value.js +8 -3
  82. package/dist/tools/dimension-management/update-element.js +14 -7
  83. package/dist/tools/fileops/container.d.ts +8 -0
  84. package/dist/tools/fileops/container.js +11 -0
  85. package/dist/tools/fileops/delete-file.js +4 -2
  86. package/dist/tools/fileops/get-file-content.js +45 -7
  87. package/dist/tools/fileops/list-files.js +5 -2
  88. package/dist/tools/fileops/search-files.js +4 -1
  89. package/dist/tools/fileops/upload-file.js +5 -2
  90. package/dist/tools/index.js +0 -2
  91. package/dist/tools/model-building/check-cube-rule.js +3 -2
  92. package/dist/tools/model-building/clear-cube.js +16 -27
  93. package/dist/tools/model-building/set-cube-rules.js +13 -20
  94. package/dist/tools/model-building/unload-cube.js +1 -1
  95. package/dist/tools/operations/get-cube-stats.js +1 -1
  96. package/dist/tools/operations/get-transaction-log.js +1 -1
  97. package/dist/tools/operations/list-error-logs.js +27 -2
  98. package/dist/tools/schemas/items-fileops.d.ts +4 -0
  99. package/dist/tools/schemas/items-fileops.js +1 -0
  100. package/dist/tools/security/list-clients.js +5 -5
  101. package/dist/tools/security/list-groups.js +1 -1
  102. package/dist/tools/subsets/create-subset.js +9 -5
  103. package/dist/tools/subsets/delete-subset.js +8 -3
  104. package/dist/tools/subsets/update-subset.js +9 -9
  105. package/dist/tools/ti-development/diff-process-with-file.js +4 -29
  106. package/dist/tools/ti-development/diff-processes.js +9 -0
  107. package/dist/tools/ti-development/export-process-to-git.js +9 -7
  108. package/dist/tools/ti-development/export-process-to-pro.js +15 -8
  109. package/dist/tools/ti-development/import-pro-file.js +10 -21
  110. package/dist/tools/ti-development/import-process-from-git.js +8 -18
  111. package/dist/tools/ti-development/install-pro-bundle.js +2 -2
  112. package/dist/tools/ti-development/upsert-process.js +26 -5
  113. package/npm-shrinkwrap.json +245 -9
  114. package/package.json +10 -5
  115. package/dist/tools/dimension-management/move-element.d.ts +0 -2
  116. 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
- // OData key encoder: double ' per OData literal rules, then percent-encode.
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('${enc(cubeName)}')/${collection}?$select=Name,MDX`);
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('${enc(cubeName)}')/Views('${enc(viewName)}')/tm1.Execute?$expand=${cellsExpand},${axesExpand}`;
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('${enc(cubeName)}')/${segment}('${enc(viewName)}')?$select=Name,MDX`;
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('${enc(cubeName)}')/${resolvedSeg}('${enc(viewName)}')/tm1.NativeView?$expand=${nativeExpand}`;
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('${enc(cubeName)}')/Views`, {
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('${enc(a.dimension)}')/Hierarchies('${enc(a.hierarchy ?? a.dimension)}')`;
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('${enc(a.subset)}')`,
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('${enc(e)}')`),
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('${enc(t.selected)}')`;
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('${enc(cubeName)}')/Views`, body);
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('${enc(cubeName)}')/Views('${enc(viewName)}')`);
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
@@ -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
- this.cubes = new CubeService(this.http);
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, withVersion } from "../annotations.js";
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 bulk REST calls. Not exhaustive: covers syntactic deprecations only (runtime/semantic differences require manual review).",
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: withVersion(READ_ONLY, "v11"),
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, withVersion } from "../annotations.js";
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 address the default hierarchy, in cube dimension order (discover with tm1_list_cubes).",
12
- "v11 only. Related: tm1_trace_feeders (statements involved), tm1_trace_cell_calculation (why has this cell value X).",
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: withVersion(READ_ONLY, "v11"),
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; alternate hierarchies are not supported. On v12 this action is unavailable.`);
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 (writes to Consolidated elements silent-fail), 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
+ 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 name per dimension: in cube dimension order, or in the order of dimensions when given. Sandboxes may be left out (bound to Base)."),
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("Dimension names for coords, any order — as passed to tm1_write_cells. Every cube dimension except Sandboxes (bound to Base) must be named."),
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: given, dimensions }, tm1Client) => {
25
- const cubes = await tm1Client.cubes.list();
26
- const cubeMeta = cubes.find((c) => c.name.toLowerCase() === cubeName.toLowerCase());
27
- if (!cubeMeta) {
28
- throw new TM1Error({
29
- code: TM1ErrorCode.NOT_FOUND,
30
- message: `Cube '${cubeName}' not found`,
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
- const dims = cubeMeta.dimensions;
34
- let coords = given;
41
+ let coords = givenCoords;
35
42
  let sandboxDefaulted;
36
43
  if (dimensions !== undefined) {
37
- if (given.length !== dimensions.length) {
38
- throw dimensionCountMismatch(cubeName, dimensions, given);
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(given);
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 hier = await tm1Client.hierarchies.get(dim, dim);
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: el.type,
74
- isNLevel: el.type !== "Consolidated",
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, full result — set fetchAll=true to avoid a partial grid) or a flat coordinate table; 'json' (default) returns the structured envelope.",
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
- const top = all ? undefined : limit;
94
- const skip = all ? undefined : offset;
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 = !all && off + count < total;
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. fetchAll keeps the whole cellset.
105
- const { axes, clipped } = all
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 for the full cellset.",
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
- const top = all ? undefined : limit;
27
- const skip = all ? undefined : offset;
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 = !all && off + count < total;
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. fetchAll keeps the whole cellset.
37
- const { axes, clipped } = all
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, withVersion } from "../annotations.js";
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 address the default hierarchy, in cube dimension order (discover with tm1_list_cubes).",
12
- "v11 only. Related: tm1_check_feeders / tm1_trace_feeders for feeder issues, tm1_get_cube_rules for the full rule text.",
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: withVersion(READ_ONLY, "v11"),
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; alternate hierarchies are not supported. On v12 this action is unavailable.`);
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, withVersion } from "../annotations.js";
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 address the default hierarchy, in cube dimension order (discover with tm1_list_cubes).",
12
- "v11 only. Related: tm1_check_feeders (fed/unfed flags), tm1_audit_feeders (static analysis).",
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: withVersion(READ_ONLY, "v11"),
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; alternate hierarchies are not supported. On v12 this action is unavailable.`);
33
+ }), `TraceFeeders failed for cube '${cubeName}'. Verify dimension order/elements via tm1_list_cubes.`);
34
34
  return {
35
35
  content: [
36
36
  {