@sassoftware/sas-score-mcp-serverjs 1.0.1-2 → 1.0.1-22
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-score-mcp-serverjs-agent.md +190 -0
- package/.skills/copilot-instructions.md +226 -0
- package/.skills/skills/README.md +125 -0
- package/.skills/skills/detail-strategy/SKILL.md +234 -0
- package/.skills/skills/find-resources/SKILL.md +152 -0
- package/.skills/skills/list-resource/SKILL.md +245 -0
- package/.skills/skills/read-strategy/SKILL.md +134 -0
- package/.skills/skills/request-routing/SKILL.md +95 -0
- package/.skills/skills/score-strategy/SKILL.md +210 -0
- package/README.md +9 -1
- package/cli.js +37 -27
- package/package.json +7 -8
- package/scripts/setup-skills.js +1 -1
- package/src/oauthHandlers/callback.js +1 -1
- package/src/processHeaders.js +1 -1
- package/src/setupSkills.js +12 -7
- package/src/setupSkills.v1.js +79 -0
- package/src/toolHelpers/_listLibrary.js +1 -1
- package/src/toolHelpers/_listTables.js +1 -1
- package/src/toolSet/findJobdef.js +5 -5
- package/src/toolSet/modelScore.js +23 -25
- package/src/toolSet/runCasProgram.js +21 -10
- package/src/toolSet/runJob.js +15 -19
- package/src/toolSet/runJobdef.js +15 -19
- package/src/toolSet/scrScore.js +60 -69
- package/src/toolSet/.claude/settings.local.json +0 -13
- /package/src/{handleGetDelete.js → handleGetDelete.txt} +0 -0
- /package/src/{handleRequest.js → handleRequest.txt} +0 -0
- /package/src/{hapiMcpServer.js → hapiMcpServer.txt} +0 -0
package/src/setupSkills.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import os from 'os';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
|
|
7
|
-
function setupSkills(clientName) {
|
|
7
|
+
function setupSkills(clientName,agentFolder) {
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
// Paths
|
|
10
10
|
let destination;
|
|
@@ -13,10 +13,13 @@ function setupSkills(clientName) {
|
|
|
13
13
|
destination = path.join(process.cwd(), clientName);
|
|
14
14
|
clientName = clientName.slice(1);
|
|
15
15
|
} else {
|
|
16
|
-
destination = path.join(os.homedir(), '.' + clientName);
|
|
16
|
+
destination = path.join(os.homedir(), '.' + clientName );
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
if (agentFolder) {
|
|
20
|
+
destination = path.join(destination, agentFolder);
|
|
21
|
+
}
|
|
22
|
+
const source = path.join(__dirname, `../.skills`);
|
|
20
23
|
console.error("==================================================================");
|
|
21
24
|
console.error(` Copying ${source} to ${destination}...`);
|
|
22
25
|
|
|
@@ -25,22 +28,24 @@ function setupSkills(clientName) {
|
|
|
25
28
|
if (!fs.existsSync(to)) fs.mkdirSync(to, { recursive: true });;
|
|
26
29
|
fs.readdirSync(from).forEach(element => {
|
|
27
30
|
const fromPath = path.join(from, element);
|
|
28
|
-
|
|
31
|
+
let toPath = path.join(to, element);
|
|
32
|
+
if (clientName === 'claude' && element === 'copilot-instructions.md') {
|
|
33
|
+
toPath = path.join(to, 'CLAUDE.md');
|
|
34
|
+
}
|
|
29
35
|
if (fs.lstatSync(fromPath).isFile()) {
|
|
30
36
|
console.error(` 📄 Copying file: ${element}`);
|
|
31
37
|
fs.copyFileSync(fromPath, toPath);
|
|
32
38
|
} else if (fs.lstatSync(fromPath).isDirectory()) {
|
|
33
39
|
console.error(`📂 Copying folder: ${element}`);
|
|
34
|
-
copyFolderSync(fromPath, toPath)
|
|
40
|
+
copyFolderSync(fromPath, toPath);
|
|
35
41
|
}
|
|
36
42
|
});
|
|
37
43
|
}
|
|
38
|
-
|
|
39
44
|
function listExpandedFolder(dir, indent = "") {
|
|
40
45
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
41
46
|
|
|
42
47
|
for (const entry of entries) {
|
|
43
|
-
console.
|
|
48
|
+
console.error(indent + entry.name);
|
|
44
49
|
|
|
45
50
|
if (entry.isDirectory()) {
|
|
46
51
|
listExpandedFolder(path.join(dir, entry.name), indent + " ");
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
function setupSkills(clientName,agentFolder) {
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
// Paths
|
|
10
|
+
let destination;
|
|
11
|
+
if (clientName.startsWith('.')) {
|
|
12
|
+
console.error('-----------------------',process.cwd());
|
|
13
|
+
destination = path.join(process.cwd(), clientName);
|
|
14
|
+
clientName = clientName.slice(1);
|
|
15
|
+
} else {
|
|
16
|
+
destination = path.join(os.homedir(), '.' + clientName );
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (agentFolder) {
|
|
20
|
+
destination = path.join(destination, agentFolder);
|
|
21
|
+
}
|
|
22
|
+
const source = path.join(__dirname, `../.skills` + '_' + clientName.toLowerCase());
|
|
23
|
+
console.error("==================================================================");
|
|
24
|
+
console.error(` Copying ${source} to ${destination}...`);
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
// Copy agents folder if it exists
|
|
29
|
+
let agentsFromPath = path.join(__dirname, `../.agents`);
|
|
30
|
+
let agentsToPath = path.join(destination, 'agents');
|
|
31
|
+
copyFolderSync(agentsFromPath, agentsToPath);
|
|
32
|
+
|
|
33
|
+
// now copy the skills folder to the destination
|
|
34
|
+
let toPath = path.join(destination, 'skills');
|
|
35
|
+
let fromPath = path.join(__dirname, `../.skills`);
|
|
36
|
+
copyFolderSync(fromPath, toPath);
|
|
37
|
+
|
|
38
|
+
// Now copy instructions
|
|
39
|
+
let instructionsFromPath = path.join(__dirname, `../.instructions`);
|
|
40
|
+
let instructionsToPath = destination;
|
|
41
|
+
copyFolderSync(instructionsFromPath, instructionsToPath);
|
|
42
|
+
|
|
43
|
+
function copyFolderSync(from, to) {
|
|
44
|
+
if (!fs.existsSync(from)) return [];
|
|
45
|
+
if (!fs.existsSync(to)) fs.mkdirSync(to, { recursive: true });;
|
|
46
|
+
fs.readdirSync(from).forEach(element => {
|
|
47
|
+
const fromPath = path.join(from, element);
|
|
48
|
+
let toPath = path.join(to, element);
|
|
49
|
+
if (clientName === 'claude' && element === 'copilot-instructions.md') {
|
|
50
|
+
toPath = path.join(to, 'CLAUDE.md');
|
|
51
|
+
}
|
|
52
|
+
if (fs.lstatSync(fromPath).isFile()) {
|
|
53
|
+
console.error(` 📄 Copying file: ${element}`);
|
|
54
|
+
fs.copyFileSync(fromPath, toPath);
|
|
55
|
+
} else if (fs.lstatSync(fromPath).isDirectory()) {
|
|
56
|
+
console.error(`📂 Copying folder: ${element}`);
|
|
57
|
+
copyFolderSync(fromPath, toPath);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function listExpandedFolder(dir, indent = "") {
|
|
62
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
63
|
+
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
console.error(indent + entry.name);
|
|
66
|
+
|
|
67
|
+
if (entry.isDirectory()) {
|
|
68
|
+
listExpandedFolder(path.join(dir, entry.name), indent + " ");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
copyFolderSync(source, destination);
|
|
74
|
+
//listExpandedFolder(destination);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error('❌ Error copying files:', err.message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export default setupSkills;
|
|
@@ -54,7 +54,7 @@ async function _listLibrary(params) {
|
|
|
54
54
|
if (appControl != null) {
|
|
55
55
|
await deleteSession(appControl);
|
|
56
56
|
}
|
|
57
|
-
return { isError: true, content: [{ type: 'text', text: JSON.stringify(err) }] };
|
|
57
|
+
return { isError: true, content: [{ type: 'text', text: (typeof err === 'string') ? err : JSON.stringify(err) }] };
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -44,7 +44,7 @@ async function _listTables(params) {
|
|
|
44
44
|
structuredContent: response};
|
|
45
45
|
} catch (err) {
|
|
46
46
|
console.error(JSON.stringify(err));
|
|
47
|
-
return {isError: true, content: [{ type: 'text', text: JSON.stringify(err) }] }
|
|
47
|
+
return {isError: true, content: [{ type: 'text', text: (typeof err === 'string') ? err : JSON.stringify(err) }] }
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
};
|
|
@@ -18,7 +18,7 @@ function findJobdef(_appContext) {
|
|
|
18
18
|
find-jobdef — locate a specific SAS Viya job definition.
|
|
19
19
|
|
|
20
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)
|
|
21
|
+
DO NOT USE for: list jobdefs (use sas-score-list-jobdefs), run jobdef (use sas-score-run-jobdef), find job/lib/table/model (use respective tools)
|
|
22
22
|
|
|
23
23
|
PARAMETERS
|
|
24
24
|
- name: string (required) — jobdef name to locate; if multiple supplied, use first
|
|
@@ -38,10 +38,10 @@ EXAMPLES
|
|
|
38
38
|
- "is there a jobdef named metricsRefresh" → { name: "metricsRefresh" }
|
|
39
39
|
|
|
40
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)
|
|
41
|
+
- "list jobdefs" (use sas-score-list-jobdefs)
|
|
42
|
+
- "run jobdef cars_job_v4" (use sas-score-run-jobdef)
|
|
43
|
+
- "find job ETL" (use sas-score-find-job)
|
|
44
|
+
- "find table cars" (use sas-score-find-table)
|
|
45
45
|
|
|
46
46
|
ERRORS
|
|
47
47
|
Returns { jobdefs: [] } if not found; { jobdefs: [name, ...] } if found. Never hallucinate jobdef names.
|
|
@@ -17,16 +17,16 @@ DO NOT USE for: find model, model metadata, list models, run programs/jobs, quer
|
|
|
17
17
|
|
|
18
18
|
PARAMETERS
|
|
19
19
|
- model: string — model name (required, exact match)
|
|
20
|
-
- scenario:
|
|
21
|
-
|
|
20
|
+
- scenario: object — input data as JSON (optional, defaults to {}). Example: {age:45, income:60000}
|
|
21
|
+
|
|
22
22
|
|
|
23
23
|
ROUTING RULES
|
|
24
|
-
- "score with model X using a=1, b=2" → { model: "X", scenario: {a:1, b:2}
|
|
25
|
-
- "predict using model Y with age=45, income=60000" → { model: "Y", scenario: {age:45, income:60000}
|
|
24
|
+
- "score with model X using a=1, b=2" → { model: "X", scenario: {a:1, b:2} }
|
|
25
|
+
- "predict using model Y with age=45, income=60000" → { model: "Y", scenario: {age:45, income:60000} }
|
|
26
26
|
|
|
27
27
|
EXAMPLES
|
|
28
|
-
- "score with model churn using age=45, income=60000" → { model: "churn", scenario: {age:45, income:60000}
|
|
29
|
-
- "predict creditScore for credit=700, debt=20000" → { model: "creditScore", scenario: {credit:700, debt:20000}
|
|
28
|
+
- "score with model churn using age=45, income=60000" → { model: "churn", scenario: {age:45, income:60000} }
|
|
29
|
+
- "predict creditScore for credit=700, debt=20000" → { model: "creditScore", scenario: {credit:700, debt:20000} }
|
|
30
30
|
|
|
31
31
|
NEGATIVE EXAMPLES (do not route here)
|
|
32
32
|
- "find model X" (use find-model)
|
|
@@ -42,40 +42,38 @@ Returns predictions, probabilities, scores merged with input data. Returns error
|
|
|
42
42
|
let spec = {
|
|
43
43
|
name: 'mas-score',
|
|
44
44
|
description: description,
|
|
45
|
-
inputSchema:z.object({
|
|
45
|
+
inputSchema: z.object({
|
|
46
46
|
model: z.string(),
|
|
47
|
-
scenario: z.
|
|
48
|
-
uflag: z.boolean().optional()
|
|
47
|
+
scenario: z.any()
|
|
49
48
|
}),
|
|
50
49
|
|
|
51
50
|
handler: async (iparams) => {
|
|
52
|
-
let params = {...iparams};
|
|
51
|
+
let params = {...iparams};
|
|
53
52
|
let scenario = params.scenario;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
let scenarioObj ={};
|
|
58
|
-
|
|
53
|
+
|
|
54
|
+
// Convert the scenario string to an object
|
|
55
|
+
// Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
|
|
56
|
+
let scenarioObj = {};
|
|
57
|
+
let count = 0;
|
|
59
58
|
if (typeof scenario === 'object') {
|
|
60
59
|
scenarioObj = scenario;
|
|
61
|
-
} else if (Array.isArray(scenario)) {
|
|
60
|
+
} else if (Array.isArray(scenario)) {
|
|
62
61
|
scenarioObj = scenario[0];
|
|
63
62
|
} else {
|
|
64
63
|
//console.error('Incoming scenario', scenario);
|
|
65
64
|
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
params.scenario= scenarioObj;
|
|
73
|
-
|
|
65
|
+
let [key, value] = pair.split('=');
|
|
66
|
+
acc[key.trim()] = value;
|
|
67
|
+
count++;
|
|
68
|
+
return acc;
|
|
69
|
+
}, {});
|
|
70
|
+
}
|
|
71
|
+
params.scenario = scenarioObj;
|
|
74
72
|
// Drop model extension (e.g., .job, .model)
|
|
75
73
|
if (params.model && params.model.includes('.')) {
|
|
76
74
|
params.model = params.model.substring(0, params.model.lastIndexOf('.'));
|
|
77
75
|
}
|
|
78
|
-
|
|
76
|
+
|
|
79
77
|
log('modelScore params', params);
|
|
80
78
|
// Check if the params.scenario is a string and parse it
|
|
81
79
|
let r = await _masScoring(params)
|
|
@@ -45,10 +45,10 @@ Log output and CAS results. If output table specified, returned as markdown tabl
|
|
|
45
45
|
description: description,
|
|
46
46
|
inputSchema:z.object({
|
|
47
47
|
src: z.string(),
|
|
48
|
-
scenario: z.
|
|
49
|
-
output: z.string().optional,
|
|
50
|
-
folder: z.string().optional,
|
|
51
|
-
limit: z.number().optional
|
|
48
|
+
scenario: z.any(),
|
|
49
|
+
output: z.string().optional(),
|
|
50
|
+
folder: z.string().optional(),
|
|
51
|
+
limit: z.number().optional()
|
|
52
52
|
}),
|
|
53
53
|
|
|
54
54
|
// NOTE: Previously 'required' incorrectly listed 'program' which does not
|
|
@@ -70,15 +70,26 @@ Log output and CAS results. If output table specified, returned as markdown tabl
|
|
|
70
70
|
`;
|
|
71
71
|
}
|
|
72
72
|
// figure out macros
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
|
|
74
|
+
// Convert the scenario string to an object
|
|
75
|
+
// Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
|
|
76
|
+
let scenarioObj = {};
|
|
77
|
+
let count = 0;
|
|
78
|
+
if (typeof scenario === 'object') {
|
|
79
|
+
scenarioObj = scenario;
|
|
80
|
+
} else if (Array.isArray(scenario)) {
|
|
81
|
+
scenarioObj = scenario[0];
|
|
82
|
+
} else {
|
|
83
|
+
//console.error('Incoming scenario', scenario);
|
|
84
|
+
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
85
|
+
let [key, value] = pair.split('=');
|
|
86
|
+
acc[key.trim()] = value;
|
|
87
|
+
count++;
|
|
79
88
|
return acc;
|
|
80
89
|
}, {});
|
|
81
90
|
}
|
|
91
|
+
params.scenario = scenarioObj;
|
|
92
|
+
|
|
82
93
|
let iparms = {
|
|
83
94
|
args: scenario,
|
|
84
95
|
output: params.output,
|
package/src/toolSet/runJob.js
CHANGED
|
@@ -16,7 +16,7 @@ DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro)
|
|
|
16
16
|
|
|
17
17
|
PARAMETERS
|
|
18
18
|
- name: string — job name (required)
|
|
19
|
-
- scenario:
|
|
19
|
+
- scenario: object — input parameters as JSON (optional, defaults to {}). Example: {month:10, year:2025}
|
|
20
20
|
|
|
21
21
|
ROUTING RULES
|
|
22
22
|
- "run job xyz" → { name: "xyz" }
|
|
@@ -41,34 +41,30 @@ Returns log output, listings, tables from job. Error if job not found.
|
|
|
41
41
|
description: description,
|
|
42
42
|
inputSchema: z.object({
|
|
43
43
|
name: z.string(),
|
|
44
|
-
scenario: z.
|
|
44
|
+
scenario: z.any()
|
|
45
45
|
}),
|
|
46
46
|
handler: async (params) => {
|
|
47
47
|
let scenario = params.scenario;
|
|
48
|
+
|
|
49
|
+
// Convert the scenario string to an object
|
|
50
|
+
// Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
|
|
48
51
|
let scenarioObj = {};
|
|
49
52
|
let count = 0;
|
|
50
|
-
|
|
51
|
-
if (scenario == null) {
|
|
52
|
-
scenarioObj = {};
|
|
53
|
-
} else if (typeof scenario === 'object') {
|
|
53
|
+
if (typeof scenario === 'object') {
|
|
54
54
|
scenarioObj = scenario;
|
|
55
55
|
} else if (Array.isArray(scenario)) {
|
|
56
56
|
scenarioObj = scenario[0];
|
|
57
|
-
} else
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
count++;
|
|
66
|
-
return acc;
|
|
67
|
-
}, {});
|
|
68
|
-
}
|
|
57
|
+
} else {
|
|
58
|
+
//console.error('Incoming scenario', scenario);
|
|
59
|
+
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
60
|
+
let [key, value] = pair.split('=');
|
|
61
|
+
acc[key.trim()] = value;
|
|
62
|
+
count++;
|
|
63
|
+
return acc;
|
|
64
|
+
}, {});
|
|
69
65
|
}
|
|
66
|
+
params.scenario = scenarioObj;
|
|
70
67
|
params.type = 'job';
|
|
71
|
-
params.scenario = scenarioObj;
|
|
72
68
|
// Provide runtime context for auth and server settings
|
|
73
69
|
let r = await _jobSubmit(params);
|
|
74
70
|
return r;
|
package/src/toolSet/runJobdef.js
CHANGED
|
@@ -17,7 +17,7 @@ DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro)
|
|
|
17
17
|
|
|
18
18
|
PARAMETERS
|
|
19
19
|
- name: string — jobdef name (required)
|
|
20
|
-
- scenario:
|
|
20
|
+
- scenario: object — input parameters as JSON (optional, defaults to {}). Example: {month:10, year:2025}
|
|
21
21
|
|
|
22
22
|
ROUTING RULES
|
|
23
23
|
- "run jobdef xyz" → { name: "xyz" }
|
|
@@ -42,34 +42,30 @@ Returns log output, listings, tables from jobdef. Error if jobdef not found.
|
|
|
42
42
|
description: description,
|
|
43
43
|
inputSchema: z.object({
|
|
44
44
|
name: z.string(),
|
|
45
|
-
scenario: z.
|
|
45
|
+
scenario: z.any()
|
|
46
46
|
}),
|
|
47
47
|
handler: async (params) => {
|
|
48
48
|
let scenario = params.scenario;
|
|
49
|
+
|
|
50
|
+
// Convert the scenario string to an object
|
|
51
|
+
// Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
|
|
49
52
|
let scenarioObj = {};
|
|
50
53
|
let count = 0;
|
|
51
|
-
|
|
52
|
-
if (scenario == null) {
|
|
53
|
-
scenarioObj = {};
|
|
54
|
-
} else if (typeof scenario === 'object') {
|
|
54
|
+
if (typeof scenario === 'object') {
|
|
55
55
|
scenarioObj = scenario;
|
|
56
56
|
} else if (Array.isArray(scenario)) {
|
|
57
57
|
scenarioObj = scenario[0];
|
|
58
|
-
} else
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
count++;
|
|
67
|
-
return acc;
|
|
68
|
-
}, {});
|
|
69
|
-
}
|
|
58
|
+
} else {
|
|
59
|
+
//console.error('Incoming scenario', scenario);
|
|
60
|
+
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
61
|
+
let [key, value] = pair.split('=');
|
|
62
|
+
acc[key.trim()] = value;
|
|
63
|
+
count++;
|
|
64
|
+
return acc;
|
|
65
|
+
}, {});
|
|
70
66
|
}
|
|
67
|
+
params.scenario = scenarioObj;
|
|
71
68
|
params.type = 'def';
|
|
72
|
-
params.scenario = scenarioObj;
|
|
73
69
|
// Provide runtime context for auth and server settings
|
|
74
70
|
let r = await _jobSubmit(params);
|
|
75
71
|
return r;
|
package/src/toolSet/scrScore.js
CHANGED
|
@@ -1,70 +1,61 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
import _scrScore from '../toolHelpers/_scrScore.js';
|
|
8
|
-
|
|
9
|
-
function scrScore(_appContext) {
|
|
10
|
-
let description = `
|
|
11
|
-
## scr-score
|
|
12
|
-
|
|
13
|
-
Purpose
|
|
14
|
-
Score a scenario using a model deployed as a SCR container in Azure or another host).
|
|
15
|
-
|
|
16
|
-
Inputs
|
|
17
|
-
- url (string, required): SCR model identifier (URL)
|
|
18
|
-
- scenario (
|
|
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
|
-
let r = await _scrScore({ url: url, scenario: scenario , _appContext: _appContext});
|
|
63
|
-
return r;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return spec;
|
|
68
|
-
}
|
|
69
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import _scrScore from '../toolHelpers/_scrScore.js';
|
|
8
|
+
|
|
9
|
+
function scrScore(_appContext) {
|
|
10
|
+
let description = `
|
|
11
|
+
## scr-score
|
|
12
|
+
|
|
13
|
+
Purpose
|
|
14
|
+
Score a scenario using a model deployed as a SCR container in Azure or another host).
|
|
15
|
+
|
|
16
|
+
Inputs
|
|
17
|
+
- url (string, required): SCR model identifier (URL)
|
|
18
|
+
- scenario (object, optional): Input values to score as a JSON object (e.g. {age:45, income:60000}). If omitted, defaults to {} and the tool will return the model's input variable definitions.
|
|
19
|
+
|
|
20
|
+
What it returns
|
|
21
|
+
- When scoring: the SCR endpoint response (predictions, probabilities, scores) merged with or alongside the supplied inputs.
|
|
22
|
+
- When \`scenario\` is omitted: metadata describing the model's input variables (names, types, required/optional).
|
|
23
|
+
|
|
24
|
+
Usage notes
|
|
25
|
+
- Run \`scr-info\` first to inspect the expected input variables and types.
|
|
26
|
+
- Prefer structured objects for numeric/date values to avoid type ambiguity; the simple string parser keeps values as strings.
|
|
27
|
+
- Ensure network connectivity and any required credentials for the target SCR service.
|
|
28
|
+
|
|
29
|
+
Examples
|
|
30
|
+
- scrScore with url="loan" and scenario={age:45, income:60000}
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
let spec = {
|
|
34
|
+
name: 'scr-score',
|
|
35
|
+
description: description,
|
|
36
|
+
inputSchema: z.object({
|
|
37
|
+
url: z.string(),
|
|
38
|
+
scenario: z.union([z.record(z.any()), z.string()]).optional()
|
|
39
|
+
}),
|
|
40
|
+
|
|
41
|
+
handler: async (params) => {
|
|
42
|
+
let {url, _appContext} = params;
|
|
43
|
+
let scenario = params.scenario;
|
|
44
|
+
if (typeof scenario === 'string') {
|
|
45
|
+
try { scenario = JSON.parse(scenario); } catch { scenario = {}; }
|
|
46
|
+
}
|
|
47
|
+
if (url === null) {
|
|
48
|
+
return { status: { statusCode: 2, msg: `SCR model ${url} was not specified` }, results: {} };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const scenarioObj = (scenario != null && typeof scenario === 'object') ? scenario : {};
|
|
52
|
+
|
|
53
|
+
let r = await _scrScore({ url: url, scenario: scenarioObj, _appContext: _appContext});
|
|
54
|
+
return r;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return spec;
|
|
59
|
+
}
|
|
60
|
+
|
|
70
61
|
export default scrScore;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"mcp__sasmcp__sas-score-deva-score",
|
|
5
|
-
"mcp__sasmcp__sas-score-find-model",
|
|
6
|
-
"mcp__sasmcp__sas-score-model-info",
|
|
7
|
-
"Bash(cp \"C:/dev/github/7-skill-integration/.claude/skills/sas-read-and-score/SKILL.md\" \"C:/dev/github/7-skill-integration/skills/sas-read-and-score/SKILL.md\")",
|
|
8
|
-
"Bash(cp \"C:/dev/github/7-skill-integration/.claude/skills/sas-read-strategy/SKILL.md\" \"C:/dev/github/7-skill-integration/skills/sas-read-strategy/SKILL.md\")",
|
|
9
|
-
"Bash(cp \"C:/dev/github/7-skill-integration/.claude/skills/sas-score-workflow/SKILL.md\" \"C:/dev/github/7-skill-integration/skills/sas-score-workflow/SKILL.md\")",
|
|
10
|
-
"WebSearch"
|
|
11
|
-
]
|
|
12
|
-
}
|
|
13
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|