@sassoftware/sas-score-mcp-serverjs 0.4.1-7 → 1.0.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 +147 -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/skills}/sas-read-strategy/SKILL.md +43 -30
- package/.skills/skills/sas-request-classifier/SKILL.md +69 -0
- package/{skills → .skills/skills}/sas-score-workflow/SKILL.md +49 -35
- package/cli.js +222 -140
- package/package.json +5 -4
- 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 -220
- package/src/createMcpServer.js +10 -5
- package/src/expressMcpServer.js +54 -186
- 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 +37 -0
- package/src/toolHelpers/_listLibrary.js +0 -1
- package/src/toolHelpers/getLogonPayload.js +5 -1
- package/src/toolHelpers/refreshToken.js +3 -2
- package/src/toolHelpers/refreshTokenOauth.js +3 -3
- package/src/toolSet/.claude/settings.local.json +2 -1
- package/src/toolSet/findModel.js +1 -1
- package/src/toolSet/findTable.js +3 -3
- package/src/toolSet/modelScore.js +2 -2
- package/src/toolSet/runJob.js +81 -81
- package/src/toolSet/runJobdef.js +82 -82
- 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
|
@@ -53,7 +53,6 @@ async function igetLogonPayload(_appContext) {
|
|
|
53
53
|
token: _appContext.bearerToken,
|
|
54
54
|
tokenType: "Bearer",
|
|
55
55
|
};
|
|
56
|
-
console.error("[Note] Bearer token in logonPayload ", _appContext.bearerToken);
|
|
57
56
|
return logonPayload;
|
|
58
57
|
}
|
|
59
58
|
|
|
@@ -122,4 +121,9 @@ async function igetLogonPayload(_appContext) {
|
|
|
122
121
|
return null;
|
|
123
122
|
}
|
|
124
123
|
}
|
|
124
|
+
|
|
125
|
+
function isExpired(expiresAt, skewSeconds = 60) {
|
|
126
|
+
return Date.now() >= (expiresAt - skewSeconds * 1000);
|
|
127
|
+
}
|
|
128
|
+
|
|
125
129
|
export default getLogonPayload;
|
|
@@ -9,10 +9,11 @@ async function refreshToken(_appContext, params) {
|
|
|
9
9
|
let url = `${host}/SASLogon/oauth/token`;
|
|
10
10
|
|
|
11
11
|
let aconnect = {
|
|
12
|
-
ca: _appContext.contexts.viyaCert.ca, // trust this CA
|
|
13
12
|
rejectUnauthorized: false // or false, if you really want to bypass checks
|
|
14
13
|
}
|
|
15
|
-
|
|
14
|
+
if (_appContext.contexts.viyaCert != null && _appContext.contexts.viyaCert.ca != null) {
|
|
15
|
+
aconnect.ca = _appContext.contexts.viyaCert.ca;
|
|
16
|
+
}
|
|
16
17
|
const agent = new Agent(aconnect);
|
|
17
18
|
|
|
18
19
|
console.error('[Info] Refreshing token...', token);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//import getOpts from './getOpts.js';
|
|
7
7
|
async function refreshTokenOauth(_appContext, oauthInfo ){
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const url = `${_appContext.VIYA_SERVER}/SASLogon/oauth/token`;
|
|
10
10
|
let opts = _appContext.contexts.appCert;
|
|
11
11
|
|
|
12
12
|
const agent = new Agent({
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
try {
|
|
24
24
|
const response = await fetch(url, {
|
|
25
25
|
method: 'POST',
|
|
26
|
+
dispatcher: agent,
|
|
26
27
|
headers: {
|
|
27
28
|
'Accept': 'application/json',
|
|
28
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
29
|
-
dispatcher: agent
|
|
29
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
30
30
|
},
|
|
31
31
|
body: body.toString()
|
|
32
32
|
});
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
"mcp__sasmcp__sas-score-model-info",
|
|
7
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
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\")"
|
|
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"
|
|
10
11
|
]
|
|
11
12
|
}
|
|
12
13
|
}
|
package/src/toolSet/findModel.js
CHANGED
|
@@ -9,7 +9,7 @@ import _listModels from '../toolHelpers/_listModels.js';
|
|
|
9
9
|
|
|
10
10
|
function findModel(_appContext) {
|
|
11
11
|
let description = `
|
|
12
|
-
find-model — locate a specific model deployed to MAS
|
|
12
|
+
find-model — locate a specific MAS model deployed to MAS server
|
|
13
13
|
|
|
14
14
|
USE when: find model, does model exist, is model deployed, lookup model, verify model exists
|
|
15
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)
|
package/src/toolSet/findTable.js
CHANGED
|
@@ -17,7 +17,7 @@ DO NOT USE for: list tables (use list-tables), table schema/columns (use table-i
|
|
|
17
17
|
PARAMETERS
|
|
18
18
|
- lib: string (required) — library name (e.g., 'Public', 'sashelp')
|
|
19
19
|
- name: string (required) — table name to locate
|
|
20
|
-
- server: 'cas' | 'sas'
|
|
20
|
+
- server: 'cas' | 'sas' . If not specified set it to 'cas' — target environment
|
|
21
21
|
|
|
22
22
|
ROUTING RULES
|
|
23
23
|
- "find table <name> in <lib>" → { lib: "<lib>", name: "<name>", server: "cas" }
|
|
@@ -47,9 +47,9 @@ Returns { tables: [] } if not found; { tables: [name, ...] } if found. Never hal
|
|
|
47
47
|
name: 'find-table',
|
|
48
48
|
description: description,
|
|
49
49
|
inputSchema: z.object({
|
|
50
|
-
name: z.string(),
|
|
51
50
|
lib: z.string(),
|
|
52
|
-
|
|
51
|
+
name: z.string(),
|
|
52
|
+
server: z.string()
|
|
53
53
|
}),
|
|
54
54
|
|
|
55
55
|
handler: async (params) => {
|
|
@@ -10,7 +10,7 @@ const log = debug('tools');
|
|
|
10
10
|
|
|
11
11
|
function modelScore(_appContext) {
|
|
12
12
|
let description = `
|
|
13
|
-
|
|
13
|
+
mas-score — score data using a deployed model on MAS.
|
|
14
14
|
|
|
15
15
|
USE when: score with model, predict using model, batch scoring, model predictions
|
|
16
16
|
DO NOT USE for: find model, model metadata, list models, run programs/jobs, query tables
|
|
@@ -40,7 +40,7 @@ Returns predictions, probabilities, scores merged with input data. Returns error
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
let spec = {
|
|
43
|
-
name: '
|
|
43
|
+
name: 'mas-score',
|
|
44
44
|
description: description,
|
|
45
45
|
inputSchema:z.object({
|
|
46
46
|
model: z.string(),
|
package/src/toolSet/runJob.js
CHANGED
|
@@ -1,81 +1,81 @@
|
|
|
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
|
|
8
|
-
|
|
9
|
-
function runJob(_appContext) {
|
|
10
|
-
|
|
11
|
-
let description = `
|
|
12
|
-
run-job —
|
|
13
|
-
|
|
14
|
-
USE when:
|
|
15
|
-
DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro), list/find jobs
|
|
16
|
-
|
|
17
|
-
PARAMETERS
|
|
18
|
-
- name: string — job name (required)
|
|
19
|
-
- scenario: string | object — input parameters. Accepts: "x=1, y=2" or {x:1, y:2}
|
|
20
|
-
|
|
21
|
-
ROUTING RULES
|
|
22
|
-
- "run job xyz" → { name: "xyz" }
|
|
23
|
-
- "run job xyz with param1=10, param2=val2" → { name: "xyz", scenario: {param1:10, param2:"val2"} }
|
|
24
|
-
|
|
25
|
-
EXAMPLES
|
|
26
|
-
- "run job xyz" → { name: "xyz" }
|
|
27
|
-
- "run job monthly_etl with month=10, year=2025" → { name: "monthly_etl", scenario: {month:10, year:2025} }
|
|
28
|
-
|
|
29
|
-
NEGATIVE EXAMPLES (do not route here)
|
|
30
|
-
- "run SAS code" (use run-sas-program)
|
|
31
|
-
- "run macro X" (use run-macro)
|
|
32
|
-
- "list jobs" (use list-jobs)
|
|
33
|
-
- "find job X" (use find-job)
|
|
34
|
-
|
|
35
|
-
ERRORS
|
|
36
|
-
Returns log output, listings, tables from job. Error if job not found.
|
|
37
|
-
`;
|
|
38
|
-
|
|
39
|
-
let spec = {
|
|
40
|
-
name: 'run-job',
|
|
41
|
-
description: description,
|
|
42
|
-
inputSchema: z.object({
|
|
43
|
-
name: z.string(),
|
|
44
|
-
scenario: z.string().optional()
|
|
45
|
-
}),
|
|
46
|
-
handler: async (params) => {
|
|
47
|
-
let scenario = params.scenario;
|
|
48
|
-
let scenarioObj = {};
|
|
49
|
-
let count = 0;
|
|
50
|
-
//
|
|
51
|
-
if (scenario == null) {
|
|
52
|
-
scenarioObj = {};
|
|
53
|
-
} else if (typeof scenario === 'object') {
|
|
54
|
-
scenarioObj = scenario;
|
|
55
|
-
} else if (Array.isArray(scenario)) {
|
|
56
|
-
scenarioObj = scenario[0];
|
|
57
|
-
} else if (typeof scenario === 'string') {
|
|
58
|
-
if (scenario.trim() === '') {
|
|
59
|
-
scenarioObj = {};
|
|
60
|
-
} else {
|
|
61
|
-
// console.error('Incoming scenario', scenario);
|
|
62
|
-
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
63
|
-
let [key, value] = pair.split('=');
|
|
64
|
-
acc[key.trim()] = value;
|
|
65
|
-
count++;
|
|
66
|
-
return acc;
|
|
67
|
-
}, {});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
params.type = 'job';
|
|
71
|
-
params.scenario = scenarioObj;
|
|
72
|
-
// Provide runtime context for auth and server settings
|
|
73
|
-
let r = await _jobSubmit(params);
|
|
74
|
-
return r;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
return spec;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export default runJob;
|
|
81
|
-
|
|
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
|
|
8
|
+
|
|
9
|
+
function runJob(_appContext) {
|
|
10
|
+
|
|
11
|
+
let description = `
|
|
12
|
+
run-job — score with a deployed SAS Viya job.
|
|
13
|
+
|
|
14
|
+
USE when: score with job, run job, execute job
|
|
15
|
+
DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro), list/find jobs
|
|
16
|
+
|
|
17
|
+
PARAMETERS
|
|
18
|
+
- name: string — job name (required)
|
|
19
|
+
- scenario: string | object — input parameters. Accepts: "x=1, y=2" or {x:1, y:2}
|
|
20
|
+
|
|
21
|
+
ROUTING RULES
|
|
22
|
+
- "run job xyz" → { name: "xyz" }
|
|
23
|
+
- "run job xyz with param1=10, param2=val2" → { name: "xyz", scenario: {param1:10, param2:"val2"} }
|
|
24
|
+
|
|
25
|
+
EXAMPLES
|
|
26
|
+
- "run job xyz" → { name: "xyz" }
|
|
27
|
+
- "run job monthly_etl with month=10, year=2025" → { name: "monthly_etl", scenario: {month:10, year:2025} }
|
|
28
|
+
|
|
29
|
+
NEGATIVE EXAMPLES (do not route here)
|
|
30
|
+
- "run SAS code" (use run-sas-program)
|
|
31
|
+
- "run macro X" (use run-macro)
|
|
32
|
+
- "list jobs" (use list-jobs)
|
|
33
|
+
- "find job X" (use find-job)
|
|
34
|
+
|
|
35
|
+
ERRORS
|
|
36
|
+
Returns log output, listings, tables from job. Error if job not found.
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
let spec = {
|
|
40
|
+
name: 'run-job',
|
|
41
|
+
description: description,
|
|
42
|
+
inputSchema: z.object({
|
|
43
|
+
name: z.string(),
|
|
44
|
+
scenario: z.string().optional()
|
|
45
|
+
}),
|
|
46
|
+
handler: async (params) => {
|
|
47
|
+
let scenario = params.scenario;
|
|
48
|
+
let scenarioObj = {};
|
|
49
|
+
let count = 0;
|
|
50
|
+
//
|
|
51
|
+
if (scenario == null) {
|
|
52
|
+
scenarioObj = {};
|
|
53
|
+
} else if (typeof scenario === 'object') {
|
|
54
|
+
scenarioObj = scenario;
|
|
55
|
+
} else if (Array.isArray(scenario)) {
|
|
56
|
+
scenarioObj = scenario[0];
|
|
57
|
+
} else if (typeof scenario === 'string') {
|
|
58
|
+
if (scenario.trim() === '') {
|
|
59
|
+
scenarioObj = {};
|
|
60
|
+
} else {
|
|
61
|
+
// console.error('Incoming scenario', scenario);
|
|
62
|
+
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
63
|
+
let [key, value] = pair.split('=');
|
|
64
|
+
acc[key.trim()] = value;
|
|
65
|
+
count++;
|
|
66
|
+
return acc;
|
|
67
|
+
}, {});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
params.type = 'job';
|
|
71
|
+
params.scenario = scenarioObj;
|
|
72
|
+
// Provide runtime context for auth and server settings
|
|
73
|
+
let r = await _jobSubmit(params);
|
|
74
|
+
return r;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
return spec;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default runJob;
|
|
81
|
+
|
package/src/toolSet/runJobdef.js
CHANGED
|
@@ -1,82 +1,82 @@
|
|
|
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
|
|
8
|
-
|
|
9
|
-
function runJobdef(_appContext) {
|
|
10
|
-
// JSON object for LLM/tooling
|
|
11
|
-
|
|
12
|
-
let description = `
|
|
13
|
-
run-jobdef —
|
|
14
|
-
|
|
15
|
-
USE when:
|
|
16
|
-
DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro), list/find jobdefs
|
|
17
|
-
|
|
18
|
-
PARAMETERS
|
|
19
|
-
- name: string — jobdef name (required)
|
|
20
|
-
- scenario: string | object — input parameters. Accepts: "x=1, y=2" or {x:1, y:2}
|
|
21
|
-
|
|
22
|
-
ROUTING RULES
|
|
23
|
-
- "run jobdef xyz" → { name: "xyz" }
|
|
24
|
-
- "run jobdef xyz with param1=10, param2=val2" → { name: "xyz", scenario: {param1:10, param2:"val2"} }
|
|
25
|
-
|
|
26
|
-
EXAMPLES
|
|
27
|
-
- "run jobdef xyz" → { name: "xyz" }
|
|
28
|
-
- "run jobdef monthly_report with month=10, year=2025" → { name: "monthly_report", scenario: {month:10, year:2025} }
|
|
29
|
-
|
|
30
|
-
NEGATIVE EXAMPLES (do not route here)
|
|
31
|
-
- "run SAS code" (use run-sas-program)
|
|
32
|
-
- "run macro X" (use run-macro)
|
|
33
|
-
- "list jobdefs" (use list-jobdefs)
|
|
34
|
-
- "find jobdef X" (use find-jobdef)
|
|
35
|
-
|
|
36
|
-
ERRORS
|
|
37
|
-
Returns log output, listings, tables from jobdef. Error if jobdef not found.
|
|
38
|
-
`;
|
|
39
|
-
|
|
40
|
-
let spec = {
|
|
41
|
-
name: 'run-jobdef',
|
|
42
|
-
description: description,
|
|
43
|
-
inputSchema: z.object({
|
|
44
|
-
name: z.string(),
|
|
45
|
-
scenario: z.string().optional()
|
|
46
|
-
}),
|
|
47
|
-
handler: async (params) => {
|
|
48
|
-
let scenario = params.scenario;
|
|
49
|
-
let scenarioObj = {};
|
|
50
|
-
let count = 0;
|
|
51
|
-
//
|
|
52
|
-
if (scenario == null) {
|
|
53
|
-
scenarioObj = {};
|
|
54
|
-
} else if (typeof scenario === 'object') {
|
|
55
|
-
scenarioObj = scenario;
|
|
56
|
-
} else if (Array.isArray(scenario)) {
|
|
57
|
-
scenarioObj = scenario[0];
|
|
58
|
-
} else if (typeof scenario === 'string') {
|
|
59
|
-
if (scenario.trim() === '') {
|
|
60
|
-
scenarioObj = {};
|
|
61
|
-
} else {
|
|
62
|
-
// console.error('Incoming scenario', scenario);
|
|
63
|
-
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
64
|
-
let [key, value] = pair.split('=');
|
|
65
|
-
acc[key.trim()] = value;
|
|
66
|
-
count++;
|
|
67
|
-
return acc;
|
|
68
|
-
}, {});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
params.type = 'def';
|
|
72
|
-
params.scenario = scenarioObj;
|
|
73
|
-
// Provide runtime context for auth and server settings
|
|
74
|
-
let r = await _jobSubmit(params);
|
|
75
|
-
return r;
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
return spec;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export default runJobdef;
|
|
82
|
-
|
|
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
|
|
8
|
+
|
|
9
|
+
function runJobdef(_appContext) {
|
|
10
|
+
// JSON object for LLM/tooling
|
|
11
|
+
|
|
12
|
+
let description = `
|
|
13
|
+
run-jobdef — score with a deployed SAS Viya job definition.
|
|
14
|
+
|
|
15
|
+
USE when: score with jobdef, run jobdef, execute jobdef
|
|
16
|
+
DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro), list/find jobdefs
|
|
17
|
+
|
|
18
|
+
PARAMETERS
|
|
19
|
+
- name: string — jobdef name (required)
|
|
20
|
+
- scenario: string | object — input parameters. Accepts: "x=1, y=2" or {x:1, y:2}
|
|
21
|
+
|
|
22
|
+
ROUTING RULES
|
|
23
|
+
- "run jobdef xyz" → { name: "xyz" }
|
|
24
|
+
- "run jobdef xyz with param1=10, param2=val2" → { name: "xyz", scenario: {param1:10, param2:"val2"} }
|
|
25
|
+
|
|
26
|
+
EXAMPLES
|
|
27
|
+
- "run jobdef xyz" → { name: "xyz" }
|
|
28
|
+
- "run jobdef monthly_report with month=10, year=2025" → { name: "monthly_report", scenario: {month:10, year:2025} }
|
|
29
|
+
|
|
30
|
+
NEGATIVE EXAMPLES (do not route here)
|
|
31
|
+
- "run SAS code" (use run-sas-program)
|
|
32
|
+
- "run macro X" (use run-macro)
|
|
33
|
+
- "list jobdefs" (use list-jobdefs)
|
|
34
|
+
- "find jobdef X" (use find-jobdef)
|
|
35
|
+
|
|
36
|
+
ERRORS
|
|
37
|
+
Returns log output, listings, tables from jobdef. Error if jobdef not found.
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
let spec = {
|
|
41
|
+
name: 'run-jobdef',
|
|
42
|
+
description: description,
|
|
43
|
+
inputSchema: z.object({
|
|
44
|
+
name: z.string(),
|
|
45
|
+
scenario: z.string().optional()
|
|
46
|
+
}),
|
|
47
|
+
handler: async (params) => {
|
|
48
|
+
let scenario = params.scenario;
|
|
49
|
+
let scenarioObj = {};
|
|
50
|
+
let count = 0;
|
|
51
|
+
//
|
|
52
|
+
if (scenario == null) {
|
|
53
|
+
scenarioObj = {};
|
|
54
|
+
} else if (typeof scenario === 'object') {
|
|
55
|
+
scenarioObj = scenario;
|
|
56
|
+
} else if (Array.isArray(scenario)) {
|
|
57
|
+
scenarioObj = scenario[0];
|
|
58
|
+
} else if (typeof scenario === 'string') {
|
|
59
|
+
if (scenario.trim() === '') {
|
|
60
|
+
scenarioObj = {};
|
|
61
|
+
} else {
|
|
62
|
+
// console.error('Incoming scenario', scenario);
|
|
63
|
+
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
64
|
+
let [key, value] = pair.split('=');
|
|
65
|
+
acc[key.trim()] = value;
|
|
66
|
+
count++;
|
|
67
|
+
return acc;
|
|
68
|
+
}, {});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
params.type = 'def';
|
|
72
|
+
params.scenario = scenarioObj;
|
|
73
|
+
// Provide runtime context for auth and server settings
|
|
74
|
+
let r = await _jobSubmit(params);
|
|
75
|
+
return r;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return spec;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default runJobdef;
|
|
82
|
+
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: mcp-tool-description-optimizer
|
|
3
|
-
description: >
|
|
4
|
-
Optimize MCP (Model Context Protocol) tool descriptions for token efficiency and LLM routing accuracy.
|
|
5
|
-
Use this skill whenever a user shares a raw, verbose, or poorly structured MCP tool description and wants
|
|
6
|
-
it improved, rewritten, or reviewed. Trigger on phrases like: "optimize this tool description",
|
|
7
|
-
"rewrite my MCP tool description", "make this tool description more efficient", "clean up my tool spec",
|
|
8
|
-
"improve how Claude picks my tool", or when a user pastes a JavaScript/TypeScript tool description string
|
|
9
|
-
and asks for help with it. Also trigger when the user mentions token efficiency, tool routing,
|
|
10
|
-
or LLM disambiguation in the context of MCP servers.
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# MCP Tool Description Optimizer
|
|
14
|
-
|
|
15
|
-
Rewrites verbose or poorly structured MCP tool descriptions into compact, signal-rich versions that
|
|
16
|
-
improve LLM tool selection accuracy while reducing token usage.
|
|
17
|
-
|
|
18
|
-
## Why this matters
|
|
19
|
-
|
|
20
|
-
Claude and other LLMs select MCP tools based entirely on the `description` field. Descriptions that are
|
|
21
|
-
too long, redundant, or badly structured waste context tokens and reduce routing precision.
|
|
22
|
-
A well-optimized description:
|
|
23
|
-
- States what the tool does and when to use it upfront
|
|
24
|
-
- Eliminates redundancy (same info repeated across sections)
|
|
25
|
-
- Uses a compact, scannable format (labeled blocks, not nested markdown)
|
|
26
|
-
- Includes clear negative examples to prevent mis-routing
|
|
27
|
-
- Keeps parameters terse — name, type, default, one-line purpose
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Optimization Process
|
|
32
|
-
|
|
33
|
-
### Step 1 — Analyze the input description
|
|
34
|
-
|
|
35
|
-
Before rewriting, identify these problems in the original:
|
|
36
|
-
|
|
37
|
-
| Problem | Example |
|
|
38
|
-
|---|---|
|
|
39
|
-
| **Redundancy** | Trigger phrases listed in 3+ places |
|
|
40
|
-
| **Filler sections** | "Rationale", "Behavior Summary", "Response Contract" with no routing signal |
|
|
41
|
-
| **Orphaned syntax** | Arrows (`→`) or bullets with no target |
|
|
42
|
-
| **Overlong examples** | Long prose examples when one-liners suffice |
|
|
43
|
-
| **Heavy markdown** | `##` headers for every minor point |
|
|
44
|
-
| **Duplicated parameter docs** | Same param described in both a table and prose |
|
|
45
|
-
|
|
46
|
-
Call out 2–4 of the most impactful issues before writing the new version.
|
|
47
|
-
|
|
48
|
-
### Step 2 — Rewrite using the standard template
|
|
49
|
-
|
|
50
|
-
Use this exact block structure for the output. Omit blocks that don't apply.
|
|
51
|
-
|
|
52
|
-
```
|
|
53
|
-
<tool-name> — <one-line purpose>.
|
|
54
|
-
|
|
55
|
-
USE when: <comma-separated user intents or trigger phrases>
|
|
56
|
-
DO NOT USE for: <comma-separated anti-patterns with → redirect where applicable>
|
|
57
|
-
|
|
58
|
-
PARAMETERS
|
|
59
|
-
- <name>: <type> (default: <val>) — <one-line purpose>
|
|
60
|
-
...
|
|
61
|
-
|
|
62
|
-
ROUTING RULES
|
|
63
|
-
- "<trigger phrase>" → { param: value }
|
|
64
|
-
- "<trigger phrase>" → { param: value }
|
|
65
|
-
- <ambiguous case> → <ask for clarification | default behavior>
|
|
66
|
-
|
|
67
|
-
EXAMPLES
|
|
68
|
-
- "<user utterance>" → { param: value, ... }
|
|
69
|
-
- "<user utterance>" → { param: value, ... }
|
|
70
|
-
|
|
71
|
-
NEGATIVE EXAMPLES (do not route here)
|
|
72
|
-
- "<user utterance>" → <correct tool>
|
|
73
|
-
|
|
74
|
-
PAGINATION (include only if tool is paginated)
|
|
75
|
-
If returned count === limit → hint: next start = start + limit.
|
|
76
|
-
If start > 1 and result empty → note paging may exceed available items.
|
|
77
|
-
|
|
78
|
-
ERRORS
|
|
79
|
-
<One or two lines: return structure, hallucination policy>
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Step 3 — Apply these rules consistently
|
|
83
|
-
|
|
84
|
-
**USE/DO NOT USE block**
|
|
85
|
-
- Write as comma-separated inline list, not a bullet list
|
|
86
|
-
- DO NOT USE entries should name the redirect tool in parentheses where known
|
|
87
|
-
|
|
88
|
-
**ROUTING RULES block**
|
|
89
|
-
- One rule per line; quote the trigger phrase; use `→ { }` for param mapping
|
|
90
|
-
- Consolidate synonyms on one line: `"cas libs / cas libraries / in cas" → { server: 'cas' }`
|
|
91
|
-
- List the default/fallback rule last
|
|
92
|
-
|
|
93
|
-
**PARAMETERS block**
|
|
94
|
-
- One line per param: `- name: type (default: val) — purpose`
|
|
95
|
-
- Skip obvious params (e.g. don't explain what `limit` means if it's standard pagination)
|
|
96
|
-
|
|
97
|
-
**EXAMPLES block**
|
|
98
|
-
- Each example fits on one line
|
|
99
|
-
- For "next page" examples, include the prior call's state inline: `"next" (prev: start:1, limit:10) → { start: 11, limit: 10 }`
|
|
100
|
-
|
|
101
|
-
**NEGATIVE EXAMPLES block**
|
|
102
|
-
- Only include when mis-routing is a real risk
|
|
103
|
-
- Format: `"<utterance>" → <correct-tool-name>`
|
|
104
|
-
|
|
105
|
-
**Tone**
|
|
106
|
-
- Imperative, terse. No filler words ("Please note that...", "It is important to...")
|
|
107
|
-
- Never include a "Rationale" or "Behavior Summary" section — if behavior matters, encode it as a rule
|
|
108
|
-
|
|
109
|
-
### Step 4 — Validate before returning
|
|
110
|
-
|
|
111
|
-
Check the rewritten description against this list:
|
|
112
|
-
|
|
113
|
-
- [ ] No trigger phrase appears in more than one block
|
|
114
|
-
- [ ] No orphaned `→` arrows or dangling bullets
|
|
115
|
-
- [ ] Parameter defaults are stated explicitly
|
|
116
|
-
- [ ] Negative examples cover the tool's most common mis-routing risks
|
|
117
|
-
- [ ] Total length is ≤ 50% of the original (target: 30–40% reduction)
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## Output format
|
|
122
|
-
|
|
123
|
-
Always return:
|
|
124
|
-
|
|
125
|
-
1. **Analysis** — 2–4 bullet points naming the key issues found in the original
|
|
126
|
-
2. **Rewritten description** — inside a JavaScript code block (matching the user's original code style)
|
|
127
|
-
3. **Change summary** — a short table or bullet list of what changed and why
|
|
128
|
-
|
|
129
|
-
See `references/examples.md` for before/after examples of real tool descriptions.
|