@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,55 @@
|
|
|
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 _scrInfo from '../toolHelpers/_scrInfo.js';
|
|
8
|
+
|
|
9
|
+
function scrInfo(_appContext) {
|
|
10
|
+
|
|
11
|
+
let description = `
|
|
12
|
+
## scr-info
|
|
13
|
+
|
|
14
|
+
Purpose
|
|
15
|
+
Return the input/output schema and metadata for an SCR (Score Code Runtime) model.
|
|
16
|
+
|
|
17
|
+
Inputs
|
|
18
|
+
- name (string): The SCR model identifier.
|
|
19
|
+
What it returns
|
|
20
|
+
- A JSON object describing the model's interface, typically including:
|
|
21
|
+
- Input variables (names, types, required/optional)
|
|
22
|
+
- Output variables (predictions, probabilities, scores)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Usage notes
|
|
26
|
+
- If no local mapping exists and \`name\` looks like a URL, the tool will attempt to fetch the schema from that URL.
|
|
27
|
+
- Ensure network connectivity and credentials for the remote SCR service when needed.
|
|
28
|
+
- Use scr-score to score data after inspecting the schema.
|
|
29
|
+
|
|
30
|
+
Examples
|
|
31
|
+
- describe scr model "https://scr-host/models/loan"
|
|
32
|
+
- info for scr model "https://scr-host/models/loan"
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
let spec = {
|
|
36
|
+
name: 'scr-info',
|
|
37
|
+
aliases: ['scrInfo','scr info','scr_info'],
|
|
38
|
+
description: description,
|
|
39
|
+
schema: {
|
|
40
|
+
name: z.string(),
|
|
41
|
+
},
|
|
42
|
+
required: ['name'],
|
|
43
|
+
handler: async (params) => {
|
|
44
|
+
let {url, _appContext} = params;
|
|
45
|
+
if (url === null) {
|
|
46
|
+
return { status: { statusCode: 2, msg: `SCR model ${url} not found` }, results: {} };
|
|
47
|
+
}
|
|
48
|
+
let r = await _scrInfo({url, _appContext});
|
|
49
|
+
return r;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return spec;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default scrInfo;
|
|
@@ -0,0 +1,71 @@
|
|
|
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 _scrScore from '../toolHelpers/_scrScore.js';
|
|
8
|
+
|
|
9
|
+
function scrScore(_appContext) {
|
|
10
|
+
let description = `
|
|
11
|
+
## scr-score
|
|
12
|
+
|
|
13
|
+
Purpose
|
|
14
|
+
Score a scenario using a model deployed as a SCR container in Azure or another host).
|
|
15
|
+
|
|
16
|
+
Inputs
|
|
17
|
+
- url (string, required): SCR model identifier (URL)
|
|
18
|
+
- scenario (string | object | array, optional): Input values to score. Accepts:
|
|
19
|
+
- a comma-separated key=value string (e.g. "x=1, y=2"),
|
|
20
|
+
- a JSON object with field names and values (recommended for typed inputs),
|
|
21
|
+
- an array of objects for batch scoring. If omitted, the tool will return the model's input variable definitions.
|
|
22
|
+
|
|
23
|
+
What it returns
|
|
24
|
+
- When scoring: the SCR endpoint response (predictions, probabilities, scores) merged with or alongside the supplied inputs.
|
|
25
|
+
- When \`scenario\` is omitted: metadata describing the model's input variables (names, types, required/optional).
|
|
26
|
+
|
|
27
|
+
Usage notes
|
|
28
|
+
- Run \`scr-info\` first to inspect the expected input variables and types.
|
|
29
|
+
- Prefer structured objects for numeric/date values to avoid type ambiguity; the simple string parser keeps values as strings.
|
|
30
|
+
- Ensure network connectivity and any required credentials for the target SCR service.
|
|
31
|
+
|
|
32
|
+
Examples
|
|
33
|
+
- scrScore with url="loan" and scenario="age=45, income=60000"
|
|
34
|
+
- scrScore with url="https://scr-host/models/loan" and scenario={age:45, income:60000}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
let spec = {
|
|
38
|
+
name: 'scr-score',
|
|
39
|
+
aliases: ['scrScore','scr score','scr_score'],
|
|
40
|
+
description: description,
|
|
41
|
+
schema: {
|
|
42
|
+
url: z.string(),
|
|
43
|
+
scenario: z.any()
|
|
44
|
+
},
|
|
45
|
+
required: ['url'],
|
|
46
|
+
handler: async (params) => {
|
|
47
|
+
let {url, scenario,_appContext} = params;
|
|
48
|
+
|
|
49
|
+
if (url === null) {
|
|
50
|
+
return { status: { statusCode: 2, msg: `SCR model ${url} was not specified` }, results: {} };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Normalize simple string scenarios like "x=1, y=2" into an object
|
|
54
|
+
if (typeof scenario === 'string' && scenario.includes('=')) {
|
|
55
|
+
scenario = scenario.split(',').reduce((acc, pair) => {
|
|
56
|
+
const [k, ...rest] = pair.split('=');
|
|
57
|
+
if (!k) return acc;
|
|
58
|
+
acc[k.trim()] = rest.join('=').trim();
|
|
59
|
+
return acc;
|
|
60
|
+
}, {});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let r = await _scrScore({ url: url, scenario: scenario , _appContext: _appContext});
|
|
64
|
+
return r;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return spec;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default scrScore;
|
|
@@ -0,0 +1,52 @@
|
|
|
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 _catalogSearch from '../toolHelpers/_catalogSearch.js';
|
|
7
|
+
import debug from 'debug';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function searchAssets(_appContext) {
|
|
11
|
+
const log = debug('tools');
|
|
12
|
+
|
|
13
|
+
let description = `
|
|
14
|
+
## searchAssets: Search the SAS Catalog for assets using a flexible search string.
|
|
15
|
+
|
|
16
|
+
- Supports searching for various asset types (e.g., datasets, dataflows, models).
|
|
17
|
+
- the default asset type is 'datasets'.
|
|
18
|
+
- The search string can include keywords, filters, and logical operators.
|
|
19
|
+
- You can specify start and limit to control pagination of results.
|
|
20
|
+
- The searchstring can include:
|
|
21
|
+
- Simple terms (e.g., customer).
|
|
22
|
+
- Key-value pairs (e.g., type:dataset or owner:john).
|
|
23
|
+
- Logical operators AND and OR between terms.
|
|
24
|
+
- Sets (e.g., status:{active,archived}).
|
|
25
|
+
- The search string is parsed according to these rules:
|
|
26
|
+
1. Terms without ':' or '=' are treated as search keywords.
|
|
27
|
+
2. Key-value pairs (with ':') are treated as filters.
|
|
28
|
+
3. AND/OR are treated as logical operators when between terms.
|
|
29
|
+
4. Sets in the form key:{value1,value2} are supported as filters.
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
let specs = {
|
|
33
|
+
name: 'searchAssets',
|
|
34
|
+
description: description,
|
|
35
|
+
schema: {
|
|
36
|
+
searchstring: z.string(),
|
|
37
|
+
assetType: z.string(),
|
|
38
|
+
start: z.number().default(0),
|
|
39
|
+
limit: z.number().default(10),
|
|
40
|
+
|
|
41
|
+
},
|
|
42
|
+
required: ['assetType'],
|
|
43
|
+
handler: async (params) => {
|
|
44
|
+
log('searchAssets params', params);
|
|
45
|
+
return await _catalogSearch(params, 'search');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return specs;
|
|
51
|
+
}
|
|
52
|
+
export default searchAssets;
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
|
|
8
|
+
function setContext(_appContext) {
|
|
9
|
+
let description = `
|
|
10
|
+
## set-context — set the CAS and SAS server contexts for subsequent tool calls
|
|
11
|
+
|
|
12
|
+
LLM Invocation Guidance (When to use)
|
|
13
|
+
Use THIS tool when:
|
|
14
|
+
- User wants to switch to a different CAS server: "Use the finance-cas-server"
|
|
15
|
+
- User wants to change the compute context: "Switch to 'SAS Studio Compute Context'"
|
|
16
|
+
- User wants to check current context: "What context am I using?"
|
|
17
|
+
- User wants to set both: "Use finance-cas-server for CAS and my-compute for SAS"
|
|
18
|
+
|
|
19
|
+
Do NOT use this tool for:
|
|
20
|
+
- Retrieving variable values (use get-env)
|
|
21
|
+
- Reading table data (use read-table)
|
|
22
|
+
- Running programs or queries (use run-sas-program or sas-query)
|
|
23
|
+
- Listing available servers or contexts (no tool for this; would require backend support)
|
|
24
|
+
|
|
25
|
+
Purpose
|
|
26
|
+
Set the active CAS server and/or SAS compute context for all subsequent tool calls in this session. This allows switching between different server environments. If neither parameter is provided, the tool returns the current context values.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
- cas (string, optional): The name of the CAS server to use for subsequent CAS operations. Examples: 'cas-shared-default', 'finance-cas-server', 'analytics-cas'
|
|
30
|
+
- sas (string, optional): The name of the SAS compute context to use for subsequent SAS operations. Examples: 'SAS Studio Compute Context', 'my-compute', 'batch-compute'
|
|
31
|
+
|
|
32
|
+
Response Contract
|
|
33
|
+
Returns a JSON object containing:
|
|
34
|
+
- cas: The current/new CAS server name (string or null)
|
|
35
|
+
- sas: The current/new SAS compute context name (string or null)
|
|
36
|
+
- If no parameters provided, returns the current context values
|
|
37
|
+
- If parameters provided, updates and returns the new context values
|
|
38
|
+
- On error: error message if context cannot be set (e.g., invalid server name)
|
|
39
|
+
|
|
40
|
+
Disambiguation & Clarification
|
|
41
|
+
- If user says "switch servers" without specifying which: ask "Which server would you like to use: CAS, SAS, or both?"
|
|
42
|
+
- If user provides a server name that may not exist: proceed with setting it (the backend will validate)
|
|
43
|
+
- If user says "reset context": ask "Should I reset the CAS context, SAS context, or both?"
|
|
44
|
+
- If user only says "context": ask "Would you like to check the current context or set a new one?"
|
|
45
|
+
|
|
46
|
+
Examples (→ mapped params)
|
|
47
|
+
- "Use the finance-cas-server" → { cas: "finance-cas-server" }
|
|
48
|
+
- "Switch to SAS Studio Compute Context" → { sas: "SAS Studio Compute Context" }
|
|
49
|
+
- "Set CAS to prod-cas and SAS to batch-compute" → { cas: "prod-cas", sas: "batch-compute" }
|
|
50
|
+
- "What's my current context?" → { } (no parameters returns current context)
|
|
51
|
+
- "Show me the active CAS server" → { } (no parameters returns current context)
|
|
52
|
+
|
|
53
|
+
Negative Examples (should NOT call set-context)
|
|
54
|
+
- "Read 10 rows from the customers table" (use read-table instead)
|
|
55
|
+
- "What's the value of myVariable?" (use get-env instead)
|
|
56
|
+
- "Run this SAS program" (use run-sas-program instead)
|
|
57
|
+
|
|
58
|
+
Related Tools
|
|
59
|
+
- get-env — to retrieve individual environment variable values
|
|
60
|
+
- read-table — to read data using the current context
|
|
61
|
+
- run-sas-program — to execute SAS programs in the current context
|
|
62
|
+
- sas-query — to execute SQL queries in the current context
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
let spec = {
|
|
66
|
+
name: 'set-context',
|
|
67
|
+
aliases: ['setContext','set context','set_context'],
|
|
68
|
+
description: description,
|
|
69
|
+
schema: {
|
|
70
|
+
cas: z.string().optional(),
|
|
71
|
+
sas: z.string().optional()
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
handler: async (params) => {
|
|
75
|
+
// Ensure contexts object exists
|
|
76
|
+
if (_appContext.contexts == null || typeof _appContext.contexts !== 'object') {
|
|
77
|
+
_appContext.contexts = {
|
|
78
|
+
cas: _appContext.DEFAULT_CAS_SERVER || null,
|
|
79
|
+
sas: null
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
let {cas, sas} = params;
|
|
83
|
+
if (typeof cas === 'string' && cas.trim().length > 0) {
|
|
84
|
+
_appContext.contexts.cas = cas.trim();
|
|
85
|
+
}
|
|
86
|
+
if (typeof sas === 'string' && sas.trim().length > 0) {
|
|
87
|
+
_appContext.contexts.sas = sas.trim();
|
|
88
|
+
}
|
|
89
|
+
// Return a structured response without extraneous keys
|
|
90
|
+
return {
|
|
91
|
+
content: [{ type: 'text', text: JSON.stringify(_appContext.contexts) }],
|
|
92
|
+
structuredContent: _appContext.contexts
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return spec;
|
|
97
|
+
}
|
|
98
|
+
export default setContext;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import _submitCode from '../toolHelpers/_submitCode.js';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
function superstat(_appContext) {
|
|
9
|
+
let desc = `
|
|
10
|
+
## superstat: compute superstat for two numbers using SAS programming
|
|
11
|
+
|
|
12
|
+
## Details
|
|
13
|
+
This is a tool to demonstrate using SAS programming to score. The SAS program for suprestat is
|
|
14
|
+
below. In a real application this would be a more complex program that is
|
|
15
|
+
available to the SAS server.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
ods html style=barrettsblue;
|
|
19
|
+
data temp;
|
|
20
|
+
superstat = (&a + &b) * 42;
|
|
21
|
+
run;
|
|
22
|
+
proc print data=temp;
|
|
23
|
+
run;
|
|
24
|
+
ods html close;
|
|
25
|
+
run;
|
|
26
|
+
|
|
27
|
+
## Sample Prompt
|
|
28
|
+
|
|
29
|
+
- compute superstat for 1 and 2
|
|
30
|
+
- compute superstat for 3,5
|
|
31
|
+
|
|
32
|
+
`;
|
|
33
|
+
let spec = {
|
|
34
|
+
name: 'superstat',
|
|
35
|
+
description: desc,
|
|
36
|
+
schema: {
|
|
37
|
+
a: z.number(),
|
|
38
|
+
b: z.number()
|
|
39
|
+
},
|
|
40
|
+
required: ['a', 'b'],
|
|
41
|
+
handler: async (params) => {
|
|
42
|
+
let src = `
|
|
43
|
+
ods html style=barrettsblue;
|
|
44
|
+
data temp;
|
|
45
|
+
superstat = (&a + &b) * 42;
|
|
46
|
+
run;
|
|
47
|
+
proc print data=temp;
|
|
48
|
+
run;
|
|
49
|
+
ods html close;
|
|
50
|
+
run;
|
|
51
|
+
`;
|
|
52
|
+
params.src = src;
|
|
53
|
+
let r = await _submitCode(params);
|
|
54
|
+
return r;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
};
|
|
58
|
+
return spec;
|
|
59
|
+
}
|
|
60
|
+
export default superstat;
|
|
@@ -0,0 +1,102 @@
|
|
|
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 _tableInfo from '../toolHelpers/_tableInfo.js';
|
|
9
|
+
function tableInfo(_appContext) {
|
|
10
|
+
|
|
11
|
+
let describe = `
|
|
12
|
+
## table-info — retrieve metadata about a table in a CAS or SAS library
|
|
13
|
+
|
|
14
|
+
LLM Invocation Guidance (When to use)
|
|
15
|
+
Use THIS tool when:
|
|
16
|
+
- User wants table structure/schema: "what columns are in the cars table?"
|
|
17
|
+
- User wants column metadata: "describe the structure of customers table"
|
|
18
|
+
- User wants to see data types: "show me the schema for sales table in Public"
|
|
19
|
+
- User wants table statistics: "how many rows in the orders table?"
|
|
20
|
+
- User wants column information: "what are the columns in the iris table?"
|
|
21
|
+
|
|
22
|
+
Do NOT use this tool for:
|
|
23
|
+
- Reading actual data rows (use read-table)
|
|
24
|
+
- Listing tables in a library (use list-tables)
|
|
25
|
+
- Checking if a table exists (use find-table)
|
|
26
|
+
- Running queries (use sas-query)
|
|
27
|
+
- Reading sample data (use read-table)
|
|
28
|
+
|
|
29
|
+
Purpose
|
|
30
|
+
Return metadata about a table in a specified library (caslib or libref). This includes column names, data types, labels, formats, and table-level statistics such as row count, file size, and timestamps.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
- table (string, required): The name of the table to inspect.
|
|
34
|
+
- lib (string, required): The caslib or libref containing the table.
|
|
35
|
+
- server (string, default 'cas'): Target server, either 'cas' or 'sas'. Defaults to 'cas' when omitted.
|
|
36
|
+
|
|
37
|
+
Response Contract
|
|
38
|
+
Returns a JSON object containing:
|
|
39
|
+
- columns: Array of column objects with:
|
|
40
|
+
- name: Column name (string)
|
|
41
|
+
- type: Data type (string) - e.g., 'numeric', 'character'
|
|
42
|
+
- label: Column label if defined (string)
|
|
43
|
+
- format: Display format if defined (string)
|
|
44
|
+
- length: Column length for character fields (number)
|
|
45
|
+
- tableInfo: Table-level metadata including:
|
|
46
|
+
- rowCount: Number of rows (number)
|
|
47
|
+
- fileSize: File size if available (number)
|
|
48
|
+
- created: Creation timestamp if available (string)
|
|
49
|
+
- modified: Last modified timestamp if available (string)
|
|
50
|
+
- Empty object if table not found or accessible
|
|
51
|
+
|
|
52
|
+
Disambiguation & Clarification
|
|
53
|
+
- Missing library: ask "Which library contains the table you want to inspect?"
|
|
54
|
+
- Missing table: ask "Which table would you like information about?"
|
|
55
|
+
- If user wants data: clarify "Do you want the table structure (use table-info) or actual data rows (use read-table)?"
|
|
56
|
+
- Ambiguous lib.table format: parse and use as separate parameters
|
|
57
|
+
|
|
58
|
+
Examples (→ mapped params)
|
|
59
|
+
- "describe table cars in sashelp" → { table: "cars", lib: "sashelp", server: "sas" }
|
|
60
|
+
- "what columns are in orders from Public" → { table: "orders", lib: "Public", server: "cas" }
|
|
61
|
+
- "show schema for sales in mylib" → { table: "sales", lib: "mylib", server: "cas" }
|
|
62
|
+
- "table info for iris in Samples" → { table: "iris", lib: "Samples", server: "cas" }
|
|
63
|
+
- "how many rows in customers on cas" → { table: "customers", lib: <lib>, server: "cas" }
|
|
64
|
+
|
|
65
|
+
Negative Examples (should NOT call table-info)
|
|
66
|
+
- "read 10 rows from cars" (use read-table instead)
|
|
67
|
+
- "list tables in sashelp" (use list-tables instead)
|
|
68
|
+
- "does table cars exist in Public?" (use find-table instead)
|
|
69
|
+
- "run query on customers table" (use sas-query instead)
|
|
70
|
+
- "show me data from the sales table" (use read-table instead)
|
|
71
|
+
|
|
72
|
+
Usage Tips
|
|
73
|
+
- Use this tool to inspect schema and column types before scoring or reading data.
|
|
74
|
+
- After inspecting structure, use read-table to fetch actual data.
|
|
75
|
+
- Combine with find-table to verify existence before inspection.
|
|
76
|
+
|
|
77
|
+
Related Tools
|
|
78
|
+
- find-table → table-info → read-table (typical workflow)
|
|
79
|
+
- list-tables — to discover tables before inspecting
|
|
80
|
+
- read-table — to fetch actual data after inspecting structure
|
|
81
|
+
- find-table — to verify a table exists before inspection
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
let specs = {
|
|
85
|
+
name: 'table-info',
|
|
86
|
+
aliases: ['tableInfo','table info','table_info'],
|
|
87
|
+
description: describe,
|
|
88
|
+
schema: {
|
|
89
|
+
table: z.string(),
|
|
90
|
+
lib: z.string(),
|
|
91
|
+
server: z.string()
|
|
92
|
+
},
|
|
93
|
+
required: ['table', 'lib'],
|
|
94
|
+
handler: async (params) => {
|
|
95
|
+
params.describe = true;
|
|
96
|
+
let r = await _tableInfo(params);
|
|
97
|
+
return r;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return specs;
|
|
101
|
+
}
|
|
102
|
+
export default tableInfo;
|
|
@@ -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
|
+
// An initial pass at using catalog search
|
|
7
|
+
// not sure of its usefulness yet since the search string
|
|
8
|
+
// syntax is designed for nerdy users.
|
|
9
|
+
// Maybe one can instruct LLM to format a user friend search into
|
|
10
|
+
// required syntax
|
|
11
|
+
// But then I am an expert on SAS Catalog
|
|
12
|
+
import restaf from '@sassoftware/restaf';
|
|
13
|
+
|
|
14
|
+
import _itemsData from '../toolHelpers/_itemsData.js';
|
|
15
|
+
|
|
16
|
+
async function _catalogSearch(params, rel) {
|
|
17
|
+
let { searchstring, start, limit , _appContext} = params;
|
|
18
|
+
let splitsearchstring = searchstring.trimStart().split(' ');
|
|
19
|
+
let assetType = ' ';
|
|
20
|
+
if (!splitsearchstring[0].includes(':')) {
|
|
21
|
+
if (['dataflows',
|
|
22
|
+
'datasets',
|
|
23
|
+
'dataplans',
|
|
24
|
+
'models',
|
|
25
|
+
'modelprojects',
|
|
26
|
+
'modelstudioprojects',
|
|
27
|
+
'report',
|
|
28
|
+
'rulesets',
|
|
29
|
+
'referencedatadomains',
|
|
30
|
+
'codefiles',
|
|
31
|
+
'decisions',
|
|
32
|
+
'riskdataprojects',
|
|
33
|
+
'riskmodels'
|
|
34
|
+
].includes(splitsearchstring[0])) {
|
|
35
|
+
assetType = `${splitsearchstring[0]}`;
|
|
36
|
+
splitsearchstring[0] = 'AssetType:' + splitsearchstring[0];
|
|
37
|
+
searchstring = splitsearchstring.join(' ');
|
|
38
|
+
} else {
|
|
39
|
+
assetType = `${splitsearchstring[0]}`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
limit = (limit) ? limit : 10;
|
|
44
|
+
start = (start) ? start : 0;
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// https://go.documentation.sas.com/doc/en/infocatcdc/v_034/infocatug/n09x2n3z9t2izln1vtx68oho8t8x.htm?requestorId=84052456-0342-4389-a344-5cc71cbec5cc
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
|
|
52
|
+
let store = restaf.initStore(_appContext.storeConfig);
|
|
53
|
+
|
|
54
|
+
let msg = await store.logon(_appContext.logonPayload);
|
|
55
|
+
|
|
56
|
+
if (rel == null) {
|
|
57
|
+
rel = 'search';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let { catalog } = await store.addServices('catalog');
|
|
61
|
+
let payload = {
|
|
62
|
+
qs: { q: searchstring, limit: limit, start: start },
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
let r = await store.apiCall(catalog.links(rel), payload);
|
|
67
|
+
|
|
68
|
+
let rx = _itemsData(r);
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: 'text', text: JSON.stringify(rx)
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
structuredContent: rx,
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error('Error in searchAssets:', JSON.stringify(err, null, 4));
|
|
79
|
+
return {
|
|
80
|
+
isError: true,
|
|
81
|
+
content:[{
|
|
82
|
+
type: 'text', text: JSON.stringify(err)
|
|
83
|
+
}]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export default _catalogSearch;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// catalog returns a lot of stuff, so we need to filter it down to just the items
|
|
7
|
+
function _itemsData(r) {
|
|
8
|
+
let rx = [];
|
|
9
|
+
let details = {};
|
|
10
|
+
let text = '';
|
|
11
|
+
if (r.itemsList().size > 0) {
|
|
12
|
+
rx = r.itemsList().toJS().map(item => {
|
|
13
|
+
let rt = r.items(item, 'data').toJS();
|
|
14
|
+
let row = {name: item, label: rt.label, type: rt.type};
|
|
15
|
+
if (rt.attributes != null) {
|
|
16
|
+
row.source = (rt.attributes.sourceSystem) ? rt.attributes.sourceSystem.toLowerCase() : '';
|
|
17
|
+
row.library = rt.attributes.library;
|
|
18
|
+
}
|
|
19
|
+
details[item] = rt;
|
|
20
|
+
text = text + '\n' + `${item}: ${JSON.stringify(rt)}`;
|
|
21
|
+
return row;
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
rx = (r.items('data') != null) ? [r.items('data').toJS()] : {warning: 'No data returned'};
|
|
25
|
+
}
|
|
26
|
+
return rx;
|
|
27
|
+
}
|
|
28
|
+
export default _itemsData;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import restaf from '@sassoftware/restaf';
|
|
6
|
+
import restaflib from '@sassoftware/restaflib';
|
|
7
|
+
import _submitCode from './_submitCode.js';
|
|
8
|
+
|
|
9
|
+
async function _jobSubmit(params) {
|
|
10
|
+
let { name, type, scenario, query, _appContext } = params;
|
|
11
|
+
console.error(`_jobSubmit called with name: ${name} type: ${type} scenario: ${JSON.stringify(scenario)}`);
|
|
12
|
+
// setup
|
|
13
|
+
if (name === 'program') {
|
|
14
|
+
let src = `
|
|
15
|
+
proc sql;
|
|
16
|
+
create table work.query_results as
|
|
17
|
+
${scenario.sql};
|
|
18
|
+
quit;
|
|
19
|
+
data work.RESULT;
|
|
20
|
+
set work.query_results;
|
|
21
|
+
run;
|
|
22
|
+
;
|
|
23
|
+
`;
|
|
24
|
+
let iparams = {
|
|
25
|
+
output: 'RESULT',
|
|
26
|
+
limit: 100,
|
|
27
|
+
args: {},
|
|
28
|
+
src: src,
|
|
29
|
+
_appContext: _appContext
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
let r = await _submitCode(iparams);
|
|
34
|
+
let response = {tabled: r.structuredContent.tabled};
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: 'text', text: JSON.stringify(response) }],
|
|
37
|
+
structuredContent: response
|
|
38
|
+
};
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(`Error in _jobSubmit for program: ${JSON.stringify(error)}`);
|
|
41
|
+
let result = {
|
|
42
|
+
tables: { Error: [{ Message: "Job failed. Please contact the owner of the job " + name }] }
|
|
43
|
+
};
|
|
44
|
+
return { isError: true, content: [{ type: 'text', text: JSON.stringify(result) }], structuredContent: result };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
|
|
50
|
+
// use a job or job definition
|
|
51
|
+
try {
|
|
52
|
+
let store = restaf.initStore(_appContext.storeConfig);
|
|
53
|
+
let msg = await store.logon(_appContext.logonPayload);
|
|
54
|
+
type = (type == null) ? 'job' : type.toLowerCase();
|
|
55
|
+
|
|
56
|
+
let r = (type === 'definition' || type === 'def')
|
|
57
|
+
? await restaflib.jesRun(store, name, scenario)
|
|
58
|
+
: await restaflib.jobRun(store, name, scenario);
|
|
59
|
+
|
|
60
|
+
let response = (query === true) ? { tabled: r.tables.rows } : { tables: r.tables, listing: r.listing, log: r.log };
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: 'text', text: JSON.stringify(response) }],
|
|
64
|
+
structuredContent: response
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
// Oops! Something went wrong
|
|
69
|
+
console.error(`Error in _jobSubmit: ${JSON.stringify(error)}`);
|
|
70
|
+
let result = {
|
|
71
|
+
tables: { Error: [{ Message: "Job failed. Please contact the owner of the job " + name }] }
|
|
72
|
+
};
|
|
73
|
+
return { isError: true, content: [{ type: 'text', text: JSON.stringify(result) }], structuredContent: result };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default _jobSubmit;
|