@sassoftware/sas-score-mcp-serverjs 1.0.1-2 → 1.0.1-22

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.
@@ -0,0 +1,134 @@
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 Step 1
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
+ ## Decision Tree
87
+
88
+ ```
89
+ User requests data from table
90
+
91
+ Is it an aggregation? (count, sum, avg, group by, distinct, etc.)
92
+ ├─ YES → Use sas-score-sas-query
93
+ └─ NO → Use sas-score-read-table
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Table Name Format
99
+
100
+ - **CAS tables**: `Caslib.table` or `Public.customers` (lowercase caslib, mixed case table)
101
+ - **SAS tables**: `LIBREF.table` or `SASHELP.cars` (uppercase libref, case-insensitive table)
102
+
103
+ ---
104
+
105
+ ## Error Handling
106
+
107
+ | Error | Action |
108
+ |---|---|
109
+ | Table not found | Verify table exists with FIND-RESOURCE first |
110
+ | Server mismatch | Use server from FIND-RESOURCE verification |
111
+ | Empty result | Ask user to adjust WHERE clause or criteria |
112
+ | Column not found | Ask user to verify column name (case sensitivity) |
113
+
114
+ ---
115
+
116
+ ## Examples
117
+
118
+ ### Example 1: Browse customer records
119
+ **Request**: "read first 20 customers from Public"
120
+ 1. Find table customers in Public → CAS
121
+ 2. Read: `sas-score-read-table({ lib: "Public", table: "customers", server: "cas", limit: 20 })`
122
+ 3. Return 20 rows
123
+
124
+ ### Example 2: Find active customers in a region
125
+ **Request**: "fetch customers from Public where status='active' and region='East'"
126
+ 1. Find table customers in Public → CAS
127
+ 2. Read: `sas-score-read-table({ lib: "Public", table: "customers", server: "cas", where: "status='active' and region='East'" })`
128
+ 3. Return matching rows
129
+
130
+ ### Example 3: Aggregate customers by region
131
+ **Request**: "how many customers by region in Public.customers"
132
+ 1. Find table customers in Public → CAS
133
+ 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" })`
134
+ 3. Return aggregated result
@@ -0,0 +1,95 @@
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` | Specify server (cas or sas). For tables, determine server here. |
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
+
41
+ ---
42
+
43
+ ## Step 3: Merge Results
44
+
45
+ Combine verification and execution results:
46
+
47
+ - For **read/query**: Return rows as-is.
48
+ - For **scoring**: Merge predictions with input scenario data.
49
+ - For **jobs/jobdefs**: Return execution results (tables, logs).
50
+
51
+ ---
52
+
53
+ ## Special Case: Read + Score (Combined Workflow)
54
+
55
+ When the user requests scoring records from a table:
56
+
57
+ 1. **Verify**: Find the table (determine server), find the model.
58
+ 2. **Read**: Fetch rows from the table using `sas-score-read-table` or `sas-score-sas-query`.
59
+ 3. **Map**: Check if table columns match model input variables. Ask user for mapping if needed.
60
+ 4. **Score**: Score each row using `sas-score-mas-score` (for MAS) or `sas-score-scr-score` (for SCR).
61
+ 5. **Merge**: Combine predictions with original rows.
62
+
63
+ ---
64
+
65
+ ## Error Handling
66
+
67
+ | Error | Action |
68
+ |---|---|
69
+ | Resource not found | Ask user to verify name and server (for tables). |
70
+ | Column/input mismatch | Ask user to map table columns to model inputs. |
71
+ | Empty result | Ask whether to adjust filter/criteria. |
72
+
73
+ ---
74
+
75
+ ## Examples
76
+
77
+ ### Example 1: Read a table
78
+ **Request**: "read customers in Public"
79
+ 1. Find library Public → Verified (CAS)
80
+ 2. Read table customers in Public (CAS)
81
+ 3. Return rows
82
+
83
+ ### Example 2: Score with inline scenario
84
+ **Request**: "score a=1, b=2 with model simplejob.job"
85
+ 1. Find job simplejob → Verified
86
+ 2. Run simplejob with scenario {a: 1, b: 2}
87
+ 3. Return result
88
+
89
+ ### Example 3: Score records from table
90
+ **Request**: "score records from Public.customers with model risk_model.mas"
91
+ 1. Find table customers in Public → Verified (CAS)
92
+ 2. Find model risk_model → Verified (MAS)
93
+ 3. Read rows from Public.customers
94
+ 4. Score each row with risk_model
95
+ 5. Return merged predictions + original data
@@ -0,0 +1,210 @@
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
+
8
+ # Score Strategy
9
+
10
+ Use this strategy when the user requests model scoring, predictions, or running jobs/jobdefs.
11
+
12
+ ## Prerequisites
13
+
14
+ 1. Verify the model/job exists using find-resources skill
15
+ 2. If scoring table rows: Verify the table exists and determine server using find-resources skill
16
+ 3. If scoring with inline scenario: Parse the scenario data
17
+
18
+ ---
19
+
20
+ ## Step 1: Parse the Score Request
21
+
22
+ Identify the scoring target (model type) and input source:
23
+
24
+ ### Identify Model Type
25
+
26
+ ```
27
+ score with model X.mas → MAS model
28
+ score with model X.job → Job
29
+ score with model X.jobdef → JobDef
30
+ score with model X.scr → SCR model
31
+ score with model X → Default to MAS
32
+ ```
33
+
34
+ ### Identify Input Source
35
+
36
+ ```
37
+ score a=1, b=2 → Inline scenario
38
+ score with scenario {...} → Inline scenario
39
+ score records from table X → Table rows
40
+ score results of query... → Query results
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Step 2: Execute Scoring
46
+
47
+ ### Option A: Score with Inline Scenario
48
+
49
+ **Trigger phrases**: "score a=1, b=2", "predict with values", "score scenario"
50
+
51
+ **Tools**:
52
+ - MAS: `sas-score-mas-score`
53
+ - Job: `sas-score-run-job`
54
+ - JobDef: `sas-score-run-jobdef`
55
+ - SCR: `sas-score-scr-score`
56
+
57
+ **Flow**:
58
+ 1. Find model (find-resources)
59
+ 2. Score with inline data
60
+ 3. Return prediction + input data merged
61
+
62
+ **Parameters** (MAS):
63
+
64
+ ```
65
+ sas-score-mas-score({
66
+ model: "<model name>",
67
+ scenario: { a: 1, b: 2 }
68
+ ```
69
+ **Parameters** (job): ):
70
+ ```
71
+ sas-score-run-job({
72
+ name: "<job name>",
73
+ scenario: { a: 1, b: 2 }
74
+ })
75
+ ```
76
+
77
+ **Parameters** (jobdef):
78
+ ```
79
+ sas-score-run-jobdef({
80
+ name: "<jobdef name>",
81
+ scenario: { a: 1, b: 2 }
82
+ })
83
+ ```
84
+
85
+ **Parameters** (SCR):
86
+ ```
87
+ sas-score-scr-score({
88
+ url: "<scr endpoint>",
89
+ scenario: { a: 1, b: 2 }
90
+ })
91
+ ```
92
+
93
+ ---
94
+
95
+ ### Option B: Score Table Rows (Read + Score)
96
+
97
+ **Trigger phrases**: "score records from", "run model on table", "predict for customers in", "score rows from"
98
+
99
+ **Flow**:
100
+ 1. Find model (find-resources)
101
+ 2. Find table (find-resources) → get server
102
+ 3. Read rows from table (read-strategy)
103
+ 4. Score each row (or batch score)
104
+ 5. Merge predictions with original rows
105
+
106
+ **Decision**: Read strategy first
107
+ - If user requests aggregation: Use `sas-score-sas-query`
108
+ - If user requests raw rows: Use `sas-score-read-table`
109
+
110
+ **Example workflow**:
111
+ ```
112
+ Request: "score records from Public.customers with model risk_model"
113
+
114
+ 1. Find table customers in Public → CAS
115
+ 2. Find model risk_model → MAS
116
+ 3. Read rows: sas-score-read-table({ lib: "Public", table: "customers", server: "cas", limit: 1000 })
117
+ 4. Score each row: for each row, sas-score-mas-score({ model: "risk_model", scenario: {row} })
118
+ 5. Merge: combine risk score with customer data
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Step 3: Result Formatting
124
+
125
+ ### MAS Scoring Result
126
+
127
+ Return merged object:
128
+ ```
129
+ Input data + Prediction fields
130
+ Example: { a: 1, b: 2, prediction: 0.85, probability_0: 0.15, probability_1: 0.85 }
131
+ ```
132
+
133
+ ### Job/JobDef Result
134
+
135
+ Return execution output:
136
+ ```
137
+ Tables, logs, listings as returned by job
138
+ ```
139
+
140
+ ### SCR Result
141
+
142
+ Return merged object:
143
+ ```
144
+ Input data + Prediction fields from SCR response
145
+ ```
146
+
147
+ ### Batch Scoring (Table Rows)
148
+
149
+ Return rows with predictions appended:
150
+ ```
151
+ [
152
+ { customer_id: 1, name: "Alice", ..., risk_score: 0.32 },
153
+ { customer_id: 2, name: "Bob", ..., risk_score: 0.78 },
154
+ ...
155
+ ]
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Column/Variable Mapping
161
+
162
+ If table columns don't match model input variable names:
163
+
164
+ 1. Ask user: "Which table column maps to model input X?"
165
+ 2. Wait for mapping: e.g., { customer_age: age, customer_income: income }
166
+ 3. Transform row data using mapping
167
+ 4. Score transformed data
168
+ 5. Return with original column names
169
+
170
+ ---
171
+
172
+ ## Error Handling
173
+
174
+ | Error | Action |
175
+ |---|---|
176
+ | Model not found | Verify model name with user |
177
+ | Table not found | Verify table name and library |
178
+ | Scenario mismatch | Ask user to verify input variable names |
179
+ | Empty table | Ask whether to adjust filter or continue |
180
+ | Scoring failure | Return error message from scoring tool |
181
+
182
+ ---
183
+
184
+ ## Examples
185
+
186
+ ### Example 1: Score with inline scenario
187
+ **Request**: "score a=1, b=2 with model simplejob.job"
188
+ 1. Find job simplejob using find-resources strategy
189
+ 2. Run: `sas-score-run-jobdef({ name: "simplejob", scenario: { a: 1, b: 2 } })`
190
+ 3. Return: `{ c: 3 }`
191
+
192
+ ### Example 2: Score with MAS model
193
+ **Request**: "predict churn for age=45, income=60000 with model churn_predictor"
194
+ 1. Find model churn_predictor.mas using find-resources strategy
195
+ 2. Score: `sas-score-mas-score({ model: "churn_predictor", scenario: { age: 45, income: 60000 } })`
196
+ 3. Return: `{ age: 45, income: 60000, churn_probability: 0.23, prediction: "no_churn" }`
197
+
198
+ ### Example 3: Score table rows
199
+ **Request**: "score all active customers with model risk_model and table Public.customers"
200
+ 1. Find model risk_model.mad using find-resources strategy
201
+ 2. Find table Public.customers using find-resources strategy
202
+ 3. Read: `sas-score-read-table({ lib: "Public", table: "customers", server: "cas", where: "status='active'" })`
203
+ 4. Score each row with risk_model
204
+ 5. Return: customers with risk_score appended
205
+
206
+ ### Example 4: Score with SCR model
207
+ **Request**: "score age=50, income=75000 with model loan.scr"
208
+ 1. Prepare: SCR URL for "loan"
209
+ 2. Score: `sas-score-scr-score({ url: "loan", scenario: { age: 50, income: 75000 } })`
210
+ 3. Return predictions from SCR endpoint
package/README.md CHANGED
@@ -196,7 +196,15 @@ NODE_EXTRA_CA_CERTS=c:\Users\<your_username>\AppData\Local\mkcert\rootCA.pem
196
196
  ```
197
197
 
198
198
  ## License
199
- This project is licensed under the [Apache 2.0 license](LICENSE).
199
+ This project is licensed under the Apache License 2.0. See [LICENSE](LICENSE).
200
+
201
+ The container image published from this repository also includes third-party software, each component under its own license:
202
+
203
+ - The npm dependencies that ship with this project, along with their respective licenses, are listed in [LICENSES.json](LICENSES.json).
204
+ - 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).
205
+
206
+ As with any container image, direct and indirect dependencies are governed by their own licenses.
207
+ Users of the published container image are responsible for ensuring that their use complies with all applicable licenses.
200
208
 
201
209
  ## Additional Resources
202
210
 
package/cli.js CHANGED
@@ -9,15 +9,11 @@
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';
17
15
  import { randomUUID } from 'node:crypto';
18
16
 
19
- //import refreshToken from './src/toolHelpers/refreshToken.js';
20
- //import getOptsViya from './src/toolHelpers/getOptsViya.js';
21
17
  import readCerts from './src/toolHelpers/readCerts.js';
22
18
 
23
19
  import { fileURLToPath } from 'url';
@@ -27,7 +23,8 @@ import setupSkills from './src/setupSkills.js';
27
23
  import { parseArgs } from "node:util";
28
24
 
29
25
  import NodeCache from 'node-cache';
30
- import { be } from 'zod/locales';
26
+ //import { be } from 'zod/locales';
27
+ //import { auth } from '@modelcontextprotocol/sdk/client/auth';
31
28
  //import getOpts from './src/toolHelpers/getOpts.js';
32
29
 
33
30
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -72,11 +69,6 @@ const args = parseArgs({
72
69
  short: 'c',
73
70
  description: 'Client ID for authentication'
74
71
  },
75
- clientsecret: {
76
- type: 'string',
77
- short: 's',
78
- description: 'Client Secret for authentication'
79
- },
80
72
  profile: {
81
73
  type: 'string',
82
74
  description: 'SAS CLI profile name'
@@ -107,6 +99,17 @@ const args = parseArgs({
107
99
  alias: 'mcpclient',
108
100
  description: 'MCP client name (github, claude...). Defaults to \'github\''
109
101
  },
102
+ folder: {
103
+ type: 'string',
104
+ short: 'f',
105
+ description: 'Subfolder under the client folder to copy the skills to, used to have different set of skills for different agents under the same client'
106
+
107
+ },
108
+ skills: {
109
+ type: 'string',
110
+ short: 's',
111
+ description: 'Copies the skills for .github and .claude to the user home directory under .clientname (e.g. .github) or current directory if client name starts with dot (e.g. ./.github), used to have different set of skills for different clients'
112
+ },
110
113
  help: {
111
114
  type: 'boolean',
112
115
  short: 'h',
@@ -125,28 +128,28 @@ const args = parseArgs({
125
128
  // Handle help flag
126
129
  if (args.values.help) {
127
130
  console.error(`
128
- SAS Viya Scoring Expert Agent - Version: ${JSON.parse(pkg).version}
131
+ sas-score-mcp-serverjs - Version: ${JSON.parse(pkg).version}
129
132
 
130
133
  Usage: npx @sassoftware/sas-score-mcp-serverjs@dev [options]
131
134
 
132
135
  Options:
133
136
  Minimal options:
134
137
  -v, --viya <url> Viya server URL
135
- -c, --clientid <id> Client ID for oauth authentication(pkce preferred. default: vscodemcp)
136
138
 
137
139
  MCP server options:
138
140
  -t, --mcptype <type> MCP server type: http or stdio (default: http)
139
141
  -m, --mcphost <host> MCP server host - can be remote URL - (default: http://localhost:8080)
140
142
 
141
- Agent options:
142
- --agent Enable agent mode with a pre-configured set of skills based on the client specified (default: false)
143
- --client <name> MCP client name (github, claude...). Defaults to 'github'.Use to install skills
144
143
  Authentication options:
144
+ -c, --clientid <id> Client ID for oauth authentication(pkce preferred. default: vscodemcp)
145
145
  -a, --authflow <flow> Authentication flow: oauth, oauthclient, sascli, code, token(default oauth)
146
- -s, --clientsecret <secret> Client Secret for oauth authentication (not needed for pkce)
147
146
  --profile <name> SAS CLI profile name for sascli flow (default: Default)
148
147
  --config <path> SAS CLI config directory for sascli flow (default: user home directory)
149
148
 
149
+ Agent/skills options:
150
+ -s, --skills <name> Copies the skills for .github and .claude
151
+ -f, --folder <folder> Subfolder to copy the skills to. ex: ./github/<folder>, used to have different set of skills for different agents under the same client.
152
+
150
153
  Other options:
151
154
  -p, --port <port> Port to run the server on (default: 8080)
152
155
  --https Use HTTPS for the server (default: false)
@@ -165,6 +168,13 @@ Environment Variables:
165
168
  `);
166
169
  process.exit(0);
167
170
  }
171
+ if (args.values.skills) {
172
+ console.error(`[Note] Settings up skills for ${args.values.skills }`);
173
+ setupSkills(args.values.skills, args.values.folder);;
174
+ console.error(`[Note] Skills setup completed. `);
175
+ process.exit(0);
176
+ }
177
+
168
178
  console.error('Parsed command line arguments:', args.values);
169
179
  // read env file and then override with command line arguments
170
180
  if (args.values.env) {
@@ -194,7 +204,7 @@ process.env.AUTHFLOW = args.values.authflow || process.env.AUTHFLOW || 'oauth';
194
204
  process.env.MCPCLIENT = args.values.client || process.env.MCPCLIENT || 'github';
195
205
  process.env.VIYA_SERVER = args.values.viya || process.env.VIYA_SERVER;
196
206
  process.env.CLIENTID = args.values.clientid || process.env.CLIENTID || 'vscodemcp';
197
- process.env.CLIENTSECRET = args.values.clientsecret || process.env.CLIENTSECRET || null;
207
+ process.env.CLIENTSECRET = null;
198
208
  process.env.SAS_CLI_PROFILE = args.values.profile || process.env.SAS_CLI_PROFILE || 'Default';
199
209
  process.env.SAS_CLI_CONFIG = args.values.config || process.env.SAS_CLI_CONFIG || os.homedir(); // default to user home directory
200
210
  process.env.CASSERVER = args.values.casserver || process.env.CASSERVER || 'cas-shared-default';
@@ -219,6 +229,7 @@ if (args.values.version) {
219
229
  console.error(`[Note] MCP client set to: ${process.env.CLIENT}`);
220
230
 
221
231
 
232
+
222
233
  /********************************* */
223
234
  const BRAND = 'sas-score'
224
235
  /********************************* */
@@ -256,8 +267,10 @@ let mcpHost = process.env.MCPHOST;
256
267
  if (authFlow === 'oauth' || authFlow === 'oauthclient') {
257
268
  authFlow = 'bearer';
258
269
  authExternal = (authFlow === 'oauthclient') ? true : false;
270
+ } else if (authFlow === 'bearer') {
271
+ authExternal = true; // in bearer token flow we assume the token is generated externally and passed in via env variable or token file, so we set authExternal to true to indicate that
259
272
  }
260
- let autoLogon = process.env.AUTOLOGON != null ? process.env.AUTOLOGON.toUpperCase() : "FALSE";
273
+ let autoLogon = process.env.AUTOLOGON != null ? process.env.AUTOLOGON.toUpperCase() : "FALSE";
261
274
  const appEnvBase = {
262
275
  version: version,
263
276
  mcpType: mcpType,
@@ -373,10 +386,13 @@ if (appEnvBase.TOKENFILE != null) {
373
386
 
374
387
  // setup skills based on client before mcp initialization
375
388
  //
389
+
390
+
391
+
376
392
  if (process.env.AGENT === 'TRUE') {
377
393
  if (process.env.CLIENT !== 'none') {
378
394
  console.error(`[Note] Setting up skills for client: ${process.env.CLIENT}...`);
379
- setupSkills(process.env.CLIENT);
395
+ setupSkills(process.env.CLIENT, args.values.folder);
380
396
  }
381
397
  } else {
382
398
  console.error(`[Note] Agent mode not enabled`);
@@ -453,14 +469,8 @@ if (mcpType === 'stdio') {
453
469
 
454
470
  } else {
455
471
  console.error('[Note] Starting HTTP MCP server...');
456
- if (useHapi === true) {
457
- process.env.AUTHTYPE = null;
458
- await hapiMcpServer(mcpServer, sessionCache, appEnvBase);
459
- console.error('[Note] Using HAPI HTTP server...')
460
- } else {
461
- await expressMcpServer(mcpServer, sessionCache, appEnvBase);
462
- console.error('[Note] MCP HTTP express server started on port ' + appEnvBase.PORT);
463
- }
472
+ await expressMcpServer(mcpServer, sessionCache, appEnvBase);
473
+ console.error('[Note] MCP HTTP express server started on port ' + appEnvBase.PORT);
464
474
  }
465
475
 
466
476
  // 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-2",
3
+ "version": "1.0.1-22",
4
4
  "description": "A mcp server for SAS Viya",
5
5
  "author": "Deva Kumar <deva.kumar@sas.com>",
6
6
  "license": "Apache-2.0",
@@ -46,11 +46,10 @@
46
46
  ],
47
47
  "dependencies": {
48
48
  "@modelcontextprotocol/sdk": "^1.29.0",
49
- "@sassoftware/restaf": "^5.6.0",
50
- "@sassoftware/restafedit": "^3.11.1-10",
51
- "@sassoftware/restaflib": "^5.6.0",
52
- "@sassoftware/viya-serverjs": "^0.6.3-0",
53
- "axios": "^1.13.2",
49
+ "@sassoftware/restaf": "^5.7.2",
50
+ "@sassoftware/restafedit": "^3.10.5",
51
+ "@sassoftware/restaflib": "^5.7.2",
52
+ "axios": "^1.13.5",
54
53
  "body-parser": "^2.2.1",
55
54
  "cors": "^2.8.5",
56
55
  "cross-env": "^10.1.0",
@@ -62,8 +61,8 @@
62
61
  "node-cache": "^5.1.2",
63
62
  "open": "^11.0.0",
64
63
  "selfsigned": "^5.2.0",
65
- "undici": "^7.16.0",
66
- "uuid": "^13.0.0",
64
+ "undici": "^7.24.0",
65
+ "uuid": "^14.0.0",
67
66
  "zod": "^4.2.1"
68
67
  },
69
68
  "devDependencies": {
@@ -6,7 +6,7 @@ import os from 'os';
6
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
7
  // Paths
8
8
  let client = (process.env.CLIENTNAME == null) ? '.github' : `.${process.env.CLIENTNAME.toLowerCase()}`;
9
- const source = path.join(__dirname, `../.skills`);
9
+ const source = path.join(__dirname, `../.github`);
10
10
  const destination = path.join(process.cwd(), client);
11
11
  // const destination = path.join(os.homedir(), client)
12
12
  console.error(`📁 Copying ${source} to ${destination}...`);
@@ -75,7 +75,7 @@ async function callback(req, res, pkceStore, codeStore, appContext) {
75
75
  // which was part of the payload from the client to /oauth/authorize
76
76
  // we trust since it was associated with the valid PKCE state
77
77
  console.error("[Note] OAuth callback complete, redirecting to MCP client");
78
- console.log(pending.clientRedirectUri.toString())
78
+ console.error(pending.clientRedirectUri.toString())
79
79
  return res.redirect(`${pending.clientRedirectUri}?${redirectParams}`);
80
80
  } catch (err) {
81
81
  console.error("[Error] OAuth callback handler error:", err);
@@ -34,7 +34,7 @@ function processHeaders(req, res, next, cache, appContext) {
34
34
  let token = (hdr != null) ? hdr.slice(7) : null;
35
35
  //console.error("[Note] Authorization token", token);
36
36
  debugger;
37
- console.log('>>>',appContext.AUTHFLOW);
37
+ console.error('>>>',appContext.AUTHFLOW);
38
38
  if (appContext.AUTHFLOW === 'bearer') {
39
39
  debugger;
40
40
  let startAuth = false;