@sassoftware/sas-score-mcp-serverjs 1.0.1-9 → 1.1.2

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 (118) 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 +11 -13
  20. package/openApi.yaml +121 -121
  21. package/package.json +16 -14
  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 +1 -18
  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 +66 -61
  61. package/src/toolSet/findJob.js +24 -9
  62. package/src/toolSet/findJobdef.js +22 -19
  63. package/src/toolSet/findLibrary.js +73 -68
  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 +6 -6
  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 +70 -61
  73. package/src/toolSet/listJobs.js +68 -61
  74. package/src/toolSet/listLibraries.js +84 -78
  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} +21 -18
  83. package/src/toolSet/readTable.js +80 -63
  84. package/src/toolSet/sasQuery.js +83 -77
  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 +70 -65
  89. package/src/toolSet/superstat.js +61 -61
  90. package/src/toolSet/tableDescribe.js +65 -0
  91. package/.agents/sas-score-mcp-serverjs-agent.md +0 -58
  92. package/.instructions/copilot-instructions.md +0 -201
  93. package/.instructions/enforce-find-resource-strategy.md +0 -35
  94. package/.skills/sas-find-library-smart/SKILL.md +0 -155
  95. package/.skills/sas-find-resource-strategy/SKILL.md +0 -105
  96. package/.skills/sas-list-resource-strategy/SKILL.md +0 -124
  97. package/.skills/sas-list-tables-smart/SKILL.md +0 -128
  98. package/.skills/sas-read-and-score-strategy/SKILL.md +0 -113
  99. package/.skills/sas-read-strategy/SKILL.md +0 -154
  100. package/.skills/sas-request-classifier/SKILL.md +0 -74
  101. package/.skills/sas-score-workflow-strategy/SKILL.md +0 -314
  102. package/scripts/optimize_final.py +0 -140
  103. package/scripts/optimize_tools.py +0 -99
  104. package/scripts/setup-skills.js +0 -34
  105. package/scripts/update_descriptions.py +0 -46
  106. package/src/authpkce.js +0 -219
  107. package/src/handleGetDelete.js +0 -34
  108. package/src/handleRequest.js +0 -112
  109. package/src/hapiMcpServer.js +0 -241
  110. package/src/toolSet/findModel.js +0 -60
  111. package/src/toolSet/listModels.js +0 -56
  112. package/src/toolSet/modelInfo.js +0 -55
  113. package/src/toolSet/modelScore.js +0 -89
  114. package/src/toolSet/runCasProgram.js +0 -98
  115. package/src/toolSet/runJob.js +0 -81
  116. package/src/toolSet/runJobdef.js +0 -82
  117. package/src/toolSet/scrInfo.js +0 -52
  118. package/src/toolSet/tableInfo.js +0 -58
@@ -0,0 +1,32 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import restaflib from '@sassoftware/restaflib';
7
+ import restaf from '@sassoftware/restaf';
8
+ async function _casScore(params) {
9
+ let { model, name, scenario, _appContext } = params;
10
+ let { casSetup, caslScore } = restaflib;
11
+
12
+ let store = restaf.initStore(_appContext.storeConfig);
13
+ let { session } = await casSetup(store, _appContext.logonPayload, null, _appContext.cas);
14
+ if (session == null) {
15
+ return { content: [{ type: 'text', text: 'Could not create a cas session' }] };
16
+ }
17
+
18
+ try {
19
+ let args = {
20
+ name: name,
21
+ modelName: model,
22
+ scenario: scenario
23
+ }
24
+ let output = await caslScore(store, session, args);
25
+ let results = output.casResults;
26
+ return { content: [{ type: 'text', text: JSON.stringify(results) }], structuredContent: results };
27
+ } catch (err) {
28
+ console.log(err);
29
+ return { isError: true, content: [{ type: 'text', text: JSON.stringify(err) }] };
30
+ }
31
+ }
32
+ export default _casScore;
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * Return lean description in agent mode, full description in standalone mode.
8
+ * @param {object} appContext - the _appContext passed to every tool factory
9
+ * @param {string} lean - short description for agent mode (~3-6 lines)
10
+ * @param {string} full - full description for standalone mode
11
+ */
12
+ export function desc(appContext, lean, full) {
13
+ return (appContext && appContext.agent) ? lean : full;
14
+ }
@@ -0,0 +1,12 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import _listJobs from './_listJobs.js';
7
+ async function _findJob(params) {
8
+ let r = await _listJobs(params);
9
+ console.error ("findJob result:" , r);
10
+ return r;
11
+ }
12
+ export default _findJob;
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import _listJobdefs from './_listJobdefs.js';
7
+ async function _findJobdef(params) {
8
+ return await _listJobdefs(params);
9
+ }
10
+ export default _findJobdef;
@@ -0,0 +1,11 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import _listLibrary from './_listLibrary.js';
7
+ async function _findLibrary(params) {
8
+ params.tool = 'find';
9
+ return await _listLibrary(params);
10
+ }
11
+ export default _findLibrary;
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import _listMas from './_listMas.js';
7
+ async function _findMas(params) {
8
+ params.tool = 'find';
9
+ let r = await _listMas(params);
10
+ console.log ("findMas result:" , r);
11
+ return r;
12
+ }
13
+ export default _findMas;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import axios from 'axios';
7
+
8
+ async function _findScr(params) {
9
+ let {name} = params;
10
+ let config = {
11
+ method: 'HEAD',
12
+ name: name + '/apiMeta/api',
13
+ headers: {
14
+ 'Accept': 'application/json'
15
+ }
16
+ }
17
+ try {
18
+ console.error('[Note] Config:', config);
19
+ let response = await axios(config);
20
+ console.error('[Note] Response status:', response.status);
21
+ if (response.status !== 200) {
22
+
23
+ return {isError: true, content: [{ type: 'text', text: `SCR model ${name} not found` }]};
24
+ } else if (response.status === 200) {
25
+ let r = { scr: [name] };
26
+ return { content: [{ type: 'text', text: JSON.stringify(r) }],
27
+ structuredOutput: r
28
+
29
+ };
30
+ }
31
+ }
32
+ catch (error) {
33
+ return {isError: true,content: [{ type: 'text', text: JSON.stringify(error) }]};
34
+ }
35
+ }
36
+ export default _findScr;
@@ -0,0 +1,11 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import _listTables from './_listTables.js';
7
+ async function _findTable(params) {
8
+ params.tool = 'find';
9
+ return await _listTables(params);
10
+ }
11
+ export default _findTable;
@@ -6,7 +6,7 @@ import restaf from '@sassoftware/restaf';
6
6
 
7
7
 
8
8
  async function _listJobdefs(params) {
9
- let { limit, start, name, _appContext } = params;
9
+ let { limit, start, name, tool, _appContext } = params;
10
10
 
11
11
  let store = restaf.initStore(_appContext.storeConfig);
12
12
  let msg = await store.logon(_appContext.logonPayload);
@@ -41,7 +41,17 @@ async function _listJobdefs(params) {
41
41
 
42
42
  let response = {jobDefinitions: Object.keys(names)};
43
43
  if (name != null) {
44
- response = { name: name, parameters: names[name].parameters };
44
+ if (tool === 'find') {
45
+ response = { job: [name] };
46
+ } else if (tool === 'describe') {
47
+ let p = [];
48
+ names[name].parameters.map((v, k) => {
49
+ if (v.name.startsWith('_') === false) {
50
+ p.push(v);
51
+ }
52
+ });
53
+ response = { describe: p };
54
+ };
45
55
  }
46
56
 
47
57
  return {
@@ -6,7 +6,7 @@
6
6
  import restaf from '@sassoftware/restaf';
7
7
 
8
8
  async function _listJobs(params) {
9
- let { limit, start, name, _appContext } = params;
9
+ let { limit, start, name, tool, _appContext } = params;
10
10
 
11
11
  let store = restaf.initStore(_appContext.storeConfig);
12
12
  let msg = await store.logon(_appContext.logonPayload);
@@ -33,21 +33,32 @@ async function _listJobs(params) {
33
33
  }
34
34
 
35
35
  let names = {};
36
-
36
+
37
37
  jobList.itemsList().map((id, n) => {
38
38
  let jname = jobList.items(id, 'data', 'jobRequest', 'name');
39
-
39
+
40
40
  if (names[jname] == null) {
41
41
  let value = jobList.items(id, 'data', 'jobRequest', 'jobDefinition', 'parameters');
42
- names[jname] = {parameters: (value == null) ? {} : value.toJS() };
42
+ names[jname] = { parameters: (value == null) ? {} : value.toJS() };
43
43
  }
44
44
  });
45
-
46
- let response = {jobs: Object.keys(names)};
45
+ console.error('parameters', JSON.stringify(names, null, 2));
46
+ let response = { jobs: Object.keys(names) };
47
+ console.error('response', JSON.stringify(response, null, 2));
47
48
 
48
49
  if (name != null) {
49
- response = { name: name, parameters: names[name].parameters };
50
- };
50
+ if (tool === 'find') {
51
+ response = { job: [name] };
52
+ } else if (tool === 'describe') {
53
+ let p = [];
54
+ names[name].parameters.map((v, k) => {
55
+ if (v.name.startsWith('_') === false) {
56
+ p.push(v);
57
+ }
58
+ });
59
+ response = { describe: p };
60
+ };
61
+ }
51
62
  return {
52
63
  content: [{ type: 'text', text: JSON.stringify(response) }],
53
64
  structuredContent: response,
@@ -5,7 +5,7 @@
5
5
 
6
6
  import restaf from '@sassoftware/restaf';
7
7
 
8
- async function _listModels(params) {
8
+ async function _listMas(params) {
9
9
  let { limit, start , name, _appContext} = params;;
10
10
  // setup
11
11
 
@@ -27,9 +27,9 @@ async function _listModels(params) {
27
27
  }
28
28
  let result = await store.apiCall(microanalyticScore.links('modules'), payload);
29
29
  if (result.itemsList().size === 0) {
30
- return { content: [{ type: 'text', text: `No models exist in MAS server` }]};
30
+ return { content: [{ type: 'text', text: `No MAS exist in MAS server` }]};
31
31
  }
32
- let list = {models: result.itemsList().toJS()};
32
+ let list = {mas: result.itemsList().toJS()};
33
33
  return { content: [{ type: 'text', text: JSON.stringify(list) }],
34
34
  structuredContent: list
35
35
  };
@@ -38,4 +38,4 @@ async function _listModels(params) {
38
38
  }
39
39
  }
40
40
 
41
- export default _listModels;
41
+ export default _listMas;
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ async function _listScr(params) {
7
+ let response = { scr: [] };
8
+ return {
9
+ content: [{ type: 'text', text: JSON.stringify(response) }],
10
+ structuredContent: response
11
+ }
12
+ }
13
+ export default _listScr;
@@ -5,11 +5,11 @@
5
5
 
6
6
  import axios from 'axios';
7
7
 
8
- async function _scrInfo(params) {
9
- let {url} = params;
8
+ async function _scrDescribe(params) {
9
+ let {name} = params;
10
10
  let config = {
11
11
  method: 'GET',
12
- url: url + '/apiMeta/api',
12
+ name: name + '/apiMeta/api',
13
13
  headers: {
14
14
  'Accept': 'application/json'
15
15
  }
@@ -29,4 +29,4 @@ async function _scrInfo(params) {
29
29
  return {isError: true,content: [{ type: 'text', text: JSON.stringify(error) }]};
30
30
  }
31
31
  }
32
- export default _scrInfo;
32
+ export default _scrDescribe;
@@ -7,7 +7,7 @@ import axios from 'axios';
7
7
 
8
8
 
9
9
  async function _scrScore(params) {
10
- let { url, scenario} = params;
10
+ let { name, scenario} = params;
11
11
 
12
12
 
13
13
  let data = scenario.split(',').reduce((acc, pair) => {
@@ -18,7 +18,7 @@ async function _scrScore(params) {
18
18
 
19
19
  let config = {
20
20
  method: 'POST',
21
- url: url,
21
+ name: name,
22
22
  headers: {
23
23
  'Content-Type': 'application/json',
24
24
  'Accept': 'application/json'
@@ -2,33 +2,35 @@
2
2
  * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
+ import restaf from '@sassoftware/restaf';
5
6
  import restaflib from '@sassoftware/restaflib';
6
7
 
7
8
  async function _submitCasl(params) {
8
9
  let { src, args, _appContext } = params;
10
+ let { casSetup, caslRun } = restaflib;
11
+ console.error(_appContext);
12
+ let store = restaf.initStore(_appContext.storeConfig);
9
13
 
10
- let store = restaf.initStore(_appContext.storeConfig);
11
-
12
- let session = await restaflib.casSetup(store, _appContext.logonPayload, null, _appContext.cas);
14
+ let { session } = await casSetup(store, _appContext.logonPayload, null, _appContext.cas);
13
15
  if (session == null) {
14
- return {content: [{ type: 'text', text: 'Could not create a cas session' }]};
16
+ return { content: [{ type: 'text', text: 'Could not create a cas session' }] };
15
17
  }
16
18
 
17
19
  try {
18
- try {
19
- let r = await caslRun(store, session, src, (args == null) ? {} : args, true);
20
- await store.apiCall( session.links( 'delete' ) );
21
-
22
- return {content: [{ type: 'text', text: JSON.stringify(r.items()) }], structuredContent: r.items() };
23
- } catch (err) {
24
- console.error(err);
25
- return { isError: true, content: [{ type: 'text', text: JSON.stringify(err) }] };
26
- }
27
- }
28
- catch (err) {
20
+ console.error(caslRun);
21
+ debugger;
22
+ let r = await caslRun(store, session, src, (args == null) ? {} : args);
23
+ console.error(r.results);
24
+ let response = (r.results == null) ? r : r.results;
25
+ await store.apiCall(session.links('delete'));
26
+ return { content: [{ type: 'text', text: JSON.stringify(response) }], structuredContent: response };
27
+ } catch (err) {
29
28
  console.error(err);
30
- await store.apiCall( session.links( 'delete' ) );
31
- return { isError: true, content: [{ type: 'text', text: JSON.stringify(err) }] };
29
+ if (session != null) {
30
+ await store.apiCall(session.links('delete'));
31
+ }
32
+ return { isError: true, content: [{ type: 'text', text: JSON.stringify(err) }] };
32
33
  }
33
34
  }
35
+
34
36
  export default _submitCasl;
@@ -5,7 +5,7 @@
5
5
  import restafedit from '@sassoftware/restafedit';
6
6
  import deleteSession from './deleteSession.js';
7
7
 
8
- async function _tableInfo(params) {
8
+ async function _tableDescribe(params) {
9
9
  let { table, lib, server, _appContext } = params;
10
10
 
11
11
 
@@ -69,4 +69,4 @@ async function _tableInfo(params) {
69
69
  return { isError: true, content: [{ type: 'text', text: JSON.stringify(err) }] };
70
70
  }
71
71
  }
72
- export default _tableInfo;
72
+ export default _tableDescribe;
@@ -58,7 +58,7 @@ async function igetLogonPayload(_appContext) {
58
58
 
59
59
  // Use user supplied refresh token-
60
60
  if (_appContext.AUTHFLOW === "refresh") {
61
- console.error("[Note] Using user supplied refresh token", _appContext.REFRESH_TOKEN);
61
+ console.error("[Note] Using user supplied refresh token", _appContext.REFRESH_TOKEN.substring(0,20) + "...");
62
62
  let token = await refreshToken(_appContext,{token: _appContext.REFRESH_TOKEN, host: _appContext.VIYA_SERVER});
63
63
  let logonPayload = {
64
64
  host: _appContext.VIYA_SERVER,
@@ -71,7 +71,7 @@ async function igetLogonPayload(_appContext) {
71
71
  }
72
72
 
73
73
  if (_appContext.AUTHFLOW === "token") {
74
- console.error("[Note] Using token supplied by user");
74
+ console.error("[Note] Using token supplied by user");
75
75
  let logonPayload = {
76
76
  host: _appContext.VIYA_SERVER,
77
77
  authType: "server",
@@ -0,0 +1,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 _casScore from '../toolHelpers/_casScore.js';
8
+
9
+ function casModelScore(_appContext) {
10
+ const isAgent = _appContext && _appContext.agent;
11
+
12
+ let description = isAgent ? `
13
+ cas-model-score — score with a model persisted as a CAS table in a CAS server.
14
+ PARAMS: name(string, required), model(string, optional),scenario (string|object, optional)
15
+ RETURNS: returns the score for the specified scenario
16
+ Notes: name is specified as a.b.cas where a is the library, b is the CAS table name, and .cas is the suffix. Model parameter is optional and can be used to specify a different model name if it is not the same as the table name.
17
+ ` : `
18
+ cas-model-score — execute a CAS model on SAS Viya cas server.
19
+
20
+ USE when: score cas model with scenario
21
+ DO NOT USE for: macros (use ${_appContext.brand}-macro-score), SAS code (use ${_appContext.brand}-program-score), jobs (use ${_appContext.brand}-job-score), jobdefs (use ${_appContext.brand}-jobdef-score), scr(use ${_appContext.brand}-scr-score)
22
+
23
+ PARAMETERS
24
+
25
+ - name: string (optional) — CAS model table to use for scoring
26
+ - model: string (optional) — name of the model to use for scoring, if different from the table name
27
+ - scenario: string or object (optional) — input parameters
28
+
29
+
30
+ ROUTING RULES
31
+ - "score "mymodel.abc.cas" with scenario "x=1, y=2" → { name: "mymodel.abc", scenario: "x=1, y=2" }
32
+
33
+
34
+ EXAMPLES
35
+ - "score "mymodel.abc.cas" with scenario "x=1, y=2" → { name: "mymodel.abc", scenario: "x=1, y=2" }
36
+
37
+ NEGATIVE EXAMPLES (do not route here)
38
+ - "score sas macro" (use ${_appContext.brand}-macro-score)
39
+ - "submit sas code" (use ${_appContext.brand}-program-score)
40
+ - "score job X" (use ${_appContext.brand}-job-score)
41
+ - "score jobdef X" (use ${_appContext.brand}-jobdef-score)
42
+ - "score scr X" (use ${_appContext.brand}-scr-score)
43
+
44
+
45
+
46
+
47
+ RESPONSE
48
+ Log output and CAS results. If output table is specified, that table's rows up to the limit.
49
+ `;
50
+
51
+ let spec = {
52
+ name: 'cas-model-score',
53
+ description: description,
54
+ inputSchema: z.object({
55
+ scenario: z.any().optional(),
56
+ model: z.string().optional(),
57
+ name: z.string()
58
+ }),
59
+ handler: async (params) => {
60
+ let {name, model, scenario, _appContext} = params;
61
+ // figure out src
62
+
63
+
64
+ // Convert the scenario string to an object
65
+ // Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
66
+ let scenarioObj = {};
67
+ if (typeof scenario === 'object' && scenario !== null) {
68
+ scenarioObj = scenario;
69
+ } else if (Array.isArray(scenario)) {
70
+ scenarioObj = scenario[0];
71
+ } else if (typeof scenario === 'string' && scenario.includes('=')) {
72
+ scenarioObj = scenario.split(',').reduce((acc, pair) => {
73
+ let [key, value] = pair.split('=');
74
+ acc[key.trim()] = value;
75
+ return acc;
76
+ }, {});
77
+ }
78
+ params.scenario = scenarioObj;
79
+
80
+ let iparms = {
81
+ args: scenarioObj,
82
+ model: params.model,
83
+ name: params.name,
84
+ _appContext: _appContext
85
+ }
86
+ let r = await _casScore(iparms);
87
+ return r;
88
+ }
89
+ }
90
+ return spec;
91
+ }
92
+
93
+ export default casModelScore;
@@ -0,0 +1,105 @@
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 _submitCasl from '../toolHelpers/_submitCasl.js';
8
+ import _casScore from '../toolHelpers/_casScore.js';
9
+
10
+ function casProgramScore(_appContext) {
11
+ const isAgent = _appContext && _appContext.agent;
12
+
13
+ let description = isAgent ? `
14
+ cas-program-score — execute a CAS program src code or score a persisted CAS model on SAS Viya server.
15
+ PARAMS: src (string, required), model(string,optional), name(string, optional),scenario (string|object, optional), output (string, optional), limit (number, optional)
16
+ RETURNS: log output and CAS results, optional output table rows
17
+ ` : `
18
+ cas-program-score — execute a CAS program model on SAS Viya server.
19
+
20
+ USE when: score cas program, run cas program, submit CASL, score cas model with scenario
21
+ DO NOT USE for: macros (use ${_appContext.brand}-macro-score), SAS code (use ${_appContext.brand}-program-score), jobs (use ${_appContext.brand}-job-score), jobdefs (use ${_appContext.brand}-jobdef-score), scr(use ${_appContext.brand}-scr-score)
22
+
23
+ PARAMETERS
24
+ - src: string (required) — CAS program or CASL code to execute verbatim
25
+ - casmodel: string (optional) — CAS model table to use for scoring
26
+ - name: string (optional) — name of the model to use for scoring, if different from the table name
27
+ - scenario: string or object (optional) — input parameters
28
+ - output: string — table name to return in response
29
+ - limit: number (default: 100) — max rows to return
30
+
31
+ **NOTE** if both src and casmodel are specified, the casmodel will take precedence
32
+ ROUTING RULES
33
+ - "run cas program "action echo" → { src: "action echo" }
34
+ - "execute cas "action simple.summary" with table=a.b → { src: "action simple.summary",scenario: {
35
+ table: "a.b"} }
36
+ - "score casmodel "mymodel.abc" with scenario "x=1, y=2" → { casmodel: "mymodel.abc", scenario: "x=1, y=2" }
37
+
38
+
39
+ EXAMPLES
40
+ - "run cas program "action echo" → { src: "action echo" }
41
+ - "score casmodel "mymodel.abc" with scenario "x=1, y=2" → { casmodel: "mymodel.abc", scenario: "x=1, y=2" }
42
+
43
+ NEGATIVE EXAMPLES (do not route here)
44
+ - "score sas macro" (use ${_appContext.brand}-macro-score)
45
+ - "submit sas code" (use ${_appContext.brand}-program-score)
46
+ - "score job X" (use ${_appContext.brand}-job-score)
47
+ - "score jobdef X" (use ${_appContext.brand}-jobdef-score)
48
+ - "score scr X" (use ${_appContext.brand}-scr-score)
49
+
50
+ NOTES
51
+ Sends src verbatim without validation. Use parameter scenario to pass arguments. For arbitrary SAS code use ${_appContext.brand}-program-score.
52
+
53
+ RESPONSE
54
+ Log output and CAS results. If output table is specified, that table's rows up to the limit.
55
+ `;
56
+
57
+ let spec = {
58
+ name: 'cas-program-score',
59
+ description: description,
60
+ inputSchema: z.object({
61
+ src: z.string(),
62
+ scenario: z.any().optional(),
63
+ output: z.string().optional(),
64
+ casmodel: z.string().optional(),
65
+ name: z.string().optional(),
66
+ limit: z.number().optional()
67
+ }),
68
+ handler: async (params) => {
69
+ let {src, scenario, _appContext} = params;
70
+ // figure out src
71
+ let isrc = src;
72
+
73
+ // Convert the scenario string to an object
74
+ // Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
75
+ let scenarioObj = {};
76
+ if (typeof scenario === 'object' && scenario !== null) {
77
+ scenarioObj = scenario;
78
+ } else if (Array.isArray(scenario)) {
79
+ scenarioObj = scenario[0];
80
+ } else if (typeof scenario === 'string' && scenario.includes('=')) {
81
+ scenarioObj = scenario.split(',').reduce((acc, pair) => {
82
+ let [key, value] = pair.split('=');
83
+ acc[key.trim()] = value;
84
+ return acc;
85
+ }, {});
86
+ }
87
+ params.scenario = scenarioObj;
88
+
89
+ let iparms = {
90
+ args: scenarioObj,
91
+ output: params.output,
92
+ limit: params.limit,
93
+ src: isrc,
94
+ model: params.casmodel,
95
+ name: params.name,
96
+ _appContext: _appContext
97
+ }
98
+ let r = (params.casmodel == null) ? await _submitCasl(iparms) : await _casScore(iparms);
99
+ return r;
100
+ }
101
+ }
102
+ return spec;
103
+ }
104
+
105
+ export default casProgramScore;