@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,76 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates and configures an MCP server instance.
|
|
8
|
+
* @param {Object} cache - The session cache to store the MCP server instance.
|
|
9
|
+
* @returns {Promise<McpServer>} The configured MCP server instance.
|
|
10
|
+
* @example
|
|
11
|
+
* Notes: Handles both http and stdio transports scenarios
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
14
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
15
|
+
import makeTools from "./toolSet/makeTools.js";
|
|
16
|
+
import getLogonPayload from "./toolHelpers/getLogonPayload.js";
|
|
17
|
+
|
|
18
|
+
async function createMcpServer(cache, _appContext) {
|
|
19
|
+
|
|
20
|
+
let mcpServer = new McpServer(
|
|
21
|
+
{
|
|
22
|
+
name: "sasmcp",
|
|
23
|
+
version: _appContext.version,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
capabilities: {
|
|
27
|
+
tools: {
|
|
28
|
+
listChanged: true,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
let toolSet = makeTools(_appContext);
|
|
34
|
+
|
|
35
|
+
//wrapping tool handler to pass _appContext
|
|
36
|
+
//can be ignored or used as needed.
|
|
37
|
+
|
|
38
|
+
const wrapf = (cache, builtin) => async (args) => {
|
|
39
|
+
let currentId = cache.get('currentId');
|
|
40
|
+
let _appContext = cache.get(currentId);
|
|
41
|
+
let params;
|
|
42
|
+
// get Viya token
|
|
43
|
+
_appContext.contexts.logonPayload = await getLogonPayload(_appContext);
|
|
44
|
+
if (_appContext.contexts.logonPayload == null) {
|
|
45
|
+
return { isError: true, content: [{ type: 'text', text: 'Unable to get authentication token for SAS Viya. Please check your configuration.' }] }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// create enhanced appContext for tool
|
|
49
|
+
if (args == null) {
|
|
50
|
+
params = {_appContext: _appContext.contexts};
|
|
51
|
+
} else {
|
|
52
|
+
params = Object.assign({}, args, {_appContext: _appContext.contexts});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// call the actual tool handler
|
|
56
|
+
let r = await builtin(params);
|
|
57
|
+
return r;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Register the tools with brand prefix
|
|
61
|
+
console.error(`[Note] Brand: ${_appContext.brand}`);
|
|
62
|
+
let toolNames = [];
|
|
63
|
+
toolSet.forEach((tool, i) => {
|
|
64
|
+
let toolName = _appContext.brand + '-' + tool.name;
|
|
65
|
+
// console.error(`\n[Note] Registering tool ${i + 1} : ${toolName}`);
|
|
66
|
+
let toolHandler = wrapf(cache, tool.handler);
|
|
67
|
+
|
|
68
|
+
mcpServer.tool(toolName, tool.description, tool.schema, toolHandler);
|
|
69
|
+
toolNames.push(toolName);
|
|
70
|
+
});
|
|
71
|
+
console.error(`[Note] Registered ${toolSet.length} tools: ${toolNames}`);
|
|
72
|
+
cache.set("mcpServer", mcpServer);
|
|
73
|
+
return mcpServer;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default createMcpServer;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
function scrModels(name) {
|
|
6
|
+
|
|
7
|
+
// This function returns the URL of the SCR model based on the name.
|
|
8
|
+
// In a real application the list of scr models would be fetched from a database or configuration file.
|
|
9
|
+
// For this example, we will use a hardcoded list of models.
|
|
10
|
+
let scrModels = {
|
|
11
|
+
"loan": "http://mcphllllll.hggmg3bshufda0et.eastus8080/mcp"
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
if (name.indexOf('http') === 0) {
|
|
15
|
+
return name;
|
|
16
|
+
}
|
|
17
|
+
if (scrModels[name]) {
|
|
18
|
+
return scrModels[name];
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default scrModels;
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
|
|
7
|
+
function devaScore(_appContext) {
|
|
8
|
+
|
|
9
|
+
let description = `
|
|
10
|
+
## deva-score — compute a numeric score based on two input values
|
|
11
|
+
|
|
12
|
+
LLM Invocation Guidance (When to use)
|
|
13
|
+
Use THIS tool when:
|
|
14
|
+
- User wants to calculate the deva score: "Calculate deva score for 5 and 10"
|
|
15
|
+
- User provides two numbers for scoring: "Score these values: 3 and 7"
|
|
16
|
+
- User wants to compute a score in a series: "Calculate scores for [list of numbers]"
|
|
17
|
+
|
|
18
|
+
Do NOT use this tool for:
|
|
19
|
+
- Scoring models (use model-score)
|
|
20
|
+
- Statistical calculations beyond deva scoring
|
|
21
|
+
- Looking up data or metadata
|
|
22
|
+
|
|
23
|
+
Purpose
|
|
24
|
+
Compute a numeric deva score by applying the formula (a + b) * 42 to two input numbers. For scoring more than two numbers, call this tool multiple times using the previous result as the first input (left-to-right fold).
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
- a (number, required): First numeric input value
|
|
28
|
+
- b (number, required): Second numeric input value
|
|
29
|
+
|
|
30
|
+
Response Contract
|
|
31
|
+
Returns a numeric result: (a + b) * 42
|
|
32
|
+
The result is always a number representing the computed deva score.
|
|
33
|
+
|
|
34
|
+
Disambiguation & Clarification
|
|
35
|
+
- If user provides more than two numbers without clear instructions: "Do you want to calculate the deva score by combining these numbers left-to-right?"
|
|
36
|
+
- If user provides non-numeric input: "Please provide numeric values"
|
|
37
|
+
|
|
38
|
+
Examples (→ mapped params)
|
|
39
|
+
- "Calculate deva score for 5 and 10" → { a: 5, b: 10 } returns 630
|
|
40
|
+
- "Score 1 and 2" → { a: 1, b: 2 } returns 126
|
|
41
|
+
- For multiple numbers, chain calls: deva(1,2)→126, then deva(126,3)→5418
|
|
42
|
+
|
|
43
|
+
Negative Examples (should NOT call deva-score)
|
|
44
|
+
- "Score this customer with the credit model" (use model-score instead)
|
|
45
|
+
- "Calculate the mean of these values" (use run-sas-program or sas-query instead)
|
|
46
|
+
|
|
47
|
+
Related Tools
|
|
48
|
+
- None directly related (this is a specialized scoring tool)
|
|
49
|
+
|
|
50
|
+
Notes
|
|
51
|
+
For sequences of numbers, use a left-to-right fold: call devaScore(first, second), then use that result as the first parameter for devaScore(result, third), and so on.
|
|
52
|
+
`;
|
|
53
|
+
let spec = {
|
|
54
|
+
name: 'deva-score',
|
|
55
|
+
aliases: ['devaScore','deva score','deva_score'],
|
|
56
|
+
description: description,
|
|
57
|
+
schema: {
|
|
58
|
+
a: z.number(),
|
|
59
|
+
b: z.number()
|
|
60
|
+
},
|
|
61
|
+
handler: async ({ a, b,_appContext }) => {
|
|
62
|
+
console.error( a, b);
|
|
63
|
+
return { content: [{ type: 'text',
|
|
64
|
+
text: String((a + b) * 42) }] }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return spec;
|
|
68
|
+
}
|
|
69
|
+
export default devaScore;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import _listJobs from '../toolHelpers/_listJobs.js';
|
|
7
|
+
function findJob(_appContext) {
|
|
8
|
+
let llmDescription= {
|
|
9
|
+
"purpose": "Map natural language requests to find a job in SAS Viya and return structured results.",
|
|
10
|
+
"param_mapping": {
|
|
11
|
+
"name": "required - single name. If missing, ask 'Which job name would you like to find?'.",
|
|
12
|
+
|
|
13
|
+
},
|
|
14
|
+
"response_schema": "{ jobs: Array<string|object> }",
|
|
15
|
+
"behavior": "Return only JSON matching response_schema when invoked by an LLM. If no matches, return { jobs: [] }"
|
|
16
|
+
};
|
|
17
|
+
let description = `
|
|
18
|
+
## find-job — locate a specific SAS Viya job
|
|
19
|
+
|
|
20
|
+
LLM Invocation Guidance
|
|
21
|
+
Use THIS tool when the user intent is to check if ONE job exists or retrieve its metadata:
|
|
22
|
+
- "find job cars_job_v4"
|
|
23
|
+
- "does job sales_summary exist"
|
|
24
|
+
- "is there a job named churnScorer"
|
|
25
|
+
- "lookup job forecast_monthly"
|
|
26
|
+
- "verify job ETL_Daily"
|
|
27
|
+
|
|
28
|
+
Do NOT use this tool when the user asks for:
|
|
29
|
+
- A list or browse of many jobs (use listJobs)
|
|
30
|
+
- Do not use this tool if the user want to find lib, find table, find model and similar requests
|
|
31
|
+
- Executing a job (use job)
|
|
32
|
+
- Running a job definition (use jobdef)
|
|
33
|
+
- Submitting arbitrary code (use program)
|
|
34
|
+
|
|
35
|
+
Purpose
|
|
36
|
+
Quickly determine whether a named job asset is present in the Viya environment and return its entry (or an empty result if not found).
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
- name (string, required): Exact job name (case preserved). If multiple tokens/names supplied, take the first and ignore the rest; optionally ask for a single name.
|
|
40
|
+
|
|
41
|
+
Behavior & Matching
|
|
42
|
+
- Attempt exact match first (backend determines sensitivity).
|
|
43
|
+
- Returns { jobs: [...] } where array length is 0 (not found) or 1+ (if backend returns multiple with the same display name).
|
|
44
|
+
- No fuzzy guesses—never fabricate a job.
|
|
45
|
+
- If no name provided: ask "Which job name would you like to find?".
|
|
46
|
+
|
|
47
|
+
Response Contract
|
|
48
|
+
- Always: { jobs: Array<string|object> }
|
|
49
|
+
- On error: propagate structured server error (do not wrap in prose when invoked programmatically).
|
|
50
|
+
|
|
51
|
+
Disambiguation Rules
|
|
52
|
+
- Input only "find job" → ask for missing name.
|
|
53
|
+
- Input contains verbs like "run" or "execute" → use run-job or run-jobdef instead.
|
|
54
|
+
- Input requesting many (e.g., "find all jobs") → use list-jobs.
|
|
55
|
+
|
|
56
|
+
Examples (→ mapped params)
|
|
57
|
+
- "find job cars_job_v4" → { name: "cars_job_v4" }
|
|
58
|
+
- "does job ETL exist" → { name: "ETL" }
|
|
59
|
+
- "is there a job named metricsRefresh" → { name: "metricsRefresh" }
|
|
60
|
+
|
|
61
|
+
Negative Examples (should NOT call find-job)
|
|
62
|
+
- "list jobs" (list-jobs)
|
|
63
|
+
- "run job cars_job_v4" (run-job)
|
|
64
|
+
- "execute jobdef cars_job_v4" (run-jobdef)
|
|
65
|
+
|
|
66
|
+
Clarifying Question Template
|
|
67
|
+
- Missing name: "Which job name would you like to find?"
|
|
68
|
+
- Multiple names: "Please provide just one job name (e.g. 'cars_job_v4')."
|
|
69
|
+
|
|
70
|
+
Notes
|
|
71
|
+
- For bulk existence checks loop over names and call find-job per name.
|
|
72
|
+
- Combine with run-job tool if user wants to execute after confirming existence.
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
let spec = {
|
|
76
|
+
name: 'find-job',
|
|
77
|
+
aliases: ['findJob','find job','find_job'],
|
|
78
|
+
description: description,
|
|
79
|
+
schema: {
|
|
80
|
+
name: z.string()
|
|
81
|
+
},
|
|
82
|
+
required: ['name'],
|
|
83
|
+
handler: async (params) => {
|
|
84
|
+
let r = await _listJobs(params);
|
|
85
|
+
return r;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return spec;
|
|
89
|
+
}
|
|
90
|
+
export default findJob;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import _listJobdefs from '../toolHelpers/_listJobdefs.js';
|
|
7
|
+
function findJobdef(_appContext) {
|
|
8
|
+
let llmDescription= {
|
|
9
|
+
"purpose": "Map natural language requests to find a jobdef (job definition) in SAS Viya and return structured results.",
|
|
10
|
+
"param_mapping": {
|
|
11
|
+
"name": "required - single name. If missing, ask 'Which jobdef name would you like to find?'.",
|
|
12
|
+
|
|
13
|
+
},
|
|
14
|
+
"response_schema": "{ jobs: Array<string|object> }",
|
|
15
|
+
"behavior": "Return only JSON matching response_schema when invoked by an LLM. If no matches, return { jobs: [] }"
|
|
16
|
+
};
|
|
17
|
+
let description = `
|
|
18
|
+
## find-jobdef — locate a specific SAS Viya job
|
|
19
|
+
|
|
20
|
+
LLM Invocation Guidance
|
|
21
|
+
Use THIS tool when the user intent is to check if ONE job exists or retrieve its metadata:
|
|
22
|
+
- "find jobdef cars_job_v4"
|
|
23
|
+
- "does jobdef sales_summary exist"
|
|
24
|
+
- "is there a jobdef named churnScorer"
|
|
25
|
+
- "lookup jobdef forecast_monthly"
|
|
26
|
+
- "verify jobdef ETL_Daily"
|
|
27
|
+
|
|
28
|
+
Do NOT use this tool when the user asks for:
|
|
29
|
+
- find job (use find-job)
|
|
30
|
+
- find table (use find-table
|
|
31
|
+
- find model (use find-model)
|
|
32
|
+
- find lib (use find-library)
|
|
33
|
+
- Executing a job (use job)
|
|
34
|
+
- Running a job definition (use jobdef)
|
|
35
|
+
- Submitting arbitrary code (use program)
|
|
36
|
+
|
|
37
|
+
Purpose
|
|
38
|
+
Quickly determine whether a named jobdef asset is present in the Viya environment and return its entry (or an empty result if not found).
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
- name (string, required): Exact jobdef name (case preserved). If multiple tokens/names supplied, take the first and ignore the rest; optionally ask for a single name.
|
|
42
|
+
|
|
43
|
+
Behavior & Matching
|
|
44
|
+
- Attempt exact match first (backend determines sensitivity).
|
|
45
|
+
- Returns { jobdefs: [...] } where array length is 0 (not found) or 1+ (if backend returns multiple with the same display name).
|
|
46
|
+
- No fuzzy guesses—never fabricate a job.
|
|
47
|
+
- If no name provided: ask "Which jobdef name would you like to find?".
|
|
48
|
+
|
|
49
|
+
Response Contract
|
|
50
|
+
- Always: { jobdefs: Array<string|object> }
|
|
51
|
+
- On error: propagate structured server error (do not wrap in prose when invoked programmatically).
|
|
52
|
+
|
|
53
|
+
Disambiguation Rules
|
|
54
|
+
- Input only "find job" → ask for missing name.
|
|
55
|
+
- Input contains verbs like "run" or "execute" → use run-job or run-jobdef instead.
|
|
56
|
+
- Input requesting many (e.g., "find all jobs") → use list-jobs.
|
|
57
|
+
|
|
58
|
+
Examples (→ mapped params)
|
|
59
|
+
- "find jobdef cars_job_v4" → { name: "cars_job_v4" }
|
|
60
|
+
- "does jobdef ETL exist" → { name: "ETL" }
|
|
61
|
+
- "is there a jobdef named metricsRefresh" → { name: "metricsRefresh" }
|
|
62
|
+
|
|
63
|
+
Negative Examples (should NOT call find-jobdef)
|
|
64
|
+
- find lib (use find-library)
|
|
65
|
+
- find table (use find-table)
|
|
66
|
+
- find model (use find-model)
|
|
67
|
+
- "list job" (list-jobdefs)
|
|
68
|
+
- "run job cars_job_v4" (run-job)
|
|
69
|
+
- "execute jobdef cars_job_v4" (run-jobdef)
|
|
70
|
+
|
|
71
|
+
Clarifying Question Template
|
|
72
|
+
- Missing name: "Which jobdef name would you like to find?"
|
|
73
|
+
- Multiple names: "Please provide just one jobdef name (e.g. 'cars_job_v4')."
|
|
74
|
+
|
|
75
|
+
Notes
|
|
76
|
+
- For bulk existence checks loop over names and call find-jobdef per name.
|
|
77
|
+
- Combine with run-jobdef tool if user wants to execute after confirming existence.
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
let spec = {
|
|
81
|
+
name: 'find-jobdef',
|
|
82
|
+
aliases: ['findJobdef','find jobdef','find_jobdef'],
|
|
83
|
+
description: description,
|
|
84
|
+
schema: {
|
|
85
|
+
name: z.string()
|
|
86
|
+
},
|
|
87
|
+
required: ['name'],
|
|
88
|
+
handler: async (params) => {
|
|
89
|
+
let r = await _listJobdefs(params);
|
|
90
|
+
return r;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return spec;
|
|
94
|
+
}
|
|
95
|
+
export default findJobdef;
|
|
@@ -0,0 +1,100 @@
|
|
|
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 _listLibrary from '../toolHelpers/_listLibrary.js';
|
|
7
|
+
function findLibrary(_appContext) {
|
|
8
|
+
|
|
9
|
+
let description = `
|
|
10
|
+
## find-library — locate a specific CAS or SAS library
|
|
11
|
+
|
|
12
|
+
LLM Invocation Guidance
|
|
13
|
+
Use THIS tool when the user asks any of the following (intent = existence / lookup of ONE library):
|
|
14
|
+
- "find library Public"
|
|
15
|
+
- "find lib Public"
|
|
16
|
+
- "does library SASHELP exist"
|
|
17
|
+
- "is PUBLIC library available in cas"
|
|
18
|
+
- "lookup library sasuser in sas"
|
|
19
|
+
- "show me library metadata for Models"
|
|
20
|
+
|
|
21
|
+
Aliases for lib are: library, caslib, libref
|
|
22
|
+
|
|
23
|
+
Do NOT use this tool when the user wants:
|
|
24
|
+
- find model -> use find-model
|
|
25
|
+
- find table -> use find-table
|
|
26
|
+
- find job -> use find-job
|
|
27
|
+
- find jobdef -> use find-jobdef
|
|
28
|
+
- Columns or schema of a table (use table-info)
|
|
29
|
+
- Creating/assigning libraries (use run-sas-program or another admin tool)
|
|
30
|
+
|
|
31
|
+
Purpose
|
|
32
|
+
Quickly verify whether a single named library exists on CAS or SAS and return its entry (or empty if not found).
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
- name (string, required) : Exact library (caslib) name to locate. If multiple names provided (comma/space separated), use the first and ignore the rest; optionally ask for one name.
|
|
36
|
+
- server (cas|sas, default 'cas') : Target environment. If omitted or ambiguous default to 'cas'.
|
|
37
|
+
|
|
38
|
+
Behavior & Matching
|
|
39
|
+
- Performs an exact name match (case-insensitive where backend supports it).
|
|
40
|
+
- Returns an object: { libraries: [...] } where the array contains zero or one items (backend may still return richer metadata).
|
|
41
|
+
- If no match: { libraries: [] } (do NOT fabricate suggestions).
|
|
42
|
+
- If user supplies no name: ask a clarifying question: "Which library name would you like to find?".
|
|
43
|
+
- If user clearly wants a list ("list", "show all", "enumerate") route to listLibrary instead.
|
|
44
|
+
|
|
45
|
+
Response Contract
|
|
46
|
+
- Always: { libraries: Array<string|object> }
|
|
47
|
+
- Never include prose when invoked programmatically; only the JSON structure.
|
|
48
|
+
|
|
49
|
+
Disambiguation Rules
|
|
50
|
+
- Input only "find library" → ask for the missing name.
|
|
51
|
+
- Input with both library name and words like "tables" → prefer listTables.
|
|
52
|
+
- Input like "find libraries" (plural) → prefer listLibrary unless user clarifies to a single name.
|
|
53
|
+
|
|
54
|
+
Examples (→ mapped params)
|
|
55
|
+
- "find lib Public" → { name: "Public", server: "cas" }
|
|
56
|
+
- "find library sasuser in sas" → { name: "sasuser", server: "sas" }
|
|
57
|
+
- "does library Formats exist" → { name: "Formats", server: "cas" }
|
|
58
|
+
- "is SystemData library in cas" → { name: "SystemData", server: "cas" }
|
|
59
|
+
|
|
60
|
+
Negative Examples (should NOT call find-library)
|
|
61
|
+
- "list libs" (list-libraries)
|
|
62
|
+
- Do not use this tool if the user want to find table, find job, find model, find job, find jobdef and similar requests
|
|
63
|
+
- "show tables in Public" (list-tables)
|
|
64
|
+
- "describe table cars in sashelp" (table-info)
|
|
65
|
+
|
|
66
|
+
Clarifying Question Template
|
|
67
|
+
- Missing name: "Which library name would you like to find?"
|
|
68
|
+
- Multiple names: "Please provide just one library name to look up (e.g. 'Public')."
|
|
69
|
+
|
|
70
|
+
Notes
|
|
71
|
+
- For bulk validation of many names, call this tool repeatedly per name.
|
|
72
|
+
- For pagination or discovery, switch to list-libraries.
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
let spec = {
|
|
76
|
+
name: 'find-library',
|
|
77
|
+
aliases: ['findLibrary','find library','find_library'],
|
|
78
|
+
description: description,
|
|
79
|
+
schema: {
|
|
80
|
+
name: z.string(),
|
|
81
|
+
server: z.string().default('cas')
|
|
82
|
+
},
|
|
83
|
+
required: ['name'],
|
|
84
|
+
handler: async (params) => {
|
|
85
|
+
// normalize server to lowercase & default
|
|
86
|
+
if (!params.server) params.server = 'cas';
|
|
87
|
+
params.server = params.server.toLowerCase();
|
|
88
|
+
|
|
89
|
+
// If multiple names passed (comma or space separated), take the first token (defensive)
|
|
90
|
+
if (params.name && /[,\s]+/.test(params.name.trim())) {
|
|
91
|
+
params.name = params.name.split(/[,\s]+/).filter(Boolean)[0];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let r = await _listLibrary(params);
|
|
95
|
+
return r;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return spec;
|
|
99
|
+
}
|
|
100
|
+
export default findLibrary;
|
|
@@ -0,0 +1,83 @@
|
|
|
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 _listModels from '../toolHelpers/_listModels.js';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function findModel(_appContext) {
|
|
11
|
+
let description = `
|
|
12
|
+
## find-model — locate a specific model deployed to MAS (Model Publish / Scoring service)
|
|
13
|
+
|
|
14
|
+
LLM Invocation Guidance (When to use)
|
|
15
|
+
Use THIS tool when the user wants to know whether ONE model exists or is deployed:
|
|
16
|
+
- "find model cancerRisk"
|
|
17
|
+
- "does model churn_tree exist"
|
|
18
|
+
- "is model sales_forecast deployed"
|
|
19
|
+
- "lookup model claimFraud"
|
|
20
|
+
- "verify model credit_score_v2 exists"
|
|
21
|
+
|
|
22
|
+
Do NOT use this tool for:
|
|
23
|
+
- Listing many / browsing models (use list-models)
|
|
24
|
+
- Retrieving detailed input/output variable metadata (use model-info)
|
|
25
|
+
- Scoring or running a model (use model-score)
|
|
26
|
+
- Searching model execution containers or SCR endpoints (use scr-info / scr-score if appropriate)
|
|
27
|
+
- Finding a table (use find-table)
|
|
28
|
+
- Finding a library (use find-library)
|
|
29
|
+
- Finding a job or jobdef (use find-job / find-jobdef)
|
|
30
|
+
|
|
31
|
+
Purpose
|
|
32
|
+
Quick existence / lookup check for a MAS‑published model. Returns a list with zero or more matches (typically 0 or 1 for an exact name).
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
- name (string, required): Exact model name. If user supplies phrases like "model named X" extract X. If multiple names are given (comma or space separated), prefer the first and (optionally) ask for a single name.
|
|
36
|
+
|
|
37
|
+
Matching Rules
|
|
38
|
+
- Attempt exact match first. If backend supports partial search, a substring match MAY return multiple models; preserve order.
|
|
39
|
+
- Do not fabricate models. Empty array means not found.
|
|
40
|
+
|
|
41
|
+
Response Contract
|
|
42
|
+
- Always: { models: Array<object|string> }
|
|
43
|
+
- Never return prose when invoked programmatically; only the JSON structure.
|
|
44
|
+
- On error: surface backend error object directly (no rewriting) so the caller can display/log it.
|
|
45
|
+
|
|
46
|
+
Disambiguation & Clarification
|
|
47
|
+
- Missing name (e.g., "find model") → ask: "Which model name would you like to find?"
|
|
48
|
+
- Plural intent (e.g., "find models" / "list models") → use list-models instead.
|
|
49
|
+
- If user requests scoring ("score model X") → route to model-score not find-model.
|
|
50
|
+
|
|
51
|
+
Examples (→ mapped params)
|
|
52
|
+
- "find model myModel" → { name: "myModel" }
|
|
53
|
+
- "does model churn_score exist" → { name: "churn_score" }
|
|
54
|
+
- "is model riskModel deployed" → { name: "riskModel" }
|
|
55
|
+
- "lookup model claims_fraud_v1" → { name: "claims_fraud_v1" }
|
|
56
|
+
|
|
57
|
+
Negative Examples (should NOT call find-model)
|
|
58
|
+
- "list models" (list-models)
|
|
59
|
+
- "score model myModel" (model-score)
|
|
60
|
+
- "describe model myModel" (model-info)
|
|
61
|
+
|
|
62
|
+
Notes
|
|
63
|
+
- Chain usage: find-model → model-info → model-score.
|
|
64
|
+
- For batch existence checks iterate over a list and call find-model per entry.
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
let spec = {
|
|
68
|
+
name: 'find-model',
|
|
69
|
+
aliases: ['findModel','find model','find_model'],
|
|
70
|
+
description: description,
|
|
71
|
+
schema: {
|
|
72
|
+
'name': z.string()
|
|
73
|
+
},
|
|
74
|
+
required: ['name'],
|
|
75
|
+
handler: async (params) => {
|
|
76
|
+
let r = await _listModels(params);
|
|
77
|
+
return r;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return spec;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default findModel;
|
|
@@ -0,0 +1,94 @@
|
|
|
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 _listTables from '../toolHelpers/_listTables.js';
|
|
9
|
+
|
|
10
|
+
function findTable(_appContext) {
|
|
11
|
+
let description = `
|
|
12
|
+
## find-table — locate a specific table in a CAS or SAS library
|
|
13
|
+
|
|
14
|
+
LLM Invocation Guidance (When to use)
|
|
15
|
+
Use THIS tool when the user wants to find or verify a table in a specific library:
|
|
16
|
+
- "find table iris in Public library in cas"
|
|
17
|
+
- "find table cars in sashelp in sas server"
|
|
18
|
+
- "does table customers exist in mylib?"
|
|
19
|
+
- "is there a table named sales in the Samples library?"
|
|
20
|
+
- "verify table orders exists in Public"
|
|
21
|
+
|
|
22
|
+
Do NOT use this tool when the user wants:
|
|
23
|
+
- find lib -> use find-library
|
|
24
|
+
- find model -> use find-model
|
|
25
|
+
- find job -> use find-job
|
|
26
|
+
- find jobdef -> use find-jobdef
|
|
27
|
+
- Columns or schema of a table (use table-info)
|
|
28
|
+
- Reading data from a table (use read-table)
|
|
29
|
+
- Listing all tables in a library (use list-tables)
|
|
30
|
+
|
|
31
|
+
Purpose
|
|
32
|
+
Locate a table contained in a specified library (caslib or libref) on a CAS or SAS server. Returns matching table names or empty array if not found.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
- lib (string, required): The library to search in (e.g., 'Public', 'sashelp', or a caslib name)
|
|
36
|
+
- name (string, required): Table name or substring to search for. Matching is case-insensitive.
|
|
37
|
+
- server (string, default 'cas'): Either 'cas' or 'sas'. Defaults to 'cas' when omitted.
|
|
38
|
+
|
|
39
|
+
Response Contract
|
|
40
|
+
Returns a JSON object with:
|
|
41
|
+
- tables: Array of matching table names (strings)
|
|
42
|
+
- Empty array { tables: [] } when no matches found
|
|
43
|
+
- Do not fabricate table names; only return actual matches
|
|
44
|
+
|
|
45
|
+
Disambiguation & Clarification
|
|
46
|
+
- Missing library: ask "Which library do you want to search in?"
|
|
47
|
+
- Missing table name: ask "Which table name would you like to find?"
|
|
48
|
+
- Server ambiguous: ask "Do you mean CAS or SAS?"
|
|
49
|
+
- If user wants multiple tables: suggest "Use list-tables to see all tables in a library"
|
|
50
|
+
|
|
51
|
+
Examples (→ mapped params)
|
|
52
|
+
- "find table iris in Public library in cas" → { lib: "Public", name: "iris", server: "cas" }
|
|
53
|
+
- "find table cars in sashelp in sas server" → { lib: "sashelp", name: "cars", server: "sas" }
|
|
54
|
+
- "does customers exist in mylib" → { lib: "mylib", name: "customers", server: "cas" }
|
|
55
|
+
- "verify table orders in Samples" → { lib: "Samples", name: "orders", server: "cas" }
|
|
56
|
+
|
|
57
|
+
Negative Examples (should NOT call find-table)
|
|
58
|
+
- "list tables in Public" (use list-tables instead)
|
|
59
|
+
- "find library Public" (use find-library instead)
|
|
60
|
+
- "what columns in cars table?" (use table-info instead)
|
|
61
|
+
- "read data from customers" (use read-table instead)
|
|
62
|
+
|
|
63
|
+
Usage Tips
|
|
64
|
+
- Use this tool to verify table existence before reading or querying
|
|
65
|
+
- For discovery of multiple tables, use list-tables instead
|
|
66
|
+
- After finding a table, use table-info for schema or read-table for data
|
|
67
|
+
|
|
68
|
+
Related Tools
|
|
69
|
+
- find-table → table-info → read-table (typical workflow)
|
|
70
|
+
- list-tables — to discover all tables in a library
|
|
71
|
+
- find-library — to verify library exists
|
|
72
|
+
- table-info — to inspect table structure after finding it
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
let spec = {
|
|
76
|
+
name: 'find-table',
|
|
77
|
+
aliases: ['findTable','find table','find_table'],
|
|
78
|
+
description: description,
|
|
79
|
+
schema: {
|
|
80
|
+
server: z.string().default('cas'), // default server is 'cas',
|
|
81
|
+
name: z.string(),
|
|
82
|
+
lib: z.string()
|
|
83
|
+
},
|
|
84
|
+
required: ['name', 'lib'],
|
|
85
|
+
handler: async (params) => {
|
|
86
|
+
// Check if the params.scenario is a string and parse it
|
|
87
|
+
let r = await _listTables(params);
|
|
88
|
+
return r;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return spec;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default findTable;
|