@sassoftware/sas-score-mcp-serverjs 1.1.1 → 1.1.3

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.
@@ -1,96 +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 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;
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;
@@ -1,80 +1,80 @@
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
- const isAgent = _appContext && _appContext.agent;
11
-
12
- let describe = isAgent ? `
13
- read-table — read rows from a CAS or SAS table.
14
- PARAMS: lib (string, required), table (string, required), server ('cas'|'sas', required), where (string, optional filter), start (number, default 1), limit (number, default 10)
15
- RETURNS: array of row objects
16
- ` : `
17
- read-table - retrieve rows from a table in a CAS or SAS library.
18
-
19
- USE when: read table, show rows, read from library, filtered data with WHERE
20
- DO NOT USE for: list tables, table structure (use table-describe), SQL queries (use sas-query), SAS programs
21
-
22
- PARAMETERS
23
- - table: string — table name (required)
24
- - lib: string — caslib or libref (required)
25
- - server: string (default: 'cas') — 'cas' or 'sas'
26
- - start: number (default: 1) — 1-based row index
27
- - limit: number (default: 10) — max rows (1-1000)
28
- - where: string — SQL WHERE clause filter
29
- - format: boolean (default: true) — formatted or raw values
30
-
31
- PARSING ROWS FROM USER INPUT
32
- "first N rows/records" → start: 1, limit: N ("first" = count from beginning, never an offset)
33
- "top N rows" → start: 1, limit: N
34
- "N rows" / "N records" → start: 1, limit: N
35
- "read N rows from lib.table" → lib: "lib", table: "table", start: 1, limit: N
36
- "rows N to M" → start: N, limit: M-N+1
37
- "starting from row N" → start: N, limit: 10 (default)
38
- (no count specified) → start: 1, limit: 10 (default)
39
-
40
- DOTTED FORMAT: "lib.table" → lib: "lib", table: "table" (split on first dot)
41
-
42
- ROUTING RULES
43
- - "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
44
- - "show 25 rows from customers" → { table: "customers", lib: "<lib>", limit: 25, start: 1 }
45
- - "read from mylib.orders where status='shipped'" → { table: "orders", lib: "mylib", where: "status='shipped'", start: 1, limit: 10 }
46
-
47
- EXAMPLES
48
- - "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
49
- - "show 25 rows from customers" → { table: "customers", lib: "mylib", limit: 25, start: 1 }
50
-
51
- NEGATIVE EXAMPLES (do not route here)
52
- - "list tables in Samples" (use list-tables)
53
- - "what columns are in cars" (use table-describe)
54
- - "execute SQL query" (use sas-query)
55
- - "run SAS code" (use program-score)
56
-
57
- ERRORS
58
- Returns rows array, total count, filtered_count, columns metadata. Empty array if no matches.
59
- `;
60
-
61
- let specs = {
62
- name: 'read-table',
63
- description: describe,
64
- inputSchema: z.object({
65
- table: z.string().min(1),
66
- lib: z.string().min(1).optional(),
67
- start: z.number().int().min(1).optional(),
68
- limit: z.number().int().min(1).max(1000).optional(),
69
- server: z.enum(['cas', 'sas']).optional(),
70
- where: z.string().min(1).optional()
71
- }),
72
- handler: async (params) => {
73
- let r = await _readTable(params,'query');
74
- return r;
75
- }
76
- }
77
- return specs;
78
- }
79
- export default readTable;
80
-
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
+ const isAgent = _appContext && _appContext.agent;
11
+
12
+ let describe = isAgent ? `
13
+ read-table — read rows from a CAS or SAS table.
14
+ PARAMS: lib (string, required), table (string, required), server ('cas'|'sas', required), where (string, optional filter), start (number, default 1), limit (number, default 10)
15
+ RETURNS: array of row objects
16
+ ` : `
17
+ read-table - retrieve rows from a table in a CAS or SAS library.
18
+
19
+ USE when: read table, show rows, read from library, filtered data with WHERE
20
+ DO NOT USE for: list tables, table structure (use table-describe), SQL queries (use sas-query), SAS programs
21
+
22
+ PARAMETERS
23
+ - table: string — table name (required)
24
+ - lib: string — caslib or libref (required)
25
+ - server: string (default: 'cas') — 'cas' or 'sas'
26
+ - start: number (default: 1) — 1-based row index
27
+ - limit: number (default: 10) — max rows (1-1000)
28
+ - where: string — SQL WHERE clause filter
29
+ - format: boolean (default: true) — formatted or raw values
30
+
31
+ PARSING ROWS FROM USER INPUT
32
+ "first N rows/records" → start: 1, limit: N ("first" = count from beginning, never an offset)
33
+ "top N rows" → start: 1, limit: N
34
+ "N rows" / "N records" → start: 1, limit: N
35
+ "read N rows from lib.table" → lib: "lib", table: "table", start: 1, limit: N
36
+ "rows N to M" → start: N, limit: M-N+1
37
+ "starting from row N" → start: N, limit: 10 (default)
38
+ (no count specified) → start: 1, limit: 10 (default)
39
+
40
+ DOTTED FORMAT: "lib.table" → lib: "lib", table: "table" (split on first dot)
41
+
42
+ ROUTING RULES
43
+ - "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
44
+ - "show 25 rows from customers" → { table: "customers", lib: "<lib>", limit: 25, start: 1 }
45
+ - "read from mylib.orders where status='shipped'" → { table: "orders", lib: "mylib", where: "status='shipped'", start: 1, limit: 10 }
46
+
47
+ EXAMPLES
48
+ - "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
49
+ - "show 25 rows from customers" → { table: "customers", lib: "mylib", limit: 25, start: 1 }
50
+
51
+ NEGATIVE EXAMPLES (do not route here)
52
+ - "list tables in Samples" (use list-tables)
53
+ - "what columns are in cars" (use table-describe)
54
+ - "execute SQL query" (use sas-query)
55
+ - "run SAS code" (use program-score)
56
+
57
+ ERRORS
58
+ Returns rows array, total count, filtered_count, columns metadata. Empty array if no matches.
59
+ `;
60
+
61
+ let specs = {
62
+ name: 'read-table',
63
+ description: describe,
64
+ inputSchema: z.object({
65
+ table: z.string().min(1),
66
+ lib: z.string().min(1).optional(),
67
+ start: z.number().int().min(1).optional(),
68
+ limit: z.number().int().min(1).max(1000).optional(),
69
+ server: z.enum(['cas', 'sas']).optional(),
70
+ where: z.string().min(1).optional()
71
+ }),
72
+ handler: async (params) => {
73
+ let r = await _readTable(params,'query');
74
+ return r;
75
+ }
76
+ }
77
+ return specs;
78
+ }
79
+ export default readTable;
80
+
@@ -1,83 +1,83 @@
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(_appContext) {
9
- const isAgent = _appContext && _appContext.agent;
10
-
11
- let description = isAgent ? `
12
- sas-query — run a SQL aggregation query against a table.
13
- PARAMS: table (string as lib.table, required), query (string, SQL WHERE/aggregation clause, required)
14
- RETURNS: aggregated query result rows
15
- ` : `
16
- sas-query - convert natural language questions into SQL queries and execute them.
17
-
18
- USE when: how many/count/total/average by, aggregated analytics, complex filtering, statistical summaries
19
- DO NOT USE for: raw reads without filtering (use read-table), table structure (use table-describe), SAS programs (use program-score), jobs/jobdefs
20
-
21
- PARAMETERS
22
- - table: string — table in lib.table format (required), e.g. "Public.cars" or "sashelp.class"
23
- - query: string — natural language question (required)
24
- - sql: string — pre-generated SQL SELECT (optional, LLM generates)
25
- - job: string (default: 'program') — job name to execute
26
-
27
- ROUTING RULES
28
- - "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" }
29
- - "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" }
30
-
31
- EXAMPLES
32
- - "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" }
33
- - "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" }
34
-
35
- NEGATIVE EXAMPLES (do not route here)
36
- - "read table cars" (use read-table)
37
- - "show 10 rows" (use read-table)
38
- - "table structure" (use table-describe)
39
- - "run SAS code" (use program-score)
40
- - "score job/macro" (use job-score/macro-score)
41
-
42
- ERRORS
43
- Returns rows array, columns metadata, log. Returns error if SQL invalid or table not found.
44
- `;
45
-
46
-
47
- let spec = {
48
- name: 'sas-query',
49
- description: description,
50
- inputSchema: z.object({
51
- query: z.string(),
52
- table: z.string(),
53
- sql: z.string().optional()
54
- }),
55
- handler: async (params) => {
56
- let {table,query, sql, job, _appContext} = params;
57
- let sqlinput = (sql == null) ? ' ' : sql.replaceAll(';', ' ').replaceAll('\n', ' ').replaceAll('\r', ' ');
58
-
59
- let iparams = {
60
- scenario: {
61
- table: table,
62
- prompt: query,
63
- sql: sqlinput
64
- },
65
- name: (job == null) ? 'program' : job,
66
- type: 'job',
67
- query: true,
68
- _appContext: _appContext
69
- };
70
- if (sql == null || sql.trim().length === 0) {
71
- 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.' }] };
72
- }
73
-
74
- let r = await _jobSubmit(iparams);
75
- return r;
76
-
77
- }
78
- }
79
- return spec;
80
- }
81
- export default sasQuery;
82
-
83
-
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(_appContext) {
9
+ const isAgent = _appContext && _appContext.agent;
10
+
11
+ let description = isAgent ? `
12
+ sas-query — run a SQL aggregation query against a table.
13
+ PARAMS: table (string as lib.table, required), query (string, SQL WHERE/aggregation clause, required)
14
+ RETURNS: aggregated query result rows
15
+ ` : `
16
+ sas-query - convert natural language questions into SQL queries and execute them.
17
+
18
+ USE when: how many/count/total/average by, aggregated analytics, complex filtering, statistical summaries
19
+ DO NOT USE for: raw reads without filtering (use read-table), table structure (use table-describe), SAS programs (use program-score), jobs/jobdefs
20
+
21
+ PARAMETERS
22
+ - table: string — table in lib.table format (required), e.g. "Public.cars" or "sashelp.class"
23
+ - query: string — natural language question (required)
24
+ - sql: string — pre-generated SQL SELECT (optional, LLM generates)
25
+ - job: string (default: 'program') — job name to execute
26
+
27
+ ROUTING RULES
28
+ - "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" }
29
+ - "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" }
30
+
31
+ EXAMPLES
32
+ - "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" }
33
+ - "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" }
34
+
35
+ NEGATIVE EXAMPLES (do not route here)
36
+ - "read table cars" (use read-table)
37
+ - "show 10 rows" (use read-table)
38
+ - "table structure" (use table-describe)
39
+ - "run SAS code" (use program-score)
40
+ - "score job/macro" (use job-score/macro-score)
41
+
42
+ ERRORS
43
+ Returns rows array, columns metadata, log. Returns error if SQL invalid or table not found.
44
+ `;
45
+
46
+
47
+ let spec = {
48
+ name: 'sas-query',
49
+ description: description,
50
+ inputSchema: z.object({
51
+ query: z.string(),
52
+ table: z.string(),
53
+ sql: z.string().optional()
54
+ }),
55
+ handler: async (params) => {
56
+ let {table,query, sql, job, _appContext} = params;
57
+ let sqlinput = (sql == null) ? ' ' : sql.replaceAll(';', ' ').replaceAll('\n', ' ').replaceAll('\r', ' ');
58
+
59
+ let iparams = {
60
+ scenario: {
61
+ table: table,
62
+ prompt: query,
63
+ sql: sqlinput
64
+ },
65
+ name: (job == null) ? 'program' : job,
66
+ type: 'job',
67
+ query: true,
68
+ _appContext: _appContext
69
+ };
70
+ if (sql == null || sql.trim().length === 0) {
71
+ 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.' }] };
72
+ }
73
+
74
+ let r = await _jobSubmit(iparams);
75
+ return r;
76
+
77
+ }
78
+ }
79
+ return spec;
80
+ }
81
+ export default sasQuery;
82
+
83
+