@sassoftware/sas-score-mcp-serverjs 0.4.1-19 → 0.4.1-21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sassoftware/sas-score-mcp-serverjs",
3
- "version": "0.4.1-19",
3
+ "version": "0.4.1-21",
4
4
  "description": "A mcp server for SAS Viya",
5
5
  "author": "Deva Kumar <deva.kumar@sas.com>",
6
6
  "license": "Apache-2.0",
@@ -43,7 +43,8 @@
43
43
  "openApi.json",
44
44
  "openApi.yaml",
45
45
  "skills",
46
- "scripts"
46
+ "scripts",
47
+ ".claude"
47
48
  ],
48
49
  "dependencies": {
49
50
  "@modelcontextprotocol/sdk": "^1.29.0",
@@ -78,6 +78,23 @@ ELSE
78
78
 
79
79
  ---
80
80
 
81
+ ## Table Reference Format
82
+
83
+ When users follow up to find or work with tables, they may provide a reference in dot notation (e.g., "find table cars in Public" or "maps.india").
84
+
85
+ **Always parse table references as: `lib.table`**
86
+ - First part (before dot) = **library name** → `lib` parameter
87
+ - Second part (after dot) = **table name** → `name` or `table` parameter
88
+
89
+ **Examples:**
90
+ - "maps.india" → `lib: "maps"`, `name: "india"`
91
+ - "Public.customers" → `lib: "Public"`, `name: "customers"`
92
+ - "sashelp.cars" → `lib: "sashelp"`, `name: "cars"`
93
+
94
+ This rule ensures consistent parsing when users reference tables in subsequent operations after finding a library.
95
+
96
+ ---
97
+
81
98
  ## Common patterns
82
99
 
83
100
  **Pattern 1 — Find library, server unspecified**
@@ -36,6 +36,21 @@ Use `sas-score-find-table` with intelligent server detection:
36
36
 
37
37
  ---
38
38
 
39
+ ## Table Reference Format
40
+
41
+ When a user provides a table reference in dot notation (e.g., "read maps.india" or "find table cars.Public"):
42
+
43
+ **Always parse as: `lib.table`**
44
+ - First part (before dot) = **library name** → `lib` parameter
45
+ - Second part (after dot) = **table name** → `name` or `table` parameter
46
+
47
+ **Examples:**
48
+ - "maps.india" → `lib: "maps"`, `name: "india"`
49
+ - "Public.customers" → `lib: "Public"`, `name: "customers"`
50
+ - "sashelp.cars" → `lib: "sashelp"`, `name: "cars"`
51
+
52
+ ---
53
+
39
54
  ## Determine the read strategy
40
55
 
41
56
  Ask yourself: does the user already have the data in hand?
@@ -69,15 +84,18 @@ sas-score-find-table({
69
84
  sas-score-read-table({
70
85
  table: "tablename",
71
86
  lib: "libraryname",
72
- server: "cas" or "sas", // determined from sas-score-find-table check
73
- limit: N, // default 10, adjust based on user request
87
+ server: "cas" or "sas", // REQUIRED: determined from sas-score-find-table check
88
+ start: 1, // REQUIRED: 1-based row offset (default: 1)
89
+ limit: N, // REQUIRED: max rows to retrieve (default: 10, max: 1000)
74
90
  where: "..." // optional SQL WHERE clause
75
91
  })
76
92
  ```
77
93
 
78
94
  **Rules:**
79
95
  - Always determine the server first using `sas-score-find-table` with smart detection (CAS → SAS)
80
- - Keep batch size 50 rows unless the user explicitly requests more
96
+ - **ALWAYS set `server` parameter** never omit it; use the result from `sas-score-find-table` to determine "cas" or "sas"
97
+ - **ALWAYS set `start` parameter** — use 1 for the first call, or the offset for pagination
98
+ - **ALWAYS set `limit` parameter** — keep batch size ≤ 50 rows unless the user explicitly requests more; cap at 1000
81
99
  - If table name is missing, ask: *"Which table should I read from? (format: lib.tablename)"*
82
100
  - If library is missing, ask: *"Which library contains the table?"*
83
101
  - If table exists in both servers, prefer CAS (already determined by smart detection)
@@ -124,12 +142,12 @@ sas-query({
124
142
  **Pattern A — Raw row retrieval**
125
143
  > "Show me the first 5 rows from Public.customers"
126
144
 
127
- → `sas-score-read-table({ table: "customers", lib: "Public", limit: 5 })`
145
+ → `sas-score-read-table({ table: "customers", lib: "Public", server: "cas", start: 1, limit: 5 })`
128
146
 
129
147
  **Pattern B — Filtered retrieval**
130
148
  > "Get all high-value orders (amount > 5000) from mylib.orders"
131
149
 
132
- → `sas-score-read-table({ table: "orders", lib: "mylib", where: "amount > 5000" })`
150
+ → `sas-score-read-table({ table: "orders", lib: "mylib", server: "sas", start: 1, limit: 50, where: "amount > 5000" })`
133
151
 
134
152
  **Pattern C — Aggregation**
135
153
  > "What is the average price by make in Public.cars?"
@@ -1,11 +1,11 @@
1
- gi ---
1
+ ---
2
2
  name: sas-score-workflow
3
3
  description: >
4
- Guide the full model scoring workflow: validate model familiarity, route to appropriate scoring tool
5
- based on model type, invoke scoring with scenario data, and present merged results. Use this skill
6
- when the user wants to run predictions on data (already fetched or user-supplied). Supports generic
7
- syntax: "score with model <name>.<type> scenario =<params>" where type is job|jobdef|mas|scr|sas.
8
- Trigger phrases: "score these records", "predict using model", "run model on", "score with model X.mas".
4
+ MANDATORY routing logic for all scoring requests. Extract model.type suffix, route to correct tool
5
+ (run-job|run-jobdef|model-score|scr-score|run-program). Complete routing decision BEFORE invoking
6
+ any tool. Handles both MAS models and alternative scoring engines (jobs, jobdefs, SCR, SAS programs).
7
+ Trigger phrases: "score with model X.job", "score X.jobdef scenario", "score with model X.mas",
8
+ "score with model X.scr", any request with "score" + model name containing a dot (.) + type suffix.
9
9
  ---
10
10
 
11
11
  # SAS Score Workflow
@@ -35,6 +35,91 @@ If no type is specified (bare model name), assume `.mas` (MAS model).
35
35
 
36
36
  ---
37
37
 
38
+ ## ⚠️ MANDATORY: ROUTING DECISION (DO THIS FIRST)
39
+
40
+ **CRITICAL: This decision must be completed BEFORE invoking ANY scoring tool.**
41
+
42
+ When a user requests scoring with a model name (e.g., `score a=10,b=20 with model simplejob.job`):
43
+
44
+ ### Step 1: Extract the Type Suffix
45
+ Check if the model name contains a dot:
46
+ - **YES** (e.g., `simplejob.job`) → Split on the last dot to extract type: `job`
47
+ - **NO** (e.g., `churn`) → Assume type is `.mas` (default MAS model)
48
+
49
+ ### Step 2: Validate the Type
50
+ Confirm the extracted type is one of: `job`, `jobdef`, `mas`, `scr`, `sas`
51
+ - **VALID** → Proceed to Step 3
52
+ - **INVALID** (e.g., `.xyz`) → Assume `.mas` and treat entire input as model name
53
+
54
+ ### Step 3: Strip the Type Suffix
55
+ Remove the `.type` from the model name:
56
+ - `simplejob.job` → base name: `simplejob`
57
+ - `churn.mas` → base name: `churn`
58
+ - `fraud_detector.jobdef` → base name: `fraud_detector`
59
+
60
+ ### Step 4: Route to the Correct Tool
61
+ Invoke the tool that matches the type:
62
+ - **`.job`** → `sas-score-run-job`
63
+ - **`.jobdef`** → `sas-score-run-jobdef`
64
+ - **`.mas`** → `sas-score-model-score`
65
+ - **`.scr`** → `sas-score-scr-score`
66
+ - **`.sas`** → `sas-score-run-sas-program`
67
+
68
+ ### Step 5: Invoke with Correct Parameters
69
+ Pass the base name (without type) to the routed tool.
70
+
71
+ ---
72
+
73
+ ## Routing Decision Tree
74
+
75
+ ```
76
+ User request: "score a=10,b=20 with model simplejob.job"
77
+
78
+ [Extract model reference] → "simplejob.job"
79
+
80
+ [Split on last dot] → base="simplejob", type="job"
81
+
82
+ [Validate type] → "job" ∈ [job, jobdef, mas, scr, sas]? YES
83
+
84
+ [Route by type]
85
+ └─ type="job" → call sas-score-run-job()
86
+
87
+ [Invoke tool] → sas-score-run-job({ name: "simplejob", scenario: {a: "10", b: "20"} })
88
+
89
+ [Return results]
90
+ ```
91
+
92
+ ---
93
+
94
+ ## ❌ Anti-Patterns (DO NOT DO THIS)
95
+
96
+ These mistakes cause incorrect routing:
97
+
98
+ | Request | Wrong Tool Called | Why It's Wrong | Correct Tool |
99
+ |---------|-------------------|----------------|---------------|
100
+ | `score simplejob.job scenario =a=10,b=20` | `deva-score` | `.job` suffix ignored; generic scorer used | `sas-score-run-job` |
101
+ | `score churn.mas where age=45` | `run-job` | Type mismatch; treated as job instead of MAS | `sas-score-model-score` |
102
+ | `score fraud.jobdef using amount=500` | `model-score` | Wrong tool for jobdef type | `sas-score-run-jobdef` |
103
+ | `score model.scr with scenario` | `run-jobdef` | SCR endpoint ignored | `sas-score-scr-score` |
104
+ | `score code.sas using x=1` | `model-score` | SAS program treated as MAS model | `sas-score-run-sas-program` |
105
+
106
+ ---
107
+
108
+ ## ✅ Checkpoint Verification
109
+
110
+ Before invoking ANY tool, verify all checkpoints:
111
+
112
+ - [ ] **Did I extract the type correctly?** (Check: is it one of the 5 types: `job`, `jobdef`, `mas`, `scr`, `sas`?)
113
+ - [ ] **Did I strip the type suffix from the model name?** (Pass base name only: `simplejob`, not `simplejob.job`)
114
+ - [ ] **Does the routing match the type?** (e.g., `.job` → `run-job`, `.mas` → `model-score`)
115
+ - [ ] **Am I using the correct parameter name?** (e.g., `name:` for jobs/jobdefs, `model:` for MAS, `url:` for SCR)
116
+ - [ ] **Is the scenario parsed correctly?** (e.g., `{ a: "10", b: "20" }` from `a=10,b=20`)
117
+ - [ ] **Have I considered if this is the default MAS case?** (If no type, assume `.mas` and use `model-score`)
118
+
119
+ ⚠️ **All checkboxes must be ✓ before invoking the tool.**
120
+
121
+ ---
122
+
38
123
  ## Type-Based Routing
39
124
 
40
125
  ### Parse and Strip Model Type
@@ -296,6 +381,8 @@ Merge the scoring output back with the input records and present as a table wher
296
381
  | Scoring error / invalid inputs | Return structured error, suggest `model-info` to check required inputs and data types |
297
382
  | Empty read result | Tell user, ask if they want to adjust the query/filter before scoring |
298
383
  | Missing input fields | Ask which table columns map to the required model inputs |
384
+ | **Type routing error (wrong tool called)** | **Check: did I complete the MANDATORY routing decision? Did I extract the type correctly? Did I route to the right tool based on type?** |
385
+ | **Type suffix not recognized** | **Assume `.mas` (default MAS model) and treat entire input as model name** |
299
386
 
300
387
  ---
301
388
 
@@ -47,8 +47,8 @@ Returns { tables: [] } if not found; { tables: [name, ...] } if found. Never hal
47
47
  name: 'find-table',
48
48
  description: description,
49
49
  inputSchema: z.object({
50
- name: z.string(),
51
50
  lib: z.string(),
51
+ name: z.string(),
52
52
  server: z.string()
53
53
  }),
54
54