@sassoftware/sas-score-mcp-serverjs 1.0.1-3 → 1.0.1-31
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/.skills/agents/sas-score-mcp-serverjs-agent.md +190 -0
- package/.skills/copilot-instructions.md +241 -0
- package/.skills/skills/README.md +125 -0
- package/.skills/skills/detail-strategy/SKILL.md +272 -0
- package/.skills/skills/find-resources/SKILL.md +155 -0
- package/.skills/skills/list-resource/SKILL.md +258 -0
- package/.skills/skills/read-strategy/SKILL.md +137 -0
- package/.skills/skills/request-routing/SKILL.md +107 -0
- package/.skills/skills/score-strategy/SKILL.md +231 -0
- package/README.md +96 -54
- package/cli.js +37 -27
- package/openApi.yaml +121 -121
- package/package.json +14 -14
- package/scripts/docs/oauth-http-transport.md +2 -2
- package/scripts/refreshtoken.js +58 -0
- package/src/createMcpServer.js +0 -1
- package/src/expressMcpServer.js +47 -49
- package/src/oauthHandlers/authorize.js +4 -1
- package/src/oauthHandlers/baseUrl.js +4 -0
- package/src/oauthHandlers/callback.js +5 -1
- package/src/oauthHandlers/getMetadata.js +4 -0
- package/src/oauthHandlers/index.js +4 -0
- package/src/oauthHandlers/token.js +4 -0
- package/src/openApi.yaml +121 -121
- package/src/processHeaders.js +10 -7
- package/src/setupSkills.js +12 -7
- package/src/toolHelpers/_findJob.js +12 -0
- package/src/toolHelpers/_findJobdef.js +10 -0
- package/src/toolHelpers/_findLibrary.js +10 -0
- package/src/toolHelpers/_findModel.js +12 -0
- package/src/toolHelpers/_findTable.js +10 -0
- package/src/toolHelpers/_listJobs.js +2 -1
- package/src/toolHelpers/_listLibrary.js +1 -1
- package/src/toolHelpers/_listTables.js +1 -1
- package/src/toolHelpers/getLogonPayload.js +2 -2
- package/src/toolSet/devaScore.js +61 -61
- package/src/toolSet/findJob.js +2 -1
- package/src/toolSet/findJobdef.js +7 -7
- package/src/toolSet/findLibrary.js +68 -68
- package/src/toolSet/findModel.js +2 -2
- package/src/toolSet/findTable.js +2 -2
- package/src/toolSet/jobInfo.js +59 -0
- package/src/toolSet/jobdefInfo.js +59 -0
- package/src/toolSet/listJobdefs.js +61 -61
- package/src/toolSet/listJobs.js +61 -61
- package/src/toolSet/listLibraries.js +78 -78
- package/src/toolSet/listModels.js +56 -56
- package/src/toolSet/listTables.js +66 -66
- package/src/toolSet/makeTools.js +3 -0
- package/src/toolSet/modelInfo.js +1 -1
- package/src/toolSet/modelScore.js +23 -25
- package/src/toolSet/readTable.js +63 -63
- package/src/toolSet/runCasProgram.js +21 -10
- package/src/toolSet/runJob.js +15 -19
- package/src/toolSet/runJobdef.js +15 -19
- package/src/toolSet/runMacro.js +82 -82
- package/src/toolSet/sasQuery.js +77 -77
- package/src/toolSet/scrScore.js +60 -69
- package/src/toolSet/setContext.js +65 -65
- package/src/toolSet/superstat.js +61 -61
- package/src/toolSet/tableInfo.js +58 -58
- package/.skills_claude/README.md +0 -303
- package/.skills_claude/TESTING_GUIDE.md +0 -252
- package/.skills_claude/agents/sas-viya-scoring-expert.md +0 -58
- package/.skills_claude/claude-desktop-config.json +0 -16
- package/.skills_claude/claude-desktop-system-prompt.md +0 -127
- package/.skills_claude/copilot-instructions.md +0 -155
- package/.skills_claude/instructions.md +0 -184
- package/.skills_claude/skills/sas-find-library-smart/SKILL.md +0 -157
- package/.skills_claude/skills/sas-find-resource-strategy/SKILL.md +0 -105
- package/.skills_claude/skills/sas-list-resource-strategy/SKILL.md +0 -124
- package/.skills_claude/skills/sas-list-tables-smart/SKILL.md +0 -126
- package/.skills_claude/skills/sas-read-and-score/SKILL.md +0 -112
- package/.skills_claude/skills/sas-read-strategy/SKILL.md +0 -154
- package/.skills_claude/skills/sas-request-classifier/SKILL.md +0 -69
- package/.skills_claude/skills/sas-score-workflow/SKILL.md +0 -200
- package/.skills_claude/skills-index.md +0 -345
- package/.skills_github/agents/sas-viya-scoring-expert.md +0 -58
- package/.skills_github/copilot-instructions.md +0 -177
- package/.skills_github/skills/sas-find-library-smart/SKILL.md +0 -155
- package/.skills_github/skills/sas-find-resource-strategy/SKILL.md +0 -105
- package/.skills_github/skills/sas-list-resource-strategy/SKILL.md +0 -124
- package/.skills_github/skills/sas-list-tables-smart/SKILL.md +0 -128
- package/.skills_github/skills/sas-read-and-score/SKILL.md +0 -113
- package/.skills_github/skills/sas-read-strategy/SKILL.md +0 -154
- package/.skills_github/skills/sas-request-classifier/SKILL.md +0 -74
- package/.skills_github/skills/sas-score-workflow/SKILL.md +0 -314
- package/scripts/optimize_final.py +0 -140
- package/scripts/optimize_tools.py +0 -99
- package/scripts/setup-skills.js +0 -34
- package/scripts/update_descriptions.py +0 -46
- package/src/authpkce.js +0 -219
- package/src/handleGetDelete.js +0 -34
- package/src/handleRequest.js +0 -112
- package/src/hapiMcpServer.js +0 -241
- package/src/toolSet/.claude/settings.local.json +0 -13
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: read-strategy
|
|
3
|
+
description: >
|
|
4
|
+
Strategy for reading data from CAS or SAS tables. Determines which read tool to use (raw reads vs analytical queries).
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Read Table Strategy
|
|
8
|
+
|
|
9
|
+
Use this strategy when the user requests to read, fetch, or query data from a table.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
Before reading:
|
|
14
|
+
1. Verify the table exists using find-resource strategy
|
|
15
|
+
2. Determine the server (CAS or SAS) from the find-resource verification step. If the server cannot be determined, ask the user to specify. Do not proceed with a default server unless explicitly instructed by the user.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Two Types of Read Operations
|
|
20
|
+
|
|
21
|
+
### Type 1: Raw Row Read
|
|
22
|
+
|
|
23
|
+
**Trigger phrases**: "read rows from", "show first N records", "fetch records where", "get data from table"
|
|
24
|
+
|
|
25
|
+
**Tool**: `sas-score-read-table`
|
|
26
|
+
|
|
27
|
+
**When to use**:
|
|
28
|
+
- User wants raw records, not aggregations
|
|
29
|
+
- User wants to filter by WHERE clause
|
|
30
|
+
- User wants to browse data
|
|
31
|
+
|
|
32
|
+
**Parameters**:
|
|
33
|
+
```
|
|
34
|
+
lib: "<library>" # from find-resource verification
|
|
35
|
+
table: "<table>" # from find-resource verification
|
|
36
|
+
server: "cas" or "sas" # from find-resource verification
|
|
37
|
+
start: <row number> # default 1
|
|
38
|
+
limit: <max rows> # default 10, max 1000
|
|
39
|
+
where: "<SQL WHERE clause>" # optional filter
|
|
40
|
+
format: true # default: use formatted values
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Example**:
|
|
44
|
+
```
|
|
45
|
+
sas-score-read-table({
|
|
46
|
+
lib: "Public",
|
|
47
|
+
table: "customers",
|
|
48
|
+
server: "cas",
|
|
49
|
+
limit: 25,
|
|
50
|
+
where: "status='active'"
|
|
51
|
+
})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
### Type 2: Analytical Query
|
|
57
|
+
|
|
58
|
+
**Trigger phrases**: "how many", "count by", "average", "total", "sum", "group by", "aggregate", "distinct", "join"
|
|
59
|
+
|
|
60
|
+
**Tool**: `sas-score-sas-query`
|
|
61
|
+
|
|
62
|
+
**When to use**:
|
|
63
|
+
- User wants aggregations (SUM, AVG, COUNT, etc.)
|
|
64
|
+
- User wants GROUP BY or distinct counts
|
|
65
|
+
- User wants JOIN across tables
|
|
66
|
+
- User wants statistical summaries
|
|
67
|
+
|
|
68
|
+
**Parameters**:
|
|
69
|
+
```
|
|
70
|
+
table: "lib.table" # CAS: "Public.customers", SAS: "SASHELP.cars"
|
|
71
|
+
query: "<natural language question>"
|
|
72
|
+
sql: "<SELECT SQL>" # optional: pre-generated SQL
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Example**:
|
|
76
|
+
```
|
|
77
|
+
sas-score-sas-query({
|
|
78
|
+
table: "Public.customers",
|
|
79
|
+
query: "count of customers by region and status",
|
|
80
|
+
sql: "SELECT region, status, COUNT(*) as count FROM Public.customers GROUP BY region, status"
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
## Decision Tree
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
User requests data from table
|
|
91
|
+
↓
|
|
92
|
+
Is it an aggregation? (count, sum, avg, group by, distinct, etc.)
|
|
93
|
+
├─ YES → Use sas-score-sas-query
|
|
94
|
+
└─ NO → Use sas-score-read-table
|
|
95
|
+
|
|
96
|
+
If the user's intent is ambiguous or mixes aggregation and raw reads, ask the user to clarify whether they want raw records or aggregated results before proceeding.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Table Name Format
|
|
102
|
+
|
|
103
|
+
- **CAS tables**: `Caslib.table` or `Public.customers` (lowercase caslib, mixed case table)
|
|
104
|
+
- **SAS tables**: `LIBREF.table` or `SASHELP.cars` (uppercase libref, case-insensitive table)
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Error Handling
|
|
109
|
+
|
|
110
|
+
| Error | Action |
|
|
111
|
+
|---|---|
|
|
112
|
+
| Table not found | Verify table exists with find-resource first |
|
|
113
|
+
| Server mismatch | Use server from find-resource verification |
|
|
114
|
+
| Empty result | Ask user to adjust WHERE clause or criteria |
|
|
115
|
+
| Column not found | Ask user to verify column name (case sensitivity) |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Examples
|
|
120
|
+
|
|
121
|
+
### Example 1: Browse customer records
|
|
122
|
+
**Request**: "read first 20 customers from Public"
|
|
123
|
+
1. Find table customers in Public → CAS
|
|
124
|
+
2. Read: `sas-score-read-table({ lib: "Public", table: "customers", server: "cas", limit: 20 })`
|
|
125
|
+
3. Return 20 rows
|
|
126
|
+
|
|
127
|
+
### Example 2: Find active customers in a region
|
|
128
|
+
**Request**: "fetch customers from Public where status='active' and region='East'"
|
|
129
|
+
1. Find table customers in Public → CAS
|
|
130
|
+
2. Read: `sas-score-read-table({ lib: "Public", table: "customers", server: "cas", where: "status='active' and region='East'" })`
|
|
131
|
+
3. Return matching rows
|
|
132
|
+
|
|
133
|
+
### Example 3: Aggregate customers by region
|
|
134
|
+
**Request**: "how many customers by region in Public.customers"
|
|
135
|
+
1. Find table customers in Public → CAS
|
|
136
|
+
2. Query: `sas-score-sas-query({ table: "Public.customers", query: "count of customers by region", sql: "SELECT region, COUNT(*) FROM Public.customers GROUP BY region" })`
|
|
137
|
+
3. Return aggregated result
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: request-routing
|
|
3
|
+
description: >
|
|
4
|
+
Universal routing strategy for all SAS Viya requests. Every request follows the same three-step workflow.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Universal Request Routing Strategy
|
|
8
|
+
|
|
9
|
+
All SAS Viya requests follow this three-step workflow:
|
|
10
|
+
|
|
11
|
+
## Step 1: Verify Resources Exist
|
|
12
|
+
|
|
13
|
+
Before executing any action, verify that the target resources exist.
|
|
14
|
+
|
|
15
|
+
| Resource Type | Find Tool | Notes |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Library | `sas-score-find-library` | For tables, if server is not specified (cas or sas), determine server here. Other resource types do not require server specification. |
|
|
18
|
+
| Table | `sas-score-find-table` | Requires library name and server. |
|
|
19
|
+
| MAS Model | `sas-score-find-model` | No server selection. |
|
|
20
|
+
| Job | `sas-score-find-job` | No server selection. |
|
|
21
|
+
| JobDef | `sas-score-find-jobdef` | No server selection. |
|
|
22
|
+
| SCR Model | Skip verification | SCR models do not require pre-verification. |
|
|
23
|
+
|
|
24
|
+
**Rule**: Always verify before executing. Exception: SCR models can be scored directly.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Step 2: Execute the Request
|
|
29
|
+
|
|
30
|
+
Once resources are verified to exist, select the appropriate execution tool:
|
|
31
|
+
|
|
32
|
+
| Request Type | Tool | Input |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| Read table rows | `sas-score-read-table` | lib, table, server (from Step 1) |
|
|
35
|
+
| Query table (aggregation) | `sas-score-sas-query` | lib.table, SQL query |
|
|
36
|
+
| Score with MAS model | `sas-score-mas-score` | model name, scenario data |
|
|
37
|
+
| Run Job | `sas-score-run-jobdef` | job name, scenario parameters |
|
|
38
|
+
| Run JobDef | `sas-score-run-jobdef` | jobdef name, scenario parameters |
|
|
39
|
+
| Score with SCR model | `sas-score-scr-score` | SCR URL, scenario data |
|
|
40
|
+
| Describe MAS model | `sas-score-model-info` | model name |
|
|
41
|
+
| Describe Job/JobDef | `sas-score-job-info` | job or jobdef name |
|
|
42
|
+
| Describe SCR model | `sas-score-scr-info` | SCR URL |
|
|
43
|
+
| Describe table | `sas-score-table-info` | lib, table, server |
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Step 3: Merge Results
|
|
48
|
+
|
|
49
|
+
Combine verification and execution results:
|
|
50
|
+
|
|
51
|
+
- For **read/query**: Return rows as-is.
|
|
52
|
+
- For **scoring**: Merge predictions with input scenario data.
|
|
53
|
+
- For **jobs/jobdefs**: Return execution results (tables, logs).
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Special Case: Read + Score (Combined Workflow)
|
|
59
|
+
|
|
60
|
+
When the user requests scoring records from a table, follow these sub-workflows in order:
|
|
61
|
+
|
|
62
|
+
1. **Verify**
|
|
63
|
+
- Find the table (determine server as described above)
|
|
64
|
+
- Find the model
|
|
65
|
+
2. **Read**
|
|
66
|
+
- Fetch rows from the table using `sas-score-read-table` or `sas-score-sas-query`
|
|
67
|
+
3. **Map**
|
|
68
|
+
- Check if table columns match model input variables
|
|
69
|
+
- If not, ask user for mapping
|
|
70
|
+
4. **Score**
|
|
71
|
+
- Score each row using `sas-score-mas-score` (for MAS) or `sas-score-scr-score` (for SCR)
|
|
72
|
+
5. **Merge**
|
|
73
|
+
- Combine predictions with original rows
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Error Handling
|
|
78
|
+
|
|
79
|
+
| Error | Action |
|
|
80
|
+
|---|---|
|
|
81
|
+
| Resource not found | Ask user to verify name and server (for tables). |
|
|
82
|
+
| Column/input mismatch | Ask user to map table columns to model inputs. |
|
|
83
|
+
| Empty result | Ask whether to adjust filter/criteria. |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Examples
|
|
88
|
+
|
|
89
|
+
### Example 1: Read a table
|
|
90
|
+
**Request**: "read customers in Public"
|
|
91
|
+
1. Find library Public → Verified (CAS)
|
|
92
|
+
2. Read table customers in Public (CAS)
|
|
93
|
+
3. Return rows
|
|
94
|
+
|
|
95
|
+
### Example 2: Score with inline scenario
|
|
96
|
+
**Request**: "score a=1, b=2 with model simplejob.job"
|
|
97
|
+
1. Find job simplejob → Verified
|
|
98
|
+
2. Run simplejob with scenario {a: 1, b: 2}
|
|
99
|
+
3. Return result
|
|
100
|
+
|
|
101
|
+
### Example 3: Score records from table
|
|
102
|
+
**Request**: "score records from Public.customers with model risk_model.mas"
|
|
103
|
+
1. Find table customers in Public → Verified (CAS)
|
|
104
|
+
2. Find model risk_model → Verified (MAS)
|
|
105
|
+
3. Read rows from Public.customers
|
|
106
|
+
4. Score each row with risk_model
|
|
107
|
+
5. Return merged predictions + original data
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: score-strategy
|
|
3
|
+
description: >
|
|
4
|
+
Unified scoring workflow. Handles MAS, Job, JobDef, SCR, and combined read+score scenarios.
|
|
5
|
+
Always verify resources before scoring.
|
|
6
|
+
|
|
7
|
+
To reduce cognitive load, follow the stepwise flowchart below for scoring requests.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Score Strategy
|
|
11
|
+
|
|
12
|
+
Use this strategy when the user requests model scoring, predictions, or running jobs/jobdefs.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
1. Verify the model/job exists using find-resources skill
|
|
17
|
+
2. If scoring table rows: Verify the table exists and determine server using find-resources skill
|
|
18
|
+
3. If scoring with inline scenario: Parse the scenario data
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Step 1: Parse the Score Request
|
|
24
|
+
|
|
25
|
+
Identify the scoring target (model type) and input source:
|
|
26
|
+
|
|
27
|
+
### Identify Model Type
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
score with model X.mas → MAS model
|
|
31
|
+
score with model X.job → Job
|
|
32
|
+
score with model X.jobdef → JobDef
|
|
33
|
+
score with model X.scr → SCR model
|
|
34
|
+
score with model X (default to MAS if type is not specified) → MAS model
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Visual Flowchart
|
|
38
|
+
|
|
39
|
+
```mermaid
|
|
40
|
+
flowchart TD
|
|
41
|
+
A[User score request] --> B{Model type specified?}
|
|
42
|
+
B -- .mas --> C[MAS model]
|
|
43
|
+
B -- .job --> D[Job]
|
|
44
|
+
B -- .jobdef --> E[JobDef]
|
|
45
|
+
B -- .scr --> F[SCR model]
|
|
46
|
+
B -- none --> C
|
|
47
|
+
C --> G[Verify MAS model]
|
|
48
|
+
D --> H[Verify Job]
|
|
49
|
+
E --> I[Verify JobDef]
|
|
50
|
+
F --> J[Skip verification]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Identify Input Source
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
score a=1, b=2 → Inline scenario
|
|
57
|
+
score with scenario {...} → Inline scenario
|
|
58
|
+
score records from table X → Table rows
|
|
59
|
+
score results of query... → Query results
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Step 2: Execute Scoring
|
|
65
|
+
|
|
66
|
+
### Option A: Score with Inline Scenario
|
|
67
|
+
|
|
68
|
+
**Trigger phrases**: "score a=1, b=2", "predict with values", "score scenario"
|
|
69
|
+
|
|
70
|
+
**Tools**:
|
|
71
|
+
- MAS: `sas-score-mas-score`
|
|
72
|
+
- Job: `sas-score-run-job`
|
|
73
|
+
- JobDef: `sas-score-run-jobdef`
|
|
74
|
+
- SCR: `sas-score-scr-score`
|
|
75
|
+
|
|
76
|
+
**Flow**:
|
|
77
|
+
1. Find model (find-resources)
|
|
78
|
+
2. Score with inline data
|
|
79
|
+
3. Return prediction + input data merged
|
|
80
|
+
|
|
81
|
+
**Parameters** (MAS):
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
sas-score-mas-score({
|
|
85
|
+
model: "<model name>",
|
|
86
|
+
scenario: { a: 1, b: 2 }
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Parameters** (job):
|
|
91
|
+
```
|
|
92
|
+
sas-score-run-job({
|
|
93
|
+
name: "<job name>",
|
|
94
|
+
scenario: { a: 1, b: 2 }
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Parameters** (jobdef):
|
|
99
|
+
```
|
|
100
|
+
sas-score-run-jobdef({
|
|
101
|
+
name: "<jobdef name>",
|
|
102
|
+
scenario: { a: 1, b: 2 }
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Parameters** (SCR):
|
|
107
|
+
```
|
|
108
|
+
sas-score-scr-score({
|
|
109
|
+
url: "<scr endpoint>",
|
|
110
|
+
scenario: { a: 1, b: 2 }
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
### Option B: Score Table Rows (Read + Score)
|
|
117
|
+
|
|
118
|
+
**Trigger phrases**: "score records from", "run model on table", "predict for customers in", "score rows from"
|
|
119
|
+
|
|
120
|
+
**Flow**:
|
|
121
|
+
1. Find model (find-resources)
|
|
122
|
+
2. Find table (find-resources) → get server
|
|
123
|
+
3. Read rows from table (read-strategy)
|
|
124
|
+
4. Score each row (or batch score)
|
|
125
|
+
5. Merge predictions with original rows
|
|
126
|
+
|
|
127
|
+
**Decision**: Read strategy first
|
|
128
|
+
- If user requests aggregation: Use `sas-score-sas-query`
|
|
129
|
+
- If user requests raw rows: Use `sas-score-read-table`
|
|
130
|
+
|
|
131
|
+
**Example workflow**:
|
|
132
|
+
```
|
|
133
|
+
Request: "score records from Public.customers with model risk_model"
|
|
134
|
+
|
|
135
|
+
1. Find table customers in Public → CAS
|
|
136
|
+
2. Find model risk_model → MAS
|
|
137
|
+
3. Read rows: sas-score-read-table({ lib: "Public", table: "customers", server: "cas", limit: 1000 })
|
|
138
|
+
4. Score each row: for each row, sas-score-mas-score({ model: "risk_model", scenario: {row} })
|
|
139
|
+
5. Merge: combine risk score with customer data
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Step 3: Result Formatting
|
|
145
|
+
|
|
146
|
+
### MAS Scoring Result
|
|
147
|
+
|
|
148
|
+
Return merged object:
|
|
149
|
+
```
|
|
150
|
+
Input data + Prediction fields
|
|
151
|
+
Example: { a: 1, b: 2, prediction: 0.85, probability_0: 0.15, probability_1: 0.85 }
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Job/JobDef Result
|
|
155
|
+
|
|
156
|
+
Return execution output:
|
|
157
|
+
```
|
|
158
|
+
Tables, logs, listings as returned by job
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### SCR Result
|
|
162
|
+
|
|
163
|
+
Return merged object:
|
|
164
|
+
```
|
|
165
|
+
Input data + Prediction fields from SCR response
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Batch Scoring (Table Rows)
|
|
169
|
+
|
|
170
|
+
Return rows with predictions appended:
|
|
171
|
+
```
|
|
172
|
+
[
|
|
173
|
+
{ customer_id: 1, name: "Alice", ..., risk_score: 0.32 },
|
|
174
|
+
{ customer_id: 2, name: "Bob", ..., risk_score: 0.78 },
|
|
175
|
+
...
|
|
176
|
+
]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Column/Variable Mapping
|
|
182
|
+
|
|
183
|
+
If table columns don't match model input variable names:
|
|
184
|
+
|
|
185
|
+
1. Ask user: "Which table column maps to model input X?"
|
|
186
|
+
2. Wait for mapping: e.g., { customer_age: age, customer_income: income }
|
|
187
|
+
3. Transform row data using mapping
|
|
188
|
+
4. Score transformed data
|
|
189
|
+
5. Return with original column names
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Error Handling
|
|
194
|
+
|
|
195
|
+
| Error | Action |
|
|
196
|
+
|---|---|
|
|
197
|
+
| Model not found | Verify model name with user |
|
|
198
|
+
| Table not found | Verify table name and library |
|
|
199
|
+
| Scenario mismatch | Ask user to verify input variable names |
|
|
200
|
+
| Empty table | Ask whether to adjust filter or continue |
|
|
201
|
+
| Scoring failure | Return error message from scoring tool |
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Examples
|
|
206
|
+
|
|
207
|
+
### Example 1: Score with inline scenario
|
|
208
|
+
**Request**: "score a=1, b=2 with model simplejob.job"
|
|
209
|
+
1. Find job simplejob using find-resources strategy
|
|
210
|
+
2. Run: `sas-score-run-jobdef({ name: "simplejob", scenario: { a: 1, b: 2 } })`
|
|
211
|
+
3. Return: `{ c: 3 }`
|
|
212
|
+
|
|
213
|
+
### Example 2: Score with MAS model
|
|
214
|
+
**Request**: "predict churn for age=45, income=60000 with model churn_predictor"
|
|
215
|
+
1. Find model churn_predictor.mas using find-resources strategy
|
|
216
|
+
2. Score: `sas-score-mas-score({ model: "churn_predictor", scenario: { age: 45, income: 60000 } })`
|
|
217
|
+
3. Return: `{ age: 45, income: 60000, churn_probability: 0.23, prediction: "no_churn" }`
|
|
218
|
+
|
|
219
|
+
### Example 3: Score table rows
|
|
220
|
+
**Request**: "score all active customers with model risk_model and table Public.customers"
|
|
221
|
+
1. Find model risk_model.mad using find-resources strategy
|
|
222
|
+
2. Find table Public.customers using find-resources strategy
|
|
223
|
+
3. Read: `sas-score-read-table({ lib: "Public", table: "customers", server: "cas", where: "status='active'" })`
|
|
224
|
+
4. Score each row with risk_model
|
|
225
|
+
5. Return: customers with risk_score appended
|
|
226
|
+
|
|
227
|
+
### Example 4: Score with SCR model
|
|
228
|
+
**Request**: "score age=50, income=75000 with model loan.scr"
|
|
229
|
+
1. Prepare: SCR URL for "loan"
|
|
230
|
+
2. Score: `sas-score-scr-score({ url: "loan", scenario: { age: 50, income: 75000 } })`
|
|
231
|
+
3. Return predictions from SCR endpoint
|
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# sas-score-mcp-serverjs
|
|
2
|
-
A Model Context Protocol (MCP) Server for Scoring with SAS Viya
|
|
2
|
+
A Model Context Protocol (MCP) Server for Scoring with SAS Viya.
|
|
3
|
+
See [wiki](https://github.com/sassoftware/sas-score-mcp-serverjs/wiki) for the capabilities of the server.
|
|
3
4
|
|
|
4
5
|
## Major changes in release 1.0.0
|
|
5
6
|
|
|
@@ -8,6 +9,8 @@ A Model Context Protocol (MCP) Server for Scoring with SAS Viya
|
|
|
8
9
|
|
|
9
10
|
- Agent - can be deployed as an agent
|
|
10
11
|
|
|
12
|
+
- Docker image: ghcr.io/sassoftware/sas-score-mcp-serverjs
|
|
13
|
+
|
|
11
14
|
|
|
12
15
|
## Overview
|
|
13
16
|
This MCP server is designed for scoring with SAS Viya.
|
|
@@ -22,6 +25,33 @@ Some examples are:
|
|
|
22
25
|
- job Definitions
|
|
23
26
|
- jobs using SAS Studio or other interfaces
|
|
24
27
|
|
|
28
|
+
## Start the mcp server
|
|
29
|
+
|
|
30
|
+
If using stdio transport, most of the mcp clients will start the server automatically.
|
|
31
|
+
But for http transport, the mcp server must be started.
|
|
32
|
+
|
|
33
|
+
If running locally
|
|
34
|
+
```sh
|
|
35
|
+
npx @sassoftware/sas-score-mcp-serverjs@latest
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The mcp is also available as a docker image. Add or remove the env variables as needed.
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
docker run -p 8080:8080 --name sasscore -e VIYA_SERVER=<yourviyaserver> -e AUTHFLOW=oauth ghcr.io/sassoftware/sas-score-mcp-serverjs:latest
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Capabilities
|
|
45
|
+
|
|
46
|
+
The tools can be grouped into these categories
|
|
47
|
+
|
|
48
|
+
- Scoring with MAS models, job, jobdef and scr
|
|
49
|
+
- Viewing and querying tables
|
|
50
|
+
|
|
51
|
+
Supporting tools can be grouped into these categories
|
|
52
|
+
- Listing of MAS models, job, jobdef , tables
|
|
53
|
+
- Describing MAS models, job, jobdef, scr and tables
|
|
54
|
+
|
|
25
55
|
## Target Audience
|
|
26
56
|
This MCP server was developed for two types of SAS users.
|
|
27
57
|
|
|
@@ -44,88 +74,102 @@ Typically these are set either in the .env file or as environment variables or a
|
|
|
44
74
|
|
|
45
75
|
### Required Options
|
|
46
76
|
|
|
47
|
-
VIYA_SERVER
|
|
48
|
-
|
|
49
|
-
MCPHOST=<url for the mcp server = http://localhost:8080 or some remote mcp server>
|
|
77
|
+
1. VIYA_SERVER=
|
|
78
|
+
<url for Viya server>.
|
|
50
79
|
|
|
51
|
-
|
|
80
|
+
### Options with defaults
|
|
52
81
|
|
|
53
|
-
|
|
82
|
+
These can be customized
|
|
54
83
|
|
|
55
|
-
|
|
84
|
+
1. AUTHFLOW=**oauth**|oauthclient|bearer|sascli|token|password
|
|
85
|
+
- Authentication method. Default is oauth
|
|
56
86
|
|
|
57
|
-
CLIENTID
|
|
87
|
+
2. CLIENTID=**vscodemcp**
|
|
88
|
+
- Clientid for oauth and oauthclient AUTHFlOW. Must be PKCE clientid.
|
|
89
|
+
|
|
58
90
|
|
|
91
|
+
3. MCPTYPE=**http**|stdio
|
|
92
|
+
- The transport protocol for the mcp server.
|
|
59
93
|
|
|
60
|
-
|
|
94
|
+
4. MCPHOST=**http://localhost:8080**
|
|
95
|
+
- URL of the mcp server. If using remote mcp server, set this to remote MCP server
|
|
61
96
|
|
|
62
|
-
|
|
63
|
-
-
|
|
97
|
+
5. PROFILE=**~/.sas**
|
|
98
|
+
- profile name used by sas-cli to store the tokens
|
|
64
99
|
|
|
65
|
-
|
|
100
|
+
6. PORT=**8080**
|
|
101
|
+
- set it to what fits your environment.
|
|
66
102
|
|
|
103
|
+
7. CASSERVER=**cas-shared-default**
|
|
104
|
+
- Set to a valid cas server
|
|
67
105
|
|
|
68
|
-
|
|
106
|
+
8. COMPUTECONTEXT=**"SAS Job Execution compute context"**
|
|
107
|
+
- Use one that is appropriate
|
|
69
108
|
|
|
70
|
-
|
|
71
|
-
PROFILE=<profile name used by sas-cli to store the tokens in ~/.sas>
|
|
72
|
-
```
|
|
109
|
+
### Clientid specifications
|
|
73
110
|
|
|
74
|
-
|
|
111
|
+
If using remote mcp server, change the url in redirect to the remote url
|
|
75
112
|
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
113
|
+
```js
|
|
114
|
+
{
|
|
115
|
+
client_id: 'vscodemcp',
|
|
116
|
+
scope: [ 'openid' ],
|
|
117
|
+
resource_ids: [ 'none' ],
|
|
118
|
+
autoapprove: true,
|
|
119
|
+
authorized_grant_types: [ 'authorization_code' ],
|
|
120
|
+
access_token_validity: 86400,
|
|
121
|
+
allowpublic: true,
|
|
122
|
+
redirect_uri: [ 'http://localhost:8080/callback' ]
|
|
123
|
+
}
|
|
81
124
|
```
|
|
82
125
|
|
|
83
|
-
|
|
126
|
+
> OauthClient Flow. Clientid with redirect appropriate for the client. Some examples are shown below. Note that the explicit port used by github copilot is not guaranteed.
|
|
127
|
+
|
|
128
|
+
- github copilot: http://127.0.0.1:33418/
|
|
129
|
+
- claude: https://claude.ai/api/mcp/auth_callback,https://claude.ai/api/auth/callback
|
|
84
130
|
|
|
85
|
-
> The mcp server can be deployed as an agent in github copilot
|
|
86
|
-
> The configuration files for claude can be installed locally. You have to move the files to the appriopiate place.
|
|
87
131
|
|
|
88
|
-
|
|
132
|
+
## Agent and skills
|
|
89
133
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
MCPCLIENT=github|claude
|
|
93
|
-
```
|
|
134
|
+
> The mcp server can be deployed as an agent in github copilot
|
|
135
|
+
> The configuration files for claude can be installed locally. You have to move the files to the appriopiate place.
|
|
94
136
|
|
|
95
|
-
|
|
137
|
+
To download the skills to your environment issue this command:
|
|
96
138
|
|
|
97
|
-
```
|
|
98
|
-
|
|
139
|
+
```sh
|
|
140
|
+
npx @sassoftware/sas-score-mcp-serverjs --skills github|claude
|
|
99
141
|
```
|
|
142
|
+
The skills and related files will be written to .github or .claude
|
|
100
143
|
|
|
101
144
|
|
|
102
145
|
## Configure the mcp client for localhost
|
|
103
146
|
|
|
104
|
-
The mcp configuration
|
|
147
|
+
The mcp configuration for oauth flow. For remote mcp, change the url to the
|
|
148
|
+
remote url
|
|
105
149
|
|
|
106
150
|
```json
|
|
107
151
|
"sasmcp": {
|
|
108
152
|
"type": "http",
|
|
109
|
-
"url": "http://localhost:8080/mcp"
|
|
153
|
+
"url": "http://localhost:8080/mcp"
|
|
110
154
|
"oauth: {
|
|
111
|
-
"type": "oauth2"
|
|
155
|
+
"type": "oauth2",
|
|
156
|
+
"clientId: "vscodemcp"
|
|
112
157
|
}
|
|
113
158
|
}
|
|
114
159
|
```
|
|
115
160
|
|
|
116
|
-
For
|
|
161
|
+
For bearer authflow.
|
|
117
162
|
```json
|
|
118
163
|
"sasmcp": {
|
|
119
164
|
"type": "http",
|
|
120
|
-
"url": "
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
|
|
165
|
+
"url": "http://localhost:8080/mcp`,
|
|
166
|
+
"headers" {
|
|
167
|
+
"Authorization": "bearer <tokenstring>"
|
|
168
|
+
}
|
|
124
169
|
}
|
|
125
170
|
```
|
|
126
171
|
|
|
127
|
-
For
|
|
128
|
-
|
|
172
|
+
For stdio scenario
|
|
129
173
|
```json
|
|
130
174
|
"sas-mcp-server": {
|
|
131
175
|
"type: "stdio"
|
|
@@ -141,17 +185,7 @@ For transport protocol stdio. For claude drop the type
|
|
|
141
185
|
}
|
|
142
186
|
```
|
|
143
187
|
|
|
144
|
-
#### Step 2: Start the mcp server
|
|
145
188
|
|
|
146
|
-
If using stdio transport, most of the mcp clients will start the server automatically.
|
|
147
|
-
But this step is necessary of using http transport.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
```sh
|
|
151
|
-
npx @sassoftware/sas-score-mcp-serverjs@latest
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Make sure that the .env file is in the current working directory or specify the options in the command line
|
|
155
189
|
|
|
156
190
|
|
|
157
191
|
## Notes
|
|
@@ -196,7 +230,15 @@ NODE_EXTRA_CA_CERTS=c:\Users\<your_username>\AppData\Local\mkcert\rootCA.pem
|
|
|
196
230
|
```
|
|
197
231
|
|
|
198
232
|
## License
|
|
199
|
-
This project is licensed under the
|
|
233
|
+
This project is licensed under the Apache License 2.0. See [LICENSE](LICENSE).
|
|
234
|
+
|
|
235
|
+
The container image published from this repository also includes third-party software, each component under its own license:
|
|
236
|
+
|
|
237
|
+
- The npm dependencies that ship with this project, along with their respective licenses, are listed in [LICENSES.json](LICENSES.json).
|
|
238
|
+
- The container is built from the 25-alpine base image; license texts for its included software ship inside the image itself. License information for each Alpine package is available at [pkgs.alpinelinux.org](https://pkgs.alpinelinux.org/packages).
|
|
239
|
+
|
|
240
|
+
As with any container image, direct and indirect dependencies are governed by their own licenses.
|
|
241
|
+
Users of the published container image are responsible for ensuring that their use complies with all applicable licenses.
|
|
200
242
|
|
|
201
243
|
## Additional Resources
|
|
202
244
|
|