@sassoftware/sas-score-mcp-serverjs 0.0.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.
- package/.babelrc +6 -0
- package/.env +13 -0
- package/.env.http +29 -0
- package/CHANGES.md +2 -0
- package/CONTRIBUTING.md +14 -0
- package/ContributorAgreement.txt +56 -0
- package/LICENSE +205 -0
- package/LICENSES.json +105 -0
- package/QUICK_REFERENCE.md +378 -0
- package/README.md +267 -0
- package/SECURITY.md +31 -0
- package/SUPPORT.md +3 -0
- package/TOOL_DESCRIPTION_TEMPLATE.md +157 -0
- package/TOOL_UPDATES_SUMMARY.md +208 -0
- package/cli.js +214 -0
- package/labs/.subclass.json +13 -0
- package/labs/README.md +4 -0
- package/mcpConfigurations/README.md +3 -0
- package/mcpConfigurations/http.json +8 -0
- package/mcpConfigurations/stdio.json +20 -0
- package/mcpConfigurations/stdiodev.json +20 -0
- package/mcpserver.png +0 -0
- package/openApi.json +106 -0
- package/openApi.yaml +84 -0
- package/package.json +72 -0
- package/sas-mcp-tools-reference.md +600 -0
- package/sasCode/sas-sql-query.sas +33 -0
- package/sasCode/sas_sql_tool.json +237 -0
- package/scripts/getViyaca.sh +8 -0
- package/src/core.js +19 -0
- package/src/coreSSE.js +14 -0
- package/src/corehttp.js +335 -0
- package/src/createHttpTransport.js +26 -0
- package/src/createMcpServer.js +76 -0
- package/src/db/scrModels.js +23 -0
- package/src/toolSet/devaScore.js +69 -0
- package/src/toolSet/findJob.js +90 -0
- package/src/toolSet/findJobdef.js +95 -0
- package/src/toolSet/findLibrary.js +100 -0
- package/src/toolSet/findModel.js +83 -0
- package/src/toolSet/findTable.js +94 -0
- package/src/toolSet/getEnv.js +72 -0
- package/src/toolSet/listJobdefs.js +96 -0
- package/src/toolSet/listJobs.js +110 -0
- package/src/toolSet/listLibraries.js +90 -0
- package/src/toolSet/listModels.js +83 -0
- package/src/toolSet/listTables.js +95 -0
- package/src/toolSet/makeTools.js +75 -0
- package/src/toolSet/mcp server .png +0 -0
- package/src/toolSet/modelInfo.js +87 -0
- package/src/toolSet/modelScore.js +131 -0
- package/src/toolSet/readTable.js +104 -0
- package/src/toolSet/runCasProgram.js +118 -0
- package/src/toolSet/runJob.js +81 -0
- package/src/toolSet/runJobdef.js +85 -0
- package/src/toolSet/runMacro.js +82 -0
- package/src/toolSet/runProgram.js +145 -0
- package/src/toolSet/sasQuery.js +126 -0
- package/src/toolSet/sasQueryTemplate.js +148 -0
- package/src/toolSet/sasQueryTemplate2.js +140 -0
- package/src/toolSet/scrInfo.js +55 -0
- package/src/toolSet/scrScore.js +71 -0
- package/src/toolSet/searchAssets.js +52 -0
- package/src/toolSet/setContext.js +98 -0
- package/src/toolSet/superstat.js +60 -0
- package/src/toolSet/tableInfo.js +102 -0
- package/src/toolhelpers/_catalogSearch.js +87 -0
- package/src/toolhelpers/_getEnv.js +10 -0
- package/src/toolhelpers/_itemsData.js +28 -0
- package/src/toolhelpers/_jobSubmit.js +78 -0
- package/src/toolhelpers/_listJobdefs.js +59 -0
- package/src/toolhelpers/_listJobs.js +63 -0
- package/src/toolhelpers/_listLibrary.js +56 -0
- package/src/toolhelpers/_listModels.js +41 -0
- package/src/toolhelpers/_listTables.js +52 -0
- package/src/toolhelpers/_masDescribe.js +27 -0
- package/src/toolhelpers/_masScoring.js +64 -0
- package/src/toolhelpers/_readTable.js +69 -0
- package/src/toolhelpers/_scrInfo.js +32 -0
- package/src/toolhelpers/_scrScore.js +49 -0
- package/src/toolhelpers/_submitCasl.js +34 -0
- package/src/toolhelpers/_submitCode.js +96 -0
- package/src/toolhelpers/_submitMacro.js +24 -0
- package/src/toolhelpers/_tableColumns.js +61 -0
- package/src/toolhelpers/_tableInfo.js +72 -0
- package/src/toolhelpers/deleteSession.js +13 -0
- package/src/toolhelpers/getLogonPayload.js +100 -0
- package/src/toolhelpers/getOpts.js +43 -0
- package/src/toolhelpers/getOptsViya.js +38 -0
- package/src/toolhelpers/getStoreOpts.js +18 -0
- package/src/toolhelpers/getToken.js +40 -0
- package/src/toolhelpers/refreshToken.js +48 -0
- package/test/README.md +63 -0
- package/test/listLibraries.test.js +245 -0
- package/tool-developer-guide.md +80 -0
- package/types.js +25 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import debug from 'debug';
|
|
8
|
+
import _masDescribe from '../toolHelpers/_masDescribe.js';
|
|
9
|
+
const log = debug('tools');
|
|
10
|
+
|
|
11
|
+
function modelInfo(_appContext) {
|
|
12
|
+
let description = `
|
|
13
|
+
## model-info — retrieve detailed metadata for a deployed model
|
|
14
|
+
|
|
15
|
+
LLM Invocation Guidance (When to use)
|
|
16
|
+
Use THIS tool when:
|
|
17
|
+
- User wants input/output variable information: "What inputs does model X need?"
|
|
18
|
+
- User wants to understand model variables: "Describe model myModel"
|
|
19
|
+
- User wants data types and roles: "Show me the variables for myModel"
|
|
20
|
+
- User wants to prepare data for scoring: "What are the required inputs for model sales_forecast?"
|
|
21
|
+
|
|
22
|
+
Do NOT use this tool for:
|
|
23
|
+
- Checking if a model exists (use find-model)
|
|
24
|
+
- Listing available models (use list-models)
|
|
25
|
+
- Scoring a model (use model-score)
|
|
26
|
+
- Looking up tables or jobs (use respective find/list tools)
|
|
27
|
+
|
|
28
|
+
Purpose
|
|
29
|
+
Retrieve detailed metadata for a model deployed to MAS (Model Aggregation Service). This includes input variable names, data types, roles/usage, ranges, and output variable information. Use this before scoring to understand what data the model requires.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
- model (string, required): The name of the model as published to MAS. Must be exact match.
|
|
33
|
+
|
|
34
|
+
Response Contract
|
|
35
|
+
Returns a JSON object containing model metadata, typically including:
|
|
36
|
+
- name: The model name
|
|
37
|
+
- inputs: Array of input variable objects with:
|
|
38
|
+
- name: Variable name
|
|
39
|
+
- type: Data type (numeric, character, etc.)
|
|
40
|
+
- role: Role in model (input, key, etc.)
|
|
41
|
+
- allowed_values or range: Optional constraints
|
|
42
|
+
- outputs: Array of output/prediction objects with:
|
|
43
|
+
- name: Prediction/output variable name
|
|
44
|
+
- type: Data type
|
|
45
|
+
- possible_values: For classification models, the class labels
|
|
46
|
+
- model_type: Type of model (regression, classification, etc.)
|
|
47
|
+
- description: Optional model description
|
|
48
|
+
|
|
49
|
+
Disambiguation & Clarification
|
|
50
|
+
- If model name is missing: ask "Which model would you like to see information for?"
|
|
51
|
+
- If user says "variables" without specifying model: ask "Which model would you like the variables for?"
|
|
52
|
+
- Multiple model requests: handle one at a time
|
|
53
|
+
|
|
54
|
+
Examples (→ mapped params)
|
|
55
|
+
- "What inputs does model churnRisk need?" → { model: "churnRisk" }
|
|
56
|
+
- "Describe model creditScore" → { model: "creditScore" }
|
|
57
|
+
- "Show the variables for myModel" → { model: "myModel" }
|
|
58
|
+
- "What are the outputs of model sales_forecast?" → { model: "sales_forecast" }
|
|
59
|
+
|
|
60
|
+
Negative Examples (should NOT call model-info)
|
|
61
|
+
- "List all available models" (use list-models instead)
|
|
62
|
+
- "Find model cancer" (use find-model instead)
|
|
63
|
+
- "Score this customer with model churnRisk" (use model-score instead)
|
|
64
|
+
|
|
65
|
+
Related Tools
|
|
66
|
+
- list-models → find-model → model-info → model-score (typical workflow)
|
|
67
|
+
- find-model — to check if a specific model exists
|
|
68
|
+
- model-score — to score data using the model
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
let spec = {
|
|
72
|
+
name: 'model-info',
|
|
73
|
+
aliases: ['modelInfo','model info','model_info'],
|
|
74
|
+
description: description,
|
|
75
|
+
schema: {
|
|
76
|
+
'model': z.string()
|
|
77
|
+
},
|
|
78
|
+
required: ['model'],
|
|
79
|
+
handler: async (params) => {
|
|
80
|
+
let r = await _masDescribe(params);
|
|
81
|
+
return r;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return spec;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default modelInfo;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import debug from 'debug';
|
|
8
|
+
import _masScoring from '../toolHelpers/_masScoring.js';
|
|
9
|
+
const log = debug('tools');
|
|
10
|
+
|
|
11
|
+
function modelScore(_appContext) {
|
|
12
|
+
let description = `
|
|
13
|
+
## model-score — score data using a deployed model on MAS
|
|
14
|
+
|
|
15
|
+
LLM Invocation Guidance (When to use)
|
|
16
|
+
Use THIS tool when:
|
|
17
|
+
- User wants to score data with a model: "score this customer with model churnRisk"
|
|
18
|
+
- User provides input values for prediction: "predict using model creditScore with age=45, income=60000"
|
|
19
|
+
- User wants batch scoring: "score these 10 customers with fraud model"
|
|
20
|
+
- User asks for model predictions: "what does the sales forecast model predict for Q4?"
|
|
21
|
+
- User wants to get predictions: "use the risk model to score this applicant"
|
|
22
|
+
- User provides scenario data for a model: "run model cancer1 with age=45, sex=M"
|
|
23
|
+
|
|
24
|
+
Do NOT use this tool for:
|
|
25
|
+
- Checking if a model exists (use find-model)
|
|
26
|
+
- Getting model metadata/variables (use model-info)
|
|
27
|
+
- Listing available models (use list-models)
|
|
28
|
+
- Running SAS programs (use run-sas-program)
|
|
29
|
+
- Running jobs (use run-job)
|
|
30
|
+
- Querying tables (use sas-query or read-table)
|
|
31
|
+
|
|
32
|
+
Purpose
|
|
33
|
+
Score user-supplied scenario data using a model published to MAS (Model Aggregation Service) on SAS Viya. Returns predictions, probabilities, scores, and other model outputs merged with the input data.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
- model (string, required): The name of the model as published to MAS. Must be exact match.
|
|
37
|
+
- scenario (string | object | array, required): Data to score. Accepts:
|
|
38
|
+
- A comma-separated key=value string (e.g., "x=1, y=2")
|
|
39
|
+
- A plain object with field names and values (e.g., {x: 1, y: 2})
|
|
40
|
+
- An array of objects for batch scoring
|
|
41
|
+
- uflag (boolean, default false): When true, returned model field names will be prefixed with an underscore.
|
|
42
|
+
|
|
43
|
+
Parsing & Behavior
|
|
44
|
+
- If scenario is a string, the tool parses comma-separated key=value pairs into an object
|
|
45
|
+
- If scenario is an array, supports batch scoring (processes multiple records)
|
|
46
|
+
- Numeric values in strings remain as strings; validate and cast as needed before scoring
|
|
47
|
+
|
|
48
|
+
Response Contract
|
|
49
|
+
Returns a JSON object containing:
|
|
50
|
+
- Input fields merged with scoring results
|
|
51
|
+
- Prediction/score field(s) from the model (field names depend on model)
|
|
52
|
+
- Probability fields for classification models (e.g., P_class1, P_class2)
|
|
53
|
+
- Model metadata: model name, version if available
|
|
54
|
+
- _variables_: Variable metadata if uflag=true
|
|
55
|
+
- Error object if scoring fails with error message
|
|
56
|
+
|
|
57
|
+
Disambiguation & Clarification
|
|
58
|
+
- Missing model name: ask "Which model would you like to use for scoring?"
|
|
59
|
+
- Missing scenario data: ask "What input values should I use for scoring?"
|
|
60
|
+
- If model unknown: suggest "Use find-model to verify the model exists or list-models to see available models"
|
|
61
|
+
- If unsure of inputs: suggest "Use model-info to see the required input variables first"
|
|
62
|
+
|
|
63
|
+
Examples (→ mapped params)
|
|
64
|
+
- "score with model churn using age=45, income=60000" → { model: "churn", scenario: { age: "45", income: "60000" }, uflag: false }
|
|
65
|
+
- "predict creditScore for applicant: credit=700, debt=20000" → { model: "creditScore", scenario: { credit: "700", debt: "20000" }, uflag: false }
|
|
66
|
+
- "use fraud model to score transaction amount=500, merchant=online" → { model: "fraud", scenario: { amount: "500", merchant: "online" }, uflag: false }
|
|
67
|
+
- "run model cancer1 with age=45, sex=M, tumor=stage2" → { model: "cancer1", scenario: { age: "45", sex: "M", tumor: "stage2" }, uflag: false }
|
|
68
|
+
|
|
69
|
+
Negative Examples (should NOT call model-score)
|
|
70
|
+
- "find model churnRisk" (use find-model instead)
|
|
71
|
+
- "what inputs does model need?" (use model-info instead)
|
|
72
|
+
- "list all models" (use list-models instead)
|
|
73
|
+
- "run job scoring_job" (use run-job instead)
|
|
74
|
+
- "read table scores from Public" (use read-table instead)
|
|
75
|
+
|
|
76
|
+
Usage Tips
|
|
77
|
+
- Use model-info first to understand required input variables and data types
|
|
78
|
+
- Verify model exists with find-model before attempting to score
|
|
79
|
+
- For batch scoring, provide an array of objects as the scenario parameter
|
|
80
|
+
- Ensure MAS connectivity and credentials are available
|
|
81
|
+
|
|
82
|
+
Related Tools
|
|
83
|
+
- list-models → find-model → model-info → model-score (typical workflow)
|
|
84
|
+
- find-model — to verify model exists before scoring
|
|
85
|
+
- model-info — to understand required inputs and expected outputs
|
|
86
|
+
- list-models — to discover available models
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
let spec = {
|
|
91
|
+
name: 'model-score',
|
|
92
|
+
aliases: ['modelScore','model score','model_score'],
|
|
93
|
+
description: description,
|
|
94
|
+
schema: {
|
|
95
|
+
'model': z.string(),
|
|
96
|
+
'scenario': z.any(),
|
|
97
|
+
'uflag': z.boolean()
|
|
98
|
+
},
|
|
99
|
+
required: ['model', 'scenario'],
|
|
100
|
+
handler: async (iparams) => {
|
|
101
|
+
let params = {...iparams};
|
|
102
|
+
let scenario = params.scenario;
|
|
103
|
+
|
|
104
|
+
// Convert the scenario string to an object
|
|
105
|
+
// Example: "x=1, y=2, z=3" to { x: 1, y: 2, z: 3 }
|
|
106
|
+
let scenarioObj ={};
|
|
107
|
+
let count = 0;
|
|
108
|
+
if (typeof scenario === 'object') {
|
|
109
|
+
scenarioObj = scenario;
|
|
110
|
+
} else if (Array.isArray(scenario)) {
|
|
111
|
+
scenarioObj = scenario[0];
|
|
112
|
+
} else {
|
|
113
|
+
//console.error('Incoming scenario', scenario);
|
|
114
|
+
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
115
|
+
let [key, value] = pair.split('=');
|
|
116
|
+
acc[key.trim()] = value;
|
|
117
|
+
count++;
|
|
118
|
+
return acc;
|
|
119
|
+
}, {});
|
|
120
|
+
}
|
|
121
|
+
params.scenario= scenarioObj;
|
|
122
|
+
log('modelScore params', params);
|
|
123
|
+
// Check if the params.scenario is a string and parse it
|
|
124
|
+
let r = await _masScoring(params)
|
|
125
|
+
return r;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return spec;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export default modelScore;
|
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
LLM Invocation Guidance (When to use)
|
|
15
|
+
Use THIS tool when:
|
|
16
|
+
- User wants to read data from a table: "read table xyz.customers"
|
|
17
|
+
- User wants sample rows: "show me 10 rows from lib.sales"
|
|
18
|
+
- User wants filtered data: "read from mylib.orders where status = 'shipped'"
|
|
19
|
+
- User wants from specific library: "read table cars in sashelp"
|
|
20
|
+
- User wants from specific server: "read table from mylib.employees on sas"
|
|
21
|
+
|
|
22
|
+
Do NOT use this tool for:
|
|
23
|
+
- Listing tables in a library (use list-tables)
|
|
24
|
+
- Getting table structure/metadata (use table-info)
|
|
25
|
+
- Running SQL queries (use sas-query)
|
|
26
|
+
- Executing SAS programs (use run-sas-program)
|
|
27
|
+
- Running statistical analysis (use appropriate analytics tools)
|
|
28
|
+
|
|
29
|
+
Purpose
|
|
30
|
+
Read one or more rows from a specified table in a CAS caslib or SAS libref. Supports pagination, filtering with WHERE clauses, and formatted/raw value display. Use this to inspect table data, sample rows, or retrieve filtered subsets.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
- table (string, required): Table name to read.
|
|
34
|
+
- lib (string, required): The caslib or libref containing the table.
|
|
35
|
+
- server (string, default 'cas'): Target server: 'cas' or 'sas'.
|
|
36
|
+
- start (number, default 1): 1-based row index to start reading from.
|
|
37
|
+
- limit (number, default 10): Maximum number of rows to return (1-1000 recommended).
|
|
38
|
+
- where (string, optional): SQL-style WHERE clause to filter rows (e.g., "age > 30 AND status = 'active'").
|
|
39
|
+
- format (boolean, default true): When true, return formatted/labeled values; when false return raw values.
|
|
40
|
+
- row (number, optional): Read a single specific row (sets start to this value and limit to 1).
|
|
41
|
+
|
|
42
|
+
Response Contract
|
|
43
|
+
Returns a JSON object containing:
|
|
44
|
+
- rows: Array of row objects with column names as keys
|
|
45
|
+
- total (optional): Total count of rows in table (if available)
|
|
46
|
+
- filtered_count (optional): Count of rows matching WHERE clause (if WHERE used)
|
|
47
|
+
- columns (optional): Column metadata including names and types
|
|
48
|
+
- Empty array if no rows match the criteria
|
|
49
|
+
|
|
50
|
+
Pagination & Filtering
|
|
51
|
+
- First page default: { start: 1, limit: 10 }
|
|
52
|
+
- To get next page: increment start by limit (e.g., { start: 11, limit: 10 })
|
|
53
|
+
- Use WHERE clause for server-side filtering: { where: "age > 30" }
|
|
54
|
+
|
|
55
|
+
Disambiguation & Clarification
|
|
56
|
+
- Missing library: ask "Which library contains the table you want to read?"
|
|
57
|
+
- Missing table: ask "Which table would you like to read?"
|
|
58
|
+
- Ambiguous lib.table format: parse and use as separate parameters
|
|
59
|
+
- Multiple tables: clarify which one (readTable handles one table at a time)
|
|
60
|
+
|
|
61
|
+
Examples (→ mapped params)
|
|
62
|
+
- "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
|
|
63
|
+
- "show 25 rows from customers" → { table: "customers", lib: <current_lib>, limit: 25, start: 1 }
|
|
64
|
+
- "read orders where status = 'shipped' limit 50" → { table: "orders", lib: <lib>, where: "status = 'shipped'", limit: 50, start: 1 }
|
|
65
|
+
- "read row 15 from employees in mylib on sas" → { table: "employees", lib: "mylib", server: "sas", row: 15 }
|
|
66
|
+
- "get next 10 rows" (after previous {start:1,limit:10}) → { table: <same>, lib: <same>, start: 11, limit: 10 }
|
|
67
|
+
|
|
68
|
+
Negative Examples (should NOT call read-table)
|
|
69
|
+
- "list tables in Samples" (use list-tables instead)
|
|
70
|
+
- "what columns are in the cars table?" (use table-info instead)
|
|
71
|
+
- "execute this SQL query" (use sas-query instead)
|
|
72
|
+
- "run this SAS code" (use run-sas-program instead)
|
|
73
|
+
|
|
74
|
+
Related Tools
|
|
75
|
+
- list-tables — to browse available tables in a library
|
|
76
|
+
- table-info — to inspect table structure, columns, and metadata
|
|
77
|
+
- find-table — to check if a table exists in a library
|
|
78
|
+
- list-libraries — to browse available libraries
|
|
79
|
+
- sas-query — to run complex SQL queries across tables
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
let specs = {
|
|
83
|
+
name: 'read-table',
|
|
84
|
+
aliases: ['readTable','read table','read_table'],
|
|
85
|
+
description: describe,
|
|
86
|
+
schema: {
|
|
87
|
+
table: z.string(),
|
|
88
|
+
lib: z.string(),
|
|
89
|
+
start: z.number(),
|
|
90
|
+
limit: z.number().default(10),
|
|
91
|
+
server: z.string().default('cas'),
|
|
92
|
+
where: z.string().default(''),
|
|
93
|
+
format: z.boolean().default(true)
|
|
94
|
+
|
|
95
|
+
},
|
|
96
|
+
required: ['table', 'lib'],
|
|
97
|
+
handler: async (params) => {
|
|
98
|
+
let r = await _readTable(params,'query');
|
|
99
|
+
return r;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return specs;
|
|
103
|
+
}
|
|
104
|
+
export default readTable;
|
|
@@ -0,0 +1,118 @@
|
|
|
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 runCasProgram(_appContext) {
|
|
10
|
+
let description = `
|
|
11
|
+
## run-cas-program
|
|
12
|
+
|
|
13
|
+
Execute a cas program on a SAS Viya server
|
|
14
|
+
|
|
15
|
+
Required Parameters
|
|
16
|
+
- src(string, required): The cas program to execute.
|
|
17
|
+
|
|
18
|
+
Optional Parameters:
|
|
19
|
+
|
|
20
|
+
- scenario (string | object ,optional): Input values to program/ Accepts:
|
|
21
|
+
- a comma-separated key=value string (e.g. "x=1, y=2"),
|
|
22
|
+
- a JSON object with field names and values (recommended for typed inputs),
|
|
23
|
+
|
|
24
|
+
LLM Invocation Guidance
|
|
25
|
+
Use THIS tool when the user wants to run a Cas program on the server:
|
|
26
|
+
- "run cas program 'action echo / code='aaaa'"
|
|
27
|
+
- "cas program 'action echo / code='aaaa'"
|
|
28
|
+
- 'submit cas "action echo / code='aaaa'"'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Do NOT use this tool when the user wants:
|
|
32
|
+
- run macro -> use run-macro
|
|
33
|
+
- submit sas -> use run-sas-program
|
|
34
|
+
- run job -> use run-job
|
|
35
|
+
- run sas -> use run-sas-program
|
|
36
|
+
- run jobdef -> use run-jobdef
|
|
37
|
+
- run job -> use run-job
|
|
38
|
+
- list jobs -> use list-jobs
|
|
39
|
+
- list jobdefs -> use list-jobdefs
|
|
40
|
+
- find job -> use find-job
|
|
41
|
+
- find jobdef -> use find-jobdef
|
|
42
|
+
- find model -> use find-model
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
Behavior & usage notes
|
|
46
|
+
- This tool sends the supplied \`src\` verbatim to the SAS execution helper. It does not modify or validate the SAS code.
|
|
47
|
+
- For invoking pre-defined SAS macros, prefer the \`runMacro\` helper which converts simple parameter formats into \`%let\` statements and invokes the macro cleanly.
|
|
48
|
+
- Be cautious when executing arbitrary code — validate or sanitize inputs in untrusted environments.
|
|
49
|
+
|
|
50
|
+
Response
|
|
51
|
+
- If output is specified and the specified table exists in the response, display the data as a markdown table.
|
|
52
|
+
Examples
|
|
53
|
+
- run program "data a; x=1; run;" - this is the simplest usage -- {src= "data a; x=1; run;", folder=" ", output=" ", limit=100}
|
|
54
|
+
- program "data work.a; x=1; run;" output=a limit=50 -- {src= "data work.a; x=1; run;", folder=" ", output="a", limit=50}
|
|
55
|
+
|
|
56
|
+
- run program sample folder=/Public/models output=A limit=50 -- {src= "sample", folder="/Public/models", output="A", limit=50}
|
|
57
|
+
- run program sample folder=/Public/models scenario="name='John', age=45" output=a limit=50 -- {src= "sample", folder="/Public/models", scenario: {name: "John", age: 45}, output="a", limit=50}
|
|
58
|
+
- run program sample folder=/Public/models with scenario name=John,age=45 output=a limit=50 -- {src= "sample.sas", folder="/Public/models", scenario: {name: "John", age: 45}, output="a", limit=50}
|
|
59
|
+
- this should be the same as the previous example and is just a different syntax. The result should be
|
|
60
|
+
{program: "sample", folder: "/Public/models", scenario: {name: "John", age: 45}, output: "a", limit: 50}
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
let spec = {
|
|
64
|
+
name: 'run-sas-program',
|
|
65
|
+
aliases: ['Program','run program'],
|
|
66
|
+
description: description,
|
|
67
|
+
schema: {
|
|
68
|
+
src: z.string(),
|
|
69
|
+
scenario: z.any().default(''),
|
|
70
|
+
output: z.string().default(''),
|
|
71
|
+
folder: z.string().default(''),
|
|
72
|
+
limit: z.number().default(100)
|
|
73
|
+
},
|
|
74
|
+
// NOTE: Previously 'required' incorrectly listed 'program' which does not
|
|
75
|
+
// exist in the schema. This prevented execution in some orchestrators that
|
|
76
|
+
// enforce required parameter presence, causing only descriptions to appear.
|
|
77
|
+
// Corrected to 'src'.
|
|
78
|
+
required: ['src'],
|
|
79
|
+
handler: async (params) => {
|
|
80
|
+
let {src, folder, scenario, _appContext} = params;
|
|
81
|
+
// figure out src
|
|
82
|
+
let isrc = src;
|
|
83
|
+
if (folder != null && folder.trim().length > 0) {
|
|
84
|
+
if (isrc.indexOf('.sas') < 0) {
|
|
85
|
+
isrc = isrc + '.sas';
|
|
86
|
+
}
|
|
87
|
+
isrc = `
|
|
88
|
+
filename mcptemp filesrvc folderpath="${folder}";
|
|
89
|
+
%include mcptemp("${isrc}");
|
|
90
|
+
filename mcptemp clear;
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
// figure out macros
|
|
94
|
+
|
|
95
|
+
if (typeof scenario === 'string' && scenario.includes('=')) {
|
|
96
|
+
scenario = scenario.split(',').reduce((acc, pair) => {
|
|
97
|
+
const [k, ...rest] = pair.split('=');
|
|
98
|
+
if (!k) return acc;
|
|
99
|
+
acc[k.trim()] = rest.join('=').trim();
|
|
100
|
+
return acc;
|
|
101
|
+
}, {});
|
|
102
|
+
}
|
|
103
|
+
let iparms = {
|
|
104
|
+
args: scenario,
|
|
105
|
+
output: params.output,
|
|
106
|
+
limit: params.limit,
|
|
107
|
+
src: isrc,
|
|
108
|
+
_appContext: _appContext
|
|
109
|
+
}
|
|
110
|
+
// console.error('iparms', iparms);
|
|
111
|
+
let r = await _submitCode(iparms);
|
|
112
|
+
return r;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return spec;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export default runCasProgram;
|
|
@@ -0,0 +1,81 @@
|
|
|
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
|
|
8
|
+
|
|
9
|
+
function runJob(_appContext) {
|
|
10
|
+
|
|
11
|
+
let description = `
|
|
12
|
+
## run-job — execute a deployed SAS Viya job
|
|
13
|
+
|
|
14
|
+
LLM Invocation Guidance (When to use)
|
|
15
|
+
Use THIS tool when:
|
|
16
|
+
- You want to run a registered Job Execution asset by name
|
|
17
|
+
- You have simple parameter inputs to pass to the job
|
|
18
|
+
|
|
19
|
+
Do NOT use this tool for:
|
|
20
|
+
- Running arbitrary SAS code (use run-sas-program)
|
|
21
|
+
- Invoking pre-defined SAS macros (use run-macro)
|
|
22
|
+
- Listing or finding jobs (use list-jobs / find-job)
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
- name (string, required): The job name to execute
|
|
26
|
+
- scenario (string | object, optional): Input values to the job. Accepts:
|
|
27
|
+
- a comma-separated key=value string (e.g. "x=1, y=2")
|
|
28
|
+
- a JSON object with field names and values (recommended)
|
|
29
|
+
|
|
30
|
+
Response
|
|
31
|
+
- Log output, listings, and tables depending on the job’s design. Table outputs will be displayed when present.
|
|
32
|
+
|
|
33
|
+
Examples (→ mapped params)
|
|
34
|
+
- "run job xyz param1=10,param2=val2" → { name: "xyz", scenario: { param1: 10, param2: "val2" } }
|
|
35
|
+
- "run-job name=monthly_etl, scenario=month=10,year=2025" → { name: "monthly_etl", scenario: { month: 10, year: 2025 } }
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
let spec = {
|
|
39
|
+
aliases: ['run job', 'run-job', 'runjob'],
|
|
40
|
+
name: 'run-job',
|
|
41
|
+
description: description,
|
|
42
|
+
schema: {
|
|
43
|
+
name: z.string(),
|
|
44
|
+
scenario: z.any().default(''),
|
|
45
|
+
},
|
|
46
|
+
required: ['name'],
|
|
47
|
+
handler: async (params) => {
|
|
48
|
+
let scenario = params.scenario;
|
|
49
|
+
let scenarioObj = {};
|
|
50
|
+
let count = 0;
|
|
51
|
+
//
|
|
52
|
+
if (scenario == null) {
|
|
53
|
+
scenarioObj = {};
|
|
54
|
+
} else if (typeof scenario === 'object') {
|
|
55
|
+
scenarioObj = scenario;
|
|
56
|
+
} else if (Array.isArray(scenario)) {
|
|
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
|
+
}
|
|
70
|
+
}
|
|
71
|
+
params.type = 'job';
|
|
72
|
+
params.scenario = scenarioObj;
|
|
73
|
+
// Provide runtime context for auth and server settings
|
|
74
|
+
let r = await _jobSubmit(params);
|
|
75
|
+
return r;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return spec;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default runJob;
|
|
@@ -0,0 +1,85 @@
|
|
|
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function runJobdef(_appContext) {
|
|
11
|
+
// JSON object for LLM/tooling
|
|
12
|
+
|
|
13
|
+
let description = `
|
|
14
|
+
## run-jobdef — execute a SAS Viya job definition
|
|
15
|
+
|
|
16
|
+
LLM Invocation Guidance (When to use)
|
|
17
|
+
Use THIS tool when:
|
|
18
|
+
- You want to run a registered Job Definition by name
|
|
19
|
+
- You can pass parameters to the job definition
|
|
20
|
+
|
|
21
|
+
Do NOT use this tool for:
|
|
22
|
+
- Running arbitrary SAS code (use run-program tool)
|
|
23
|
+
- Invoking SAS macros (use run-macro tool)
|
|
24
|
+
- Listing or finding jobdefs (use list-jobdefs tool / find-jobdef tool)
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
- name (string, required): The job definition name to execute
|
|
28
|
+
- scenario (string | object, optional): Input values. Accepts:
|
|
29
|
+
- a comma-separated key=value string (e.g. "x=1, y=2")
|
|
30
|
+
- a JSON object with field names and values (recommended)
|
|
31
|
+
|
|
32
|
+
Response
|
|
33
|
+
- Log output, listings, and tables depending on the job definition's design. Table outputs will be displayed when present.
|
|
34
|
+
|
|
35
|
+
Examples (→ mapped params)
|
|
36
|
+
- "jobdef xyz param1=10,param2=val2" → { name: "xyz", scenario: { param1: 10, param2: "val2" } }
|
|
37
|
+
- "run jobdef name=monthly_report, scenario=month=10,year=2025" → { name: "monthly_report", scenario: { month: 10, year: 2025 } }
|
|
38
|
+
- "run-jobdef xyz param1=10,param2=val2" → { name: "xyz", scenario: { param1: 10, param2: "val2" } }
|
|
39
|
+
- "jobdef name=monthly_report, scenario=month=10,year=2025" → { name: "monthly_report", scenario: { month: 10, year: 2025 }
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
let spec = {
|
|
43
|
+
name: 'run-jobdef',
|
|
44
|
+
aliases: ['jobDef','jobdef','jobdef'],
|
|
45
|
+
description: description,
|
|
46
|
+
schema: {
|
|
47
|
+
name: z.string(),
|
|
48
|
+
scenario: z.any().default(''),
|
|
49
|
+
},
|
|
50
|
+
required: ['name'],
|
|
51
|
+
handler: async (params) => {
|
|
52
|
+
let scenario = params.scenario;
|
|
53
|
+
let scenarioObj = {};
|
|
54
|
+
let count = 0;
|
|
55
|
+
//
|
|
56
|
+
if (scenario == null) {
|
|
57
|
+
scenarioObj = {};
|
|
58
|
+
} else if (typeof scenario === 'object') {
|
|
59
|
+
scenarioObj = scenario;
|
|
60
|
+
} else if (Array.isArray(scenario)) {
|
|
61
|
+
scenarioObj = scenario[0];
|
|
62
|
+
} else if (typeof scenario === 'string') {
|
|
63
|
+
if (scenario.trim() === '') {
|
|
64
|
+
scenarioObj = {};
|
|
65
|
+
} else {
|
|
66
|
+
// console.error('Incoming scenario', scenario);
|
|
67
|
+
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
68
|
+
let [key, value] = pair.split('=');
|
|
69
|
+
acc[key.trim()] = value;
|
|
70
|
+
count++;
|
|
71
|
+
return acc;
|
|
72
|
+
}, {});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
params.type = 'def';
|
|
76
|
+
params.scenario = scenarioObj;
|
|
77
|
+
// Provide runtime context for auth and server settings
|
|
78
|
+
let r = await _jobSubmit(params);
|
|
79
|
+
return r;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
return spec;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default runJobdef;
|