@sassoftware/sas-score-mcp-serverjs 1.0.1-3 → 1.0.1-31

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 (96) hide show
  1. package/.skills/agents/sas-score-mcp-serverjs-agent.md +190 -0
  2. package/.skills/copilot-instructions.md +241 -0
  3. package/.skills/skills/README.md +125 -0
  4. package/.skills/skills/detail-strategy/SKILL.md +272 -0
  5. package/.skills/skills/find-resources/SKILL.md +155 -0
  6. package/.skills/skills/list-resource/SKILL.md +258 -0
  7. package/.skills/skills/read-strategy/SKILL.md +137 -0
  8. package/.skills/skills/request-routing/SKILL.md +107 -0
  9. package/.skills/skills/score-strategy/SKILL.md +231 -0
  10. package/README.md +96 -54
  11. package/cli.js +37 -27
  12. package/openApi.yaml +121 -121
  13. package/package.json +14 -14
  14. package/scripts/docs/oauth-http-transport.md +2 -2
  15. package/scripts/refreshtoken.js +58 -0
  16. package/src/createMcpServer.js +0 -1
  17. package/src/expressMcpServer.js +47 -49
  18. package/src/oauthHandlers/authorize.js +4 -1
  19. package/src/oauthHandlers/baseUrl.js +4 -0
  20. package/src/oauthHandlers/callback.js +5 -1
  21. package/src/oauthHandlers/getMetadata.js +4 -0
  22. package/src/oauthHandlers/index.js +4 -0
  23. package/src/oauthHandlers/token.js +4 -0
  24. package/src/openApi.yaml +121 -121
  25. package/src/processHeaders.js +10 -7
  26. package/src/setupSkills.js +12 -7
  27. package/src/toolHelpers/_findJob.js +12 -0
  28. package/src/toolHelpers/_findJobdef.js +10 -0
  29. package/src/toolHelpers/_findLibrary.js +10 -0
  30. package/src/toolHelpers/_findModel.js +12 -0
  31. package/src/toolHelpers/_findTable.js +10 -0
  32. package/src/toolHelpers/_listJobs.js +2 -1
  33. package/src/toolHelpers/_listLibrary.js +1 -1
  34. package/src/toolHelpers/_listTables.js +1 -1
  35. package/src/toolHelpers/getLogonPayload.js +2 -2
  36. package/src/toolSet/devaScore.js +61 -61
  37. package/src/toolSet/findJob.js +2 -1
  38. package/src/toolSet/findJobdef.js +7 -7
  39. package/src/toolSet/findLibrary.js +68 -68
  40. package/src/toolSet/findModel.js +2 -2
  41. package/src/toolSet/findTable.js +2 -2
  42. package/src/toolSet/jobInfo.js +59 -0
  43. package/src/toolSet/jobdefInfo.js +59 -0
  44. package/src/toolSet/listJobdefs.js +61 -61
  45. package/src/toolSet/listJobs.js +61 -61
  46. package/src/toolSet/listLibraries.js +78 -78
  47. package/src/toolSet/listModels.js +56 -56
  48. package/src/toolSet/listTables.js +66 -66
  49. package/src/toolSet/makeTools.js +3 -0
  50. package/src/toolSet/modelInfo.js +1 -1
  51. package/src/toolSet/modelScore.js +23 -25
  52. package/src/toolSet/readTable.js +63 -63
  53. package/src/toolSet/runCasProgram.js +21 -10
  54. package/src/toolSet/runJob.js +15 -19
  55. package/src/toolSet/runJobdef.js +15 -19
  56. package/src/toolSet/runMacro.js +82 -82
  57. package/src/toolSet/sasQuery.js +77 -77
  58. package/src/toolSet/scrScore.js +60 -69
  59. package/src/toolSet/setContext.js +65 -65
  60. package/src/toolSet/superstat.js +61 -61
  61. package/src/toolSet/tableInfo.js +58 -58
  62. package/.skills_claude/README.md +0 -303
  63. package/.skills_claude/TESTING_GUIDE.md +0 -252
  64. package/.skills_claude/agents/sas-viya-scoring-expert.md +0 -58
  65. package/.skills_claude/claude-desktop-config.json +0 -16
  66. package/.skills_claude/claude-desktop-system-prompt.md +0 -127
  67. package/.skills_claude/copilot-instructions.md +0 -155
  68. package/.skills_claude/instructions.md +0 -184
  69. package/.skills_claude/skills/sas-find-library-smart/SKILL.md +0 -157
  70. package/.skills_claude/skills/sas-find-resource-strategy/SKILL.md +0 -105
  71. package/.skills_claude/skills/sas-list-resource-strategy/SKILL.md +0 -124
  72. package/.skills_claude/skills/sas-list-tables-smart/SKILL.md +0 -126
  73. package/.skills_claude/skills/sas-read-and-score/SKILL.md +0 -112
  74. package/.skills_claude/skills/sas-read-strategy/SKILL.md +0 -154
  75. package/.skills_claude/skills/sas-request-classifier/SKILL.md +0 -69
  76. package/.skills_claude/skills/sas-score-workflow/SKILL.md +0 -200
  77. package/.skills_claude/skills-index.md +0 -345
  78. package/.skills_github/agents/sas-viya-scoring-expert.md +0 -58
  79. package/.skills_github/copilot-instructions.md +0 -177
  80. package/.skills_github/skills/sas-find-library-smart/SKILL.md +0 -155
  81. package/.skills_github/skills/sas-find-resource-strategy/SKILL.md +0 -105
  82. package/.skills_github/skills/sas-list-resource-strategy/SKILL.md +0 -124
  83. package/.skills_github/skills/sas-list-tables-smart/SKILL.md +0 -128
  84. package/.skills_github/skills/sas-read-and-score/SKILL.md +0 -113
  85. package/.skills_github/skills/sas-read-strategy/SKILL.md +0 -154
  86. package/.skills_github/skills/sas-request-classifier/SKILL.md +0 -74
  87. package/.skills_github/skills/sas-score-workflow/SKILL.md +0 -314
  88. package/scripts/optimize_final.py +0 -140
  89. package/scripts/optimize_tools.py +0 -99
  90. package/scripts/setup-skills.js +0 -34
  91. package/scripts/update_descriptions.py +0 -46
  92. package/src/authpkce.js +0 -219
  93. package/src/handleGetDelete.js +0 -34
  94. package/src/handleRequest.js +0 -112
  95. package/src/hapiMcpServer.js +0 -241
  96. package/src/toolSet/.claude/settings.local.json +0 -13
@@ -1,63 +1,63 @@
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 debug from 'debug';
7
-
8
- import _readTable from '../toolHelpers/_readTable.js';
9
- function readTable(_appContext) {
10
-
11
- let describe = `
12
- read-table — retrieve rows from a table in a CAS or SAS library.
13
-
14
- USE when: read table, show rows, read from library, filtered data with WHERE
15
- DO NOT USE for: list tables, table structure (use table-info), SQL queries (use sas-query), SAS programs
16
-
17
- PARAMETERS
18
- - table: string — table name (required)
19
- - lib: string — caslib or libref (required)
20
- - server: string (default: 'cas') — 'cas' or 'sas'
21
- - start: number (default: 1) — 1-based row index
22
- - limit: number (default: 10) — max rows (1-1000)
23
- - where: string — SQL WHERE clause filter
24
- - format: boolean (default: true) — formatted or raw values
25
-
26
- ROUTING RULES
27
- - "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
28
- - "show 25 rows from customers" → { table: "customers", lib: "<lib>", limit: 25, start: 1 }
29
- - "read from mylib.orders where status='shipped'" → { table: "orders", lib: "mylib", where: "status='shipped'", start: 1, limit: 10 }
30
-
31
- EXAMPLES
32
- - "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
33
- - "show 25 rows from customers" → { table: "customers", lib: "mylib", limit: 25, start: 1 }
34
-
35
- NEGATIVE EXAMPLES (do not route here)
36
- - "list tables in Samples" (use list-tables)
37
- - "what columns are in cars" (use table-info)
38
- - "execute SQL query" (use sas-query)
39
- - "run SAS code" (use run-sas-program)
40
-
41
- ERRORS
42
- Returns rows array, total count, filtered_count, columns metadata. Empty array if no matches.
43
- `;
44
-
45
- let specs = {
46
- name: 'read-table',
47
- description: describe,
48
- inputSchema: z.object({
49
- table: z.string(),
50
- lib: z.string().optional(),
51
- start: z.number().optional(),
52
- limit: z.number().optional(),
53
- server: z.string().optional(),
54
- where: z.string().optional()
55
- }),
56
- handler: async (params) => {
57
- let r = await _readTable(params,'query');
58
- return r;
59
- }
60
- }
61
- return specs;
62
- }
63
- export default readTable;
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 debug from 'debug';
7
+
8
+ import _readTable from '../toolHelpers/_readTable.js';
9
+ function readTable(_appContext) {
10
+
11
+ let describe = `
12
+ read-table — retrieve rows from a table in a CAS or SAS library.
13
+
14
+ USE when: read table, show rows, read from library, filtered data with WHERE
15
+ DO NOT USE for: list tables, table structure (use table-info), SQL queries (use sas-query), SAS programs
16
+
17
+ PARAMETERS
18
+ - table: string — table name (required)
19
+ - lib: string — caslib or libref (required)
20
+ - server: string (default: 'cas') — 'cas' or 'sas'
21
+ - start: number (default: 1) — 1-based row index
22
+ - limit: number (default: 10) — max rows (1-1000)
23
+ - where: string — SQL WHERE clause filter
24
+ - format: boolean (default: true) — formatted or raw values
25
+
26
+ ROUTING RULES
27
+ - "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
28
+ - "show 25 rows from customers" → { table: "customers", lib: "<lib>", limit: 25, start: 1 }
29
+ - "read from mylib.orders where status='shipped'" → { table: "orders", lib: "mylib", where: "status='shipped'", start: 1, limit: 10 }
30
+
31
+ EXAMPLES
32
+ - "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
33
+ - "show 25 rows from customers" → { table: "customers", lib: "mylib", limit: 25, start: 1 }
34
+
35
+ NEGATIVE EXAMPLES (do not route here)
36
+ - "list tables in Samples" (use list-tables)
37
+ - "what columns are in cars" (use table-info)
38
+ - "execute SQL query" (use sas-query)
39
+ - "run SAS code" (use run-sas-program)
40
+
41
+ ERRORS
42
+ Returns rows array, total count, filtered_count, columns metadata. Empty array if no matches.
43
+ `;
44
+
45
+ let specs = {
46
+ name: 'read-table',
47
+ description: describe,
48
+ inputSchema: z.object({
49
+ table: z.string(),
50
+ lib: z.string().optional(),
51
+ start: z.number().optional(),
52
+ limit: z.number().optional(),
53
+ server: z.string().optional(),
54
+ where: z.string().optional()
55
+ }),
56
+ handler: async (params) => {
57
+ let r = await _readTable(params,'query');
58
+ return r;
59
+ }
60
+ }
61
+ return specs;
62
+ }
63
+ export default readTable;
@@ -45,10 +45,10 @@ Log output and CAS results. If output table specified, returned as markdown tabl
45
45
  description: description,
46
46
  inputSchema:z.object({
47
47
  src: z.string(),
48
- scenario: z.string(),
49
- output: z.string().optional,
50
- folder: z.string().optional,
51
- limit: z.number().optional
48
+ scenario: z.any(),
49
+ output: z.string().optional(),
50
+ folder: z.string().optional(),
51
+ limit: z.number().optional()
52
52
  }),
53
53
 
54
54
  // NOTE: Previously 'required' incorrectly listed 'program' which does not
@@ -70,15 +70,26 @@ Log output and CAS results. If output table specified, returned as markdown tabl
70
70
  `;
71
71
  }
72
72
  // figure out macros
73
-
74
- 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();
73
+
74
+ // Convert the scenario string to an object
75
+ // Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
76
+ let scenarioObj = {};
77
+ let count = 0;
78
+ if (typeof scenario === 'object') {
79
+ scenarioObj = scenario;
80
+ } else if (Array.isArray(scenario)) {
81
+ scenarioObj = scenario[0];
82
+ } else {
83
+ //console.error('Incoming scenario', scenario);
84
+ scenarioObj = scenario.split(',').reduce((acc, pair) => {
85
+ let [key, value] = pair.split('=');
86
+ acc[key.trim()] = value;
87
+ count++;
79
88
  return acc;
80
89
  }, {});
81
90
  }
91
+ params.scenario = scenarioObj;
92
+
82
93
  let iparms = {
83
94
  args: scenario,
84
95
  output: params.output,
@@ -16,7 +16,7 @@ DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro)
16
16
 
17
17
  PARAMETERS
18
18
  - name: string — job name (required)
19
- - scenario: string | object — input parameters. Accepts: "x=1, y=2" or {x:1, y:2}
19
+ - scenario: object — input parameters as JSON (optional, defaults to {}). Example: {month:10, year:2025}
20
20
 
21
21
  ROUTING RULES
22
22
  - "run job xyz" → { name: "xyz" }
@@ -41,34 +41,30 @@ Returns log output, listings, tables from job. Error if job not found.
41
41
  description: description,
42
42
  inputSchema: z.object({
43
43
  name: z.string(),
44
- scenario: z.string().optional()
44
+ scenario: z.any()
45
45
  }),
46
46
  handler: async (params) => {
47
47
  let scenario = params.scenario;
48
+
49
+ // Convert the scenario string to an object
50
+ // Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
48
51
  let scenarioObj = {};
49
52
  let count = 0;
50
- //
51
- if (scenario == null) {
52
- scenarioObj = {};
53
- } else if (typeof scenario === 'object') {
53
+ if (typeof scenario === 'object') {
54
54
  scenarioObj = scenario;
55
55
  } else if (Array.isArray(scenario)) {
56
56
  scenarioObj = scenario[0];
57
- } else 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
- }
57
+ } else {
58
+ //console.error('Incoming scenario', scenario);
59
+ scenarioObj = scenario.split(',').reduce((acc, pair) => {
60
+ let [key, value] = pair.split('=');
61
+ acc[key.trim()] = value;
62
+ count++;
63
+ return acc;
64
+ }, {});
69
65
  }
66
+ params.scenario = scenarioObj;
70
67
  params.type = 'job';
71
- params.scenario = scenarioObj;
72
68
  // Provide runtime context for auth and server settings
73
69
  let r = await _jobSubmit(params);
74
70
  return r;
@@ -17,7 +17,7 @@ DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro)
17
17
 
18
18
  PARAMETERS
19
19
  - name: string — jobdef name (required)
20
- - scenario: string | object — input parameters. Accepts: "x=1, y=2" or {x:1, y:2}
20
+ - scenario: object — input parameters as JSON (optional, defaults to {}). Example: {month:10, year:2025}
21
21
 
22
22
  ROUTING RULES
23
23
  - "run jobdef xyz" → { name: "xyz" }
@@ -42,34 +42,30 @@ Returns log output, listings, tables from jobdef. Error if jobdef not found.
42
42
  description: description,
43
43
  inputSchema: z.object({
44
44
  name: z.string(),
45
- scenario: z.string().optional()
45
+ scenario: z.any()
46
46
  }),
47
47
  handler: async (params) => {
48
48
  let scenario = params.scenario;
49
+
50
+ // Convert the scenario string to an object
51
+ // Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
49
52
  let scenarioObj = {};
50
53
  let count = 0;
51
- //
52
- if (scenario == null) {
53
- scenarioObj = {};
54
- } else if (typeof scenario === 'object') {
54
+ if (typeof scenario === 'object') {
55
55
  scenarioObj = scenario;
56
56
  } else if (Array.isArray(scenario)) {
57
57
  scenarioObj = scenario[0];
58
- } else 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
- }
58
+ } else {
59
+ //console.error('Incoming scenario', scenario);
60
+ scenarioObj = scenario.split(',').reduce((acc, pair) => {
61
+ let [key, value] = pair.split('=');
62
+ acc[key.trim()] = value;
63
+ count++;
64
+ return acc;
65
+ }, {});
70
66
  }
67
+ params.scenario = scenarioObj;
71
68
  params.type = 'def';
72
- params.scenario = scenarioObj;
73
69
  // Provide runtime context for auth and server settings
74
70
  let r = await _jobSubmit(params);
75
71
  return r;
@@ -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 _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 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,77 +1,77 @@
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
7
-
8
- function sasQuery() {
9
-
10
- let description = `
11
- sas-query — convert natural language questions into SQL queries and execute them.
12
-
13
- USE when: how many/count/total/average by, aggregated analytics, complex filtering, statistical summaries
14
- DO NOT USE for: raw reads without filtering (use read-table), table structure (use table-info), SAS programs (use run-sas-program), jobs/jobdefs
15
-
16
- PARAMETERS
17
- - table: string — table in lib.table format (required), e.g. "Public.cars" or "sashelp.class"
18
- - query: string — natural language question (required)
19
- - sql: string — pre-generated SQL SELECT (optional, LLM generates)
20
- - job: string (default: 'program') — job name to execute
21
-
22
- ROUTING RULES
23
- - "how many cars by make in sashelp.cars" → { table: "sashelp.cars", query: "how many cars by make", sql: "SELECT make, COUNT(*) FROM sashelp.cars GROUP BY make" }
24
- - "average salary by department in Public.employees" → { table: "Public.employees", query: "average salary by department", sql: "SELECT department, AVG(salary) FROM Public.employees GROUP BY department" }
25
-
26
- EXAMPLES
27
- - "how many cars by make in sashelp.cars" → { table: "sashelp.cars", query: "how many cars by make", sql: "SELECT make, COUNT(*) FROM sashelp.cars GROUP BY make" }
28
- - "total sales by region from mylib.sales" → { table: "mylib.sales", query: "total sales by region", sql: "SELECT region, SUM(amount) FROM mylib.sales GROUP BY region" }
29
-
30
- NEGATIVE EXAMPLES (do not route here)
31
- - "read table cars" (use read-table)
32
- - "show 10 rows" (use read-table)
33
- - "table structure" (use table-info)
34
- - "run SAS code" (use run-sas-program)
35
- - "run job/macro" (use run-job/run-macro)
36
-
37
- ERRORS
38
- Returns rows array, columns metadata, log. Returns error if SQL invalid or table not found.
39
- `;
40
-
41
-
42
- let spec = {
43
- name: 'sas-query',
44
- description: description,
45
- inputSchema: z.object({
46
- query: z.string(),
47
- table: z.string(),
48
- sql: z.string().optional()
49
- }),
50
- handler: async (params) => {
51
- let {table,query, sql, job, _appContext} = params;
52
- let sqlinput = (sql == null) ? ' ' : sql.replaceAll(';', ' ').replaceAll('\n', ' ').replaceAll('\r', ' ');
53
-
54
- let iparams = {
55
- scenario: {
56
- table: table,
57
- prompt: query,
58
- sql: sqlinput
59
- },
60
- name: (job == null) ? 'program' : job,
61
- type: 'job',
62
- query: true,
63
- _appContext: _appContext
64
- };
65
- if (sql == null || sql.trim().length === 0) {
66
- return { content: [{ type: 'text', text: 'Error: The SQL statement generated is blank. Please provide a valid natural language query that can be converted to SQL.' }] };
67
- }
68
-
69
- let r = await _jobSubmit(iparams);
70
- return r;
71
-
72
- }
73
- }
74
- return spec;
75
- }
76
- export default sasQuery;
77
-
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
7
+
8
+ function sasQuery() {
9
+
10
+ let description = `
11
+ sas-query — convert natural language questions into SQL queries and execute them.
12
+
13
+ USE when: how many/count/total/average by, aggregated analytics, complex filtering, statistical summaries
14
+ DO NOT USE for: raw reads without filtering (use read-table), table structure (use table-info), SAS programs (use run-sas-program), jobs/jobdefs
15
+
16
+ PARAMETERS
17
+ - table: string — table in lib.table format (required), e.g. "Public.cars" or "sashelp.class"
18
+ - query: string — natural language question (required)
19
+ - sql: string — pre-generated SQL SELECT (optional, LLM generates)
20
+ - job: string (default: 'program') — job name to execute
21
+
22
+ ROUTING RULES
23
+ - "how many cars by make in sashelp.cars" → { table: "sashelp.cars", query: "how many cars by make", sql: "SELECT make, COUNT(*) FROM sashelp.cars GROUP BY make" }
24
+ - "average salary by department in Public.employees" → { table: "Public.employees", query: "average salary by department", sql: "SELECT department, AVG(salary) FROM Public.employees GROUP BY department" }
25
+
26
+ EXAMPLES
27
+ - "how many cars by make in sashelp.cars" → { table: "sashelp.cars", query: "how many cars by make", sql: "SELECT make, COUNT(*) FROM sashelp.cars GROUP BY make" }
28
+ - "total sales by region from mylib.sales" → { table: "mylib.sales", query: "total sales by region", sql: "SELECT region, SUM(amount) FROM mylib.sales GROUP BY region" }
29
+
30
+ NEGATIVE EXAMPLES (do not route here)
31
+ - "read table cars" (use read-table)
32
+ - "show 10 rows" (use read-table)
33
+ - "table structure" (use table-info)
34
+ - "run SAS code" (use run-sas-program)
35
+ - "run job/macro" (use run-job/run-macro)
36
+
37
+ ERRORS
38
+ Returns rows array, columns metadata, log. Returns error if SQL invalid or table not found.
39
+ `;
40
+
41
+
42
+ let spec = {
43
+ name: 'sas-query',
44
+ description: description,
45
+ inputSchema: z.object({
46
+ query: z.string(),
47
+ table: z.string(),
48
+ sql: z.string().optional()
49
+ }),
50
+ handler: async (params) => {
51
+ let {table,query, sql, job, _appContext} = params;
52
+ let sqlinput = (sql == null) ? ' ' : sql.replaceAll(';', ' ').replaceAll('\n', ' ').replaceAll('\r', ' ');
53
+
54
+ let iparams = {
55
+ scenario: {
56
+ table: table,
57
+ prompt: query,
58
+ sql: sqlinput
59
+ },
60
+ name: (job == null) ? 'program' : job,
61
+ type: 'job',
62
+ query: true,
63
+ _appContext: _appContext
64
+ };
65
+ if (sql == null || sql.trim().length === 0) {
66
+ return { content: [{ type: 'text', text: 'Error: The SQL statement generated is blank. Please provide a valid natural language query that can be converted to SQL.' }] };
67
+ }
68
+
69
+ let r = await _jobSubmit(iparams);
70
+ return r;
71
+
72
+ }
73
+ }
74
+ return spec;
75
+ }
76
+ export default sasQuery;
77
+