@sassoftware/sas-score-mcp-serverjs 0.3.19 → 0.4.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/cli.js +141 -26
- package/package.json +4 -3
- package/skills/mcp-tool-description-optimizer/SKILL.md +129 -0
- package/skills/mcp-tool-description-optimizer/references/examples.md +123 -0
- package/skills/sas-read-and-score/SKILL.md +91 -0
- package/skills/sas-read-strategy/SKILL.md +143 -0
- package/skills/sas-score-workflow/SKILL.md +282 -0
- package/src/createMcpServer.js +1 -0
- package/src/expressMcpServer.js +68 -28
- package/src/handleGetDelete.js +6 -3
- package/src/hapiMcpServer.js +30 -0
- package/src/openAPIJson.js +175 -175
- package/src/toolHelpers/_jobSubmit.js +2 -0
- package/src/toolHelpers/_listLibrary.js +56 -39
- package/src/toolHelpers/getLogonPayload.js +9 -7
- package/src/toolHelpers/getStoreOpts.js +1 -2
- package/src/toolHelpers/getToken.js +0 -1
- package/src/toolHelpers/refreshToken.js +48 -46
- package/src/toolHelpers/refreshTokenOauth.js +2 -2
- package/src/toolHelpers/tlogon.js +9 -0
- package/src/toolSet/devaScore.js +30 -38
- package/src/toolSet/findJob.js +23 -49
- package/src/toolSet/findJobdef.js +24 -54
- package/src/toolSet/findLibrary.js +25 -57
- package/src/toolSet/findModel.js +31 -53
- package/src/toolSet/findTable.js +25 -54
- package/src/toolSet/getEnv.js +20 -38
- package/src/toolSet/listJobdefs.js +24 -58
- package/src/toolSet/listJobs.js +24 -72
- package/src/toolSet/listLibraries.js +37 -47
- package/src/toolSet/listModels.js +20 -47
- package/src/toolSet/listTables.js +29 -58
- package/src/toolSet/makeTools.js +3 -0
- package/src/toolSet/modelInfo.js +18 -49
- package/src/toolSet/modelScore.js +27 -69
- package/src/toolSet/readTable.js +25 -62
- package/src/toolSet/runCasProgram.js +23 -43
- package/src/toolSet/runJob.js +20 -19
- package/src/toolSet/runJobdef.js +21 -23
- package/src/toolSet/runMacro.js +20 -20
- package/src/toolSet/runProgram.js +24 -71
- package/src/toolSet/sasQuery.js +23 -70
- package/src/toolSet/scrInfo.js +3 -4
- package/src/toolSet/setContext.js +22 -48
- package/src/toolSet/tableInfo.js +28 -71
- package/src/toolHelpers/getOpts.js +0 -51
- package/src/toolHelpers/getOptsViya.js +0 -44
|
@@ -1,49 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
let {host, token} = params;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { Agent, fetch } from 'undici';
|
|
6
|
+
|
|
7
|
+
async function refreshToken(_appContext, params) {
|
|
8
|
+
let { host, token } = params;
|
|
9
|
+
let url = `${host}/SASLogon/oauth/token`;
|
|
10
|
+
|
|
11
|
+
let aconnect = {
|
|
12
|
+
ca: _appContext.contexts.viyaCert.ca, // trust this CA
|
|
13
|
+
rejectUnauthorized: false // or false, if you really want to bypass checks
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const agent = new Agent(aconnect);
|
|
17
|
+
|
|
18
|
+
console.error('[Info] Refreshing token...', token);
|
|
19
|
+
const ibody = {
|
|
20
|
+
grant_type: 'refresh_token',
|
|
21
|
+
refresh_token: token,
|
|
22
|
+
client_id: 'sas.cli'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
let body = new URLSearchParams(ibody);
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch(url, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: {
|
|
30
|
+
'Accept': 'application/json',
|
|
31
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
32
|
+
dispatcher: agent
|
|
33
|
+
},
|
|
34
|
+
body: body.toString()
|
|
21
35
|
});
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'Accept': 'application/json',
|
|
28
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
29
|
-
dispatcher: agent
|
|
30
|
-
},
|
|
31
|
-
body: body.toString()
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
if (!response.ok) {
|
|
35
|
-
const error = await response.text();
|
|
36
|
-
console.error('[Error] Failed to refresh token: ', error);
|
|
37
|
-
throw new Error(error);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const data = await response.json();
|
|
41
|
-
console.error('[Info] Token refreshed successfully: ', data.access_token);
|
|
42
|
-
return data.access_token;
|
|
43
|
-
} catch (err) {
|
|
44
|
-
console.error('[Error] Failed to refresh token: ', err);
|
|
45
|
-
throw err;
|
|
36
|
+
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
const error = await response.text();
|
|
39
|
+
console.error('[Error] Failed to refresh token: ', error);
|
|
40
|
+
throw new Error(error);
|
|
46
41
|
}
|
|
42
|
+
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
return data.access_token;
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.error('[Error] Failed to refresh token: ', err);
|
|
47
|
+
throw err;
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default refreshToken;
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { Agent, fetch } from 'undici';
|
|
6
|
-
import getOpts from './getOpts.js';
|
|
6
|
+
//import getOpts from './getOpts.js';
|
|
7
7
|
async function refreshTokenOauth(_appContext, oauthInfo ){
|
|
8
8
|
|
|
9
9
|
const url = `${process.env.VIYA_SERVER}/SASLogon/oauth/token`;
|
|
10
|
-
let opts =
|
|
10
|
+
let opts = _appContext.contexts.appCert;
|
|
11
11
|
|
|
12
12
|
const agent = new Agent({
|
|
13
13
|
connect: opts
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import getLogonPayload from "./getLogonPayload.js";
|
|
2
|
+
|
|
3
|
+
async function tlogon(_appContext) {
|
|
4
|
+
console.error('[Info] Starting tlogon...');
|
|
5
|
+
let r = await getLogonPayload(_appContext);
|
|
6
|
+
console.error('[Info] tlogon completed');
|
|
7
|
+
return r;
|
|
8
|
+
}
|
|
9
|
+
export default tlogon;
|
package/src/toolSet/devaScore.js
CHANGED
|
@@ -7,49 +7,36 @@ import {z} from 'zod';
|
|
|
7
7
|
function devaScore(_appContext) {
|
|
8
8
|
|
|
9
9
|
let description = `
|
|
10
|
-
|
|
10
|
+
deva-score — compute a numeric score based on two input values.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- User wants to calculate the deva score: "Calculate deva score for 5 and 10"
|
|
15
|
-
- User provides two numbers for scoring: "Score these values: 3 and 7"
|
|
16
|
-
- User wants to compute a score in a series: "Calculate scores for [list of numbers]"
|
|
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
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- Looking up data or metadata
|
|
15
|
+
PARAMETERS
|
|
16
|
+
- a: number (required) — first input value
|
|
17
|
+
- b: number (required) — second input value
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
Compute a numeric deva score by applying the formula (a + b) * 42 to two input numbers. For scoring more than two numbers, call this tool multiple times using the previous result as the first input (left-to-right fold).
|
|
19
|
+
FORMULA: (a + b) * 42
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
- a
|
|
28
|
-
-
|
|
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)
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
Returns a numeric result: (a + b) * 42
|
|
32
|
-
The result is always a number representing the computed deva score.
|
|
33
|
-
|
|
34
|
-
Disambiguation & Clarification
|
|
35
|
-
- If user provides more than two numbers without clear instructions: "Do you want to calculate the deva score by combining these numbers left-to-right?"
|
|
36
|
-
- If user provides non-numeric input: "Please provide numeric values"
|
|
37
|
-
|
|
38
|
-
Examples (→ mapped params)
|
|
27
|
+
EXAMPLES
|
|
39
28
|
- "Calculate deva score for 5 and 10" → { a: 5, b: 10 } returns 630
|
|
40
29
|
- "Score 1 and 2" → { a: 1, b: 2 } returns 126
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
Negative Examples (should NOT call deva-score)
|
|
44
|
-
- "Score this customer with the credit model" (use model-score instead)
|
|
45
|
-
- "Calculate the mean of these values" (use run-sas-program or sas-query instead)
|
|
30
|
+
- "Deva score 20 and 30" → { a: 20, b: 30 } returns 2100
|
|
46
31
|
|
|
47
|
-
|
|
48
|
-
-
|
|
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)
|
|
49
36
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`;
|
|
37
|
+
RESPONSE
|
|
38
|
+
Returns { score: (a + b) * 42 }
|
|
39
|
+
`;
|
|
53
40
|
let spec = {
|
|
54
41
|
name: 'deva-score',
|
|
55
42
|
aliases: ['devaScore','deva score','deva_score'],
|
|
@@ -58,12 +45,17 @@ For sequences of numbers, use a left-to-right fold: call devaScore(first, second
|
|
|
58
45
|
a: z.number(),
|
|
59
46
|
b: z.number()
|
|
60
47
|
},
|
|
61
|
-
handler: async ({ a, b
|
|
48
|
+
handler: async ({ a, b }) => {
|
|
62
49
|
console.error( a, b);
|
|
63
|
-
|
|
64
|
-
|
|
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
|
+
}
|
|
65
57
|
}
|
|
66
|
-
|
|
58
|
+
|
|
67
59
|
return spec;
|
|
68
60
|
}
|
|
69
61
|
export default devaScore;
|
package/src/toolSet/findJob.js
CHANGED
|
@@ -16,61 +16,35 @@ function findJob(_appContext) {
|
|
|
16
16
|
"behavior": "Return only JSON matching response_schema when invoked by an LLM. If no matches, return { jobs: [] }"
|
|
17
17
|
};
|
|
18
18
|
let description = `
|
|
19
|
-
|
|
19
|
+
find-job — locate a specific SAS Viya job.
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- "find job cars_job_v4"
|
|
24
|
-
- "does job sales_summary exist"
|
|
25
|
-
- "is there a job named churnScorer"
|
|
26
|
-
- "lookup job forecast_monthly"
|
|
27
|
-
- "verify job ETL_Daily"
|
|
21
|
+
USE when: find job, does job exist, is there a job named, lookup job, verify job exists
|
|
22
|
+
DO NOT USE for: list jobs (use list-jobs), run job (use run-job), execute jobdef (use run-jobdef), find lib/table/model (use respective tools)
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- Do not use this tool if the user want to find lib, find table, find model and similar requests
|
|
32
|
-
- Executing a job (use job)
|
|
33
|
-
- Running a job definition (use jobdef)
|
|
34
|
-
- Submitting arbitrary code (use program)
|
|
24
|
+
PARAMETERS
|
|
25
|
+
- name: string (required) — job name to locate; if multiple supplied, use first
|
|
35
26
|
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
ROUTING RULES
|
|
28
|
+
- "find job <name>" → { name: "<name>" }
|
|
29
|
+
- "does job <name> exist" → { name: "<name>" }
|
|
30
|
+
- "is there a job named <name>" → { name: "<name>" }
|
|
31
|
+
- "lookup/verify job <name>" → { name: "<name>" }
|
|
32
|
+
- "find job" with no name → ask "Which job name would you like to find?"
|
|
33
|
+
- "find all jobs / list jobs" → use list-jobs instead
|
|
34
|
+
- "run job <name>" → use run-job instead
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
EXAMPLES
|
|
37
|
+
- "find job cars_job_v4" → { name: "cars_job_v4" }
|
|
38
|
+
- "does job ETL exist" → { name: "ETL" }
|
|
39
|
+
- "is there a job named metricsRefresh" → { name: "metricsRefresh" }
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- If no name provided: ask "Which job name would you like to find?".
|
|
41
|
+
NEGATIVE EXAMPLES (do not route here)
|
|
42
|
+
- "list jobs" (use list-jobs)
|
|
43
|
+
- "run job cars_job_v4" (use run-job)
|
|
44
|
+
- "execute jobdef cars_job_v4" (use run-jobdef)
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
- On error: propagate structured server error (do not wrap in prose when invoked programmatically).
|
|
51
|
-
|
|
52
|
-
Disambiguation Rules
|
|
53
|
-
- Input only "find job" → ask for missing name.
|
|
54
|
-
- Input contains verbs like "run" or "execute" → use run-job or run-jobdef instead.
|
|
55
|
-
- Input requesting many (e.g., "find all jobs") → use list-jobs.
|
|
56
|
-
|
|
57
|
-
Examples (→ mapped params)
|
|
58
|
-
- "find job cars_job_v4" → { name: "cars_job_v4" }
|
|
59
|
-
- "does job ETL exist" → { name: "ETL" }
|
|
60
|
-
- "is there a job named metricsRefresh" → { name: "metricsRefresh" }
|
|
61
|
-
|
|
62
|
-
Negative Examples (should NOT call find-job)
|
|
63
|
-
- "list jobs" (list-jobs)
|
|
64
|
-
- "run job cars_job_v4" (run-job)
|
|
65
|
-
- "execute jobdef cars_job_v4" (run-jobdef)
|
|
66
|
-
|
|
67
|
-
Clarifying Question Template
|
|
68
|
-
- Missing name: "Which job name would you like to find?"
|
|
69
|
-
- Multiple names: "Please provide just one job name (e.g. 'cars_job_v4')."
|
|
70
|
-
|
|
71
|
-
Notes
|
|
72
|
-
- For bulk existence checks loop over names and call find-job per name.
|
|
73
|
-
- Combine with run-job tool if user wants to execute after confirming existence.
|
|
46
|
+
ERRORS
|
|
47
|
+
Returns { jobs: [] } if not found; { jobs: [name, ...] } if found. Never hallucinate job names.
|
|
74
48
|
`;
|
|
75
49
|
|
|
76
50
|
let spec = {
|
|
@@ -15,66 +15,36 @@ function findJobdef(_appContext) {
|
|
|
15
15
|
"behavior": "Return only JSON matching response_schema when invoked by an LLM. If no matches, return { jobs: [] }"
|
|
16
16
|
};
|
|
17
17
|
let description = `
|
|
18
|
-
|
|
18
|
+
find-jobdef — locate a specific SAS Viya job definition.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- "find jobdef cars_job_v4"
|
|
23
|
-
- "does jobdef sales_summary exist"
|
|
24
|
-
- "is there a jobdef named churnScorer"
|
|
25
|
-
- "lookup jobdef forecast_monthly"
|
|
26
|
-
- "verify jobdef ETL_Daily"
|
|
20
|
+
USE when: find jobdef, does jobdef exist, is there a jobdef named, lookup jobdef, verify jobdef exists
|
|
21
|
+
DO NOT USE for: list jobdefs (use list-jobdefs), run jobdef (use run-jobdef), find job/lib/table/model (use respective tools)
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- find table (use find-table
|
|
31
|
-
- find model (use find-model)
|
|
32
|
-
- find lib (use find-library)
|
|
33
|
-
- Executing a job (use job)
|
|
34
|
-
- Running a job definition (use jobdef)
|
|
35
|
-
- Submitting arbitrary code (use program)
|
|
23
|
+
PARAMETERS
|
|
24
|
+
- name: string (required) — jobdef name to locate; if multiple supplied, use first
|
|
36
25
|
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
ROUTING RULES
|
|
27
|
+
- "find jobdef <name>" → { name: "<name>" }
|
|
28
|
+
- "does jobdef <name> exist" → { name: "<name>" }
|
|
29
|
+
- "is there a jobdef named <name>" → { name: "<name>" }
|
|
30
|
+
- "lookup/verify jobdef <name>" → { name: "<name>" }
|
|
31
|
+
- "find jobdef" with no name → ask "Which jobdef name would you like to find?"
|
|
32
|
+
- "find all jobdefs / list jobdefs" → use list-jobdefs instead
|
|
33
|
+
- "run jobdef <name>" → use run-jobdef instead
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
EXAMPLES
|
|
36
|
+
- "find jobdef cars_job_v4" → { name: "cars_job_v4" }
|
|
37
|
+
- "does jobdef ETL exist" → { name: "ETL" }
|
|
38
|
+
- "is there a jobdef named metricsRefresh" → { name: "metricsRefresh" }
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
NEGATIVE EXAMPLES (do not route here)
|
|
41
|
+
- "list jobdefs" (use list-jobdefs)
|
|
42
|
+
- "run jobdef cars_job_v4" (use run-jobdef)
|
|
43
|
+
- "find job ETL" (use find-job)
|
|
44
|
+
- "find table cars" (use find-table)
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
- On error: propagate structured server error (do not wrap in prose when invoked programmatically).
|
|
52
|
-
|
|
53
|
-
Disambiguation Rules
|
|
54
|
-
- Input only "find job" → ask for missing name.
|
|
55
|
-
- Input contains verbs like "run" or "execute" → use run-job or run-jobdef instead.
|
|
56
|
-
- Input requesting many (e.g., "find all jobs") → use list-jobs.
|
|
57
|
-
|
|
58
|
-
Examples (→ mapped params)
|
|
59
|
-
- "find jobdef cars_job_v4" → { name: "cars_job_v4" }
|
|
60
|
-
- "does jobdef ETL exist" → { name: "ETL" }
|
|
61
|
-
- "is there a jobdef named metricsRefresh" → { name: "metricsRefresh" }
|
|
62
|
-
|
|
63
|
-
Negative Examples (should NOT call find-jobdef)
|
|
64
|
-
- find lib (use find-library)
|
|
65
|
-
- find table (use find-table)
|
|
66
|
-
- find model (use find-model)
|
|
67
|
-
- "list job" (list-jobdefs)
|
|
68
|
-
- "run job cars_job_v4" (run-job)
|
|
69
|
-
- "execute jobdef cars_job_v4" (run-jobdef)
|
|
70
|
-
|
|
71
|
-
Clarifying Question Template
|
|
72
|
-
- Missing name: "Which jobdef name would you like to find?"
|
|
73
|
-
- Multiple names: "Please provide just one jobdef name (e.g. 'cars_job_v4')."
|
|
74
|
-
|
|
75
|
-
Notes
|
|
76
|
-
- For bulk existence checks loop over names and call find-jobdef per name.
|
|
77
|
-
- Combine with run-jobdef tool if user wants to execute after confirming existence.
|
|
46
|
+
ERRORS
|
|
47
|
+
Returns { jobdefs: [] } if not found; { jobdefs: [name, ...] } if found. Never hallucinate jobdef names.
|
|
78
48
|
`;
|
|
79
49
|
|
|
80
50
|
let spec = {
|
|
@@ -7,69 +7,37 @@ import _listLibrary from '../toolHelpers/_listLibrary.js';
|
|
|
7
7
|
function findLibrary(_appContext) {
|
|
8
8
|
|
|
9
9
|
let description = `
|
|
10
|
-
|
|
10
|
+
find-library — locate a specific CAS or SAS library.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- "find library Public"
|
|
15
|
-
- "find lib Public"
|
|
16
|
-
- "does library SASHELP exist"
|
|
17
|
-
- "is PUBLIC library available in cas"
|
|
18
|
-
- "lookup library sasuser in sas"
|
|
19
|
-
- "show me library metadata for Models"
|
|
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)
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- find model -> use find-model
|
|
25
|
-
- find table -> use find-table
|
|
26
|
-
- find job -> use find-job
|
|
27
|
-
- find jobdef -> use find-jobdef
|
|
28
|
-
- Columns or schema of a table (use table-info)
|
|
29
|
-
- Creating/assigning libraries (use run-sas-program or another admin tool)
|
|
30
|
-
|
|
31
|
-
Purpose
|
|
32
|
-
Quickly verify whether a single named library exists on CAS or SAS and return its entry (or empty if not found).
|
|
33
|
-
|
|
34
|
-
Parameters
|
|
35
|
-
- name (string, required) : Exact library (caslib) name to locate. If multiple names provided (comma/space separated), use the first and ignore the rest; optionally ask for one name.
|
|
36
|
-
- server (cas|sas, default 'cas') : Target environment. If omitted or ambiguous default to 'cas'.
|
|
37
|
-
|
|
38
|
-
Behavior & Matching
|
|
39
|
-
- Performs an exact name match (case-insensitive where backend supports it).
|
|
40
|
-
- Returns an object: { libraries: [...] } where the array contains zero or one items (backend may still return richer metadata).
|
|
41
|
-
- If no match: { libraries: [] } (do NOT fabricate suggestions).
|
|
42
|
-
- If user supplies no name: ask a clarifying question: "Which library name would you like to find?".
|
|
43
|
-
- If user clearly wants a list ("list", "show all", "enumerate") route to listLibrary instead.
|
|
44
|
-
|
|
45
|
-
Response Contract
|
|
46
|
-
- Always: { libraries: Array<string|object> }
|
|
47
|
-
- Never include prose when invoked programmatically; only the JSON structure.
|
|
48
|
-
|
|
49
|
-
Disambiguation Rules
|
|
50
|
-
- Input only "find library" → ask for the missing name.
|
|
51
|
-
- Input with both library name and words like "tables" → prefer listTables.
|
|
52
|
-
- Input like "find libraries" (plural) → prefer listLibrary unless user clarifies to a single name.
|
|
15
|
+
PARAMETERS
|
|
16
|
+
- name: string (required) — library/caslib name; if multiple supplied, use first
|
|
17
|
+
- server: 'cas' | 'sas' (default: 'cas') — target environment
|
|
53
18
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
59
27
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- "describe table cars in sashelp" (table-info)
|
|
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" }
|
|
65
32
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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)
|
|
69
38
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
- For pagination or discovery, switch to list-libraries.
|
|
39
|
+
ERRORS
|
|
40
|
+
Returns { libraries: [] } if not found; { libraries: [name, ...] } if found. Never hallucinate library names.
|
|
73
41
|
`;
|
|
74
42
|
|
|
75
43
|
let spec = {
|
package/src/toolSet/findModel.js
CHANGED
|
@@ -9,59 +9,37 @@ import _listModels from '../toolHelpers/_listModels.js';
|
|
|
9
9
|
|
|
10
10
|
function findModel(_appContext) {
|
|
11
11
|
let description = `
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- Never return prose when invoked programmatically; only the JSON structure.
|
|
44
|
-
- On error: surface backend error object directly (no rewriting) so the caller can display/log it.
|
|
45
|
-
|
|
46
|
-
Disambiguation & Clarification
|
|
47
|
-
- Missing name (e.g., "find model") → ask: "Which model name would you like to find?"
|
|
48
|
-
- Plural intent (e.g., "find models" / "list models") → use list-models instead.
|
|
49
|
-
- If user requests scoring ("score model X") → route to model-score not find-model.
|
|
50
|
-
|
|
51
|
-
Examples (→ mapped params)
|
|
52
|
-
- "find model myModel" → { name: "myModel" }
|
|
53
|
-
- "does model churn_score exist" → { name: "churn_score" }
|
|
54
|
-
- "is model riskModel deployed" → { name: "riskModel" }
|
|
55
|
-
- "lookup model claims_fraud_v1" → { name: "claims_fraud_v1" }
|
|
56
|
-
|
|
57
|
-
Negative Examples (should NOT call find-model)
|
|
58
|
-
- "list models" (list-models)
|
|
59
|
-
- "score model myModel" (model-score)
|
|
60
|
-
- "describe model myModel" (model-info)
|
|
61
|
-
|
|
62
|
-
Notes
|
|
63
|
-
- Chain usage: find-model → model-info → model-score.
|
|
64
|
-
- For batch existence checks iterate over a list and call find-model per entry.
|
|
12
|
+
find-model — locate a specific model deployed to MAS (Model Aggregation Service).
|
|
13
|
+
|
|
14
|
+
USE when: find model, does model exist, is model deployed, lookup model, verify model exists
|
|
15
|
+
DO NOT USE for: list models (use list-models), model info/variables (use model-info), score model (use model-score), find table/job/lib (use respective tools), scr models (use scr-info/scr-score)
|
|
16
|
+
|
|
17
|
+
PARAMETERS
|
|
18
|
+
- name: string (required) — model name to locate; if multiple supplied, use first
|
|
19
|
+
|
|
20
|
+
ROUTING RULES
|
|
21
|
+
- "find model <name>" → { name: "<name>" }
|
|
22
|
+
- "does model <name> exist" → { name: "<name>" }
|
|
23
|
+
- "is model <name> deployed" → { name: "<name>" }
|
|
24
|
+
- "lookup/verify model <name>" → { name: "<name>" }
|
|
25
|
+
- "find model" with no name → ask "Which model name would you like to find?"
|
|
26
|
+
- "find all models / list models" → use list-models instead
|
|
27
|
+
- "score model <name>" → use model-score instead
|
|
28
|
+
- "describe model / model info" → use model-info instead
|
|
29
|
+
|
|
30
|
+
EXAMPLES
|
|
31
|
+
- "find model myModel" → { name: "myModel" }
|
|
32
|
+
- "does model churn_score exist" → { name: "churn_score" }
|
|
33
|
+
- "is model riskModel deployed" → { name: "riskModel" }
|
|
34
|
+
- "lookup model claims_fraud_v1" → { name: "claims_fraud_v1" }
|
|
35
|
+
|
|
36
|
+
NEGATIVE EXAMPLES (do not route here)
|
|
37
|
+
- "list models" (use list-models)
|
|
38
|
+
- "score model myModel" (use model-score)
|
|
39
|
+
- "model info for churnRisk" (use model-info)
|
|
40
|
+
|
|
41
|
+
ERRORS
|
|
42
|
+
Returns { models: [] } if not found; { models: [name, ...] } if found. Never hallucinate model names.
|
|
65
43
|
`;
|
|
66
44
|
|
|
67
45
|
let spec = {
|