@sassoftware/sas-score-mcp-serverjs 1.0.1-8 → 1.1.1

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.
Files changed (121) hide show
  1. package/.skills/.claude-plugin/plugin.json +59 -0
  2. package/.skills/agents/sas-score-mcp-serverjs-agent.md +26 -0
  3. package/.skills/copilot-instructions.md +62 -0
  4. package/.skills/skills/README.md +204 -0
  5. package/.skills/skills/detail-strategy/SKILL.md +316 -0
  6. package/.skills/skills/find-library-server/SKILL.md +62 -0
  7. package/.skills/skills/find-resources/SKILL.md +66 -0
  8. package/.skills/skills/list-library/SKILL.md +30 -0
  9. package/.skills/skills/list-mas-job-jobdef/SKILL.md +31 -0
  10. package/.skills/skills/list-tables/SKILL.md +30 -0
  11. package/.skills/skills/read-strategy/SKILL.md +87 -0
  12. package/.skills/skills/request-routing/SKILL.md +112 -0
  13. package/.skills/skills/score-cas/SKILL.md +95 -0
  14. package/.skills/skills/score-job-jobdef/SKILL.md +58 -0
  15. package/.skills/skills/score-mas-scr/SKILL.md +58 -0
  16. package/.skills/skills/score-program/SKILL.md +59 -0
  17. package/.skills/skills/score-strategy/SKILL.md +39 -0
  18. package/README.md +96 -54
  19. package/cli.js +29 -38
  20. package/openApi.yaml +121 -121
  21. package/package.json +15 -13
  22. package/scripts/docs/SCORE_SKILL_REFERENCE.md +17 -16
  23. package/scripts/docs/TOOL_DESCRIPTION_TEMPLATE.md +3 -3
  24. package/scripts/docs/TOOL_UPDATES_SUMMARY.md +65 -63
  25. package/scripts/docs/oauth-http-transport.md +2 -2
  26. package/scripts/docs/sas-mcp-tools-reference.md +43 -32
  27. package/scripts/plot_msrp_usa.py +49 -0
  28. package/scripts/refreshtoken.js +58 -0
  29. package/scripts/runListScr.mjs +16 -0
  30. package/src/createMcpServer.js +4 -1
  31. package/src/expressMcpServer.js +47 -49
  32. package/src/oauthHandlers/authorize.js +4 -1
  33. package/src/oauthHandlers/baseUrl.js +4 -0
  34. package/src/oauthHandlers/callback.js +4 -0
  35. package/src/oauthHandlers/getMetadata.js +4 -0
  36. package/src/oauthHandlers/index.js +4 -0
  37. package/src/oauthHandlers/token.js +4 -0
  38. package/src/openApi.yaml +121 -121
  39. package/src/processHeaders.js +10 -7
  40. package/src/setupSkills.js +6 -9
  41. package/src/toolHelpers/_casScore.js +32 -0
  42. package/src/toolHelpers/_desc.js +14 -0
  43. package/src/toolHelpers/_findJob.js +12 -0
  44. package/src/toolHelpers/_findJobdef.js +10 -0
  45. package/src/toolHelpers/_findLibrary.js +11 -0
  46. package/src/toolHelpers/_findMas.js +13 -0
  47. package/src/toolHelpers/_findScr.js +36 -0
  48. package/src/toolHelpers/_findTable.js +11 -0
  49. package/src/toolHelpers/_listJobdefs.js +12 -2
  50. package/src/toolHelpers/_listJobs.js +19 -8
  51. package/src/toolHelpers/{_listModels.js → _listMas.js} +4 -4
  52. package/src/toolHelpers/_listScr.js +13 -0
  53. package/src/toolHelpers/{_scrInfo.js → _scrDescribe.js} +4 -4
  54. package/src/toolHelpers/_scrScore.js +2 -2
  55. package/src/toolHelpers/_submitCasl.js +19 -17
  56. package/src/toolHelpers/{_tableInfo.js → _tableDescribe.js} +2 -2
  57. package/src/toolHelpers/getLogonPayload.js +2 -2
  58. package/src/toolSet/casModelScore.js +93 -0
  59. package/src/toolSet/casProgramScore.js +105 -0
  60. package/src/toolSet/devaScore.js +11 -6
  61. package/src/toolSet/findJob.js +74 -59
  62. package/src/toolSet/findJobdef.js +67 -64
  63. package/src/toolSet/findLibrary.js +28 -23
  64. package/src/toolSet/findMas.js +72 -0
  65. package/src/toolSet/findScr.js +69 -0
  66. package/src/toolSet/findTable.js +34 -27
  67. package/src/toolSet/getEnv.js +57 -57
  68. package/src/toolSet/jobDescribe.js +65 -0
  69. package/src/toolSet/jobScore.js +90 -0
  70. package/src/toolSet/jobdefDescribe.js +67 -0
  71. package/src/toolSet/jobdefScore.js +85 -0
  72. package/src/toolSet/listJobdefs.js +17 -8
  73. package/src/toolSet/listJobs.js +15 -8
  74. package/src/toolSet/listLibraries.js +16 -10
  75. package/src/toolSet/listMas.js +71 -0
  76. package/src/toolSet/listScr.js +62 -0
  77. package/src/toolSet/listTables.js +78 -66
  78. package/src/toolSet/{runMacro.js → macroScore.js} +86 -82
  79. package/src/toolSet/makeTools.js +39 -25
  80. package/src/toolSet/masDescribe.js +67 -0
  81. package/src/toolSet/masScore.js +95 -0
  82. package/src/toolSet/{runProgram.js → programScore.js} +96 -93
  83. package/src/toolSet/readTable.js +43 -26
  84. package/src/toolSet/sasQuery.js +24 -18
  85. package/src/toolSet/scrDescribe.js +55 -0
  86. package/src/toolSet/scrScore.js +63 -70
  87. package/src/toolSet/searchAssets.js +1 -1
  88. package/src/toolSet/setContext.js +8 -3
  89. package/src/toolSet/superstat.js +61 -61
  90. package/src/toolSet/tableDescribe.js +65 -0
  91. package/.skills/sas-find-library-smart/SKILL.md +0 -155
  92. package/.skills/sas-find-resource-strategy/SKILL.md +0 -105
  93. package/.skills/sas-list-resource-strategy/SKILL.md +0 -124
  94. package/.skills/sas-list-tables-smart/SKILL.md +0 -128
  95. package/.skills/sas-read-and-score-strategy/SKILL.md +0 -113
  96. package/.skills/sas-read-strategy/SKILL.md +0 -154
  97. package/.skills/sas-request-classifier/SKILL.md +0 -74
  98. package/.skills/sas-score-workflow-strategy/SKILL.md +0 -314
  99. package/.skills_claude/CLAUDE.md +0 -201
  100. package/.skills_claude/agents/sas-score-mcp-serverjs-agent.md +0 -58
  101. package/.skills_claude/enforce-find-resource-strategy.md +0 -35
  102. package/.skills_github/agents/sas-score-mcp-serverjs-agent.md +0 -58
  103. package/.skills_github/copilot-instructions.md +0 -201
  104. package/.skills_github/enforce-find-resource-strategy.md +0 -35
  105. package/scripts/optimize_final.py +0 -140
  106. package/scripts/optimize_tools.py +0 -99
  107. package/scripts/setup-skills.js +0 -34
  108. package/scripts/update_descriptions.py +0 -46
  109. package/src/authpkce.js +0 -219
  110. package/src/handleGetDelete.js +0 -34
  111. package/src/handleRequest.js +0 -112
  112. package/src/hapiMcpServer.js +0 -241
  113. package/src/toolSet/findModel.js +0 -60
  114. package/src/toolSet/listModels.js +0 -56
  115. package/src/toolSet/modelInfo.js +0 -55
  116. package/src/toolSet/modelScore.js +0 -89
  117. package/src/toolSet/runCasProgram.js +0 -98
  118. package/src/toolSet/runJob.js +0 -81
  119. package/src/toolSet/runJobdef.js +0 -82
  120. package/src/toolSet/scrInfo.js +0 -52
  121. package/src/toolSet/tableInfo.js +0 -58
@@ -1,82 +1,86 @@
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 _submitCode from '../toolHelpers/_submitCode.js';
8
-
9
-
10
- function runMacro(_appContext) {
11
- let description = `
12
- run-macro submit and execute a SAS macro on SAS Viya server.
13
-
14
- USE when: run macro, execute macro with parameters
15
- DO NOT USE for: arbitrary SAS code (use run-sas-program), jobs, jobdefs
16
-
17
- PARAMETERS
18
- - macro: string — macro name without "%" (required)
19
- - scenario: string — parameters or setup code (optional). Accepts: "x=1, y=abc" or "%let x=1; %let y=abc;"
20
-
21
- ROUTING RULES
22
- - "run macro abc" → { macro: "abc", scenario: "" }
23
- - "run macro abc with x=1, y=2" → { macro: "abc", scenario: "x=1, y=2" }
24
- - "run macro xyz with %let a=1; %let b=2;" → { macro: "xyz", scenario: "%let a=1; %let b=2;" }
25
-
26
- EXAMPLES
27
- - "run macro abc" → { macro: "abc", scenario: "" }
28
- - "run macro summarize with x=1, y=2" → { macro: "summarize", scenario: "x=1, y=2" }
29
-
30
- NEGATIVE EXAMPLES (do not route here)
31
- - "run SAS code" (use run-sas-program)
32
- - "run job X" (use run-job)
33
- - "run jobdef X" (use run-jobdef)
34
-
35
- ERRORS
36
- Returns log, ods, tables created by macro. Auto-converts "x=1, y=2" to "%let x=1; %let y=2;" format.
37
- `;
38
-
39
- let spec = {
40
- name: 'run-macro',
41
- description: description,
42
-
43
- inputSchema: z.object({
44
- macro: z.string(),
45
- scenario: z.string().optional()
46
- }),
47
-
48
- handler: async (params) => {
49
- const scenarioRaw = (params.scenario || '').trim();
50
- let setup = '';
51
- if (scenarioRaw) {
52
- // If the scenario already contains macro syntax, send it through unchanged
53
- const hasMacroSyntax = /%let\b|%[a-zA-Z_]\w*\s*\(|%[a-zA-Z_]\w*\s*;/.test(scenarioRaw) || scenarioRaw.includes('%');
54
- if (hasMacroSyntax) {
55
- setup = scenarioRaw;
56
- } else {
57
- // Convert "x=1,y=abc" -> "%let x=1; %let y=abc;"
58
- setup = scenarioRaw.split(',')
59
- .map(p => p.trim())
60
- .filter(Boolean)
61
- .map(p => {
62
- const [k, ...rest] = p.split('=');
63
- if (!k) return '';
64
- const key = k.trim();
65
- const val = rest.join('=').trim();
66
- return `%let ${key}=${val};`;
67
- })
68
- .filter(Boolean)
69
- .join(' ');
70
- }
71
- }
72
- const src = `${setup} %${params.macro};`;
73
- params.src = src;
74
- let r = await _submitCode(params);
75
- return r;
76
- }
77
- }
78
- return spec;
79
- }
80
-
81
- export default runMacro;
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 _submitCode from '../toolHelpers/_submitCode.js';
8
+
9
+
10
+ function macroScore(_appContext) {
11
+ const isAgent = _appContext && _appContext.agent;
12
+ let description = isAgent ? `
13
+ macro-score — execute a SAS macro model.
14
+ PARAMS: macro (string, required name without %), scenario (string, optional — "x=1,y=2" or %let syntax)
15
+ RETURNS: log, ODS output, and tables created by macro
16
+ ` : `
17
+ macro-score — submit and execute a SAS macro model on SAS Viya server.
18
+
19
+ USE when: score macro, execute macro with parameters
20
+ DO NOT USE for: arbitrary SAS code (use program-score), jobs (use job-score), jobdefs (use jobdef-score)
21
+
22
+ PARAMETERS
23
+ - macro: string macro name without "%" (required)
24
+ - scenario: string parameters or setup code (optional). Accepts: "x=1, y=abc" or "%let x=1; %let y=abc;"
25
+
26
+ ROUTING RULES
27
+ - "score macro abc" → { macro: "abc", scenario: "" }
28
+ - "score macro abc with x=1, y=2" → { macro: "abc", scenario: "x=1, y=2" }
29
+ - "score macro xyz with %let a=1; %let b=2;" → { macro: "xyz", scenario: "%let a=1; %let b=2;" }
30
+
31
+ EXAMPLES
32
+ - "score macro abc" { macro: "abc", scenario: "" }
33
+ - "score macro summarize with x=1, y=2" { macro: "summarize", scenario: "x=1, y=2" }
34
+
35
+ NEGATIVE EXAMPLES (do not route here)
36
+ - "score SAS code" (use program-score)
37
+ - "score job X" (use job-score)
38
+ - "score jobdef X" (use jobdef-score)
39
+
40
+ ERRORS
41
+ Returns log, ods, tables created by macro. Auto-converts "x=1, y=2" to "%let x=1; %let y=2;" format.
42
+ `;
43
+
44
+ let spec = {
45
+ name: 'macro-score',
46
+ description: description,
47
+
48
+ inputSchema: z.object({
49
+ macro: z.string(),
50
+ scenario: z.string().optional()
51
+ }),
52
+
53
+ handler: async (params) => {
54
+ const scenarioRaw = (params.scenario || '').trim();
55
+ let setup = '';
56
+ if (scenarioRaw) {
57
+ // If the scenario already contains macro syntax, send it through unchanged
58
+ const hasMacroSyntax = /%let\b|%[a-zA-Z_]\w*\s*\(|%[a-zA-Z_]\w*\s*;/.test(scenarioRaw) || scenarioRaw.includes('%');
59
+ if (hasMacroSyntax) {
60
+ setup = scenarioRaw;
61
+ } else {
62
+ // Convert "x=1,y=abc" -> "%let x=1; %let y=abc;"
63
+ setup = scenarioRaw.split(',')
64
+ .map(p => p.trim())
65
+ .filter(Boolean)
66
+ .map(p => {
67
+ const [k, ...rest] = p.split('=');
68
+ if (!k) return '';
69
+ const key = k.trim();
70
+ const val = rest.join('=').trim();
71
+ return `%let ${key}=${val};`;
72
+ })
73
+ .filter(Boolean)
74
+ .join(' ');
75
+ }
76
+ }
77
+ const src = `${setup} %${params.macro};`;
78
+ params.src = src;
79
+ let r = await _submitCode(params);
80
+ return r;
81
+ }
82
+ }
83
+ return spec;
84
+ }
85
+
86
+ export default macroScore;
@@ -3,67 +3,81 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
- import listModels from './listModels.js';
7
- import listTables from './listTables.js';
8
- import modelScore from './modelScore.js';
9
- import modelInfo from './modelInfo.js';
6
+ import listMas from './listMas.js';
7
+
8
+
9
+ import masScore from './masScore.js';
10
+ import masDescribe from './masDescribe.js';
11
+ import findMas from './findMas.js';
12
+
10
13
  import findLibrary from './findLibrary.js';
11
- import readTable from './readTable.js';
12
- import tableInfo from './tableInfo.js';
13
14
  import listLibraries from './listLibraries.js';
14
15
 
15
- import scrInfo from './scrInfo.js';
16
+ import listTables from './listTables.js';
17
+ import findTable from './findTable.js';
18
+ import tableDescribe from './tableDescribe.js';
19
+ import readTable from './readTable.js';
20
+
21
+ import findScr from './findScr.js';
22
+ import listScr from './listScr.js';
23
+ import scrDescribe from './scrDescribe.js';
16
24
  import scrScore from './scrScore.js';
17
25
 
18
26
  import devaScore from './devaScore.js';
19
27
 
20
- import findTable from './findTable.js';
21
- import findModel from './findModel.js';
22
- import runProgram from './runProgram.js';
23
- import runMacro from './runMacro.js';
24
- import runJob from './runJob.js';
28
+ import programScore from './programScore.js';
29
+ import casProgramScore from './casProgramScore.js';
30
+ import casModelScore from './casModelScore.js';
31
+ import macroScore from './macroScore.js';
32
+ import jobScore from './jobScore.js';
25
33
  import listJobs from './listJobs.js';
26
- import runJobdef from './runJobdef.js';
34
+ import jobdefScore from './jobdefScore.js';
27
35
  import findJob from './findJob.js';
28
36
  import listJobdefs from './listJobdefs.js';
29
37
  import findJobdef from './findJobdef.js';
38
+ import jobDescribe from './jobDescribe.js';
39
+ import jobdefDescribe from './jobdefDescribe.js';
30
40
 
31
41
  import sasQuery from './sasQuery.js';
32
42
  import setContext from './setContext.js';
33
43
 
34
-
35
44
  function makeTools(_appContext) {
36
45
  // wrap all tools with
37
46
  let customTools = [];
38
47
 
39
48
  // get the tool definitions and handler
40
49
  let list = [
41
- listModels(_appContext),
42
-
43
- findModel(_appContext),
44
- modelInfo(_appContext),
45
- modelScore(_appContext),
50
+ listMas(_appContext),
51
+ listScr(_appContext),
52
+ findMas(_appContext),
53
+ masDescribe(_appContext),
54
+ masScore(_appContext),
46
55
 
47
- scrInfo(_appContext),
56
+ scrDescribe(_appContext),
48
57
  scrScore(_appContext),
58
+ findScr(_appContext),
49
59
 
50
60
  findLibrary(_appContext),
51
61
  listLibraries(_appContext),
52
62
  findTable(_appContext),
53
- tableInfo(_appContext),
63
+ tableDescribe(_appContext),
54
64
  listTables(_appContext),
55
65
  readTable(_appContext),
56
66
  sasQuery(_appContext),
57
67
 
58
- runProgram(_appContext),
59
- runMacro(_appContext),
68
+ programScore(_appContext),
69
+ casProgramScore(_appContext),
70
+ casModelScore(_appContext),
71
+ macroScore(_appContext),
60
72
 
61
73
  findJob(_appContext),
62
74
  listJobs(_appContext),
63
- runJob(_appContext),
75
+ jobScore(_appContext),
64
76
  listJobdefs(_appContext),
65
77
  findJobdef(_appContext),
66
- runJobdef(_appContext),
78
+ jobDescribe(_appContext),
79
+ jobdefDescribe(_appContext),
80
+ jobdefScore(_appContext),
67
81
 
68
82
  devaScore(_appContext),
69
83
  setContext(_appContext)
@@ -0,0 +1,67 @@
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 debug from 'debug';
8
+ import _masDescribe from '../toolHelpers/_masDescribe.js';
9
+ const log = debug('tools');
10
+
11
+ function masDescribe(_appContext) {
12
+ const isAgent = _appContext && _appContext.agent;
13
+ let description = isAgent ? `
14
+ mas-describe — return input/output schema for a MAS model.
15
+ PARAMS: intent ('describe', required), name (string, required)
16
+ RETURNS: input variables, output variables, model type, description
17
+ ` : `
18
+ mas-describe — return detailed information about a specific MAS model, including its inputs, outputs, and metadata.
19
+
20
+ USE when: what inputs does model need, describe model, show variables for model, model inputs/outputs
21
+ DO NOT USE for: find model, list models, score model, table/job operations
22
+
23
+ PARAMETERS
24
+ - intent: must be 'describe' — only pass if user explicitly asked to describe/inspect a MAS model. Do NOT use for find or verify existence.
25
+ - model: string — model name (required, exact match)
26
+
27
+ ROUTING RULES
28
+ - "what inputs does mas X need?" → { model: "X" }
29
+ - "describe mas Y" → { model: "Y" }
30
+ - "describe mas Y.mas" → { model: "Y" }
31
+ - "show variables for mas Z" → { model: "Z" }
32
+
33
+ EXAMPLES
34
+ - "What inputs does mas churnRisk need?" → { model: "churnRisk" }
35
+ - "Describe mas creditScore" → { model: "creditScore" }
36
+ - "Show variables for myModel" → { model: "myModel" }
37
+
38
+ NEGATIVE EXAMPLES (do not route here)
39
+ - "list mas" (use list-models)
40
+ - "find mas X" (use find-model)
41
+ - "score with mas X" (use model-score)
42
+
43
+ ERRORS
44
+ Returns model metadata: inputs (name, type, role), outputs (name, type, possible_values), model_type, description.
45
+ `;
46
+
47
+ let spec = {
48
+ name: 'mas-describe',
49
+ description: description,
50
+ inputSchema: z.object({
51
+ intent: z.literal('describe'),
52
+ model: z.string()
53
+ }),
54
+ handler: async (params) => {
55
+ const { intent, ...rest } = params;
56
+ if (rest.model != null && rest.model.endsWith('.mas')) {
57
+ rest.model = rest.model.slice(0, -4);
58
+ }
59
+ let r = await _masDescribe(rest);
60
+ return r;
61
+ }
62
+ }
63
+ return spec;
64
+ }
65
+
66
+ export default masDescribe;
67
+
@@ -0,0 +1,95 @@
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 debug from 'debug';
8
+ import _masScoring from '../toolHelpers/_masScoring.js';
9
+ const log = debug('tools');
10
+
11
+ function masScore(_appContext) {
12
+ const isAgent = _appContext && _appContext.agent;
13
+ let description = isAgent ? `
14
+ mas-score — score data using a deployed MAS model.
15
+ PARAMS: model (string, required), scenario (object, optional key=value pairs)
16
+ RETURNS: predictions merged with input values
17
+ ` : `
18
+ mas-score — score data using a deployed model on MAS.
19
+
20
+ USE when: score with model, predict using model, batch scoring, model predictions
21
+ DO NOT USE for: find model, model metadata, list models, run programs/jobs, query tables
22
+
23
+ PARAMETERS
24
+ - model: string — model name (required, exact match)
25
+ - scenario: object — input data as JSON (optional, defaults to {}). Example: {age:45, income:60000}
26
+
27
+
28
+ ROUTING RULES
29
+ - "score with mas model X using a=1, b=2" → { model: "X", scenario: {a:1, b:2} }
30
+ - "score Y.mas for a=1, b=2" → { model: "Y", scenario: {a:1, b:2} }
31
+ - "predict using mas model Y with age=45, income=60000" → { model: "Y", scenario: {age:45, income:60000} }
32
+
33
+ EXAMPLES
34
+ - "score with mas model churn using age=45, income=60000" → { model: "churn", scenario: {age:45, income:60000} }
35
+ - "predict mas model creditScore for credit=700, debt=20000" → { model: "creditScore", scenario: {credit:700, debt:20000} }
36
+
37
+ NEGATIVE EXAMPLES (do not route here)
38
+ - "find model X" (use find-model)
39
+ - "what inputs does model need" (use model-info)
40
+ - "list models" (use list-models)
41
+ - "score job X" (use job-score)
42
+
43
+ ERRORS
44
+ Returns predictions, probabilities, scores merged with input data. Returns error if model not found or scoring fails.
45
+ `;
46
+
47
+
48
+ let spec = {
49
+ name: 'mas-score',
50
+ description: description,
51
+ inputSchema: z.object({
52
+ model: z.string(),
53
+ scenario: z.any()
54
+ }),
55
+
56
+ handler: async (iparams) => {
57
+ let params = {...iparams};
58
+ let scenario = params.scenario;
59
+
60
+ // Convert the scenario string to an object
61
+ // Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
62
+ let scenarioObj = {};
63
+ let count = 0;
64
+ if (typeof scenario === 'object') {
65
+ scenarioObj = scenario;
66
+ } else if (Array.isArray(scenario)) {
67
+ scenarioObj = scenario[0];
68
+ } else {
69
+ //console.error('Incoming scenario', scenario);
70
+ scenarioObj = scenario.split(',').reduce((acc, pair) => {
71
+ let [key, value] = pair.split('=');
72
+ acc[key.trim()] = value;
73
+ count++;
74
+ return acc;
75
+ }, {});
76
+ }
77
+ params.scenario = scenarioObj;
78
+ // Drop model extension (e.g., .job, .model)
79
+ if (params.model != null) {
80
+ if (params.model.endsWith('.mas')) {
81
+ params.model = params.model.slice(0, -4);
82
+ }
83
+ }
84
+
85
+ log('masScore params', params);
86
+ // Check if the params.scenario is a string and parse it
87
+ let r = await _masScoring(params)
88
+ return r;
89
+ }
90
+ }
91
+ return spec;
92
+ }
93
+
94
+ export default masScore;
95
+
@@ -1,93 +1,96 @@
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 _submitCode from '../toolHelpers/_submitCode.js';
8
-
9
- function runProgram(_appContext) {
10
- let description = `
11
- run-sas-program execute SAS code or programs on SAS Viya server.
12
-
13
- USE when: run program, execute SAS code, run .sas file
14
- DO NOT USE for: macros (use run-macro), jobs (use run-job), jobdefs (use run-jobdef), SQL queries (use sas-query), read data (use read-table)
15
-
16
- PARAMETERS
17
- - src: string — SAS code or .sas filename (required)
18
- - folder: string (default: '') server folder path for .sas files
19
- - scenario: string | object parameter values. Accepts: "x=1, y=2" or {x:1, y:2}
20
- - output: string (default: '') — table name to return (case-sensitive)
21
- - limit: number (default: 100) — max rows from output
22
-
23
- ROUTING RULES
24
- - "run program 'data a; x=1; run;'" { src: "data a; x=1; run;", folder: "", output: "", limit: 100 }
25
- - "run sas program sample folder=/Public/models" → { src: "sample", folder: "/Public/models", output: "", limit: 100 }
26
- - "run program with name=John, age=45" → { src: "<code>", scenario: {name:"John", age:45}, output: "", limit: 100 }
27
-
28
- EXAMPLES
29
- - "run program 'data a; x=1; run;'" → { src: "data a; x=1; run;", folder: "", output: "", limit: 100 }
30
- - "run sas file sample in /Public" → { src: "sample", folder: "/Public", output: "", limit: 100 }
31
-
32
- NEGATIVE EXAMPLES (do not route here)
33
- - "run macro X" (use run-macro)
34
- - "run job X" (use run-job)
35
- - "run jobdef X" (use run-jobdef)
36
- - "SQL query" (use sas-query)
37
- - "read table" (use read-table)
38
-
39
- ERRORS
40
- Returns log, ods, tables array, data (if output specified). Error if execution fails.
41
- `;
42
-
43
- let spec = {
44
- name: 'run-program',
45
- description: description,
46
- inputSchema: z.object({
47
- src: z.string(),
48
- scenario: z.string().optional(),
49
- output: z.string().optional(),
50
- folder: z.string().optional(),
51
- limit: z.number().optional()
52
- }),
53
- handler: async (params) => {
54
- let {src, folder, scenario, _appContext} = params;
55
- // figure out src
56
- let isrc = src;
57
- if (folder != null && folder.trim().length > 0) {
58
- if (isrc.indexOf('.sas') < 0) {
59
- isrc = isrc + '.sas';
60
- }
61
- isrc = `
62
- filename mcptemp filesrvc folderpath="${folder}";
63
- %include mcptemp("${isrc}");
64
- filename mcptemp clear;
65
- `;
66
- }
67
- // figure out macros
68
-
69
- if (typeof scenario === 'string' && scenario.includes('=')) {
70
- scenario = scenario.split(',').reduce((acc, pair) => {
71
- const [k, ...rest] = pair.split('=');
72
- if (!k) return acc;
73
- acc[k.trim()] = rest.join('=').trim();
74
- return acc;
75
- }, {});
76
- }
77
- let iparms = {
78
- args: scenario,
79
- output: params.output,
80
- limit: params.limit,
81
- src: isrc,
82
- _appContext: _appContext
83
- }
84
- // console.error('iparms', iparms);
85
- let r = await _submitCode(iparms);
86
- return r;
87
- }
88
- }
89
- return spec;
90
- }
91
-
92
- export default runProgram;
93
-
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 _submitCode from '../toolHelpers/_submitCode.js';
8
+
9
+ function programScore(_appContext) {
10
+ const isAgent = _appContext && _appContext.agent;
11
+ let description = isAgent ? `
12
+ program-score — execute a SAS program model.
13
+ PARAMS: src (string, required), folder (string, optional), scenario (string|object, optional), output (string, optional), limit (number, optional)
14
+ RETURNS: log, ODS output, optional output table rows
15
+ ` : `
16
+ program-score — execute a SAS program model on SAS Viya server.
17
+
18
+ USE when: score program, execute SAS code, run .sas file
19
+ DO NOT USE for: macros (use macro-score), jobs (use job-score), jobdefs (use jobdef-score), SQL queries (use sas-query), read data (use read-table)
20
+
21
+ PARAMETERS
22
+ - src: string — SAS code or .sas filename (required)
23
+ - folder: string (default: '') — server folder path for .sas files
24
+ - scenario: string | object parameter values. Accepts: "x=1, y=2" or {x:1, y:2}
25
+ - output: string (default: '') table name to return (case-sensitive)
26
+ - limit: number (default: 100) max rows from output
27
+
28
+ ROUTING RULES
29
+ - "score program 'data a; x=1; run;'" → { src: "data a; x=1; run;", folder: "", output: "", limit: 100 }
30
+ - "score sas program sample folder=/Public/models" → { src: "sample", folder: "/Public/models", output: "", limit: 100 }
31
+ - "score program with name=John, age=45" → { src: "<code>", scenario: {name:"John", age:45}, output: "", limit: 100 }
32
+
33
+ EXAMPLES
34
+ - "score program 'data a; x=1; run;'" { src: "data a; x=1; run;", folder: "", output: "", limit: 100 }
35
+ - "score sas file sample in /Public" { src: "sample", folder: "/Public", output: "", limit: 100 }
36
+
37
+ NEGATIVE EXAMPLES (do not route here)
38
+ - "score macro X" (use macro-score)
39
+ - "score job X" (use job-score)
40
+ - "score jobdef X" (use jobdef-score)
41
+ - "SQL query" (use sas-query)
42
+ - "read table" (use read-table)
43
+
44
+ ERRORS
45
+ Returns log, ods, tables array, data (if output specified). Error if execution fails.
46
+ `;
47
+
48
+ let spec = {
49
+ name: 'program-score',
50
+ description: description,
51
+ inputSchema: z.object({
52
+ src: z.string(),
53
+ scenario: z.string().optional(),
54
+ output: z.string().optional(),
55
+ folder: z.string().optional(),
56
+ limit: z.number().optional()
57
+ }),
58
+ handler: async (params) => {
59
+ let {src, folder, scenario, _appContext} = params;
60
+ // figure out src
61
+ let isrc = src;
62
+ if (folder != null && folder.trim().length > 0) {
63
+ if (isrc.indexOf('.sas') < 0) {
64
+ isrc = isrc + '.sas';
65
+ }
66
+ isrc = `
67
+ filename mcptemp filesrvc folderpath="${folder}";
68
+ %include mcptemp("${isrc}");
69
+ filename mcptemp clear;
70
+ `;
71
+ }
72
+ // figure out macros
73
+
74
+ if (typeof scenario === 'string' && scenario.includes('=')) {
75
+ scenario = scenario.split(',').reduce((acc, pair) => {
76
+ const [k, ...rest] = pair.split('=');
77
+ if (!k) return acc;
78
+ acc[k.trim()] = rest.join('=').trim();
79
+ return acc;
80
+ }, {});
81
+ }
82
+ let iparms = {
83
+ args: scenario,
84
+ output: params.output,
85
+ limit: params.limit,
86
+ src: isrc,
87
+ _appContext: _appContext
88
+ }
89
+ let r = await _submitCode(iparms);
90
+ return r;
91
+ }
92
+ }
93
+ return spec;
94
+ }
95
+
96
+ export default programScore;