@ffschrattenecker/tm1-mcp-server 6.1.1 → 7.0.1
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 +43 -1
- package/README.md +23 -5
- package/dist/config.d.ts +9 -0
- package/dist/config.js +89 -64
- package/dist/connections.d.ts +69 -0
- package/dist/connections.js +262 -0
- package/dist/http-transport.d.ts +2 -2
- package/dist/index.js +45 -40
- package/dist/lib/callgraph/tm1-adapter.d.ts +4 -1
- package/dist/lib/callgraph/tm1-adapter.js +5 -2
- package/dist/lib/slim-json-schema.d.ts +8 -0
- package/dist/lib/slim-json-schema.js +34 -0
- package/dist/lib/strip-comments.js +1 -1
- package/dist/lib/tm1-events.d.ts +2 -0
- package/dist/prompts/index.js +2 -2
- package/dist/resources/index.d.ts +4 -2
- package/dist/resources/index.js +93 -53
- package/dist/resources/subscriptions.d.ts +2 -1
- package/dist/resources/subscriptions.js +16 -7
- package/dist/server-instructions.d.ts +3 -0
- package/dist/server-instructions.js +12 -0
- package/dist/tm1-client/http.d.ts +1 -1
- package/dist/tm1-client/http.js +21 -4
- package/dist/tm1-client/services/process-service.d.ts +7 -1
- package/dist/tm1-client/services/process-service.js +7 -5
- package/dist/tm1-client.d.ts +1 -1
- package/dist/tm1-client.js +2 -11
- package/dist/tools/analysis/audit-naming.js +1 -1
- package/dist/tools/analysis/invalidate-callgraph-cache.js +2 -1
- package/dist/tools/analysis/trace-data-flow.js +1 -1
- package/dist/tools/confirm.js +1 -1
- package/dist/tools/define-tool.d.ts +35 -11
- package/dist/tools/define-tool.js +51 -4
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js +6 -12
- package/dist/tools/metadata/list-cubes.js +3 -3
- package/dist/tools/metadata/list-processes.js +11 -7
- package/dist/tools/model-building/unload-cube.js +1 -1
- package/dist/tools/operations/get-audit-log.js +1 -1
- package/dist/tools/operations/get-jobs.js +2 -2
- package/dist/tools/operations/get-message-log.js +1 -1
- package/dist/tools/operations/get-server-state.js +1 -1
- package/dist/tools/operations/get-threads.js +2 -2
- package/dist/tools/operations/get-transaction-log.js +1 -1
- package/dist/tools/operations/list-connections.d.ts +2 -0
- package/dist/tools/operations/list-connections.js +39 -0
- package/dist/tools/operations/save-data.js +1 -1
- package/dist/tools/ti-development/execute-process.js +2 -2
- package/dist/tools/ti-development/import-pro-file.js +1 -1
- package/dist/tools/ti-development/search-code.js +1 -1
- package/dist/tools/ti-development/upsert-process.js +2 -2
- package/dist/tools/with-annotations.d.ts +10 -0
- package/dist/tools/with-annotations.js +58 -3
- package/npm-shrinkwrap.json +2 -2
- package/package.json +4 -3
- package/dist/tools/ti-development/get-process-code.d.ts +0 -2
- package/dist/tools/ti-development/get-process-code.js +0 -100
- package/dist/tools/ti-development/get-process-datasource.d.ts +0 -2
- package/dist/tools/ti-development/get-process-datasource.js +0 -20
- package/dist/tools/ti-development/get-process-parameters.d.ts +0 -2
- package/dist/tools/ti-development/get-process-parameters.js +0 -25
- package/dist/tools/ti-development/get-process-variables.d.ts +0 -2
- package/dist/tools/ti-development/get-process-variables.js +0 -38
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineTool } from "../define-tool.js";
|
|
3
|
+
import { READ_ONLY } from "../annotations.js";
|
|
4
|
+
const ConnectionSchema = z.object({
|
|
5
|
+
name: z.string(),
|
|
6
|
+
mode: z.enum(["readwrite", "readonly"]).optional(),
|
|
7
|
+
environment: z.enum(["dev", "test", "prod"]).optional(),
|
|
8
|
+
modeReason: z.string().optional(),
|
|
9
|
+
tm1Version: z.string().optional(),
|
|
10
|
+
connected: z.boolean(),
|
|
11
|
+
error: z.string().optional(),
|
|
12
|
+
});
|
|
13
|
+
export const registerListConnections = defineTool({
|
|
14
|
+
name: "tm1_list_connections",
|
|
15
|
+
description: "List the TM1 connections this server can reach: name (the `connection` argument of every other tool), readonly/readwrite mode, TM1_ENVIRONMENT label (and why mode was forced), TM1 version, and whether a session is open. Makes no TM1 call.",
|
|
16
|
+
annotations: READ_ONLY,
|
|
17
|
+
output: z.object({ connections: z.array(ConnectionSchema) }),
|
|
18
|
+
input: {},
|
|
19
|
+
connectionless: true,
|
|
20
|
+
handler: (_args, registry) => {
|
|
21
|
+
const connections = registry.status().map((c) => ({
|
|
22
|
+
name: c.name,
|
|
23
|
+
...(c.mode && { mode: c.mode }),
|
|
24
|
+
...(c.environment && { environment: c.environment }),
|
|
25
|
+
...(c.modeReason && { modeReason: c.modeReason }),
|
|
26
|
+
...(c.tm1Version && { tm1Version: c.tm1Version }),
|
|
27
|
+
connected: c.connected,
|
|
28
|
+
...((c.configError ?? c.lastError) && {
|
|
29
|
+
error: c.configError ?? c.lastError,
|
|
30
|
+
}),
|
|
31
|
+
}));
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{ type: "text", text: JSON.stringify({ connections }) },
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=list-connections.js.map
|
|
@@ -13,7 +13,7 @@ export const registerSaveData = defineTool({
|
|
|
13
13
|
"Executes as an unbound TI process via ExecuteProcessWithReturn; no process object is created on the server.",
|
|
14
14
|
],
|
|
15
15
|
annotations: withVersion(IDEMPOTENT_WRITE, "v11"),
|
|
16
|
-
|
|
16
|
+
version: 11,
|
|
17
17
|
output: MutationResultSchema,
|
|
18
18
|
input: {
|
|
19
19
|
cube: z
|
|
@@ -32,7 +32,7 @@ export const registerExecuteProcess = defineTool({
|
|
|
32
32
|
description: [
|
|
33
33
|
"Execute a TurboIntegrator process on the TM1 server with optional parameters.",
|
|
34
34
|
"Non-idempotent: each call re-runs the process — do not retry blindly on transport errors without checking server state.",
|
|
35
|
-
"Before: tm1_check_process_code (syntax) and/or tm1_compile_process (full compile). Discover required params with
|
|
35
|
+
"Before: tm1_check_process_code (syntax) and/or tm1_compile_process (full compile). Discover required params with tm1_get_process.",
|
|
36
36
|
"When TM1 wrote an error log for the run (failure or minor errors), its last 40 lines come back as errorLog. For cascade siblings and older logs use tm1_diagnose_process_error.",
|
|
37
37
|
],
|
|
38
38
|
annotations: DESTRUCTIVE,
|
|
@@ -86,7 +86,7 @@ export const registerExecuteProcess = defineTool({
|
|
|
86
86
|
const result = await withToolHint(tm1Client.processes.execute(processName, parameters, {
|
|
87
87
|
signal: extra?.signal,
|
|
88
88
|
...(timeoutMs ? { timeoutMs } : {}),
|
|
89
|
-
}), `Process '${processName}' failed at runtime. Inspect cascade with tm1_diagnose_process_error(processName='${processName}', includeRelated=true). Verify parameter shape via
|
|
89
|
+
}), `Process '${processName}' failed at runtime. Inspect cascade with tm1_diagnose_process_error(processName='${processName}', includeRelated=true). Verify parameter shape via tm1_get_process; check syntax with tm1_compile_process before re-running.`);
|
|
90
90
|
// TM1 names the run's own error log when it wrote one (minor errors
|
|
91
91
|
// included). Attach its tail so judging the run takes no extra call.
|
|
92
92
|
const errorLog = result.errorLogFile
|
|
@@ -79,7 +79,7 @@ export const registerImportProFile = defineTool({
|
|
|
79
79
|
if (failure)
|
|
80
80
|
return preflightResult(failure);
|
|
81
81
|
}
|
|
82
|
-
const allProcs = await tm1Client.processes.list();
|
|
82
|
+
const allProcs = await tm1Client.processes.list({ namesOnly: true });
|
|
83
83
|
const exists = allProcs.some((p) => p.name === processName);
|
|
84
84
|
if (mode === "create" && exists) {
|
|
85
85
|
throw new TM1Error({
|
|
@@ -14,7 +14,7 @@ export const registerSearchCode = defineTool({
|
|
|
14
14
|
"Regex search across all TI process code (Prolog/Metadata/Data/Epilog).",
|
|
15
15
|
"Returns matches paginated (default 50/page) with process name, tab, line number, and trimmed line text.",
|
|
16
16
|
"Wrapper over tm1_get_all_processes_code that avoids dumping ~MB of code through the channel.",
|
|
17
|
-
"Use
|
|
17
|
+
"Use tm1_get_process on a hit to inspect the surrounding context.",
|
|
18
18
|
"Set groupBy='process' (or 'tab') for a sorted count aggregation instead of individual match lines.",
|
|
19
19
|
],
|
|
20
20
|
annotations: READ_ONLY,
|
|
@@ -13,7 +13,7 @@ import { backupProcess } from "./process-backup.js";
|
|
|
13
13
|
// The same data source shape the git round-trip and check_process_code use.
|
|
14
14
|
// This tool used to carry its own copy, which had drifted: it was missing
|
|
15
15
|
// `query`, so an ODBC source could be created here but its SQL could not —
|
|
16
|
-
// while
|
|
16
|
+
// while tm1_get_process reads it back.
|
|
17
17
|
// Strict, so a misspelled field is rejected instead of silently dropped.
|
|
18
18
|
const dataSourceSchema = sharedDataSourceSchema.strict();
|
|
19
19
|
const parameterSchema = z.object({
|
|
@@ -215,7 +215,7 @@ export const registerUpsertProcess = defineTool({
|
|
|
215
215
|
// 60s callgraph TTL so the next analysis sees fresh references instead of stale graph.
|
|
216
216
|
const { cleared: callgraphEntriesCleared } = invalidateCallgraphCache();
|
|
217
217
|
// Read the code back: TM1 stores what REST sent, so a mismatch means the
|
|
218
|
-
// write did not land as sent. Saves the caller a
|
|
218
|
+
// write did not land as sent. Saves the caller a get_process turn.
|
|
219
219
|
const sent = { prolog, metadata, data, epilog };
|
|
220
220
|
const sentTabs = TABS.filter((t) => sent[t] !== undefined);
|
|
221
221
|
let verified;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type pino from "pino";
|
|
2
2
|
import { type McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { type McpToolResult } from "./error-format.js";
|
|
3
4
|
export declare function deriveTitle(toolName: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Cut an oversized page down to the longest prefix of its items that fits,
|
|
7
|
+
* turning it into an ordinary shorter page: count, has_more and next_offset
|
|
8
|
+
* are rewritten so the caller walks on from exactly where it stopped. The
|
|
9
|
+
* result is still valid JSON of the declared shape — unlike a text cut — and
|
|
10
|
+
* the call is not wasted. Returns undefined when not even one item fits (or
|
|
11
|
+
* the payload is not a page), leaving the caller to report the overflow.
|
|
12
|
+
*/
|
|
13
|
+
export declare function trimPageToFit(result: McpToolResult, maxChars: number): McpToolResult | undefined;
|
|
4
14
|
export declare function narrowingHint(inputKeys: ReadonlySet<string>): string;
|
|
5
15
|
export declare function withAnnotations(server: McpServer, logger: pino.Logger, mode: "readwrite" | "readonly", responseMode?: "legacy" | "structured", maxResponseChars?: number): McpServer;
|
|
6
16
|
//# sourceMappingURL=with-annotations.d.ts.map
|
|
@@ -14,7 +14,7 @@ const TITLE_CASING = {
|
|
|
14
14
|
v11: "v11",
|
|
15
15
|
};
|
|
16
16
|
// Derive a human-readable title from a snake_case tool name:
|
|
17
|
-
// "
|
|
17
|
+
// "tm1_get_process" → "Get Process". Single point of derivation —
|
|
18
18
|
// no per-tool overrides in ANNOTATION_MAP.
|
|
19
19
|
export function deriveTitle(toolName) {
|
|
20
20
|
return toolName
|
|
@@ -49,6 +49,55 @@ function responseChars(result) {
|
|
|
49
49
|
return text;
|
|
50
50
|
return JSON.stringify(result.structuredContent).length;
|
|
51
51
|
}
|
|
52
|
+
function isPagePayload(value) {
|
|
53
|
+
if (typeof value !== "object" || value === null)
|
|
54
|
+
return false;
|
|
55
|
+
const v = value;
|
|
56
|
+
return (Array.isArray(v.items) &&
|
|
57
|
+
typeof v.offset === "number" &&
|
|
58
|
+
typeof v.total === "number");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Cut an oversized page down to the longest prefix of its items that fits,
|
|
62
|
+
* turning it into an ordinary shorter page: count, has_more and next_offset
|
|
63
|
+
* are rewritten so the caller walks on from exactly where it stopped. The
|
|
64
|
+
* result is still valid JSON of the declared shape — unlike a text cut — and
|
|
65
|
+
* the call is not wasted. Returns undefined when not even one item fits (or
|
|
66
|
+
* the payload is not a page), leaving the caller to report the overflow.
|
|
67
|
+
*/
|
|
68
|
+
export function trimPageToFit(result, maxChars) {
|
|
69
|
+
const page = result.structuredContent;
|
|
70
|
+
if (!isPagePayload(page))
|
|
71
|
+
return undefined;
|
|
72
|
+
const withText = (result.content ?? []).some((c) => c.type === "text");
|
|
73
|
+
const build = (n) => {
|
|
74
|
+
const trimmed = {
|
|
75
|
+
...page,
|
|
76
|
+
count: n,
|
|
77
|
+
has_more: true, // n < items.length: at least one item was dropped
|
|
78
|
+
next_offset: page.offset + n,
|
|
79
|
+
items: page.items.slice(0, n),
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
...result,
|
|
83
|
+
content: withText
|
|
84
|
+
? [{ type: "text", text: JSON.stringify(trimmed) }]
|
|
85
|
+
: [],
|
|
86
|
+
structuredContent: trimmed,
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
// Largest n in [1, items.length) whose serialization fits.
|
|
90
|
+
let lo = 0;
|
|
91
|
+
let hi = page.items.length - 1;
|
|
92
|
+
while (lo < hi) {
|
|
93
|
+
const mid = Math.ceil((lo + hi) / 2);
|
|
94
|
+
if (responseChars(build(mid)) <= maxChars)
|
|
95
|
+
lo = mid;
|
|
96
|
+
else
|
|
97
|
+
hi = mid - 1;
|
|
98
|
+
}
|
|
99
|
+
return lo > 0 ? build(lo) : undefined;
|
|
100
|
+
}
|
|
52
101
|
// How to narrow a call, derived from the tool's own input keys so a new tool
|
|
53
102
|
// gets a correct hint without a per-tool table to keep in step.
|
|
54
103
|
export function narrowingHint(inputKeys) {
|
|
@@ -199,12 +248,18 @@ responseMode = "legacy", maxResponseChars = DEFAULT_MAX_RESPONSE_CHARS) {
|
|
|
199
248
|
}
|
|
200
249
|
};
|
|
201
250
|
const SLOW_TOOL_MS = 5000;
|
|
202
|
-
//
|
|
203
|
-
//
|
|
251
|
+
// An oversized page is cut to the items that fit (see trimPageToFit).
|
|
252
|
+
// Anything else becomes an error that says how to narrow the call — never a
|
|
253
|
+
// truncated text: a cut JSON payload is worse than none.
|
|
204
254
|
const guardSize = (toolName, result, hint) => {
|
|
205
255
|
const size = responseChars(result);
|
|
206
256
|
if (size <= maxResponseChars)
|
|
207
257
|
return result;
|
|
258
|
+
const trimmed = trimPageToFit(result, maxResponseChars);
|
|
259
|
+
if (trimmed) {
|
|
260
|
+
logger.info({ tool: toolName, size, maxResponseChars }, "page trimmed to fit the response limit");
|
|
261
|
+
return trimmed;
|
|
262
|
+
}
|
|
208
263
|
logger.warn({ tool: toolName, size, maxResponseChars }, "response too large");
|
|
209
264
|
return formatTm1ErrorResult(new TM1Error({
|
|
210
265
|
code: TM1ErrorCode.RESPONSE_TOO_LARGE,
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ffschrattenecker/tm1-mcp-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@ffschrattenecker/tm1-mcp-server",
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "7.0.1",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@modelcontextprotocol/sdk": "1.30.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ffschrattenecker/tm1-mcp-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "MCP server for IBM Planning Analytics / TM1 — full model lifecycle via REST API",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -60,11 +60,12 @@
|
|
|
60
60
|
"lint:mutation-envelope": "node scripts/check-mutation-envelope.mjs",
|
|
61
61
|
"lint:format": "prettier --check .",
|
|
62
62
|
"format": "prettier --write .",
|
|
63
|
-
"verify": "npm run typecheck && npm run typecheck:tests && npm run lint:no-flat-api && npm run lint:output-schema-budget && npm run lint:tool-registration && npm run lint:input-naming && npm run lint:mutation-envelope && npm run lint:format && npm run lint:eslint && npm run coverage:check",
|
|
63
|
+
"verify": "npm run typecheck && npm run typecheck:tests && npm run lint:no-flat-api && npm run lint:output-schema-budget && npm run lint:tool-surface-budget && npm run lint:tool-registration && npm run lint:input-naming && npm run lint:mutation-envelope && npm run lint:format && npm run lint:eslint && npm run coverage:check",
|
|
64
64
|
"tools:list": "node scripts/gen-tool-list.mjs",
|
|
65
65
|
"tools:update-readme": "node scripts/gen-tool-list.mjs --write-readme",
|
|
66
66
|
"lint:eslint": "eslint .",
|
|
67
|
-
"measure:tools": "node scripts/measure-tool-surface.mjs"
|
|
67
|
+
"measure:tools": "node scripts/measure-tool-surface.mjs",
|
|
68
|
+
"lint:tool-surface-budget": "node scripts/measure-tool-surface.mjs --top 0 --budget 133000"
|
|
68
69
|
},
|
|
69
70
|
"keywords": [
|
|
70
71
|
"mcp",
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { commentStats, stripCommentBlocks } from "../../lib/strip-comments.js";
|
|
3
|
-
import { maskCode, resolveMaskSecrets } from "../../lib/mask-secrets.js";
|
|
4
|
-
import { ProcessCodeSchema } from "../schemas/items.js";
|
|
5
|
-
import { READ_ONLY } from "../annotations.js";
|
|
6
|
-
import { defineTool } from "../define-tool.js";
|
|
7
|
-
// A tab counts as comment-heavy (worth flagging for stripComments) only when it
|
|
8
|
-
// is both long enough to matter and mostly comments.
|
|
9
|
-
const HEAVY_MIN_LINES = 20;
|
|
10
|
-
const HEAVY_RATIO = 0.4;
|
|
11
|
-
const TABS = ["prolog", "metadata", "data", "epilog"];
|
|
12
|
-
export const registerGetProcessCode = defineTool({
|
|
13
|
-
name: "tm1_get_process_code",
|
|
14
|
-
description: "Get the source code of all four tabs (Prolog, Metadata, Data, Epilog) of a TI process. " +
|
|
15
|
-
"Grown TM1 models often carry large blocks of commented-out dead code; set stripComments=true " +
|
|
16
|
-
"to collapse runs of 4+ comment lines into a `# [... N lines ...]` marker and save context.",
|
|
17
|
-
annotations: READ_ONLY,
|
|
18
|
-
output: ProcessCodeSchema,
|
|
19
|
-
input: {
|
|
20
|
-
processName: z.string().describe("Name of the TI process"),
|
|
21
|
-
stripComments: z
|
|
22
|
-
.boolean()
|
|
23
|
-
.optional()
|
|
24
|
-
.default(false)
|
|
25
|
-
.describe("If true, collapse blocks of 4+ consecutive comment lines into a single marker (dead-code reduction). " +
|
|
26
|
-
"Inline and short comments are kept. Default false (full verbatim source)."),
|
|
27
|
-
maskSecrets: z
|
|
28
|
-
.boolean()
|
|
29
|
-
.optional()
|
|
30
|
-
.default(true)
|
|
31
|
-
.describe("Redact credential literals in the returned code. Masks the password arg of ODBCOpen() and quoted values " +
|
|
32
|
-
"assigned to credential-named identifiers (pPwd, sToken, …). Default: true. Set false only when explicitly auditing credentials."),
|
|
33
|
-
includeSecurityAccess: z
|
|
34
|
-
.boolean()
|
|
35
|
-
.optional()
|
|
36
|
-
.default(false)
|
|
37
|
-
.describe("Also fetch process's HasSecurityAccess elevation flag (one extra GET). " +
|
|
38
|
-
"Default false — pure code reads stay single request."),
|
|
39
|
-
},
|
|
40
|
-
handler: async ({ processName, stripComments, maskSecrets, includeSecurityAccess }, tm1Client) => {
|
|
41
|
-
const code = await tm1Client.processes.getCode(processName);
|
|
42
|
-
let hint;
|
|
43
|
-
if (stripComments) {
|
|
44
|
-
let removedLines = 0;
|
|
45
|
-
let collapsedBlocks = 0;
|
|
46
|
-
for (const tab of TABS) {
|
|
47
|
-
const r = stripCommentBlocks(code[tab]);
|
|
48
|
-
code[tab] = r.code;
|
|
49
|
-
removedLines += r.removedLines;
|
|
50
|
-
collapsedBlocks += r.collapsedBlocks;
|
|
51
|
-
}
|
|
52
|
-
if (collapsedBlocks > 0) {
|
|
53
|
-
hint =
|
|
54
|
-
`stripComments collapsed ${collapsedBlocks} comment block(s) (${removedLines} lines) into markers. ` +
|
|
55
|
-
`Re-run without stripComments for the full source.`;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
let worst;
|
|
60
|
-
for (const tab of TABS) {
|
|
61
|
-
const s = commentStats(code[tab]);
|
|
62
|
-
if (s.totalLines < HEAVY_MIN_LINES)
|
|
63
|
-
continue;
|
|
64
|
-
const ratio = s.commentLines / s.totalLines;
|
|
65
|
-
if (ratio >= HEAVY_RATIO && (!worst || ratio > worst.ratio)) {
|
|
66
|
-
worst = {
|
|
67
|
-
tab,
|
|
68
|
-
total: s.totalLines,
|
|
69
|
-
comment: s.commentLines,
|
|
70
|
-
ratio,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (worst) {
|
|
75
|
-
const pct = Math.round(worst.ratio * 100);
|
|
76
|
-
hint =
|
|
77
|
-
`${worst.tab} tab is ${pct}% comments (${worst.comment}/${worst.total} lines). ` +
|
|
78
|
-
`Set stripComments=true to collapse dead-code blocks and save context.`;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (resolveMaskSecrets(maskSecrets)) {
|
|
82
|
-
for (const tab of TABS)
|
|
83
|
-
code[tab] = maskCode(code[tab]);
|
|
84
|
-
}
|
|
85
|
-
let hasSecurityAccess;
|
|
86
|
-
if (includeSecurityAccess) {
|
|
87
|
-
hasSecurityAccess = (await tm1Client.processes.getDeployMeta(processName))
|
|
88
|
-
.hasSecurityAccess;
|
|
89
|
-
}
|
|
90
|
-
const payload = {
|
|
91
|
-
...code,
|
|
92
|
-
...(hasSecurityAccess !== undefined ? { hasSecurityAccess } : {}),
|
|
93
|
-
...(hint ? { hint } : {}),
|
|
94
|
-
};
|
|
95
|
-
return {
|
|
96
|
-
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
97
|
-
};
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
//# sourceMappingURL=get-process-code.js.map
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { FORMAT_SCHEMA, payloadResponse, renderKV } from "../format.js";
|
|
3
|
-
import { DataSourceSchema } from "../schemas/items.js";
|
|
4
|
-
import { READ_ONLY } from "../annotations.js";
|
|
5
|
-
import { defineTool } from "../define-tool.js";
|
|
6
|
-
export const registerGetProcessDatasource = defineTool({
|
|
7
|
-
name: "tm1_get_process_datasource",
|
|
8
|
-
description: "Get the data source configuration of a TurboIntegrator process. The password field is always redacted.",
|
|
9
|
-
annotations: READ_ONLY,
|
|
10
|
-
output: DataSourceSchema,
|
|
11
|
-
input: {
|
|
12
|
-
processName: z.string().describe("Name of the TI process"),
|
|
13
|
-
...FORMAT_SCHEMA,
|
|
14
|
-
},
|
|
15
|
-
handler: async ({ processName, format }, tm1Client) => {
|
|
16
|
-
const ds = await tm1Client.processes.getDataSource(processName);
|
|
17
|
-
return payloadResponse(ds, format, (d) => renderKV(d, `Datasource of ${processName}`));
|
|
18
|
-
},
|
|
19
|
-
});
|
|
20
|
-
//# sourceMappingURL=get-process-datasource.js.map
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { FORMAT_SCHEMA, payloadResponse, renderTable, columnsOf, } from "../format.js";
|
|
3
|
-
import { ProcessParameterSchema } from "../schemas/items.js";
|
|
4
|
-
import { READ_ONLY } from "../annotations.js";
|
|
5
|
-
import { defineTool } from "../define-tool.js";
|
|
6
|
-
export const registerGetProcessParameters = defineTool({
|
|
7
|
-
name: "tm1_get_process_parameters",
|
|
8
|
-
description: "Get the parameters of a TurboIntegrator process including names, types and defaults",
|
|
9
|
-
annotations: READ_ONLY,
|
|
10
|
-
output: {
|
|
11
|
-
processName: z.string(),
|
|
12
|
-
parameters: z.array(ProcessParameterSchema),
|
|
13
|
-
},
|
|
14
|
-
input: {
|
|
15
|
-
processName: z.string().describe("Name of the TI process"),
|
|
16
|
-
...FORMAT_SCHEMA,
|
|
17
|
-
},
|
|
18
|
-
handler: async ({ processName, format }, tm1Client) => {
|
|
19
|
-
const params = await tm1Client.processes.getParameters(processName);
|
|
20
|
-
const payload = { processName, parameters: params };
|
|
21
|
-
const columns = columnsOf(["name", "type", "defaultValue", "prompt"]);
|
|
22
|
-
return payloadResponse(payload, format, (p) => `## Parameters of ${p.processName}\n\n${renderTable(p.parameters, columns)}`);
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
//# sourceMappingURL=get-process-parameters.js.map
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { FORMAT_SCHEMA, payloadResponse, renderTable, columnsOf, } from "../format.js";
|
|
3
|
-
import { IgnoredColumnSchema, ProcessVariableSchema, } from "../schemas/items.js";
|
|
4
|
-
import { READ_ONLY } from "../annotations.js";
|
|
5
|
-
import { defineTool } from "../define-tool.js";
|
|
6
|
-
export const registerGetProcessVariables = defineTool({
|
|
7
|
-
name: "tm1_get_process_variables",
|
|
8
|
-
description: "Get the variables (column-name mapping for ASCII/ODBC sources) of a TurboIntegrator process. " +
|
|
9
|
-
"Columns set to 'Ignore' carry no variable and are reported separately in ignoredColumns — " +
|
|
10
|
-
"they are the reason variable positions can have gaps.",
|
|
11
|
-
annotations: READ_ONLY,
|
|
12
|
-
output: {
|
|
13
|
-
processName: z.string(),
|
|
14
|
-
variables: z.array(ProcessVariableSchema),
|
|
15
|
-
ignoredColumns: z.array(IgnoredColumnSchema),
|
|
16
|
-
},
|
|
17
|
-
input: {
|
|
18
|
-
processName: z.string().describe("Name of the TI process"),
|
|
19
|
-
...FORMAT_SCHEMA,
|
|
20
|
-
},
|
|
21
|
-
handler: async ({ processName, format }, tm1Client) => {
|
|
22
|
-
const { variables, ignoredColumns } = await tm1Client.processes.getVariableLayout(processName);
|
|
23
|
-
const payload = { processName, variables, ignoredColumns };
|
|
24
|
-
const columns = columnsOf([
|
|
25
|
-
"name",
|
|
26
|
-
"type",
|
|
27
|
-
"position",
|
|
28
|
-
"startByte",
|
|
29
|
-
"endByte",
|
|
30
|
-
]);
|
|
31
|
-
const ignoredCols = columnsOf(["position", "name"]);
|
|
32
|
-
return payloadResponse(payload, format, (p) => `## Variables of ${p.processName}\n\n${renderTable(p.variables, columns)}` +
|
|
33
|
-
(p.ignoredColumns.length > 0
|
|
34
|
-
? `\n\n### Ignored columns\n\n${renderTable(p.ignoredColumns, ignoredCols)}`
|
|
35
|
-
: ""));
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
//# sourceMappingURL=get-process-variables.js.map
|