@sassoftware/sas-score-mcp-serverjs 1.0.1-12 → 1.0.1-14
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/.instructions/copilot-instructions.md +24 -2
- package/.skills/sas-find-resource-strategy/SKILL.md +29 -8
- package/.skills/sas-list-resource-strategy/SKILL.md +48 -5
- package/.skills/sas-read-and-score-strategy/SKILL.md +9 -12
- package/.skills/sas-read-strategy/SKILL.md +7 -0
- package/.skills/sas-request-classifier/SKILL.md +7 -6
- package/.skills/sas-score-workflow-strategy/SKILL.md +1 -0
- package/cli.js +2 -10
- package/package.json +4 -5
- package/src/toolSet/modelScore.js +8 -24
- package/src/toolSet/runJob.js +5 -25
- package/src/toolSet/runJobdef.js +5 -25
- package/src/toolSet/scrScore.js +60 -69
- /package/src/{handleGetDelete.js → handleGetDelete.txt} +0 -0
- /package/src/{handleRequest.js → handleRequest.txt} +0 -0
- /package/src/{hapiMcpServer.js → hapiMcpServer.txt} +0 -0
|
@@ -26,7 +26,7 @@ Before using SAS MCP tools, classify the request into one of these categories:
|
|
|
26
26
|
- SAS code or program analysis
|
|
27
27
|
- CAS data, caslibs, tables, or resources
|
|
28
28
|
- SAS data, librefs, tables, or resources
|
|
29
|
-
- MAS model, SAS job model, SAS jobdef model
|
|
29
|
+
- MAS model, SAS job model, SAS jobdef model, SCR model
|
|
30
30
|
- Score model / scoring artifact / scoring execution
|
|
31
31
|
- General SAS content or metadata discovery
|
|
32
32
|
- Authentication, connection, or environment issue
|
|
@@ -38,7 +38,7 @@ Before invoking MCP tools, decide whether one or more SAS skills should be used.
|
|
|
38
38
|
Prefer loading the most relevant SAS skill for the request category.
|
|
39
39
|
Use more than one skill only when the task clearly spans multiple domains, for example:
|
|
40
40
|
- CAS discovery + scoring
|
|
41
|
-
- model
|
|
41
|
+
- find model + score workflow
|
|
42
42
|
- content discovery + code analysis
|
|
43
43
|
|
|
44
44
|
Do not load unrelated skills.
|
|
@@ -131,6 +131,28 @@ See also: `./enforce-find-resource-strategy.md` for details and examples.
|
|
|
131
131
|
|
|
132
132
|
---
|
|
133
133
|
|
|
134
|
+
# Scoring workflow envforcement (MANDATORY)
|
|
135
|
+
|
|
136
|
+
For any request to score with a model (e.g., "score with model abc", "run model abc.jobdef", "score with model abc.mas", "score with model abc.scr"), you MUST follow this workflow:
|
|
137
|
+
Step 1. **Always use the sas-find-resource-strategy**
|
|
138
|
+
|
|
139
|
+
- For models of type MAS, job, or jobdef:
|
|
140
|
+
- Use the `sas-find-resource-strategy` skill to verify the model exists before scoring, except for SCR models where the model existence check is not required before scoring.
|
|
141
|
+
- Do not assume the model exists or skip this step, even if the model name is commonly used.
|
|
142
|
+
- If a table is used as data source for scoring, use the `sas-find-resource-strategy` skill to locate the table and determine which server (CAS or SAS) contains it.
|
|
143
|
+
- Do not assume the server or skip this step, even if the library or table is commonly found in one server.
|
|
144
|
+
- If the table exists in both, ask the user which server to use.
|
|
145
|
+
- If the table is not found, inform the user and do not attempt to read.
|
|
146
|
+
|
|
147
|
+
Step 2. **Only after confirming the table location:**
|
|
148
|
+
- Use the correct `sas-score-read-table` or `sas-score-sas-query` tool, specifying the server explicitly.
|
|
149
|
+
|
|
150
|
+
3. **If you see a direct read-table/tool call without a prior find step:**
|
|
151
|
+
- Correct the workflow and restart from the find resource step.
|
|
152
|
+
|
|
153
|
+
This workflow is mandatory for all score requests. If you are unsure, always perform the find resource step before scoring.
|
|
154
|
+
|
|
155
|
+
---
|
|
134
156
|
|
|
135
157
|
# Available Skills
|
|
136
158
|
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
name: sas-find-resource-strategy
|
|
3
3
|
description: >
|
|
4
4
|
Unified strategy for finding SAS Viya resources using find tools only. Supports library, table,
|
|
5
|
-
|
|
5
|
+
mas, job, and jobdef lookup with server-aware behavior for CAS/SAS resources. Treat the generic
|
|
6
|
+
term "model" as an alias that may refer to mas, job, or jobdef resources. Use this skill
|
|
6
7
|
whenever the task is to verify whether a resource exists.
|
|
7
8
|
---
|
|
8
9
|
|
|
@@ -15,10 +16,16 @@ Use this skill to verify resource existence without using list tools.
|
|
|
15
16
|
Supported resource types:
|
|
16
17
|
- library
|
|
17
18
|
- table
|
|
18
|
-
-
|
|
19
|
+
- mas
|
|
19
20
|
- job
|
|
20
21
|
- jobdef
|
|
21
22
|
|
|
23
|
+
Generic model aliases:
|
|
24
|
+
- `model` with no type defaults to `mas`
|
|
25
|
+
- `model mas` or `mas model` -> `mas`
|
|
26
|
+
- `model job` or `job model` -> `job`
|
|
27
|
+
- `model jobdef` or `jobdef model` -> `jobdef`
|
|
28
|
+
|
|
22
29
|
## Mandatory rule
|
|
23
30
|
|
|
24
31
|
- Use the sas-find-resource strategy to check for the existence of a resource before attempting to read or use it. This ensures that follow-up actions are based on accurate information about resource availability.
|
|
@@ -28,7 +35,7 @@ Supported resource types:
|
|
|
28
35
|
|
|
29
36
|
- Library -> `sas-score-find-library`
|
|
30
37
|
- Table -> `sas-score-find-table`
|
|
31
|
-
-
|
|
38
|
+
- MAS -> `sas-score-find-model`
|
|
32
39
|
- Job -> `sas-score-find-job`
|
|
33
40
|
- Jobdef -> `sas-score-find-jobdef`
|
|
34
41
|
|
|
@@ -60,14 +67,23 @@ If server is not provided:
|
|
|
60
67
|
2. If not found, check SAS (uppercase library) - `sas-score-find-table({ lib: "<LIB>", name: "<table>", server: "sas" })`
|
|
61
68
|
3. If found in both, report both and ask user which server to use for follow-up reads.
|
|
62
69
|
|
|
63
|
-
### Find
|
|
70
|
+
### Find models by type
|
|
64
71
|
|
|
65
|
-
|
|
66
|
-
- `
|
|
72
|
+
Support requests in either form:
|
|
73
|
+
- `find model <name>`
|
|
74
|
+
- `find model <type> <name>`
|
|
75
|
+
- `find <type> model <name>`
|
|
76
|
+
|
|
77
|
+
Type routing rules:
|
|
78
|
+
- If type is omitted, assume `mas`
|
|
79
|
+
- `mas` -> use `sas-score-find-model({ name: "<name>" })`
|
|
80
|
+
- `job` -> use `sas-score-find-job({ name: "<name>" })`
|
|
81
|
+
- `jobdef` -> use `sas-score-find-jobdef({ name: "<name>" })`
|
|
67
82
|
|
|
68
83
|
If a model type suffix is provided (for example `.mas`, `.job`, `.jobdef`, `.scr`):
|
|
69
84
|
- For MAS model discovery, strip suffix and look up base name with `sas-score-find-model`.
|
|
70
|
-
- For `.job` and `.jobdef`, use job and jobdef find tools instead of
|
|
85
|
+
- For `.job` and `.jobdef`, use job and jobdef find tools instead of MAS find.
|
|
86
|
+
- For `.scr`, do not use this strategy; route to SCR-specific model handling.
|
|
71
87
|
|
|
72
88
|
### Find job
|
|
73
89
|
|
|
@@ -84,7 +100,7 @@ Use:
|
|
|
84
100
|
Ask one focused question if required fields are missing:
|
|
85
101
|
- Library missing for table lookup: "Which library contains the table?"
|
|
86
102
|
- Table name missing: "Which table name should I check?"
|
|
87
|
-
- Resource type ambiguous: "Do you want me to find a library, table,
|
|
103
|
+
- Resource type ambiguous: "Do you want me to find a library, table, mas, job, or jobdef resource?"
|
|
88
104
|
|
|
89
105
|
## Output format
|
|
90
106
|
|
|
@@ -101,5 +117,10 @@ For not found resources:
|
|
|
101
117
|
- "find library Samples" -> CAS first, then SAS fallback using uppercase.
|
|
102
118
|
- "find table costchange in Samples" -> CAS first, then SAS fallback using uppercase library.
|
|
103
119
|
- "find model breastcancer" -> `sas-score-find-model`.
|
|
120
|
+
- "find model mas breastcancer" -> `sas-score-find-model`.
|
|
121
|
+
- "find mas model breastcancer" -> `sas-score-find-model`.
|
|
122
|
+
- "find model job simplejob" -> `sas-score-find-job`.
|
|
123
|
+
- "find job model simplejob" -> `sas-score-find-job`.
|
|
124
|
+
- "find model jobdef daily_run" -> `sas-score-find-jobdef`.
|
|
104
125
|
- "find job simplejob" -> `sas-score-find-job`.
|
|
105
126
|
- "find jobdef daily_run" -> `sas-score-find-jobdef`.
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
name: sas-list-resource-strategy
|
|
3
3
|
description: >
|
|
4
4
|
Unified strategy for listing SAS Viya resources using list tools. Supports libraries,
|
|
5
|
-
tables,
|
|
5
|
+
tables, mas resources, jobs, and jobdefs. Treat the generic term "model" as an alias that may
|
|
6
|
+
refer to mas, job, or jobdef resources. Always normalize pagination so start and limit are
|
|
6
7
|
never null: default start=1 and limit=10 when not provided.
|
|
7
8
|
---
|
|
8
9
|
|
|
@@ -15,10 +16,17 @@ Use this skill to list resources with consistent pagination defaults.
|
|
|
15
16
|
Supported resource types:
|
|
16
17
|
- library
|
|
17
18
|
- table
|
|
18
|
-
-
|
|
19
|
+
- mas
|
|
19
20
|
- job
|
|
20
21
|
- jobdef
|
|
21
22
|
|
|
23
|
+
Generic model aliases:
|
|
24
|
+
- `model` with no type defaults to `mas`
|
|
25
|
+
- `model mas` or `mas model` -> `mas`
|
|
26
|
+
- `model job` or `job model` -> `job`
|
|
27
|
+
- `model jobdef` or `jobdef model` -> `jobdef`
|
|
28
|
+
|
|
29
|
+
|
|
22
30
|
## Mandatory pagination rule
|
|
23
31
|
|
|
24
32
|
For every list operation:
|
|
@@ -37,7 +45,7 @@ limit = (limit == null) ? 10 : limit
|
|
|
37
45
|
|
|
38
46
|
- Library -> `sas-score-list-libraries`
|
|
39
47
|
- Table -> `sas-score-list-table`
|
|
40
|
-
-
|
|
48
|
+
- MAS -> `sas-score-list-models`
|
|
41
49
|
- Job -> `sas-score-list-jobs`
|
|
42
50
|
- Jobdef -> `sas-score-list-jobdefs`
|
|
43
51
|
|
|
@@ -75,7 +83,19 @@ If `lib` is missing, ask: "Which library should I list tables from?"
|
|
|
75
83
|
|
|
76
84
|
### List models
|
|
77
85
|
|
|
78
|
-
|
|
86
|
+
Support requests in either form:
|
|
87
|
+
- `list model`
|
|
88
|
+
- `list models`
|
|
89
|
+
- `list model <type>`
|
|
90
|
+
- `list <type> model`
|
|
91
|
+
|
|
92
|
+
Type routing rules:
|
|
93
|
+
- If type is omitted, assume `mas`
|
|
94
|
+
- `mas` -> use `sas-score-list-models`
|
|
95
|
+
- `job` -> use `sas-score-list-jobs`
|
|
96
|
+
- `jobdef` -> use `sas-score-list-jobdefs`
|
|
97
|
+
|
|
98
|
+
Use for `mas`:
|
|
79
99
|
```
|
|
80
100
|
sas-score-list-models({
|
|
81
101
|
start: 1,
|
|
@@ -83,6 +103,22 @@ sas-score-list-models({
|
|
|
83
103
|
})
|
|
84
104
|
```
|
|
85
105
|
|
|
106
|
+
Use for `job`:
|
|
107
|
+
```
|
|
108
|
+
sas-score-list-jobs({
|
|
109
|
+
start: 1,
|
|
110
|
+
limit: 10
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Use for `jobdef`:
|
|
115
|
+
```
|
|
116
|
+
sas-score-list-jobdefs({
|
|
117
|
+
start: 1,
|
|
118
|
+
limit: 10
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
86
122
|
### List jobs
|
|
87
123
|
|
|
88
124
|
Use:
|
|
@@ -106,7 +142,7 @@ sas-score-list-jobdefs({
|
|
|
106
142
|
## Clarifying prompts
|
|
107
143
|
|
|
108
144
|
Ask one focused question if required context is missing:
|
|
109
|
-
- Resource type unclear: "Do you want libraries, tables,
|
|
145
|
+
- Resource type unclear: "Do you want libraries, tables, mas resources, jobs, or jobdefs?"
|
|
110
146
|
- Listing tables without library: "Which library should I list tables from?"
|
|
111
147
|
|
|
112
148
|
## Output and paging
|
|
@@ -118,7 +154,14 @@ Ask one focused question if required context is missing:
|
|
|
118
154
|
## Examples
|
|
119
155
|
|
|
120
156
|
- "list jobs" -> `sas-score-list-jobs({ start: 1, limit: 10 })`
|
|
157
|
+
- "list model" -> `sas-score-list-models({ start: 1, limit: 10 })`
|
|
121
158
|
- "list models" -> `sas-score-list-models({ start: 1, limit: 10 })`
|
|
159
|
+
- "list model mas" -> `sas-score-list-models({ start: 1, limit: 10 })`
|
|
160
|
+
- "list mas model" -> `sas-score-list-models({ start: 1, limit: 10 })`
|
|
161
|
+
- "list model job" -> `sas-score-list-jobs({ start: 1, limit: 10 })`
|
|
162
|
+
- "list job model" -> `sas-score-list-jobs({ start: 1, limit: 10 })`
|
|
163
|
+
- "list model jobdef" -> `sas-score-list-jobdefs({ start: 1, limit: 10 })`
|
|
164
|
+
- "list jobdef model" -> `sas-score-list-jobdefs({ start: 1, limit: 10 })`
|
|
122
165
|
- "list jobdefs" -> `sas-score-list-jobdefs({ start: 1, limit: 10 })`
|
|
123
166
|
- "list tables in Public" -> `sas-score-list-tables({ lib: "Public", start: 1, limit: 10 })`
|
|
124
167
|
- "next jobs" after `{ start: 1, limit: 10 }` -> `sas-score-list-jobs({ start: 11, limit: 10 })`
|
|
@@ -19,11 +19,10 @@ with a deployed MAS model.
|
|
|
19
19
|
|
|
20
20
|
## Pre-flight verification
|
|
21
21
|
|
|
22
|
-
**Before attempting to read or score table
|
|
23
|
-
1. **Verify
|
|
24
|
-
2. **Verify
|
|
25
|
-
3. **
|
|
26
|
-
4. **Confirm server location**: Ensure you know which server (CAS or SAS) contains the data
|
|
22
|
+
**Before attempting to read, query, or score a table:**
|
|
23
|
+
1. **Verify table exists**: Use `sas-find-resource-strategy` to confirm the library and table exist before trying to read
|
|
24
|
+
2. **Verify model exists**: Use `sas-find-resource-strategy` to confirm the model exists before scoring, except for SCR models
|
|
25
|
+
3. **Confirm server location**: Ensure you know which server (CAS or SAS) contains the data
|
|
27
26
|
|
|
28
27
|
This ensures consistent behavior with other data access operations.
|
|
29
28
|
|
|
@@ -42,8 +41,8 @@ The typical flow involves:
|
|
|
42
41
|
## Scenario: User already has data
|
|
43
42
|
|
|
44
43
|
If the user provides scenario data directly (e.g., "Score age=45, income=60000 with model X"):
|
|
45
|
-
- Extract the scenario values
|
|
46
|
-
- Validate against model's input schema
|
|
44
|
+
- Extract the scenario values and format it as a json object(ex: a=10,b=20 → {"a":10,"b":20})
|
|
45
|
+
- Validate against model's input schema if model is mas or scr.
|
|
47
46
|
- Invoke scoring
|
|
48
47
|
- Return prediction
|
|
49
48
|
|
|
@@ -52,8 +51,8 @@ If the user provides scenario data directly (e.g., "Score age=45, income=60000 w
|
|
|
52
51
|
## Scenario: User wants to score table rows
|
|
53
52
|
|
|
54
53
|
If the user specifies a table (e.g., "Score all customers in Public.customers with model X"):
|
|
54
|
+
- Verify table exists using `sas-find-resource-strategy`
|
|
55
55
|
- Fetch raw rows (possibly filtered: "where status='active'")
|
|
56
|
-
- Validate model compatibility with input columns
|
|
57
56
|
- Invoke scoring on each row
|
|
58
57
|
- Merge results with original data
|
|
59
58
|
- Display combined table
|
|
@@ -64,7 +63,6 @@ If the user specifies a table (e.g., "Score all customers in Public.customers wi
|
|
|
64
63
|
|
|
65
64
|
If the user wants to score aggregated/filtered results (e.g., "Score high-value customers (spend > 5000) with model X"):
|
|
66
65
|
- Determine which records meet criteria (aggregation/filtering)
|
|
67
|
-
- Validate model expects these input columns
|
|
68
66
|
- Invoke scoring
|
|
69
67
|
- Merge predictions with summary data
|
|
70
68
|
- Display results
|
|
@@ -83,9 +81,8 @@ If the user specifies a model name that's new/unknown:
|
|
|
83
81
|
|
|
84
82
|
## Rules
|
|
85
83
|
|
|
86
|
-
- Always validate table
|
|
84
|
+
- Always validate table existence before attempting to read
|
|
87
85
|
- Always check model exists before invoking `sas-score-model-score`
|
|
88
|
-
- Match table columns to model input variables; warn on mismatch
|
|
89
86
|
- Use find tools only for resource existence checks; do not use list tools to find resources
|
|
90
87
|
- If multiple records: score batch if possible; fall back to row-by-row
|
|
91
88
|
- Merge predictions with original data using row index or key column
|
|
@@ -108,6 +105,6 @@ If the user specifies a model name that's new/unknown:
|
|
|
108
105
|
|
|
109
106
|
## Integration with other skills
|
|
110
107
|
|
|
111
|
-
- **Before this workflow**: Use `sas-find-resource-strategy` to verify
|
|
108
|
+
- **Before this workflow**: Use `sas-find-resource-strategy` to verify table/model resources exist
|
|
112
109
|
- **For data retrieval**: Use `sas-read-strategy` to choose the right read tool (read-table vs sas-query)
|
|
113
110
|
- **For scoring**: Use `sas-score-workflow-strategy` for advanced scoring options beyond MAS models
|
|
@@ -20,6 +20,7 @@ Before retrieving data, locate the specific table first and determine which serv
|
|
|
20
20
|
**Step 1: Locate the specific table (table-first)**
|
|
21
21
|
- Use `sas-find-resource-strategy` to check if the table exists in CAS first, then SAS if needed
|
|
22
22
|
- Do not perform a separate library-first lookup; table lookup already handles server-aware discovery
|
|
23
|
+
- Do not use list tools for existence checks; rely on find tools to determine if the resource exists and where it exists.
|
|
23
24
|
- Possible outcomes:
|
|
24
25
|
- If table exists **only in CAS** → set `server: "cas"`
|
|
25
26
|
- If table exists **only in SAS** → set `server: "sas"`
|
|
@@ -49,7 +50,10 @@ Ask yourself: does the user already have the data in hand?
|
|
|
49
50
|
- Simple WHERE filtering (e.g., "where status = 'active'")
|
|
50
51
|
- Pagination needed ("first 50 rows", "next 10 rows")
|
|
51
52
|
|
|
53
|
+
|
|
52
54
|
**How:**
|
|
55
|
+
|
|
56
|
+
Step 1: Determine
|
|
53
57
|
```
|
|
54
58
|
sas-score-read-table({
|
|
55
59
|
table: "tablename",
|
|
@@ -80,6 +84,9 @@ sas-score-read-table({
|
|
|
80
84
|
- User's question is phrased analytically ("compare", "analyze", "breakdown", "trend")
|
|
81
85
|
|
|
82
86
|
**How:**
|
|
87
|
+
1. Use `sas-find-resource-strategy` to check if the table exists in CAS first, then SAS if needed
|
|
88
|
+
2. If table does not exist inform the user and suggest verifying the table name
|
|
89
|
+
3. If table exists run the sas-query tool with the appropriate server and a generated SQL statement based on the user's natural language question. For example:
|
|
83
90
|
```
|
|
84
91
|
sas-query({
|
|
85
92
|
table: "lib.tablename",
|
|
@@ -10,6 +10,7 @@ Use this skill to determine what kind of SAS object, workflow, or environment th
|
|
|
10
10
|
## When to use
|
|
11
11
|
Use this skill when the request contains ambiguous domain terms such as:
|
|
12
12
|
- model
|
|
13
|
+
- mas
|
|
13
14
|
- score
|
|
14
15
|
- scoring
|
|
15
16
|
- read
|
|
@@ -29,8 +30,8 @@ Map the user request to the most likely SAS domain and hand off to the correct d
|
|
|
29
30
|
|
|
30
31
|
## Classification targets
|
|
31
32
|
Classify the request into one or more of these categories:
|
|
32
|
-
- Resource existence lookup (library/table/
|
|
33
|
-
- Resource listing (library/table/
|
|
33
|
+
- Resource existence lookup (library/table/mas/job/jobdef, with generic "model" phrasing allowed) -> Route to **sas-find-resource-strategy**
|
|
34
|
+
- Resource listing (library/table/mas/job/jobdef, with generic "model" phrasing allowed) -> Route to **sas-list-resource-strategy**
|
|
34
35
|
- Reading or querying tables → Route to **sas-read-strategy**
|
|
35
36
|
- CAS resource, SAS resource, caslib, or table discovery → Route to **sas-find-library-smart**
|
|
36
37
|
- SAS data, libref, or table discovery → Route to **sas-find-library-smart**
|
|
@@ -47,10 +48,10 @@ Classify the request into one or more of these categories:
|
|
|
47
48
|
3. Decide the most likely SAS domain and matching skill.
|
|
48
49
|
4. If confidence is low, ask one focused clarifying question.
|
|
49
50
|
5. If confidence is high, load and use the relevant skill:
|
|
50
|
-
- **sas-find-resource-strategy** — Unified find-only strategy for library/table/
|
|
51
|
-
- **sas-list-resource-strategy** — Unified list strategy for library/table/
|
|
51
|
+
- **sas-find-resource-strategy** — Unified find-only strategy for library/table/mas/job/jobdef, where "model" may mean mas, job, or jobdef
|
|
52
|
+
- **sas-list-resource-strategy** — Unified list strategy for library/table/mas/job/jobdef, where "model" may mean mas, job, or jobdef, with non-null pagination defaults
|
|
52
53
|
- **sas-find-library-smart** — Find CAS or SAS libraries
|
|
53
|
-
- **sas-list-tables-smart** —
|
|
54
|
+
- **sas-list-tables-smart** — List tables in a library
|
|
54
55
|
- **sas-read-strategy** — Choose read-table vs. sas-query for data retrieval
|
|
55
56
|
- **sas-read-and-score-strategy** — Combine data reading with model scoring
|
|
56
57
|
- **sas-score-workflow-strategy** — Route scoring requests to correct execution engine
|
|
@@ -58,7 +59,7 @@ Classify the request into one or more of these categories:
|
|
|
58
59
|
|
|
59
60
|
## Disambiguation hints
|
|
60
61
|
- "Run" often implies job execution, but may also mean scoring or model invocation. Check for "score" or "model" context.
|
|
61
|
-
- "Model" may refer to MAS
|
|
62
|
+
- "Model" is a generic term that may refer to MAS, SCR,job, or jobdef resources in find/list requests. Prefer explicit patterns like `model <type>` or `<type> model`; default bare "model" to MAS unless the surrounding request implies job or jobdef.
|
|
62
63
|
- "Score" may refer to model scoring or job execution. Look for model name or context.
|
|
63
64
|
- "Table" usually suggests CAS or SAS but confirm library name and server context.
|
|
64
65
|
- "Find" — resource lookup. Route to sas-find-resource-strategy.
|
|
@@ -22,6 +22,7 @@ Users can invoke scoring with a unified syntax that automatically routes to the
|
|
|
22
22
|
```
|
|
23
23
|
score with model <name>.<type> [scenario =<key=value pairs>]
|
|
24
24
|
score <name>.<type> [scenario =<key=value pairs>]
|
|
25
|
+
score key=value,... with model <name>.<type>
|
|
25
26
|
```
|
|
26
27
|
|
|
27
28
|
**Type determines the routing:**
|
package/cli.js
CHANGED
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
import coreSSE from './src/coreSSE.js';
|
|
11
11
|
import expressMcpServer from './src/expressMcpServer.js';
|
|
12
|
-
import hapiMcpServer from './src/hapiMcpServer.js';
|
|
13
|
-
|
|
14
12
|
import createMcpServer from './src/createMcpServer.js';
|
|
15
13
|
// import dotenvExpand from 'dotenv-expand';
|
|
16
14
|
import fs from 'fs';
|
|
@@ -468,14 +466,8 @@ if (mcpType === 'stdio') {
|
|
|
468
466
|
|
|
469
467
|
} else {
|
|
470
468
|
console.error('[Note] Starting HTTP MCP server...');
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
await hapiMcpServer(mcpServer, sessionCache, appEnvBase);
|
|
474
|
-
console.error('[Note] Using HAPI HTTP server...')
|
|
475
|
-
} else {
|
|
476
|
-
await expressMcpServer(mcpServer, sessionCache, appEnvBase);
|
|
477
|
-
console.error('[Note] MCP HTTP express server started on port ' + appEnvBase.PORT);
|
|
478
|
-
}
|
|
469
|
+
await expressMcpServer(mcpServer, sessionCache, appEnvBase);
|
|
470
|
+
console.error('[Note] MCP HTTP express server started on port ' + appEnvBase.PORT);
|
|
479
471
|
}
|
|
480
472
|
|
|
481
473
|
// custom reader for .env file to avoid dotenv logging to console
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sassoftware/sas-score-mcp-serverjs",
|
|
3
|
-
"version": "1.0.1-
|
|
3
|
+
"version": "1.0.1-14",
|
|
4
4
|
"description": "A mcp server for SAS Viya",
|
|
5
5
|
"author": "Deva Kumar <deva.kumar@sas.com>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -51,8 +51,7 @@
|
|
|
51
51
|
"@sassoftware/restaf": "^5.7.2",
|
|
52
52
|
"@sassoftware/restafedit": "^3.10.5",
|
|
53
53
|
"@sassoftware/restaflib": "^5.7.2",
|
|
54
|
-
"
|
|
55
|
-
"axios": "^1.13.2",
|
|
54
|
+
"axios": "^1.13.5",
|
|
56
55
|
"body-parser": "^2.2.1",
|
|
57
56
|
"cors": "^2.8.5",
|
|
58
57
|
"cross-env": "^10.1.0",
|
|
@@ -64,8 +63,8 @@
|
|
|
64
63
|
"node-cache": "^5.1.2",
|
|
65
64
|
"open": "^11.0.0",
|
|
66
65
|
"selfsigned": "^5.2.0",
|
|
67
|
-
"undici": "^7.
|
|
68
|
-
"uuid": "^
|
|
66
|
+
"undici": "^7.24.0",
|
|
67
|
+
"uuid": "^14.0.0",
|
|
69
68
|
"zod": "^4.2.1"
|
|
70
69
|
},
|
|
71
70
|
"devDependencies": {
|
|
@@ -17,7 +17,7 @@ DO NOT USE for: find model, model metadata, list models, run programs/jobs, quer
|
|
|
17
17
|
|
|
18
18
|
PARAMETERS
|
|
19
19
|
- model: string — model name (required, exact match)
|
|
20
|
-
- scenario:
|
|
20
|
+
- scenario: object — input data as JSON (optional, defaults to {}). Example: {age:45, income:60000}
|
|
21
21
|
- uflag: boolean (default: false) — prefix model fields with underscore when true
|
|
22
22
|
|
|
23
23
|
ROUTING RULES
|
|
@@ -42,34 +42,18 @@ Returns predictions, probabilities, scores merged with input data. Returns error
|
|
|
42
42
|
let spec = {
|
|
43
43
|
name: 'mas-score',
|
|
44
44
|
description: description,
|
|
45
|
-
inputSchema:z.object({
|
|
45
|
+
inputSchema: z.object({
|
|
46
46
|
model: z.string(),
|
|
47
|
-
scenario: z.string(),
|
|
47
|
+
scenario: z.union([z.record(z.any()), z.string()]).optional(),
|
|
48
48
|
uflag: z.boolean().optional()
|
|
49
49
|
}),
|
|
50
50
|
|
|
51
51
|
handler: async (iparams) => {
|
|
52
|
-
let params = {...iparams};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
let scenarioObj ={};
|
|
58
|
-
let count = 0;
|
|
59
|
-
if (typeof scenario === 'object') {
|
|
60
|
-
scenarioObj = scenario;
|
|
61
|
-
} else if (Array.isArray(scenario)) {
|
|
62
|
-
scenarioObj = scenario[0];
|
|
63
|
-
} else {
|
|
64
|
-
//console.error('Incoming scenario', scenario);
|
|
65
|
-
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
66
|
-
let [key, value] = pair.split('=');
|
|
67
|
-
acc[key.trim()] = value;
|
|
68
|
-
count++;
|
|
69
|
-
return acc;
|
|
70
|
-
}, {});
|
|
71
|
-
}
|
|
72
|
-
params.scenario= scenarioObj;
|
|
52
|
+
let params = {...iparams};
|
|
53
|
+
if (typeof params.scenario === 'string') {
|
|
54
|
+
try { params.scenario = JSON.parse(params.scenario); } catch { params.scenario = {}; }
|
|
55
|
+
}
|
|
56
|
+
params.scenario = (params.scenario != null && typeof params.scenario === 'object') ? params.scenario : {};
|
|
73
57
|
|
|
74
58
|
// Drop model extension (e.g., .job, .model)
|
|
75
59
|
if (params.model && params.model.includes('.')) {
|
package/src/toolSet/runJob.js
CHANGED
|
@@ -16,7 +16,7 @@ DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro)
|
|
|
16
16
|
|
|
17
17
|
PARAMETERS
|
|
18
18
|
- name: string — job name (required)
|
|
19
|
-
- scenario:
|
|
19
|
+
- scenario: object — input parameters as JSON (optional, defaults to {}). Example: {month:10, year:2025}
|
|
20
20
|
|
|
21
21
|
ROUTING RULES
|
|
22
22
|
- "run job xyz" → { name: "xyz" }
|
|
@@ -41,34 +41,14 @@ Returns log output, listings, tables from job. Error if job not found.
|
|
|
41
41
|
description: description,
|
|
42
42
|
inputSchema: z.object({
|
|
43
43
|
name: z.string(),
|
|
44
|
-
scenario: z.string().optional()
|
|
44
|
+
scenario: z.union([z.record(z.any()), z.string()]).optional()
|
|
45
45
|
}),
|
|
46
46
|
handler: async (params) => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let count = 0;
|
|
50
|
-
//
|
|
51
|
-
if (scenario == null) {
|
|
52
|
-
scenarioObj = {};
|
|
53
|
-
} else if (typeof scenario === 'object') {
|
|
54
|
-
scenarioObj = scenario;
|
|
55
|
-
} else if (Array.isArray(scenario)) {
|
|
56
|
-
scenarioObj = scenario[0];
|
|
57
|
-
} else if (typeof scenario === 'string') {
|
|
58
|
-
if (scenario.trim() === '') {
|
|
59
|
-
scenarioObj = {};
|
|
60
|
-
} else {
|
|
61
|
-
// console.error('Incoming scenario', scenario);
|
|
62
|
-
scenarioObj = scenario.split(',').reduce((acc, pair) => {
|
|
63
|
-
let [key, value] = pair.split('=');
|
|
64
|
-
acc[key.trim()] = value;
|
|
65
|
-
count++;
|
|
66
|
-
return acc;
|
|
67
|
-
}, {});
|
|
68
|
-
}
|
|
47
|
+
if (typeof params.scenario === 'string') {
|
|
48
|
+
try { params.scenario = JSON.parse(params.scenario); } catch { params.scenario = {}; }
|
|
69
49
|
}
|
|
50
|
+
params.scenario = (params.scenario != null && typeof params.scenario === 'object') ? params.scenario : {};
|
|
70
51
|
params.type = 'job';
|
|
71
|
-
params.scenario = scenarioObj;
|
|
72
52
|
// Provide runtime context for auth and server settings
|
|
73
53
|
let r = await _jobSubmit(params);
|
|
74
54
|
return r;
|
package/src/toolSet/runJobdef.js
CHANGED
|
@@ -17,7 +17,7 @@ DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro)
|
|
|
17
17
|
|
|
18
18
|
PARAMETERS
|
|
19
19
|
- name: string — jobdef name (required)
|
|
20
|
-
- scenario:
|
|
20
|
+
- scenario: object — input parameters as JSON (optional, defaults to {}). Example: {month:10, year:2025}
|
|
21
21
|
|
|
22
22
|
ROUTING RULES
|
|
23
23
|
- "run jobdef xyz" → { name: "xyz" }
|
|
@@ -42,34 +42,14 @@ Returns log output, listings, tables from jobdef. Error if jobdef not found.
|
|
|
42
42
|
description: description,
|
|
43
43
|
inputSchema: z.object({
|
|
44
44
|
name: z.string(),
|
|
45
|
-
scenario: z.string().optional()
|
|
45
|
+
scenario: z.union([z.record(z.any()), z.string()]).optional()
|
|
46
46
|
}),
|
|
47
47
|
handler: async (params) => {
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
}
|
|
48
|
+
if (typeof params.scenario === 'string') {
|
|
49
|
+
try { params.scenario = JSON.parse(params.scenario); } catch { params.scenario = {}; }
|
|
70
50
|
}
|
|
51
|
+
params.scenario = (params.scenario != null && typeof params.scenario === 'object') ? params.scenario : {};
|
|
71
52
|
params.type = 'def';
|
|
72
|
-
params.scenario = scenarioObj;
|
|
73
53
|
// Provide runtime context for auth and server settings
|
|
74
54
|
let r = await _jobSubmit(params);
|
|
75
55
|
return r;
|
package/src/toolSet/scrScore.js
CHANGED
|
@@ -1,70 +1,61 @@
|
|
|
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 (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
let r = await _scrScore({ url: url, scenario: scenario , _appContext: _appContext});
|
|
63
|
-
return r;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return spec;
|
|
68
|
-
}
|
|
69
|
-
|
|
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 (object, optional): Input values to score as a JSON object (e.g. {age:45, income:60000}). If omitted, defaults to {} and the tool will return the model's input variable definitions.
|
|
19
|
+
|
|
20
|
+
What it returns
|
|
21
|
+
- When scoring: the SCR endpoint response (predictions, probabilities, scores) merged with or alongside the supplied inputs.
|
|
22
|
+
- When \`scenario\` is omitted: metadata describing the model's input variables (names, types, required/optional).
|
|
23
|
+
|
|
24
|
+
Usage notes
|
|
25
|
+
- Run \`scr-info\` first to inspect the expected input variables and types.
|
|
26
|
+
- Prefer structured objects for numeric/date values to avoid type ambiguity; the simple string parser keeps values as strings.
|
|
27
|
+
- Ensure network connectivity and any required credentials for the target SCR service.
|
|
28
|
+
|
|
29
|
+
Examples
|
|
30
|
+
- scrScore with url="loan" and scenario={age:45, income:60000}
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
let spec = {
|
|
34
|
+
name: 'scr-score',
|
|
35
|
+
description: description,
|
|
36
|
+
inputSchema: z.object({
|
|
37
|
+
url: z.string(),
|
|
38
|
+
scenario: z.union([z.record(z.any()), z.string()]).optional()
|
|
39
|
+
}),
|
|
40
|
+
|
|
41
|
+
handler: async (params) => {
|
|
42
|
+
let {url, _appContext} = params;
|
|
43
|
+
let scenario = params.scenario;
|
|
44
|
+
if (typeof scenario === 'string') {
|
|
45
|
+
try { scenario = JSON.parse(scenario); } catch { scenario = {}; }
|
|
46
|
+
}
|
|
47
|
+
if (url === null) {
|
|
48
|
+
return { status: { statusCode: 2, msg: `SCR model ${url} was not specified` }, results: {} };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const scenarioObj = (scenario != null && typeof scenario === 'object') ? scenario : {};
|
|
52
|
+
|
|
53
|
+
let r = await _scrScore({ url: url, scenario: scenarioObj, _appContext: _appContext});
|
|
54
|
+
return r;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return spec;
|
|
59
|
+
}
|
|
60
|
+
|
|
70
61
|
export default scrScore;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|