@sassoftware/sas-score-mcp-serverjs 0.4.0 → 0.4.1-3
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/cli.js +1 -2
- package/package.json +2 -4
- package/src/createMcpServer.js +9 -4
- package/src/expressMcpServer.js +1 -1
- package/src/handleGetDelete.js +1 -1
- package/src/toolHelpers/readCerts.js +4 -4
- package/src/toolSet/devaScore.js +62 -61
- package/src/toolSet/findJob.js +9 -16
- package/src/toolSet/findJobdef.js +4 -5
- package/src/toolSet/findLibrary.js +67 -68
- package/src/toolSet/findModel.js +4 -5
- package/src/toolSet/findTable.js +6 -6
- package/src/toolSet/getEnv.js +10 -7
- package/src/toolSet/listJobdefs.js +61 -62
- package/src/toolSet/listJobs.js +61 -62
- package/src/toolSet/listLibraries.js +78 -80
- package/src/toolSet/listModels.js +56 -56
- package/src/toolSet/listTables.js +65 -66
- package/src/toolSet/makeTools.js +1 -3
- package/src/toolSet/modelInfo.js +4 -5
- package/src/toolSet/modelScore.js +7 -7
- package/src/toolSet/readTable.js +63 -67
- package/src/toolSet/runCasProgram.js +9 -9
- package/src/toolSet/runJob.js +81 -82
- package/src/toolSet/runJobdef.js +82 -83
- package/src/toolSet/runMacro.js +81 -82
- package/src/toolSet/runProgram.js +9 -10
- package/src/toolSet/sasQuery.js +77 -79
- package/src/toolSet/sasQueryTemplate.js +4 -5
- package/src/toolSet/sasQueryTemplate2.js +4 -5
- package/src/toolSet/scrInfo.js +3 -5
- package/src/toolSet/scrScore.js +70 -70
- package/src/toolSet/searchAssets.js +5 -6
- package/src/toolSet/setContext.js +65 -66
- package/src/toolSet/superstat.js +61 -60
- package/src/toolSet/tableInfo.js +57 -59
- package/skills/mcp-tool-description-optimizer/SKILL.md +0 -129
- package/skills/mcp-tool-description-optimizer/references/examples.md +0 -123
- package/skills/sas-read-and-score/SKILL.md +0 -91
- package/skills/sas-read-strategy/SKILL.md +0 -143
- package/skills/sas-score-workflow/SKILL.md +0 -282
package/cli.js
CHANGED
|
@@ -354,7 +354,6 @@ console.error('[Note] appEnvBase is', JSON.stringify(appEnvBase, null,2));
|
|
|
354
354
|
// creat a dummy sessionId for stdio since there is only one session and transport in that case, and tools need a sessionId to access the appEnvBase and contexts
|
|
355
355
|
let sessionId = randomUUID();
|
|
356
356
|
sessionCache.set(sessionId, appEnvBase);
|
|
357
|
-
useHapi = false; // for now disable hapi for authflow code since it is not working well, and code flow is not commonly used in server side applications, and http transport works better for code flow since it requires multiple back and forth calls to complete the auth process which is not ideal for stdio transport, this will be revisited in the future to either fix the issues with hapi or implement the code flow in a way that works better with stdio transport
|
|
358
357
|
if (mcpType === 'stdio') {
|
|
359
358
|
console.error('[Note] Setting up stdio transport with sessionId:', sessionId);
|
|
360
359
|
console.error('[Note] Used in setting up tools and some persistence(not all).');
|
|
@@ -368,7 +367,7 @@ if (mcpType === 'stdio') {
|
|
|
368
367
|
console.error('[Note] Using HAPI HTTP server...')
|
|
369
368
|
} else {
|
|
370
369
|
await expressMcpServer(mcpServer, sessionCache, appEnvBase);
|
|
371
|
-
console.error('[Note] MCP HTTP server started on port ' + appEnvBase.PORT);
|
|
370
|
+
console.error('[Note] MCP HTTP express server started on port ' + appEnvBase.PORT);
|
|
372
371
|
}
|
|
373
372
|
}
|
|
374
373
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sassoftware/sas-score-mcp-serverjs",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1-3",
|
|
4
4
|
"description": "A mcp server for SAS Viya",
|
|
5
5
|
"author": "Deva Kumar <deva.kumar@sas.com>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"skills"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
47
47
|
"@sassoftware/restaf": "^5.6.0",
|
|
48
48
|
"@sassoftware/restafedit": "^3.11.1-10",
|
|
49
49
|
"@sassoftware/restaflib": "^5.6.0",
|
|
@@ -57,10 +57,8 @@
|
|
|
57
57
|
"express-list-endpoints": "^7.1.1",
|
|
58
58
|
"express-rate-limit": "^8.2.1",
|
|
59
59
|
"helmet": "^8.1.0",
|
|
60
|
-
"mcp-framework": "^0.2.16",
|
|
61
60
|
"node-cache": "^5.1.2",
|
|
62
61
|
"open": "^11.0.0",
|
|
63
|
-
"puppeteer": "^24.34.0",
|
|
64
62
|
"selfsigned": "^5.2.0",
|
|
65
63
|
"undici": "^7.16.0",
|
|
66
64
|
"uuid": "^13.0.0",
|
package/src/createMcpServer.js
CHANGED
|
@@ -73,11 +73,16 @@ async function createMcpServer(cache, _appContext) {
|
|
|
73
73
|
let toolNames = [];
|
|
74
74
|
toolSet.forEach((tool, i) => {
|
|
75
75
|
let toolName = _appContext.brand + '-' + tool.name;
|
|
76
|
-
//
|
|
76
|
+
//tool.inputSchema.additionalProperties = false; // disallow extra properties
|
|
77
|
+
let config = {
|
|
78
|
+
description: tool.description,
|
|
79
|
+
inputSchema: tool.inputSchema
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.error(`\n[Note] Registering tool ${i + 1} : ${toolName}`);
|
|
77
83
|
let toolHandler = wrapf(cache, tool.handler);
|
|
78
|
-
|
|
79
|
-
mcpServer.
|
|
80
|
-
|
|
84
|
+
console.log(`[Note] Registering tool ${toolName} with config: ${JSON.stringify(config)}`);
|
|
85
|
+
let r = mcpServer.registerTool(toolName, config, toolHandler);
|
|
81
86
|
toolNames.push(toolName);
|
|
82
87
|
});
|
|
83
88
|
console.error(`[Note] Registered ${toolSet.length} tools: ${toolNames}`);
|
package/src/expressMcpServer.js
CHANGED
|
@@ -129,7 +129,7 @@ function requireBearer(req, res, next) {
|
|
|
129
129
|
cache.set("headerCache", headerCache);
|
|
130
130
|
next();
|
|
131
131
|
console.error("Finished processing headers for /mcp request");
|
|
132
|
-
console.
|
|
132
|
+
console.error("=======================================================");
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
// process mcp endpoint requests
|
package/src/handleGetDelete.js
CHANGED
|
@@ -17,7 +17,7 @@ async function handleGetDelete(mcpServer, cache, req, h) {
|
|
|
17
17
|
if (req.method === "GET") {
|
|
18
18
|
// You can customize the response as needed
|
|
19
19
|
console.error("[Note] Payload:", req.payload);
|
|
20
|
-
console.
|
|
20
|
+
console.error("======================================================");
|
|
21
21
|
await transport.handleRequest(req.raw.req, req.raw.res, req.payload);
|
|
22
22
|
return h.abandon;
|
|
23
23
|
}
|
|
@@ -9,23 +9,23 @@ function getCerts(tlsdir) {
|
|
|
9
9
|
return null;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
console.
|
|
12
|
+
console.error(`[Note] Reading certs from directory: ` + tlsdir);
|
|
13
13
|
if (fs.existsSync(tlsdir) === false) {
|
|
14
14
|
console.error("[Warning] Specified cert dir does not exist: " + tlsdir);
|
|
15
15
|
return null;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
let listOfFiles = fs.readdirSync(tlsdir);
|
|
19
|
-
console.
|
|
19
|
+
console.error("[Note] TLS/SSL files found: " + listOfFiles);
|
|
20
20
|
let options = {};
|
|
21
21
|
for(let i=0; i < listOfFiles.length; i++) {
|
|
22
22
|
let fname = listOfFiles[i];
|
|
23
23
|
let name = tlsdir + '/' + listOfFiles[i];
|
|
24
24
|
let key = fname.split('.')[0];
|
|
25
|
-
console.
|
|
25
|
+
console.error('Reading TLS file: ' + name + ' as key: ' + key);
|
|
26
26
|
options[key] = fs.readFileSync(name, { encoding: 'utf8' });
|
|
27
27
|
}
|
|
28
|
-
console.
|
|
28
|
+
console.error('cert files', Object.keys(options));
|
|
29
29
|
|
|
30
30
|
return options;
|
|
31
31
|
|
package/src/toolSet/devaScore.js
CHANGED
|
@@ -1,61 +1,62 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
*/
|
|
5
|
-
import {z} from 'zod';
|
|
6
|
-
|
|
7
|
-
function devaScore(_appContext) {
|
|
8
|
-
|
|
9
|
-
let description = `
|
|
10
|
-
deva-score — compute a numeric score based on two input values.
|
|
11
|
-
|
|
12
|
-
USE when: calculate deva score, score these values, compute score for numbers
|
|
13
|
-
DO NOT USE for: model scoring (use model-score), statistical calculations, data lookup
|
|
14
|
-
|
|
15
|
-
PARAMETERS
|
|
16
|
-
- a: number (required) — first input value
|
|
17
|
-
- b: number (required) — second input value
|
|
18
|
-
|
|
19
|
-
FORMULA: (a + b) * 42
|
|
20
|
-
|
|
21
|
-
ROUTING RULES
|
|
22
|
-
- "calculate deva score for 5 and 10" → { a: 5, b: 10 }
|
|
23
|
-
- "score 1 and 2" → { a: 1, b: 2 }
|
|
24
|
-
- "deva score a=3, b=7" → { a: 3, b: 7 }
|
|
25
|
-
- Multiple numbers → chain calls left-to-right: call(first, second), then call(result, third)
|
|
26
|
-
|
|
27
|
-
EXAMPLES
|
|
28
|
-
- "Calculate deva score for 5 and 10" → { a: 5, b: 10 } returns 630
|
|
29
|
-
- "Score 1 and 2" → { a: 1, b: 2 } returns 126
|
|
30
|
-
- "Deva score 20 and 30" → { a: 20, b: 30 } returns 2100
|
|
31
|
-
|
|
32
|
-
NEGATIVE EXAMPLES (do not route here)
|
|
33
|
-
- "Score this customer with credit model" (use model-score)
|
|
34
|
-
- "Calculate the mean of these values" (use run-sas-program or sas-query)
|
|
35
|
-
- "Statistical analysis of numbers" (use sas-query)
|
|
36
|
-
|
|
37
|
-
RESPONSE
|
|
38
|
-
Returns { score: (a + b) * 42 }
|
|
39
|
-
`;
|
|
40
|
-
let spec = {
|
|
41
|
-
name: 'deva-score',
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
handler: async ({ a, b }) => {
|
|
49
|
-
console.error(
|
|
50
|
-
let r = {score: (a + b) * 42};
|
|
51
|
-
console.error('deva score result', r);
|
|
52
|
-
return {
|
|
53
|
-
content: [{type: 'text', text: 'deva score result: ' + JSON.stringify(r)}],
|
|
54
|
-
structuredContent: r
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return spec;
|
|
60
|
-
}
|
|
61
|
-
export default devaScore;
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
function devaScore(_appContext) {
|
|
8
|
+
|
|
9
|
+
let description = `
|
|
10
|
+
deva-score — compute a numeric score based on two input values.
|
|
11
|
+
|
|
12
|
+
USE when: calculate deva score, score these values, compute score for numbers
|
|
13
|
+
DO NOT USE for: model scoring (use model-score), statistical calculations, data lookup
|
|
14
|
+
|
|
15
|
+
PARAMETERS
|
|
16
|
+
- a: number (required) — first input value
|
|
17
|
+
- b: number (required) — second input value
|
|
18
|
+
|
|
19
|
+
FORMULA: (a + b) * 42
|
|
20
|
+
|
|
21
|
+
ROUTING RULES
|
|
22
|
+
- "calculate deva score for 5 and 10" → { a: 5, b: 10 }
|
|
23
|
+
- "score 1 and 2" → { a: 1, b: 2 }
|
|
24
|
+
- "deva score a=3, b=7" → { a: 3, b: 7 }
|
|
25
|
+
- Multiple numbers → chain calls left-to-right: call(first, second), then call(result, third)
|
|
26
|
+
|
|
27
|
+
EXAMPLES
|
|
28
|
+
- "Calculate deva score for 5 and 10" → { a: 5, b: 10 } returns 630
|
|
29
|
+
- "Score 1 and 2" → { a: 1, b: 2 } returns 126
|
|
30
|
+
- "Deva score 20 and 30" → { a: 20, b: 30 } returns 2100
|
|
31
|
+
|
|
32
|
+
NEGATIVE EXAMPLES (do not route here)
|
|
33
|
+
- "Score this customer with credit model" (use model-score)
|
|
34
|
+
- "Calculate the mean of these values" (use run-sas-program or sas-query)
|
|
35
|
+
- "Statistical analysis of numbers" (use sas-query)
|
|
36
|
+
|
|
37
|
+
RESPONSE
|
|
38
|
+
Returns { score: (a + b) * 42 }
|
|
39
|
+
`;
|
|
40
|
+
let spec = {
|
|
41
|
+
name: 'deva-score',
|
|
42
|
+
description: description,
|
|
43
|
+
inputSchema: z.object({
|
|
44
|
+
a: z.number(),
|
|
45
|
+
b: z.number(),
|
|
46
|
+
}),
|
|
47
|
+
required: ['a', 'b'],
|
|
48
|
+
handler: async ({ a, b }) => {
|
|
49
|
+
console.error(a, b);
|
|
50
|
+
let r = { score: (a + b) * 42 };
|
|
51
|
+
console.error('deva score result', r);
|
|
52
|
+
return {
|
|
53
|
+
content: [{ type: 'text', text: 'deva score result: ' + JSON.stringify(r) }],
|
|
54
|
+
structuredContent: r
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return spec;
|
|
60
|
+
}
|
|
61
|
+
export default devaScore;
|
|
62
|
+
|
package/src/toolSet/findJob.js
CHANGED
|
@@ -5,16 +5,7 @@
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import _listJobs from '../toolHelpers/_listJobs.js';
|
|
7
7
|
function findJob(_appContext) {
|
|
8
|
-
|
|
9
|
-
"purpose": "Map natural language requests to find a job in SAS Viya and return structured results.",
|
|
10
|
-
"param_mapping": {
|
|
11
|
-
"name": "required - single name. If missing, ask 'Which job name would you like to find?'.",
|
|
12
|
-
"_userPrompt": "the original user prompt that triggered this tool."
|
|
13
|
-
|
|
14
|
-
},
|
|
15
|
-
"response_schema": "{ jobs: Array<string|object> }",
|
|
16
|
-
"behavior": "Return only JSON matching response_schema when invoked by an LLM. If no matches, return { jobs: [] }"
|
|
17
|
-
};
|
|
8
|
+
|
|
18
9
|
let description = `
|
|
19
10
|
find-job — locate a specific SAS Viya job.
|
|
20
11
|
|
|
@@ -49,18 +40,20 @@ Returns { jobs: [] } if not found; { jobs: [name, ...] } if found. Never halluci
|
|
|
49
40
|
|
|
50
41
|
let spec = {
|
|
51
42
|
name: 'find-job',
|
|
52
|
-
aliases: ['findJob','find job','find_job'],
|
|
53
43
|
description: description,
|
|
54
|
-
|
|
55
|
-
name: z.string()
|
|
56
|
-
|
|
57
|
-
},
|
|
58
|
-
required: ['name'],
|
|
44
|
+
inputSchema: z.object({
|
|
45
|
+
name: z.string()
|
|
46
|
+
}),
|
|
59
47
|
handler: async (params) => {
|
|
60
48
|
let r = await _listJobs(params);
|
|
61
49
|
return r;
|
|
62
50
|
}
|
|
63
51
|
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
/* correct spec for registerTool with inputSchema */
|
|
55
|
+
|
|
64
56
|
return spec;
|
|
65
57
|
}
|
|
66
58
|
export default findJob;
|
|
59
|
+
|
|
@@ -49,12 +49,10 @@ Returns { jobdefs: [] } if not found; { jobdefs: [name, ...] } if found. Never h
|
|
|
49
49
|
|
|
50
50
|
let spec = {
|
|
51
51
|
name: 'find-jobdef',
|
|
52
|
-
aliases: ['findJobdef','find jobdef','find_jobdef'],
|
|
53
52
|
description: description,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
},
|
|
57
|
-
required: ['name'],
|
|
53
|
+
inputSchema: z.object({
|
|
54
|
+
name: z.string()
|
|
55
|
+
}),
|
|
58
56
|
handler: async (params) => {
|
|
59
57
|
let r = await _listJobdefs(params);
|
|
60
58
|
return r;
|
|
@@ -63,3 +61,4 @@ Returns { jobdefs: [] } if not found; { jobdefs: [name, ...] } if found. Never h
|
|
|
63
61
|
return spec;
|
|
64
62
|
}
|
|
65
63
|
export default findJobdef;
|
|
64
|
+
|
|
@@ -1,68 +1,67 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import _listLibrary from '../toolHelpers/_listLibrary.js';
|
|
7
|
-
function findLibrary(_appContext) {
|
|
8
|
-
|
|
9
|
-
let description = `
|
|
10
|
-
find-library — locate a specific CAS or SAS library.
|
|
11
|
-
|
|
12
|
-
USE when: find library, find lib, does library exist, is library available, lookup library
|
|
13
|
-
DO NOT USE for: list libraries (use list-libraries), find table/job/jobdef/model (use respective tools), table structure (use table-info), create library (use run-sas-program)
|
|
14
|
-
|
|
15
|
-
PARAMETERS
|
|
16
|
-
- name: string (required) — library/caslib name; if multiple supplied, use first
|
|
17
|
-
- server: 'cas' | 'sas' (default: 'cas') — target environment
|
|
18
|
-
|
|
19
|
-
ROUTING RULES
|
|
20
|
-
- "find lib <name>" → { name: "<name>", server: "cas" }
|
|
21
|
-
- "find lib <name> in cas" → { name: "<name>", server: "cas" }
|
|
22
|
-
- "find library <name> in sas" → { name: "<name>", server: "sas" }
|
|
23
|
-
- "does library <name> exist" → { name: "<name>", server: "cas" }
|
|
24
|
-
- "find lib" with no name → ask "Which library name would you like to find?"
|
|
25
|
-
- "list libraries / list libs" → use list-libraries instead
|
|
26
|
-
- "tables in <lib>" → use list-tables instead
|
|
27
|
-
|
|
28
|
-
EXAMPLES
|
|
29
|
-
- "find lib Public" → { name: "Public", server: "cas" }
|
|
30
|
-
- "find library sasuser in sas" → { name: "sasuser", server: "sas" }
|
|
31
|
-
- "does library Formats exist" → { name: "Formats", server: "cas" }
|
|
32
|
-
|
|
33
|
-
NEGATIVE EXAMPLES (do not route here)
|
|
34
|
-
- "list libs" (use list-libraries)
|
|
35
|
-
- "show tables in Public" (use list-tables)
|
|
36
|
-
- "find table cars in sashelp" (use find-table)
|
|
37
|
-
- "find job cars_job" (use find-job)
|
|
38
|
-
|
|
39
|
-
ERRORS
|
|
40
|
-
Returns { libraries: [] } if not found; { libraries: [name, ...] } if found. Never hallucinate library names.
|
|
41
|
-
`;
|
|
42
|
-
|
|
43
|
-
let spec = {
|
|
44
|
-
name: 'find-library',
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
export default findLibrary;
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import _listLibrary from '../toolHelpers/_listLibrary.js';
|
|
7
|
+
function findLibrary(_appContext) {
|
|
8
|
+
|
|
9
|
+
let description = `
|
|
10
|
+
find-library — locate a specific CAS or SAS library.
|
|
11
|
+
|
|
12
|
+
USE when: find library, find lib, does library exist, is library available, lookup library
|
|
13
|
+
DO NOT USE for: list libraries (use list-libraries), find table/job/jobdef/model (use respective tools), table structure (use table-info), create library (use run-sas-program)
|
|
14
|
+
|
|
15
|
+
PARAMETERS
|
|
16
|
+
- name: string (required) — library/caslib name; if multiple supplied, use first
|
|
17
|
+
- server: 'cas' | 'sas' (default: 'cas') — target environment
|
|
18
|
+
|
|
19
|
+
ROUTING RULES
|
|
20
|
+
- "find lib <name>" → { name: "<name>", server: "cas" }
|
|
21
|
+
- "find lib <name> in cas" → { name: "<name>", server: "cas" }
|
|
22
|
+
- "find library <name> in sas" → { name: "<name>", server: "sas" }
|
|
23
|
+
- "does library <name> exist" → { name: "<name>", server: "cas" }
|
|
24
|
+
- "find lib" with no name → ask "Which library name would you like to find?"
|
|
25
|
+
- "list libraries / list libs" → use list-libraries instead
|
|
26
|
+
- "tables in <lib>" → use list-tables instead
|
|
27
|
+
|
|
28
|
+
EXAMPLES
|
|
29
|
+
- "find lib Public" → { name: "Public", server: "cas" }
|
|
30
|
+
- "find library sasuser in sas" → { name: "sasuser", server: "sas" }
|
|
31
|
+
- "does library Formats exist" → { name: "Formats", server: "cas" }
|
|
32
|
+
|
|
33
|
+
NEGATIVE EXAMPLES (do not route here)
|
|
34
|
+
- "list libs" (use list-libraries)
|
|
35
|
+
- "show tables in Public" (use list-tables)
|
|
36
|
+
- "find table cars in sashelp" (use find-table)
|
|
37
|
+
- "find job cars_job" (use find-job)
|
|
38
|
+
|
|
39
|
+
ERRORS
|
|
40
|
+
Returns { libraries: [] } if not found; { libraries: [name, ...] } if found. Never hallucinate library names.
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
let spec = {
|
|
44
|
+
name: 'find-library',
|
|
45
|
+
description: description,
|
|
46
|
+
inputSchema: z.object({
|
|
47
|
+
name: z.string()
|
|
48
|
+
}),
|
|
49
|
+
|
|
50
|
+
handler: async (params) => {
|
|
51
|
+
// normalize server to lowercase & default
|
|
52
|
+
if (!params.server) params.server = 'cas';
|
|
53
|
+
params.server = params.server.toLowerCase();
|
|
54
|
+
|
|
55
|
+
// If multiple names passed (comma or space separated), take the first token (defensive)
|
|
56
|
+
if (params.name && /[,\s]+/.test(params.name.trim())) {
|
|
57
|
+
params.name = params.name.split(/[,\s]+/).filter(Boolean)[0];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let r = await _listLibrary(params);
|
|
61
|
+
return r;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return spec;
|
|
65
|
+
}
|
|
66
|
+
export default findLibrary;
|
|
67
|
+
|
package/src/toolSet/findModel.js
CHANGED
|
@@ -44,12 +44,10 @@ Returns { models: [] } if not found; { models: [name, ...] } if found. Never hal
|
|
|
44
44
|
|
|
45
45
|
let spec = {
|
|
46
46
|
name: 'find-model',
|
|
47
|
-
aliases: ['findModel','find model','find_model'],
|
|
48
47
|
description: description,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
required: ['name'],
|
|
48
|
+
inputSchema: z.object({
|
|
49
|
+
name: z.string()
|
|
50
|
+
}),
|
|
53
51
|
handler: async (params) => {
|
|
54
52
|
let r = await _listModels(params);
|
|
55
53
|
return r;
|
|
@@ -59,3 +57,4 @@ Returns { models: [] } if not found; { models: [name, ...] } if found. Never hal
|
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
export default findModel;
|
|
60
|
+
|
package/src/toolSet/findTable.js
CHANGED
|
@@ -45,14 +45,13 @@ Returns { tables: [] } if not found; { tables: [name, ...] } if found. Never hal
|
|
|
45
45
|
|
|
46
46
|
let spec = {
|
|
47
47
|
name: 'find-table',
|
|
48
|
-
aliases: ['findTable','find table','find_table'],
|
|
49
48
|
description: description,
|
|
50
|
-
|
|
51
|
-
server: z.string().default('cas'), // default server is 'cas',
|
|
49
|
+
inputSchema: z.object({
|
|
52
50
|
name: z.string(),
|
|
53
|
-
lib: z.string()
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
lib: z.string().optional(),
|
|
52
|
+
server: z.string().optional() // default server is 'cas'
|
|
53
|
+
}),
|
|
54
|
+
|
|
56
55
|
handler: async (params) => {
|
|
57
56
|
// Check if the params.scenario is a string and parse it
|
|
58
57
|
let r = await _listTables(params);
|
|
@@ -63,3 +62,4 @@ Returns { tables: [] } if not found; { tables: [name, ...] } if found. Never hal
|
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
export default findTable;
|
|
65
|
+
|
package/src/toolSet/getEnv.js
CHANGED
|
@@ -38,17 +38,20 @@ Returns variable name with current value, or null if not found. Return structure
|
|
|
38
38
|
`;
|
|
39
39
|
|
|
40
40
|
let spec = {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
name: 'get-env',
|
|
42
|
+
description: description,
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
name: { type: 'string' }
|
|
47
|
+
},
|
|
48
|
+
required: ['name']
|
|
46
49
|
},
|
|
47
|
-
|
|
48
|
-
handler: async (params) => {
|
|
50
|
+
handler: async (params) => {
|
|
49
51
|
return await _getEnv(params);
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
return spec;
|
|
53
55
|
}
|
|
54
56
|
export default getEnv;
|
|
57
|
+
|
|
@@ -1,62 +1,61 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import _listJobdefs from '../toolHelpers/_listJobdefs.js';
|
|
7
|
-
function listJobdefs(_appContext) {
|
|
8
|
-
|
|
9
|
-
let description = `
|
|
10
|
-
list-jobdefs — enumerate SAS Viya job definitions (jobdefs) assets.
|
|
11
|
-
|
|
12
|
-
USE when: list jobdefs, show jobdefs, browse jobdefs, list available jobdefs, next page
|
|
13
|
-
DO NOT USE for: find single jobdef (use find-jobdef), execute jobdef (use run-jobdef), find job (use find-job), sas code (use run-sas-program)
|
|
14
|
-
|
|
15
|
-
PARAMETERS
|
|
16
|
-
- limit: number (default: 10) — number of jobdefs per page
|
|
17
|
-
- start: number (default: 1) — 1-based page offset
|
|
18
|
-
- where: string (default: '') — optional filter expression
|
|
19
|
-
|
|
20
|
-
ROUTING RULES
|
|
21
|
-
- list jobdefs → { start: 1, limit: 10 }
|
|
22
|
-
- show me 25 jobdefs → { start: 1, limit: 25 }
|
|
23
|
-
- next jobdefs → { start: previousStart + previousLimit, limit: previousLimit }
|
|
24
|
-
|
|
25
|
-
EXAMPLES
|
|
26
|
-
- list jobdefs → { start: 1, limit: 10 }
|
|
27
|
-
- list 25 jobdefs → { start: 1, limit: 25 }
|
|
28
|
-
- next jobdefs → { start: 11, limit: 10 }
|
|
29
|
-
|
|
30
|
-
NEGATIVE EXAMPLES (do not route here)
|
|
31
|
-
- find jobdef abc (use find-jobdef)
|
|
32
|
-
- list jobs (use list-jobs)
|
|
33
|
-
- run jobdef abc (use run-jobdef)
|
|
34
|
-
- list models (use list-models)
|
|
35
|
-
|
|
36
|
-
PAGINATION
|
|
37
|
-
If returned length === limit, hint: next start = start + limit. Empty result with start > 1 means paged past end.
|
|
38
|
-
|
|
39
|
-
ERRORS
|
|
40
|
-
Surface backend error directly; never fabricate jobdef names.
|
|
41
|
-
`;
|
|
42
|
-
|
|
43
|
-
let spec = {
|
|
44
|
-
name: 'list-jobdefs',
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
export default listJobdefs;
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import _listJobdefs from '../toolHelpers/_listJobdefs.js';
|
|
7
|
+
function listJobdefs(_appContext) {
|
|
8
|
+
|
|
9
|
+
let description = `
|
|
10
|
+
list-jobdefs — enumerate SAS Viya job definitions (jobdefs) assets.
|
|
11
|
+
|
|
12
|
+
USE when: list jobdefs, show jobdefs, browse jobdefs, list available jobdefs, next page
|
|
13
|
+
DO NOT USE for: find single jobdef (use find-jobdef), execute jobdef (use run-jobdef), find job (use find-job), sas code (use run-sas-program)
|
|
14
|
+
|
|
15
|
+
PARAMETERS
|
|
16
|
+
- limit: number (default: 10) — number of jobdefs per page
|
|
17
|
+
- start: number (default: 1) — 1-based page offset
|
|
18
|
+
- where: string (default: '') — optional filter expression
|
|
19
|
+
|
|
20
|
+
ROUTING RULES
|
|
21
|
+
- list jobdefs → { start: 1, limit: 10 }
|
|
22
|
+
- show me 25 jobdefs → { start: 1, limit: 25 }
|
|
23
|
+
- next jobdefs → { start: previousStart + previousLimit, limit: previousLimit }
|
|
24
|
+
|
|
25
|
+
EXAMPLES
|
|
26
|
+
- list jobdefs → { start: 1, limit: 10 }
|
|
27
|
+
- list 25 jobdefs → { start: 1, limit: 25 }
|
|
28
|
+
- next jobdefs → { start: 11, limit: 10 }
|
|
29
|
+
|
|
30
|
+
NEGATIVE EXAMPLES (do not route here)
|
|
31
|
+
- find jobdef abc (use find-jobdef)
|
|
32
|
+
- list jobs (use list-jobs)
|
|
33
|
+
- run jobdef abc (use run-jobdef)
|
|
34
|
+
- list models (use list-models)
|
|
35
|
+
|
|
36
|
+
PAGINATION
|
|
37
|
+
If returned length === limit, hint: next start = start + limit. Empty result with start > 1 means paged past end.
|
|
38
|
+
|
|
39
|
+
ERRORS
|
|
40
|
+
Surface backend error directly; never fabricate jobdef names.
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
let spec = {
|
|
44
|
+
name: 'list-jobdefs',
|
|
45
|
+
description: description,
|
|
46
|
+
inputSchema: z.object({
|
|
47
|
+
limit: z.number().optional(),
|
|
48
|
+
start: z.number().optional(),
|
|
49
|
+
where: z.string().optional()
|
|
50
|
+
}),
|
|
51
|
+
// No 'server' required; backend context is implicit in helper
|
|
52
|
+
handler: async (params) => {
|
|
53
|
+
// _listJobdefs handles all validation and defaults
|
|
54
|
+
const result = await _listJobdefs(params);
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return spec;
|
|
59
|
+
}
|
|
60
|
+
export default listJobdefs;
|
|
61
|
+
|