@sassoftware/sas-score-mcp-serverjs 1.0.1-2 → 1.0.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.
@@ -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
+
200
+
201
+ This project is licensed under the Apache License 2.0. See LICENSE.
202
+
203
+ The container image published from this repository also includes third-party software, each component under its own license:
204
+
205
+ The npm dependencies that ship with this project, along with their respective licenses, are listed in LICENSES.json.
206
+ 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.
207
+ As with any container image, direct and indirect dependencies are governed by their own licenses. 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';
@@ -72,11 +68,6 @@ const args = parseArgs({
72
68
  short: 'c',
73
69
  description: 'Client ID for authentication'
74
70
  },
75
- clientsecret: {
76
- type: 'string',
77
- short: 's',
78
- description: 'Client Secret for authentication'
79
- },
80
71
  profile: {
81
72
  type: 'string',
82
73
  description: 'SAS CLI profile name'
@@ -107,6 +98,17 @@ const args = parseArgs({
107
98
  alias: 'mcpclient',
108
99
  description: 'MCP client name (github, claude...). Defaults to \'github\''
109
100
  },
101
+ folder: {
102
+ type: 'string',
103
+ short: 'f',
104
+ 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'
105
+
106
+ },
107
+ skills: {
108
+ type: 'string',
109
+ short: 's',
110
+ 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'
111
+ },
110
112
  help: {
111
113
  type: 'boolean',
112
114
  short: 'h',
@@ -125,28 +127,28 @@ const args = parseArgs({
125
127
  // Handle help flag
126
128
  if (args.values.help) {
127
129
  console.error(`
128
- SAS Viya Scoring Expert Agent - Version: ${JSON.parse(pkg).version}
130
+ sas-score-mcp-serverjs - Version: ${JSON.parse(pkg).version}
129
131
 
130
132
  Usage: npx @sassoftware/sas-score-mcp-serverjs@dev [options]
131
133
 
132
134
  Options:
133
135
  Minimal options:
134
136
  -v, --viya <url> Viya server URL
135
- -c, --clientid <id> Client ID for oauth authentication(pkce preferred. default: vscodemcp)
136
137
 
137
138
  MCP server options:
138
139
  -t, --mcptype <type> MCP server type: http or stdio (default: http)
139
140
  -m, --mcphost <host> MCP server host - can be remote URL - (default: http://localhost:8080)
140
141
 
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
142
  Authentication options:
143
+ -c, --clientid <id> Client ID for oauth authentication(pkce preferred. default: vscodemcp)
145
144
  -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
145
  --profile <name> SAS CLI profile name for sascli flow (default: Default)
148
146
  --config <path> SAS CLI config directory for sascli flow (default: user home directory)
149
147
 
148
+ Agent/skills options:
149
+ -s, --skills <name> Copies the skills for .github and .claude
150
+ -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.
151
+
150
152
  Other options:
151
153
  -p, --port <port> Port to run the server on (default: 8080)
152
154
  --https Use HTTPS for the server (default: false)
@@ -165,6 +167,13 @@ Environment Variables:
165
167
  `);
166
168
  process.exit(0);
167
169
  }
170
+ if (args.values.skills) {
171
+ console.error(`[Note] Settings up skills for ${args.values.skills }`);
172
+ setupSkills(args.values.skills, args.values.folder);;
173
+ console.error(`[Note] Skills setup completed. `);
174
+ process.exit(0);
175
+ }
176
+
168
177
  console.error('Parsed command line arguments:', args.values);
169
178
  // read env file and then override with command line arguments
170
179
  if (args.values.env) {
@@ -194,7 +203,7 @@ process.env.AUTHFLOW = args.values.authflow || process.env.AUTHFLOW || 'oauth';
194
203
  process.env.MCPCLIENT = args.values.client || process.env.MCPCLIENT || 'github';
195
204
  process.env.VIYA_SERVER = args.values.viya || process.env.VIYA_SERVER;
196
205
  process.env.CLIENTID = args.values.clientid || process.env.CLIENTID || 'vscodemcp';
197
- process.env.CLIENTSECRET = args.values.clientsecret || process.env.CLIENTSECRET || null;
206
+ process.env.CLIENTSECRET = null;
198
207
  process.env.SAS_CLI_PROFILE = args.values.profile || process.env.SAS_CLI_PROFILE || 'Default';
199
208
  process.env.SAS_CLI_CONFIG = args.values.config || process.env.SAS_CLI_CONFIG || os.homedir(); // default to user home directory
200
209
  process.env.CASSERVER = args.values.casserver || process.env.CASSERVER || 'cas-shared-default';
@@ -219,6 +228,7 @@ if (args.values.version) {
219
228
  console.error(`[Note] MCP client set to: ${process.env.CLIENT}`);
220
229
 
221
230
 
231
+
222
232
  /********************************* */
223
233
  const BRAND = 'sas-score'
224
234
  /********************************* */
@@ -373,10 +383,13 @@ if (appEnvBase.TOKENFILE != null) {
373
383
 
374
384
  // setup skills based on client before mcp initialization
375
385
  //
386
+
387
+
388
+
376
389
  if (process.env.AGENT === 'TRUE') {
377
390
  if (process.env.CLIENT !== 'none') {
378
391
  console.error(`[Note] Setting up skills for client: ${process.env.CLIENT}...`);
379
- setupSkills(process.env.CLIENT);
392
+ setupSkills(process.env.CLIENT, args.values.folder);
380
393
  }
381
394
  } else {
382
395
  console.error(`[Note] Agent mode not enabled`);
@@ -453,14 +466,8 @@ if (mcpType === 'stdio') {
453
466
 
454
467
  } else {
455
468
  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
- }
469
+ await expressMcpServer(mcpServer, sessionCache, appEnvBase);
470
+ console.error('[Note] MCP HTTP express server started on port ' + appEnvBase.PORT);
464
471
  }
465
472
 
466
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-2",
3
+ "version": "1.0.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",
@@ -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;