@sassoftware/sas-score-mcp-serverjs 0.4.1-7 → 1.0.0
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-viya-scoring-expert.md +58 -0
- package/.skills/copilot-instructions.md +147 -0
- package/.skills/skills/sas-find-library-smart/SKILL.md +154 -0
- package/.skills/skills/sas-list-tables-smart/SKILL.md +127 -0
- package/.skills/skills/sas-read-and-score/SKILL.md +111 -0
- package/{skills → .skills/skills}/sas-read-strategy/SKILL.md +43 -30
- package/.skills/skills/sas-request-classifier/SKILL.md +69 -0
- package/{skills → .skills/skills}/sas-score-workflow/SKILL.md +49 -35
- package/cli.js +222 -140
- package/package.json +5 -4
- package/scripts/docs/SCORE_SKILL_REFERENCE.md +142 -0
- package/scripts/docs/TOOL_DESCRIPTION_TEMPLATE.md +157 -0
- package/scripts/docs/TOOL_UPDATES_SUMMARY.md +208 -0
- package/scripts/docs/mcp-localhost-config-guide.md +184 -0
- package/scripts/docs/oauth-http-transport.md +96 -0
- package/scripts/docs/sas-mcp-tools-reference.md +600 -0
- package/scripts/getViyaca.sh +1 -0
- package/scripts/optimize_final.py +140 -0
- package/scripts/optimize_tools.py +99 -0
- package/scripts/setup-skills.js +34 -0
- package/scripts/update_descriptions.py +46 -0
- package/scripts/viyatls.sh +3 -0
- package/src/authpkce.js +219 -220
- package/src/createMcpServer.js +10 -5
- package/src/expressMcpServer.js +54 -186
- package/src/oauthHandlers/authorize.js +46 -0
- package/src/oauthHandlers/baseUrl.js +8 -0
- package/src/oauthHandlers/callback.js +96 -0
- package/src/oauthHandlers/getMetadata.js +27 -0
- package/src/oauthHandlers/index.js +7 -0
- package/src/oauthHandlers/token.js +37 -0
- package/src/processHeaders.js +88 -0
- package/src/setupSkills.js +37 -0
- package/src/toolHelpers/_listLibrary.js +0 -1
- package/src/toolHelpers/getLogonPayload.js +5 -1
- package/src/toolHelpers/refreshToken.js +3 -2
- package/src/toolHelpers/refreshTokenOauth.js +3 -3
- package/src/toolSet/.claude/settings.local.json +2 -1
- package/src/toolSet/findModel.js +1 -1
- package/src/toolSet/findTable.js +3 -3
- package/src/toolSet/modelScore.js +2 -2
- package/src/toolSet/runJob.js +81 -81
- package/src/toolSet/runJobdef.js +82 -82
- package/skills/mcp-tool-description-optimizer/SKILL.md +0 -129
- package/skills/mcp-tool-description-optimizer/references/examples.md +0 -123
- package/skills/sas-read-and-score/SKILL.md +0 -91
package/cli.js
CHANGED
|
@@ -23,9 +23,11 @@ import readCerts from './src/toolHelpers/readCerts.js';
|
|
|
23
23
|
import { fileURLToPath } from 'url';
|
|
24
24
|
import { dirname, join } from 'path';
|
|
25
25
|
import os from 'os';
|
|
26
|
+
import setupSkills from './src/setupSkills.js';
|
|
26
27
|
import { parseArgs } from "node:util";
|
|
27
28
|
|
|
28
29
|
import NodeCache from 'node-cache';
|
|
30
|
+
import { be } from 'zod/locales';
|
|
29
31
|
//import getOpts from './src/toolHelpers/getOpts.js';
|
|
30
32
|
|
|
31
33
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -45,15 +47,40 @@ const args = parseArgs({
|
|
|
45
47
|
short: 'm',
|
|
46
48
|
description: 'MCP server type (http or stdio)'
|
|
47
49
|
},
|
|
50
|
+
https: {
|
|
51
|
+
type: 'boolean',
|
|
52
|
+
description: 'Use HTTPS for the server (default: FALSE)'
|
|
53
|
+
},
|
|
54
|
+
'skills-folder': {
|
|
55
|
+
type: 'string',
|
|
56
|
+
short: 'f',
|
|
57
|
+
description: 'Skills folder name'
|
|
58
|
+
},
|
|
59
|
+
|
|
48
60
|
viya: {
|
|
49
61
|
type: 'string',
|
|
50
62
|
short: 'v',
|
|
51
63
|
description: 'Viya server URL'
|
|
52
64
|
},
|
|
65
|
+
mcphost: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
short: 'm',
|
|
68
|
+
description: 'MCP server host (default: http://localhost:8080)'
|
|
69
|
+
},
|
|
53
70
|
authflow: {
|
|
54
71
|
type: 'string',
|
|
55
72
|
short: 'a',
|
|
56
|
-
description: 'Authentication flow (sascli, code, token)'
|
|
73
|
+
description: 'Authentication flow (sascli, code, token, oauth, oauth,oauthclient)'
|
|
74
|
+
},
|
|
75
|
+
clientid: {
|
|
76
|
+
type: 'string',
|
|
77
|
+
short: 'c',
|
|
78
|
+
description: 'Client ID for authentication'
|
|
79
|
+
},
|
|
80
|
+
clientsecret: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
short: 's',
|
|
83
|
+
description: 'Client Secret for authentication'
|
|
57
84
|
},
|
|
58
85
|
profile: {
|
|
59
86
|
type: 'string',
|
|
@@ -63,10 +90,22 @@ const args = parseArgs({
|
|
|
63
90
|
type: 'string',
|
|
64
91
|
description: 'SAS CLI config directory'
|
|
65
92
|
},
|
|
66
|
-
|
|
93
|
+
casserver: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
description: 'CAS server name (default: cas-shared-default)'
|
|
96
|
+
},
|
|
97
|
+
computecontext: {
|
|
98
|
+
type: 'string',
|
|
99
|
+
description: 'Compute session name or context (default: SAS Job Execution compute context)'
|
|
100
|
+
},
|
|
101
|
+
env: {
|
|
67
102
|
type: 'string',
|
|
68
103
|
short: 'e',
|
|
69
|
-
description: 'Environment file path'
|
|
104
|
+
description: 'Environment file path (default: .env in current working directory)'
|
|
105
|
+
},
|
|
106
|
+
client: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
description: 'MCP client name (github, claude...). Defaults to \'github\''
|
|
70
109
|
},
|
|
71
110
|
help: {
|
|
72
111
|
type: 'boolean',
|
|
@@ -75,13 +114,9 @@ const args = parseArgs({
|
|
|
75
114
|
},
|
|
76
115
|
version: {
|
|
77
116
|
type: 'boolean',
|
|
78
|
-
short: 'v',
|
|
79
117
|
description: 'Show version'
|
|
80
118
|
},
|
|
81
|
-
|
|
82
|
-
type: 'boolean',
|
|
83
|
-
description: 'Install bundled skills to ~/.claude/skills/'
|
|
84
|
-
}
|
|
119
|
+
|
|
85
120
|
},
|
|
86
121
|
strict: false,
|
|
87
122
|
allowPositionals: false
|
|
@@ -90,27 +125,86 @@ const args = parseArgs({
|
|
|
90
125
|
// Handle help flag
|
|
91
126
|
if (args.values.help) {
|
|
92
127
|
console.error(`
|
|
93
|
-
|
|
128
|
+
SAS Viya Scoring Expert Agent - Version: ${JSON.parse(pkg).version}
|
|
129
|
+
|
|
130
|
+
Usage: npx @sassoftware/sas-score-mcp-serverjs@dev [options]
|
|
94
131
|
|
|
95
132
|
Options:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
133
|
+
Minimal options:
|
|
134
|
+
-v, --viya <url> Viya server URL
|
|
135
|
+
-c, --clientid <id> Client ID for oauth authentication(pkce preferred. default: vscodemcp)
|
|
136
|
+
|
|
137
|
+
MCP server options:
|
|
138
|
+
-t, --mcptype <type> MCP server type: http or stdio (default: http)
|
|
139
|
+
-m, --mcphost <host> MCP server host - can be remote URL - (default: http://localhost:8080)
|
|
140
|
+
--client <name> MCP client name (github, claude...). Defaults to 'github'.Use to install skills
|
|
141
|
+
Authentication options:
|
|
142
|
+
-a, --authflow <flow> Authentication flow: oauth, oauthclient, sascli, code, token(default oauth)
|
|
143
|
+
-s, --clientsecret <secret> Client Secret for authentication(if necessary). See clientid option as well.
|
|
144
|
+
--profile <name> SAS CLI profile name for sascli flow (default: Default)
|
|
145
|
+
--config <path> SAS CLI config directory for sascli flow (default: user home directory)
|
|
146
|
+
|
|
147
|
+
Other options:
|
|
148
|
+
-p, --port <port> Port to run the server on (default: 8080)
|
|
149
|
+
--https Use HTTPS for the server (default: false)
|
|
150
|
+
--casserver <name> CAS server name (default: cas-shared-default)
|
|
151
|
+
--computecontext <name> Compute session name or context (default: SAS Job Execution compute context)
|
|
152
|
+
|
|
153
|
+
-e, --envfile <path> Environment file path
|
|
154
|
+
-h, --help Show this help message
|
|
155
|
+
--version Show version
|
|
156
|
+
|
|
106
157
|
|
|
107
158
|
Environment Variables:
|
|
108
159
|
Use .env file or set environment variables for configuration.
|
|
160
|
+
A alternative to cmd line arguments, and in some cases required for sensitive information like client secrets.
|
|
109
161
|
See README.md for more information.
|
|
110
162
|
`);
|
|
111
163
|
process.exit(0);
|
|
112
164
|
}
|
|
113
165
|
|
|
166
|
+
// read env file and then override with command line arguments
|
|
167
|
+
if (args.values.env) {
|
|
168
|
+
console.error('Working Directory', process.cwd());
|
|
169
|
+
let envf = process.cwd() + '\\' + args.values.env;
|
|
170
|
+
//__dirname + '\\.env';
|
|
171
|
+
console.error('Env file:', envf);
|
|
172
|
+
if (fs.existsSync(envf)) {
|
|
173
|
+
console.error(`Loading environment variables from ${envf}...`);
|
|
174
|
+
let e = iconfig(envf); // avoid dotenv since it writes to console.error
|
|
175
|
+
console.error('[Note]: Environment variables loaded from .env file...');
|
|
176
|
+
console.error('Loaded env variables:', e);
|
|
177
|
+
// dotenvExpand.expand(e);
|
|
178
|
+
} else {
|
|
179
|
+
console.error(
|
|
180
|
+
'[Note]: No env file found, Using default environment variables...'
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Apply command line arguments to override environment variables
|
|
185
|
+
|
|
186
|
+
process.env.PORT = process.env.PORT || '8080';
|
|
187
|
+
process.env.HTTPS = (args.values.https) ? 'TRUE' : 'FALSE';
|
|
188
|
+
process.env.MCPTYPE = args.values.mcptype || process.env.MCPTYPE || 'http';
|
|
189
|
+
process.env.MCPHOST = args.values.mcphost || process.env.MCPHOST || 'http://localhost:8080';
|
|
190
|
+
process.env.AUTHFLOW = args.values.authflow || process.env.AUTHFLOW || 'oauth';
|
|
191
|
+
process.env.MCPCLIENT = args.values.client || process.env.MCPCLIENT || 'github';
|
|
192
|
+
process.env.VIYA_SERVER = args.values.viya || process.env.VIYA_SERVER || null;
|
|
193
|
+
process.env.CLIENTID = args.values.clientid || process.env.CLIENTID || 'vscodemcp';
|
|
194
|
+
process.env.CLIENTSECRET = args.values.clientsecret || process.env.CLIENTSECRET || null;
|
|
195
|
+
process.env.SAS_CLI_PROFILE = args.values.profile || process.env.SAS_CLI_PROFILE || 'Default';
|
|
196
|
+
process.env.SAS_CLI_CONFIG = args.values.config || process.env.SAS_CLI_CONFIG || process.env.HOME; // default to user home directory
|
|
197
|
+
process.env.CASSERVER = args.values.casserver || process.env.CASSERVER || 'cas-shared-default';
|
|
198
|
+
process.env.COMPUTECONTEXT = args.values.computecontext || process.env.COMPUTECONTEXT || 'SAS Job Execution compute context';
|
|
199
|
+
process.env.APPHOST = 'localhost';
|
|
200
|
+
process.env.CLIENT = args.values.client || process.env.CLIENT || 'github';
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
process.env.SAMESITE = 'Lax,secure';
|
|
205
|
+
process.env.APPHOST = '0.0.0.0';
|
|
206
|
+
process.env.APPNAME = 'sas-score-mcp-serverjs';
|
|
207
|
+
|
|
114
208
|
// Handle version flag
|
|
115
209
|
if (args.values.version) {
|
|
116
210
|
let pkgJson = JSON.parse(pkg);
|
|
@@ -118,16 +212,29 @@ if (args.values.version) {
|
|
|
118
212
|
process.exit(0);
|
|
119
213
|
}
|
|
120
214
|
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
215
|
+
// copy the skills to directory based on the client name, so that different MCP clients can have different sets of skills if needed
|
|
216
|
+
// the -client indicates the current mcp client
|
|
217
|
+
console.error(`[Note] MCP client set to: ${process.env.CLIENT}`);
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
let client = process.env.CLIENT;
|
|
221
|
+
if (client !== 'none') {
|
|
222
|
+
console.error(`[Note] Setting up skills for client: ${client}...`);
|
|
223
|
+
setupSkills(client);
|
|
224
|
+
} else {
|
|
225
|
+
console.error(`[Note] No client specified, skipping skill setup...`);
|
|
226
|
+
}
|
|
125
227
|
|
|
228
|
+
|
|
229
|
+
/*
|
|
230
|
+
if (client !== 'none') {
|
|
231
|
+
let destdir = '.' + client;
|
|
232
|
+
let skillsDest = join(os.homedir(), destdir,'skills');
|
|
233
|
+
const skillsSrc = join(__dirname, '.github');
|
|
126
234
|
if (!fs.existsSync(skillsSrc)) {
|
|
127
235
|
console.error('No skills directory found in this package.');
|
|
128
236
|
process.exit(1);
|
|
129
237
|
}
|
|
130
|
-
|
|
131
238
|
fs.mkdirSync(skillsDest, { recursive: true });
|
|
132
239
|
|
|
133
240
|
const skills = fs.readdirSync(skillsSrc, { withFileTypes: true })
|
|
@@ -135,77 +242,26 @@ if (args.values['install-skills']) {
|
|
|
135
242
|
.map(d => d.name);
|
|
136
243
|
|
|
137
244
|
if (skills.length === 0) {
|
|
138
|
-
console.error('No skills found to install.');
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
for (const skill of skills) {
|
|
143
|
-
const src = join(skillsSrc, skill);
|
|
144
|
-
const dest = join(skillsDest, skill);
|
|
145
|
-
fs.cpSync(src, dest, { recursive: true });
|
|
146
|
-
console.error(` installed: ${skill}`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
console.error(`\n${skills.length} skill(s) installed to ${skillsDest}`);
|
|
150
|
-
console.error('Restart Claude Code to activate the new skills.');
|
|
151
|
-
process.exit(0);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (process.env.ENVFILE === 'FALSE') {
|
|
155
|
-
//use this when using remote mcp server and no .env file is desired
|
|
156
|
-
console.error('[Note]: Skipping .env file as ENVFILE is set to FALSE...');
|
|
157
|
-
} else {
|
|
158
|
-
console.error('Working Directory', process.cwd());
|
|
159
|
-
let envf = process.env.ENVFILE || (process.cwd() + '\\.env');
|
|
160
|
-
//__dirname + '\\.env';
|
|
161
|
-
console.error('Env file:', envf);
|
|
162
|
-
if (fs.existsSync(envf)) {
|
|
163
|
-
console.error(`Loading environment variables from ${envf}...`);
|
|
164
|
-
let e = iconfig(envf); // avoid dotenv since it writes to console.error
|
|
165
|
-
console.error('[Note]: Environment variables loaded from .env file...');
|
|
166
|
-
console.error('Loaded env variables:', e);
|
|
167
|
-
// dotenvExpand.expand(e);
|
|
245
|
+
console.error('[Note]No skills found to install.');
|
|
168
246
|
} else {
|
|
169
|
-
console.error(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (args.values.mcptype) {
|
|
182
|
-
process.env.MCPTYPE = args.values.mcptype;
|
|
183
|
-
console.error(`[Note] MCPTYPE set from command line: ${args.values.mcptype}`);
|
|
184
|
-
}
|
|
247
|
+
console.error(`Installing ${skills.length} skill(s) to ${skillsDest}...`);
|
|
248
|
+
for (const skill of skills) {
|
|
249
|
+
const src = join(skillsSrc, skill);
|
|
250
|
+
const dest = join(skillsDest, skill);
|
|
251
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
252
|
+
console.error(` installed: ${skill}`);
|
|
253
|
+
}
|
|
254
|
+
console.error(`\n installed in cli. ${client}`);
|
|
255
|
+
console.error(`\n${skills.length} skill(s) installed to ${skillsDest}`);
|
|
256
|
+
console.error('[Note] Skills are ready for use.');
|
|
185
257
|
|
|
186
|
-
|
|
187
|
-
process.env.VIYA_SERVER = args.values.viya;
|
|
188
|
-
console.error(`[Note] VIYA_SERVER set from command line: ${args.values.viya}`);
|
|
258
|
+
}
|
|
189
259
|
}
|
|
260
|
+
*/
|
|
190
261
|
|
|
191
|
-
if (args.values.authflow) {
|
|
192
|
-
process.env.AUTHFLOW = args.values.authflow;
|
|
193
|
-
console.error(`[Note] AUTHFLOW set from command line: ${args.values.authflow}`);
|
|
194
|
-
}
|
|
195
262
|
|
|
196
|
-
if (args.values.profile) {
|
|
197
|
-
process.env.SAS_CLI_PROFILE = args.values.profile;
|
|
198
|
-
console.error(`[Note] SAS_CLI_PROFILE set from command line: ${args.values.profile}`);
|
|
199
|
-
}
|
|
200
263
|
|
|
201
|
-
if (args.values.config) {
|
|
202
|
-
process.env.SAS_CLI_CONFIG = args.values.config;
|
|
203
|
-
console.error(`[Note] SAS_CLI_CONFIG set from command line: ${args.values.config}`);
|
|
204
|
-
}
|
|
205
264
|
|
|
206
|
-
if (process.env.APPHOST == null) {
|
|
207
|
-
process.env.APPHOST = 'localhost';
|
|
208
|
-
}
|
|
209
265
|
/********************************* */
|
|
210
266
|
const BRAND = 'sas-score'
|
|
211
267
|
/********************************* */
|
|
@@ -214,16 +270,16 @@ let version = pkgJson.version;
|
|
|
214
270
|
let mcpType = process.env.MCPTYPE || 'http';
|
|
215
271
|
console.error(
|
|
216
272
|
`\nStarting MCP ServerJS - Version: ${pkgJson.version} - ${new Date().toISOString()}\n
|
|
217
|
-
brand: ${process.env.BRAND || BRAND}
|
|
218
|
-
mcpType: ${mcpType}
|
|
219
|
-
viyaServer: ${process.env.VIYA_SERVER}
|
|
273
|
+
brand: ${process.env.BRAND || BRAND}
|
|
274
|
+
mcpType: ${mcpType}
|
|
275
|
+
viyaServer: ${process.env.VIYA_SERVER}`
|
|
220
276
|
);
|
|
221
277
|
// session sessionCache
|
|
222
278
|
// For more robust caching consider products like Redis
|
|
223
279
|
// and storage provided by cloud providers
|
|
224
|
-
|
|
280
|
+
|
|
225
281
|
debugger;
|
|
226
|
-
let sessionCache = new NodeCache({ stdTTL: 24 *60*60, checkperiod: 2 * 60, useClones: false });
|
|
282
|
+
let sessionCache = new NodeCache({ stdTTL: 24 * 60 * 60, checkperiod: 2 * 60, useClones: false });
|
|
227
283
|
|
|
228
284
|
//
|
|
229
285
|
// Load environment variables from .env file if present
|
|
@@ -231,35 +287,25 @@ let sessionCache = new NodeCache({ stdTTL: 24 *60*60, checkperiod: 2 * 60, useCl
|
|
|
231
287
|
// stdio: set the env in the mcp config
|
|
232
288
|
// http: use dotenv-cli to load env before starting the mcp server
|
|
233
289
|
|
|
234
|
-
|
|
235
|
-
// need to tell core what transport to use(http or stdio)
|
|
236
|
-
|
|
237
|
-
// subclasses for sasQuery tool (special use case)
|
|
238
|
-
// to be replaced by the planned adding external tool definition capability
|
|
239
|
-
|
|
240
|
-
let subclassJson = [];
|
|
241
|
-
if (process.env.SUBCLASS != null) {
|
|
242
|
-
console.error(`Using subclass: ${process.env.SUBCLASS}`);
|
|
243
|
-
let subclass = process.env.SUBCLASS;
|
|
244
|
-
if (fs.existsSync(subclass)) {
|
|
245
|
-
console.error(`Loading subclass information from ${subclass}...`);
|
|
246
|
-
let s = fs.readFileSync(subclass, 'utf8');
|
|
247
|
-
subclassJson = JSON.parse(s);
|
|
248
|
-
console.error(`Loaded subclass: ${JSON.stringify(subclassJson, null, 2)}`);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
290
|
// setup base appEnv
|
|
252
291
|
// for stdio this is the _appContext
|
|
253
292
|
// for http each session a copy of this as appEnvTemplate is created in corehttp
|
|
254
293
|
|
|
255
|
-
// backward compability variables
|
|
256
|
-
let clientID = process.env.CLIENTID || process.env.CLIENTIDPW || null;
|
|
257
|
-
let clientSecret = process.env.CLIENTSECRET || process.env.CLIENTSECRETPW || null;
|
|
258
294
|
let https = process.env.HTTPS != null ? process.env.HTTPS.toUpperCase() : "FALSE";
|
|
295
|
+
let authExternal = false;
|
|
296
|
+
let authFlow = process.env.AUTHFLOW;
|
|
297
|
+
let mcpHost = process.env.MCPHOST;
|
|
298
|
+
|
|
299
|
+
if (authFlow === 'oauth' || authFlow === 'oauthclient') {
|
|
300
|
+
authFlow = 'bearer';
|
|
301
|
+
authExternal = (authFlow === 'oauthclient') ? true : false;
|
|
302
|
+
}
|
|
259
303
|
let autoLogon = process.env.AUTOLOGON != null ? process.env.AUTOLOGON.toUpperCase() : "FALSE";
|
|
260
304
|
const appEnvBase = {
|
|
261
305
|
version: version,
|
|
262
|
-
mcpType: mcpType,
|
|
306
|
+
mcpType: mcpType,
|
|
307
|
+
mcpClient: process.env.MCPCLIENT || 'github',
|
|
308
|
+
mcpHost: (process.env.MCPHOST == null) ? 'http://localhost:8080' : process.env.MCPHOST,
|
|
263
309
|
brand: (process.env.BRAND == null) ? BRAND : process.env.BRAND,
|
|
264
310
|
HTTPS: https,
|
|
265
311
|
SAS_CLI_PROFILE: process.env.SAS_CLI_PROFILE || 'Default',
|
|
@@ -267,27 +313,25 @@ const appEnvBase = {
|
|
|
267
313
|
SSLCERT: process.env.SSLCERT || null,
|
|
268
314
|
VIYACERT: process.env.VIYACERT || null,
|
|
269
315
|
|
|
270
|
-
AUTHFLOW:
|
|
316
|
+
AUTHFLOW: authFlow,
|
|
317
|
+
AUTHEXTERNAL: authExternal,
|
|
318
|
+
BEARERTOKEN: null,
|
|
271
319
|
AUTOLOGON: autoLogon,
|
|
272
320
|
VIYA_SERVER: process.env.VIYA_SERVER,
|
|
273
321
|
PORT: process.env.PORT || 8080,
|
|
274
322
|
USERNAME: process.env.USERNAME || null,
|
|
275
323
|
PASSWORD: process.env.PASSWORD || null,
|
|
276
|
-
CLIENTID:
|
|
277
|
-
CLIENTSECRET:
|
|
324
|
+
CLIENTID: process.env.CLIENTID || null,
|
|
325
|
+
CLIENTSECRET: process.env.CLIENTSECRET || null,
|
|
278
326
|
PKCE: process.env.PKCE || null,
|
|
279
327
|
|
|
280
328
|
TOKEN: process.env.TOKEN || null,
|
|
281
329
|
REFRESH_TOKEN: process.env.REFRESH_TOKEN || null,
|
|
282
330
|
TOKENFILE: process.env.TOKENFILE || null,
|
|
283
331
|
TLS_CREATE: process.env.TLS_CREATE || null,
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
toolsets:
|
|
288
|
-
process.env.TOOLSETS != null
|
|
289
|
-
? process.env.TOOLSETS.split(',')
|
|
290
|
-
: ['default'],
|
|
332
|
+
CASSERVER: process.env.CASSERVER,
|
|
333
|
+
COMPUTECONTEXT: process.env.COMPUTECONTEXT,
|
|
334
|
+
|
|
291
335
|
// command line arguments
|
|
292
336
|
cliArgs: args.values,
|
|
293
337
|
// user defined tools
|
|
@@ -307,7 +351,8 @@ const appEnvBase = {
|
|
|
307
351
|
tlsOpts: null,
|
|
308
352
|
oauthInfo: null,
|
|
309
353
|
contexts: {
|
|
310
|
-
AUTHFLOW:
|
|
354
|
+
AUTHFLOW: authFlow,
|
|
355
|
+
AUTHEXTERNAL: authExternal,
|
|
311
356
|
host: process.env.VIYA_SERVER,
|
|
312
357
|
APPHOST: process.env.APPHOST || 'localhost',
|
|
313
358
|
APPNAME: process.env.APPNAME || 'sas-score-mcp-serverjs',
|
|
@@ -316,8 +361,8 @@ const appEnvBase = {
|
|
|
316
361
|
store: null, /* for restaf users */
|
|
317
362
|
storeConfig: {},
|
|
318
363
|
oauthInfo: null,
|
|
319
|
-
CLIENTID:
|
|
320
|
-
CLIENTSECRET:
|
|
364
|
+
CLIENTID: process.env.CLIENTID || null,
|
|
365
|
+
CLIENTSECRET: process.env.CLIENTSECRET || null,
|
|
321
366
|
pkce: process.env.PKCE || null,
|
|
322
367
|
casSession: null, /* restaf cas session object */
|
|
323
368
|
computeSession: null, /* restaf compute session object */
|
|
@@ -332,15 +377,14 @@ const appEnvBase = {
|
|
|
332
377
|
}
|
|
333
378
|
};
|
|
334
379
|
|
|
335
|
-
process.env.APPPORT=appEnvBase.PORT;
|
|
380
|
+
process.env.APPPORT = appEnvBase.PORT;
|
|
381
|
+
let useHapi = process.env.USEHAPI === 'TRUE' ? true : false;
|
|
382
|
+
appEnvBase.useHapi = useHapi;
|
|
336
383
|
|
|
337
384
|
// setup TLS options for viya calls
|
|
338
|
-
console.error('[Note]Viya SSL dir set to: ' + appEnvBase.VIYACERT);
|
|
339
385
|
appEnvBase.contexts.viyaCert = readCerts(appEnvBase.VIYACERT); /* appEnvBase.contexts.viyaCert is set here */
|
|
340
386
|
|
|
341
387
|
// setup TLS options for app server (expressMcpServer or hapiMcpServer)
|
|
342
|
-
|
|
343
|
-
console.error('[Note]App SSL dir set to: ' + appEnvBase.SSLCERT);
|
|
344
388
|
appEnvBase.tlsOpts = readCerts(appEnvBase.SSLCERT);
|
|
345
389
|
appEnvBase.contexts.appCert = appEnvBase.tlsOpts; /* just for completeness */
|
|
346
390
|
|
|
@@ -354,7 +398,7 @@ if (appEnvBase.TOKENFILE != null) {
|
|
|
354
398
|
console.error(`[Note]Loading token from file: ${appEnvBase.TOKENFILE}...`);
|
|
355
399
|
appEnvBase.TOKEN = fs.readFileSync(appEnvBase.TOKENFILE, { encoding: 'utf8' });
|
|
356
400
|
appEnvBase.AUTHFLOW = 'token';
|
|
357
|
-
appEnvBase.
|
|
401
|
+
appEnvBase.contexts.logonPayload = {
|
|
358
402
|
host: appEnvBase.VIYA_SERVER,
|
|
359
403
|
authType: 'server',
|
|
360
404
|
token: appEnvBase.TOKEN,
|
|
@@ -367,10 +411,6 @@ if (appEnvBase.TOKENFILE != null) {
|
|
|
367
411
|
}
|
|
368
412
|
|
|
369
413
|
|
|
370
|
-
|
|
371
|
-
// if authflow is cli or code, postpone getting logonPayload until needed
|
|
372
|
-
|
|
373
|
-
|
|
374
414
|
// setup mcpServer (both http and stdio use this)
|
|
375
415
|
// this is singleton - best practices recommend this
|
|
376
416
|
|
|
@@ -381,22 +421,64 @@ let appEnvTemplate = Object.assign({}, appEnvBase);
|
|
|
381
421
|
|
|
382
422
|
sessionCache.set('appEnvTemplate', appEnvTemplate);
|
|
383
423
|
|
|
424
|
+
// prime transport cache
|
|
384
425
|
let transports = {
|
|
385
426
|
"dummy": null
|
|
386
427
|
};
|
|
387
428
|
sessionCache.set('transports', transports);
|
|
429
|
+
let tokenlist = {
|
|
430
|
+
dummy: null
|
|
431
|
+
}
|
|
432
|
+
sessionCache.set('tokenlist', tokenlist);
|
|
388
433
|
|
|
389
434
|
// set this for stdio transport use
|
|
390
435
|
// dummy sessionId for use in the tools
|
|
391
|
-
|
|
392
|
-
console.error('[Note] appEnvBase is', JSON.stringify(appEnvBase, null,2));
|
|
436
|
+
|
|
393
437
|
// creat a dummy sessionId for stdio since there is only one session and transport in that case, and tools need a sessionId to access the appEnvBase and contexts
|
|
394
438
|
let sessionId = randomUUID();
|
|
395
439
|
sessionCache.set(sessionId, appEnvBase);
|
|
396
440
|
sessionCache.set('currentId', sessionId);
|
|
397
|
-
useHapi = false;
|
|
398
|
-
if (mcpType === 'stdio') {
|
|
399
441
|
|
|
442
|
+
console.error('===================================================================');
|
|
443
|
+
console.error(`MCP ServerJS - Version: ${pkgJson.version} - ${new Date().toISOString()}`);
|
|
444
|
+
console.error(`
|
|
445
|
+
Usage: sas-score-mcp-serverjs [options]
|
|
446
|
+
|
|
447
|
+
Options:
|
|
448
|
+
Minimal options:
|
|
449
|
+
VIYA_SERVER ${appEnvBase.VIYA_SERVER}
|
|
450
|
+
CLIENTID ${appEnvBase.CLIENTID}
|
|
451
|
+
|
|
452
|
+
MCP server options:
|
|
453
|
+
MCPTYPE ${appEnvBase.mcpType}
|
|
454
|
+
MCPHOST ${appEnvBase.mcpHost}
|
|
455
|
+
PORT ${appEnvBase.PORT}
|
|
456
|
+
HTTPS ${appEnvBase.contexts.HTTPS}
|
|
457
|
+
CLIENT ${appEnvBase.mcpClient}
|
|
458
|
+
|
|
459
|
+
Authentication options:
|
|
460
|
+
AUTHFLOW ${process.env.AUTHFLOW}
|
|
461
|
+
CLIENTSECRET ${appEnvBase.CLIENTSECRET}
|
|
462
|
+
PROFILE ${appEnvBase.SAS_CLI_PROFILE}
|
|
463
|
+
CONFIG ${appEnvBase.SAS_CLI_CONFIG}
|
|
464
|
+
|
|
465
|
+
Other options:
|
|
466
|
+
CASSERVER ${appEnvBase.CASSERVER}
|
|
467
|
+
COMPUTECONTEXT ${appEnvBase.COMPUTECONTEXT}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
`);
|
|
471
|
+
|
|
472
|
+
console.error('===================================================================');
|
|
473
|
+
console.error(`
|
|
474
|
+
[Note] The SAS Viya Scoring Expert agent has been installed successfully.
|
|
475
|
+
Depending on the client you are using, the agent might not be active
|
|
476
|
+
If the agent does not appear in the agent dropdown list your options are:
|
|
477
|
+
- use the /subagent command
|
|
478
|
+
- exit this app and issue the npx command to restart the server
|
|
479
|
+
`);
|
|
480
|
+
|
|
481
|
+
if (mcpType === 'stdio') {
|
|
400
482
|
console.error('[Note] Setting up stdio transport with sessionId:', sessionId);
|
|
401
483
|
console.error('[Note] Used in setting up tools and some persistence(not all).');
|
|
402
484
|
await coreSSE(mcpServer);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sassoftware/sas-score-mcp-serverjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "A mcp server for SAS Viya",
|
|
5
5
|
"author": "Deva Kumar <deva.kumar@sas.com>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"deploy": "bash ./deploy.sh",
|
|
19
19
|
"push2acr": "cd docker && bash ./push2acr.sh",
|
|
20
20
|
"bump": "npm version prerelease",
|
|
21
|
-
"pub": "npm publish --tag dev --access public"
|
|
21
|
+
"pub": "npm publish --tag dev --access public",
|
|
22
|
+
"setup-skills": "node scripts/setup-skills.js"
|
|
22
23
|
},
|
|
23
24
|
"repository": "https://github.com/sassoftware/sas-score-mcp-serverjs",
|
|
24
25
|
"keywords": [
|
|
@@ -40,7 +41,8 @@
|
|
|
40
41
|
"cli.js",
|
|
41
42
|
"openApi.json",
|
|
42
43
|
"openApi.yaml",
|
|
43
|
-
"
|
|
44
|
+
"scripts",
|
|
45
|
+
".skills"
|
|
44
46
|
],
|
|
45
47
|
"dependencies": {
|
|
46
48
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
@@ -71,7 +73,6 @@
|
|
|
71
73
|
"@babel/preset-env": "^7.28.5",
|
|
72
74
|
"@types/debug": "^4.1.12",
|
|
73
75
|
"@types/node": "^25.0.3",
|
|
74
|
-
"npm-check-updates": "^19.2.0",
|
|
75
76
|
"rimraf": "^6.1.2",
|
|
76
77
|
"typescript": "^5.9.3"
|
|
77
78
|
}
|