@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
@@ -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;
@@ -5,12 +5,17 @@
5
5
  import { z } from 'zod';
6
6
 
7
7
  function devaScore(_appContext) {
8
-
9
- let description = `
8
+ const isAgent = _appContext && _appContext.agent;
9
+ let brand = _appContext.brand + '-';
10
+ let description = isAgent ? `
11
+ deva-score — run a demonstration scoring calculation.
12
+ PARAMS: a (number, required), b (number, required)
13
+ RETURNS: computed result for a and b
14
+ ` : `
10
15
  deva-score — compute a numeric score based on two input values.
11
16
 
12
17
  USE when: calculate deva score, score these values, compute score for numbers
13
- DO NOT USE for: model scoring (use model-score), statistical calculations, data lookup
18
+ DO NOT USE for: model scoring (use ${brand}model-score), statistical calculations, data lookup
14
19
 
15
20
  PARAMETERS
16
21
  - a: number (required) — first input value
@@ -30,9 +35,9 @@ EXAMPLES
30
35
  - "Deva score 20 and 30" → { a: 20, b: 30 } returns 2100
31
36
 
32
37
  NEGATIVE EXAMPLES (do not route here)
33
- - "Score this customer with credit model" (use model-score)
34
- - "Calculate the mean of these values" (use run-sas-program or sas-query)
35
- - "Statistical analysis of numbers" (use sas-query)
38
+ - "Score this customer with credit model" (use ${brand}model-score)
39
+ - "Calculate the mean of these values" (use ${brand}program-score or ${brand}sas-query)
40
+ - "Statistical analysis of numbers" (use ${brand}sas-query)
36
41
 
37
42
  RESPONSE
38
43
  Returns { score: (a + b) * 42 }
@@ -1,59 +1,74 @@
1
- /*
2
- * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- import { z } from 'zod';
6
- import _listJobs from '../toolHelpers/_listJobs.js';
7
- function findJob(_appContext) {
8
-
9
- let description = `
10
- find-job locate a specific SAS Viya job.
11
-
12
- USE when: find job, does job exist, is there a job named, lookup job, verify job exists
13
- DO NOT USE for: list jobs (use list-jobs), run job (use run-job), execute jobdef (use run-jobdef), find lib/table/model (use respective tools)
14
-
15
- PARAMETERS
16
- - name: string (required) — job name to locate; if multiple supplied, use first
17
-
18
- ROUTING RULES
19
- - "find job <name>" → { name: "<name>" }
20
- - "does job <name> exist" → { name: "<name>" }
21
- - "is there a job named <name>" { name: "<name>" }
22
- - "lookup/verify job <name>" → { name: "<name>" }
23
- - "find job" with no name → ask "Which job name would you like to find?"
24
- - "find all jobs / list jobs" use list-jobs instead
25
- - "run job <name>" → use run-job instead
26
-
27
- EXAMPLES
28
- - "find job cars_job_v4" → { name: "cars_job_v4" }
29
- - "does job ETL exist" → { name: "ETL" }
30
- - "is there a job named metricsRefresh" → { name: "metricsRefresh" }
31
-
32
- NEGATIVE EXAMPLES (do not route here)
33
- - "list jobs" (use list-jobs)
34
- - "run job cars_job_v4" (use run-job)
35
- - "execute jobdef cars_job_v4" (use run-jobdef)
36
-
37
- ERRORS
38
- Returns { jobs: [] } if not found; { jobs: [name, ...] } if found. Never hallucinate job names.
39
- `;
40
-
41
- let spec = {
42
- name: 'find-job',
43
- description: description,
44
- inputSchema: z.object({
45
- name: z.string()
46
- }),
47
- handler: async (params) => {
48
- let r = await _listJobs(params);
49
- return r;
50
- }
51
- }
52
-
53
-
54
- /* correct spec for registerTool with inputSchema */
55
-
56
- return spec;
57
- }
58
- export default findJob;
59
-
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { z } from 'zod';
6
+ import _listJobs from '../toolHelpers/_listJobs.js';
7
+ import _findJob from '../toolHelpers/_findJob.js';
8
+ function findJob(_appContext) {
9
+ const isAgent = _appContext && _appContext.agent;
10
+ let description = isAgent ? `
11
+ find-job — verify a Job model exists.
12
+ PARAMS: name (string, required)
13
+ RETURNS: job metadata if found, error if not found
14
+ ` : `
15
+ find-job — locate a specific SAS Viya job.
16
+
17
+ USE when: find job, does job exist, is there a job named, lookup job, verify job exists
18
+ DO NOT USE for: list jobs (use ${_appContext.brand}-list-jobs), score job (use ${_appContext.brand}-job-score), score jobdef (use ${_appContext.brand}-jobdef-score), find lib/table/model (use respective tools)
19
+
20
+ PARAMETERS
21
+ - name: string (required) job name to locate; if multiple supplied, use first
22
+
23
+ Naming Rules:
24
+ - If user provides name with "job" suffix ".job", strip the suffix (e.g., "cars_job_v4.job"), and look for "cars_job_v4".
25
+
26
+ ROUTING RULES
27
+ - "find job <name>" → { name: "<name>" }
28
+ - "find name.job" → { name: "<name>" }
29
+ - "does job <name> exist" → { name: "<name>" }
30
+ - "is there a job named <name>" → { name: "<name>" }
31
+ - "lookup/verify job <name>" → { name: "<name>" }
32
+ - "find job" with no name → ask "Which job name would you like to find?"
33
+ - "find all jobs / list jobs" use ${_appContext.brand}-list-jobs instead
34
+ - "score job <name>" use ${_appContext.brand}-job-score instead
35
+
36
+ EXAMPLES
37
+ - "find job cars_job_v4" → { name: "cars_job_v4" }
38
+ - "does job ETL exist" { name: "ETL" }
39
+ - "is there a job named metricsRefresh" → { name: "metricsRefresh" }
40
+
41
+ NEGATIVE EXAMPLES (do not route here)
42
+ - "list jobs" (use ${_appContext.brand}-list-jobs)
43
+ - "score job cars_job_v4" (use ${_appContext.brand}-job-score)
44
+ - "score jobdef cars_job_v4" (use ${_appContext.brand}-jobdef-score)
45
+
46
+ ERRORS
47
+ Returns { jobs: [] } if not found; { jobs: [name, ...] } if found. Never hallucinate job names.
48
+ `;
49
+
50
+ let spec = {
51
+ name: 'find-job',
52
+ description: description,
53
+ inputSchema: z.object({
54
+ name: z.string()
55
+ }),
56
+ handler: async (params) => {
57
+ if (params.name != null) {
58
+ if (params.name.endsWith('.job')) {
59
+ params.name = params.name.slice(0, -4);
60
+ }
61
+ }
62
+ params.tool = 'find';
63
+ let r = await _findJob(params);
64
+ return r;
65
+ }
66
+ }
67
+
68
+
69
+ /* correct spec for registerTool with inputSchema */
70
+
71
+ return spec;
72
+ }
73
+ export default findJob;
74
+
@@ -1,64 +1,67 @@
1
- /*
2
- * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- import { z } from 'zod';
6
- import _listJobdefs from '../toolHelpers/_listJobdefs.js';
7
- function findJobdef(_appContext) {
8
- let llmDescription= {
9
- "purpose": "Map natural language requests to find a jobdef (job definition) in SAS Viya and return structured results.",
10
- "param_mapping": {
11
- "name": "required - single name. If missing, ask 'Which jobdef name would you like to find?'.",
12
-
13
- },
14
- "response_schema": "{ jobs: Array<string|object> }",
15
- "behavior": "Return only JSON matching response_schema when invoked by an LLM. If no matches, return { jobs: [] }"
16
- };
17
- let description = `
18
- find-jobdef — locate a specific SAS Viya job definition.
19
-
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)
22
-
23
- PARAMETERS
24
- - name: string (required) jobdef name to locate; if multiple supplied, use first
25
-
26
- ROUTING RULES
27
- - "find jobdef <name>" → { name: "<name>" }
28
- - "does jobdef <name> exist" { name: "<name>" }
29
- - "is there a jobdef named <name>" → { name: "<name>" }
30
- - "lookup/verify jobdef <name>" → { name: "<name>" }
31
- - "find jobdef" with no name → ask "Which jobdef name would you like to find?"
32
- - "find all jobdefs / list jobdefs" → use list-jobdefs instead
33
- - "run jobdef <name>" → use run-jobdef instead
34
-
35
- EXAMPLES
36
- - "find jobdef cars_job_v4" → { name: "cars_job_v4" }
37
- - "does jobdef ETL exist" → { name: "ETL" }
38
- - "is there a jobdef named metricsRefresh" { name: "metricsRefresh" }
39
-
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)
45
-
46
- ERRORS
47
- Returns { jobdefs: [] } if not found; { jobdefs: [name, ...] } if found. Never hallucinate jobdef names.
48
- `;
49
-
50
- let spec = {
51
- name: 'find-jobdef',
52
- description: description,
53
- inputSchema: z.object({
54
- name: z.string()
55
- }),
56
- handler: async (params) => {
57
- let r = await _listJobdefs(params);
58
- return r;
59
- }
60
- }
61
- return spec;
62
- }
63
- export default findJobdef;
64
-
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { z } from 'zod';
6
+ import _findJobdef from '../toolHelpers/_findJobdef.js';
7
+ function findJobdef(_appContext) {
8
+ const isAgent = _appContext && _appContext.agent;
9
+ let description = isAgent ? `
10
+ find-jobdef — verify a JobDef model exists.
11
+ PARAMS: name (string, required)
12
+ RETURNS: jobdef metadata if found, error if not found
13
+ ` : `
14
+ find-jobdef locate a specific SAS Viya job definition.
15
+
16
+ USE when: find jobdef, does jobdef exist, is there a jobdef named, lookup jobdef, verify jobdef exists
17
+ DO NOT USE for: list jobdefs (use ${_appContext.brand}-list-jobdefs), score jobdef (use ${_appContext.brand}-jobdef-score), find job/lib/table/model (use respective tools)
18
+
19
+ PARAMETERS
20
+ - name: string (required) jobdef name to locate; if multiple supplied, use first
21
+
22
+ ROUTING RULES
23
+ - "find jobdef <name>" → { name: "<name>" }
24
+ - "find name.jobdef" { name: "<name>" }
25
+ - "does jobdef <name> exist" → { name: "<name>" }
26
+ - "is there a jobdef named <name>" → { name: "<name>" }
27
+ - "lookup/verify jobdef <name>" → { name: "<name>" }
28
+ - "find jobdef" with no name → ask "Which jobdef name would you like to find?"
29
+ - "find all jobdefs / list jobdefs" → use ${_appContext.brand}-list-jobdefs instead
30
+ - "score jobdef <name>" → use ${_appContext.brand}-jobdef-score instead
31
+
32
+ EXAMPLES
33
+ - "find jobdef cars_job_v4" → { name: "cars_job_v4" }
34
+ - "does jobdef ETL exist" → { name: "ETL" }
35
+ - "is there a jobdef named metricsRefresh" → { name: "metricsRefresh" }
36
+
37
+ NEGATIVE EXAMPLES (do not route here)
38
+ - "list jobdefs" (use ${_appContext.brand}-list-jobdefs)
39
+ - "score jobdef cars_job_v4" (use ${_appContext.brand}-jobdef-score)
40
+ - "find job ETL" (use ${_appContext.brand}-find-job)
41
+ - "find table cars" (use ${_appContext.brand}-find-table)
42
+
43
+ ERRORS
44
+ Returns { jobdefs: [] } if not found; { jobdefs: [name, ...] } if found. Never hallucinate jobdef names.
45
+ `;
46
+
47
+ let spec = {
48
+ name: 'find-jobdef',
49
+ description: description,
50
+ inputSchema: z.object({
51
+ name: z.string()
52
+ }),
53
+ handler: async (params) => {
54
+ if (params.name != null) {
55
+ if (params.name.endsWith('.jobdef')) {
56
+ params.name = params.name.slice(0, -7);
57
+ }
58
+ }
59
+ params.tool = 'find';
60
+ let r = await _findJobdef(params);
61
+ return r;
62
+ }
63
+ }
64
+ return spec;
65
+ }
66
+ export default findJobdef;
67
+