@sassoftware/sas-score-mcp-serverjs 0.4.1 → 1.0.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.skills/agents/sas-viya-scoring-expert.md +58 -0
- package/.skills/copilot-instructions.md +155 -0
- package/.skills/skills/sas-find-library-smart/SKILL.md +154 -0
- package/.skills/skills/sas-list-tables-smart/SKILL.md +127 -0
- package/.skills/skills/sas-read-and-score/SKILL.md +111 -0
- package/.skills/skills/sas-read-strategy/SKILL.md +156 -0
- package/.skills/skills/sas-request-classifier/SKILL.md +69 -0
- package/.skills/skills/sas-score-workflow/SKILL.md +314 -0
- package/cli.js +311 -70
- package/package.json +7 -7
- package/scripts/docs/SCORE_SKILL_REFERENCE.md +142 -0
- package/scripts/docs/TOOL_DESCRIPTION_TEMPLATE.md +157 -0
- package/scripts/docs/TOOL_UPDATES_SUMMARY.md +208 -0
- package/scripts/docs/mcp-localhost-config-guide.md +184 -0
- package/scripts/docs/oauth-http-transport.md +96 -0
- package/scripts/docs/sas-mcp-tools-reference.md +600 -0
- package/scripts/getViyaca.sh +1 -0
- package/scripts/optimize_final.py +140 -0
- package/scripts/optimize_tools.py +99 -0
- package/scripts/setup-skills.js +34 -0
- package/scripts/update_descriptions.py +46 -0
- package/scripts/viyatls.sh +3 -0
- package/src/authpkce.js +219 -0
- package/src/createMcpServer.js +16 -5
- package/src/expressMcpServer.js +350 -308
- package/src/handleGetDelete.js +6 -3
- package/src/hapiMcpServer.js +10 -18
- package/src/oauthHandlers/authorize.js +46 -0
- package/src/oauthHandlers/baseUrl.js +8 -0
- package/src/oauthHandlers/callback.js +96 -0
- package/src/oauthHandlers/getMetadata.js +27 -0
- package/src/oauthHandlers/index.js +7 -0
- package/src/oauthHandlers/token.js +37 -0
- package/src/processHeaders.js +88 -0
- package/src/setupSkills.js +46 -0
- package/src/toolHelpers/_jobSubmit.js +2 -0
- package/src/toolHelpers/_listLibrary.js +55 -39
- package/src/toolHelpers/getLogonPayload.js +7 -1
- package/src/toolHelpers/readCerts.js +4 -4
- package/src/toolHelpers/refreshToken.js +3 -2
- package/src/toolHelpers/refreshTokenOauth.js +3 -3
- package/src/toolSet/.claude/settings.local.json +13 -0
- package/src/toolSet/devaScore.js +61 -69
- package/src/toolSet/findJob.js +38 -71
- package/src/toolSet/findJobdef.js +28 -59
- package/src/toolSet/findLibrary.js +68 -100
- package/src/toolSet/findModel.js +35 -58
- package/src/toolSet/findTable.js +31 -60
- package/src/toolSet/getEnv.js +30 -45
- package/src/toolSet/listJobdefs.js +61 -96
- package/src/toolSet/listJobs.js +61 -110
- package/src/toolSet/listLibraries.js +78 -90
- package/src/toolSet/listModels.js +56 -83
- package/src/toolSet/listTables.js +66 -95
- package/src/toolSet/makeTools.js +1 -0
- package/src/toolSet/modelInfo.js +22 -54
- package/src/toolSet/modelScore.js +35 -77
- package/src/toolSet/readTable.js +63 -104
- package/src/toolSet/runCasProgram.js +32 -52
- package/src/toolSet/runJob.js +24 -24
- package/src/toolSet/runJobdef.js +26 -29
- package/src/toolSet/runMacro.js +82 -82
- package/src/toolSet/runProgram.js +32 -84
- package/src/toolSet/sasQuery.js +77 -126
- package/src/toolSet/sasQueryTemplate.js +4 -5
- package/src/toolSet/sasQueryTemplate2.js +4 -5
- package/src/toolSet/scrInfo.js +4 -7
- package/src/toolSet/scrScore.js +69 -70
- package/src/toolSet/searchAssets.js +5 -6
- package/src/toolSet/setContext.js +65 -92
- package/src/toolSet/superstat.js +61 -60
- package/src/toolSet/tableInfo.js +58 -102
package/src/toolSet/devaScore.js
CHANGED
|
@@ -1,69 +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
|
-
|
|
7
|
-
function devaScore(_appContext) {
|
|
8
|
-
|
|
9
|
-
let description = `
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
console.error( a, b);
|
|
63
|
-
return `MagicScore ${ (a + b) * 42 }`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
return spec;
|
|
68
|
-
}
|
|
69
|
-
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
|
+
handler: async ({ a, b }) => {
|
|
48
|
+
console.error(a, b);
|
|
49
|
+
let r = { score: (a + b) * 42 };
|
|
50
|
+
console.error('deva score result', r);
|
|
51
|
+
return {
|
|
52
|
+
content: [{ type: 'text', text: 'deva score result: ' + JSON.stringify(r) }],
|
|
53
|
+
structuredContent: r
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return spec;
|
|
59
|
+
}
|
|
60
|
+
export default devaScore;
|
|
61
|
+
|
package/src/toolSet/findJob.js
CHANGED
|
@@ -5,88 +5,55 @@
|
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Response Contract
|
|
49
|
-
- Always: { jobs: Array<string|object> }
|
|
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.
|
|
10
|
+
find-job — locate a specific SAS Viya job.
|
|
11
|
+
|
|
12
|
+
USE when: find job, does job exist, is there a job named, lookup job, verify job exists
|
|
13
|
+
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)
|
|
14
|
+
|
|
15
|
+
PARAMETERS
|
|
16
|
+
- name: string (required) — job name to locate; if multiple supplied, use first
|
|
17
|
+
|
|
18
|
+
ROUTING RULES
|
|
19
|
+
- "find job <name>" → { name: "<name>" }
|
|
20
|
+
- "does job <name> exist" → { name: "<name>" }
|
|
21
|
+
- "is there a job named <name>" → { name: "<name>" }
|
|
22
|
+
- "lookup/verify job <name>" → { name: "<name>" }
|
|
23
|
+
- "find job" with no name → ask "Which job name would you like to find?"
|
|
24
|
+
- "find all jobs / list jobs" → use list-jobs instead
|
|
25
|
+
- "run job <name>" → use run-job instead
|
|
26
|
+
|
|
27
|
+
EXAMPLES
|
|
28
|
+
- "find job cars_job_v4" → { name: "cars_job_v4" }
|
|
29
|
+
- "does job ETL exist" → { name: "ETL" }
|
|
30
|
+
- "is there a job named metricsRefresh" → { name: "metricsRefresh" }
|
|
31
|
+
|
|
32
|
+
NEGATIVE EXAMPLES (do not route here)
|
|
33
|
+
- "list jobs" (use list-jobs)
|
|
34
|
+
- "run job cars_job_v4" (use run-job)
|
|
35
|
+
- "execute jobdef cars_job_v4" (use run-jobdef)
|
|
36
|
+
|
|
37
|
+
ERRORS
|
|
38
|
+
Returns { jobs: [] } if not found; { jobs: [name, ...] } if found. Never hallucinate job names.
|
|
74
39
|
`;
|
|
75
40
|
|
|
76
41
|
let spec = {
|
|
77
42
|
name: 'find-job',
|
|
78
|
-
aliases: ['findJob','find job','find_job'],
|
|
79
43
|
description: description,
|
|
80
|
-
|
|
81
|
-
name: z.string()
|
|
82
|
-
|
|
83
|
-
},
|
|
84
|
-
required: ['name'],
|
|
44
|
+
inputSchema: z.object({
|
|
45
|
+
name: z.string()
|
|
46
|
+
}),
|
|
85
47
|
handler: async (params) => {
|
|
86
48
|
let r = await _listJobs(params);
|
|
87
49
|
return r;
|
|
88
50
|
}
|
|
89
51
|
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
/* correct spec for registerTool with inputSchema */
|
|
55
|
+
|
|
90
56
|
return spec;
|
|
91
57
|
}
|
|
92
58
|
export default findJob;
|
|
59
|
+
|
|
@@ -15,76 +15,44 @@ 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 = {
|
|
81
51
|
name: 'find-jobdef',
|
|
82
|
-
aliases: ['findJobdef','find jobdef','find_jobdef'],
|
|
83
52
|
description: description,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
},
|
|
87
|
-
required: ['name'],
|
|
53
|
+
inputSchema: z.object({
|
|
54
|
+
name: z.string()
|
|
55
|
+
}),
|
|
88
56
|
handler: async (params) => {
|
|
89
57
|
let r = await _listJobdefs(params);
|
|
90
58
|
return r;
|
|
@@ -93,3 +61,4 @@ function findJobdef(_appContext) {
|
|
|
93
61
|
return spec;
|
|
94
62
|
}
|
|
95
63
|
export default findJobdef;
|
|
64
|
+
|
|
@@ -1,100 +1,68 @@
|
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Notes
|
|
71
|
-
- For bulk validation of many names, call this tool repeatedly per name.
|
|
72
|
-
- For pagination or discovery, switch to list-libraries.
|
|
73
|
-
`;
|
|
74
|
-
|
|
75
|
-
let spec = {
|
|
76
|
-
name: 'find-library',
|
|
77
|
-
aliases: ['findLibrary','find library','find_library'],
|
|
78
|
-
description: description,
|
|
79
|
-
schema: {
|
|
80
|
-
name: z.string(),
|
|
81
|
-
server: z.string().default('cas')
|
|
82
|
-
},
|
|
83
|
-
required: ['name'],
|
|
84
|
-
handler: async (params) => {
|
|
85
|
-
// normalize server to lowercase & default
|
|
86
|
-
if (!params.server) params.server = 'cas';
|
|
87
|
-
params.server = params.server.toLowerCase();
|
|
88
|
-
|
|
89
|
-
// If multiple names passed (comma or space separated), take the first token (defensive)
|
|
90
|
-
if (params.name && /[,\s]+/.test(params.name.trim())) {
|
|
91
|
-
params.name = params.name.split(/[,\s]+/).filter(Boolean)[0];
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
let r = await _listLibrary(params);
|
|
95
|
-
return r;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return spec;
|
|
99
|
-
}
|
|
100
|
-
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
|
+
server: z.string().optional()
|
|
49
|
+
}),
|
|
50
|
+
|
|
51
|
+
handler: async (params) => {
|
|
52
|
+
// normalize server to lowercase & default
|
|
53
|
+
if (!params.server) params.server = 'cas';
|
|
54
|
+
params.server = params.server.toLowerCase();
|
|
55
|
+
|
|
56
|
+
// If multiple names passed (comma or space separated), take the first token (defensive)
|
|
57
|
+
if (params.name && /[,\s]+/.test(params.name.trim())) {
|
|
58
|
+
params.name = params.name.split(/[,\s]+/).filter(Boolean)[0];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let r = await _listLibrary(params);
|
|
62
|
+
return r;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return spec;
|
|
66
|
+
}
|
|
67
|
+
export default findLibrary;
|
|
68
|
+
|