@sassoftware/sas-score-mcp-serverjs 0.3.19 → 0.4.1

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/cli.js CHANGED
@@ -16,14 +16,15 @@ import createMcpServer from './src/createMcpServer.js';
16
16
  import fs from 'fs';
17
17
  import { randomUUID } from 'node:crypto';
18
18
 
19
- import refreshToken from './src/toolHelpers/refreshToken.js';
20
- import getOptsViya from './src/toolHelpers/getOptsViya.js';
19
+ //import refreshToken from './src/toolHelpers/refreshToken.js';
20
+ //import getOptsViya from './src/toolHelpers/getOptsViya.js';
21
+ import readCerts from './src/toolHelpers/readCerts.js';
21
22
 
22
23
  import { fileURLToPath } from 'url';
23
24
  import { dirname } from 'path';
24
25
 
25
26
  import NodeCache from 'node-cache';
26
- import getOpts from './src/toolHelpers/getOpts.js';
27
+ //import getOpts from './src/toolHelpers/getOpts.js';
27
28
 
28
29
  const __dirname = dirname(fileURLToPath(import.meta.url));
29
30
 
@@ -70,7 +71,7 @@ console.error(
70
71
  // and storage provided by cloud providers
71
72
  console.error(process.env.COMPUTECONTEXT);
72
73
  debugger;
73
- let sessionCache = new NodeCache({ stdTTL: 0, checkperiod: 2 * 60, useClones: false });
74
+ let sessionCache = new NodeCache({ stdTTL: 60*60, checkperiod: 2 * 60, useClones: false });
74
75
 
75
76
  //
76
77
  // Load environment variables from .env file if present
@@ -167,6 +168,7 @@ const appEnvBase = {
167
168
  casSession: null, /* restaf cas session object */
168
169
  computeSession: null, /* restaf compute session object */
169
170
  viyaCert: null, /* ssl/tsl certificates to connect to viya */
171
+ appCert: null,
170
172
  logonPayload: null, /* viya logon payload to connect to viya */
171
173
  casServerId: null,
172
174
  computeSessonId: null,
@@ -179,10 +181,15 @@ const appEnvBase = {
179
181
  process.env.APPPORT=appEnvBase.PORT;
180
182
 
181
183
  // setup TLS options for viya calls
182
-
183
184
  console.error('[Note]Viya SSL dir set to: ' + appEnvBase.VIYACERT);
184
- await getOptsViya(appEnvBase); /* appEnvBase.contexts.viyaCert is set here */
185
- appEnvBase.tlsOpts = getOpts(appEnvBase);
185
+ appEnvBase.contexts.viyaCert = readCerts(appEnvBase.VIYACERT); /* appEnvBase.contexts.viyaCert is set here */
186
+
187
+ // setup TLS options for app server (expressMcpServer or hapiMcpServer)
188
+
189
+ console.error('[Note]App SSL dir set to: ' + appEnvBase.SSLCERT);
190
+ appEnvBase.tlsOpts = readCerts(appEnvBase.SSLCERT);
191
+ appEnvBase.contexts.appCert = appEnvBase.tlsOpts; /* just for completeness */
192
+
186
193
  appEnvBase.contexts.storeConfig = {
187
194
  casProxy: true,
188
195
  options: { ns: null, proxyServer: null, httpOptions: appEnvBase.contexts.viyaCert }
@@ -205,19 +212,7 @@ if (appEnvBase.TOKENFILE != null) {
205
212
  }
206
213
  }
207
214
 
208
- // handle refresh token flow
209
- // use this for testing only.
210
- if (appEnvBase.REFRESH_TOKEN != null) {
211
- appEnvBase.refreshToken = appEnvBase.REFRESH_TOKEN;
212
- appEnvBase.AUTHFLOW = 'refresh';
213
- let t = await refreshToken(appEnvBase, { token: appEnvBase.REFRESH_TOKEN, host: appEnvBase.VIYA_SERVER });
214
- appEnvBase.contexts.logonPayload = {
215
- host: appEnvBase.VIYA_SERVER,
216
- authType: 'server',
217
- token: t,
218
- tokenType: 'Bearer'
219
- }
220
- }
215
+
221
216
 
222
217
  // if authflow is cli or code, postpone getting logonPayload until needed
223
218
 
@@ -232,12 +227,13 @@ let appEnvTemplate = Object.assign({}, appEnvBase);
232
227
 
233
228
  sessionCache.set('appEnvTemplate', appEnvTemplate);
234
229
 
235
- let transports = {};
230
+ let transports = {'dummy': null};
236
231
  sessionCache.set('transports', transports);
237
232
 
238
233
  // set this for stdio transport use
239
234
  // dummy sessionId for use in the tools
240
- let useHapi = process.env.AUTHFLOW === 'code' ? true : false;
235
+ let useHapi = (process.env.USEHAPI === 'TRUE') ? true : false;
236
+ console.error('[Note] appEnvBase is', JSON.stringify(appEnvBase, null,2));
241
237
  if (mcpType === 'stdio') {
242
238
  let sessionId = randomUUID();
243
239
  sessionCache.set('currentId', sessionId);
@@ -249,6 +245,7 @@ if (mcpType === 'stdio') {
249
245
  } else {
250
246
  console.error('[Note] Starting HTTP MCP server...');
251
247
  if (useHapi === true) {
248
+ process.env.AUTHFLOW = null;;
252
249
  await hapiMcpServer(mcpServer, sessionCache, appEnvBase);
253
250
  console.error('[Note] Using HAPI HTTP server...')
254
251
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sassoftware/sas-score-mcp-serverjs",
3
- "version": "0.3.19",
3
+ "version": "0.4.1",
4
4
  "description": "A mcp server for SAS Viya",
5
5
  "author": "Deva Kumar <deva.kumar@sas.com>",
6
6
  "license": "Apache-2.0",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "type": "module",
12
12
  "scripts": {
13
- "start": "node cli.js",
13
+ "start": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 node cli.js",
14
14
  "testi": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 npx @modelcontextprotocol/inspector",
15
15
  "test": "cd test && node",
16
16
  "debug": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 node --inspect-brk cli.js",
@@ -6,17 +6,14 @@ import express from "express";
6
6
 
7
7
  import https from "https";
8
8
  import cors from "cors";
9
- //import rateLimit from "express-rate-limit";
10
- //import helmet from "helmet";
11
9
  import bodyParser from "body-parser";
12
-
13
10
  import selfsigned from "selfsigned";
14
11
  import openAPIJson from "./openAPIJson.js";
15
- import fs from "fs";
16
12
 
17
13
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
18
14
  import { randomUUID } from "node:crypto";
19
15
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
16
+ import tlogon from "./toolHelpers/tlogon.js";
20
17
 
21
18
 
22
19
  // setup express server
@@ -115,14 +112,16 @@ function requireBearer(req, res, next) {
115
112
  if (hdr != null) {
116
113
  headerCache.bearerToken = hdr.slice(7);
117
114
  headerCache.AUTHFLOW = "bearer";
115
+ console.error("[Note] Using user supplied bearer token for authorization");
118
116
  }
119
117
 
120
118
  // faking out api key since Viya does not support
121
119
  // not ideal for production
122
120
  const hdr2 = req.header("X-REFRESH-TOKEN");
123
121
  if (hdr2 != null) {
124
- headerCache.refreshToken = hdr2;
122
+ headerCache.REFRESH_TOKEN = hdr2;
125
123
  headerCache.AUTHFLOW = "refresh";
124
+ console.error("[Note] Using user supplied refresh token for authorization");
126
125
  }
127
126
  cache.set("headerCache", headerCache);
128
127
  next();
@@ -132,6 +131,10 @@ function requireBearer(req, res, next) {
132
131
  const handleRequest = async (req, res) => {
133
132
  let transport;
134
133
  let transports = cache.get("transports");
134
+ if (transports == null) {
135
+ console.error("[Error] No transports found in cache. Initializing empty transports object.");
136
+ transports = {'dummy': null };
137
+ }
135
138
  console.error("current transports in cache:", Object.keys(transports));
136
139
  try {
137
140
 
@@ -221,12 +224,15 @@ const handleRequest = async (req, res) => {
221
224
  };
222
225
  const handleGetDelete = async (req, res) => {
223
226
  console.error(req.method, "/mcp called");
227
+
224
228
  const sessionId = req.headers["mcp-session-id"];
229
+ console.error("Headers:", sessionId);
225
230
  console.error("Handling GET/DELETE for session ID:", sessionId);
226
231
  let transports = cache.get("transports");
227
232
  let transport = (sessionId == null) ? null : transports[sessionId];
233
+ console.error("Found transport:", transport != null);
228
234
  if (!sessionId || transport == null) {
229
- res.status(400).send(`[Error] In ${req.method}: Invalid or missing session ID ${sessionId}`);
235
+ res.status(200).send(`[Error] In ${req.method}: Invalid or missing session ID ${sessionId}`);
230
236
  return;
231
237
  }
232
238
  await transport.handleRequest(req, res);
@@ -248,7 +254,15 @@ app.get("/startup", (_req, res) => {
248
254
  }
249
255
  return res.status(200).json({ status: "started" });
250
256
  });
251
-
257
+ app.get("/tlogon", async (_req, res) => {
258
+ console.error(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Testing logon");
259
+ if (appStatus === false) {
260
+ return res.status(500).json({ status: "not ready" });
261
+ }
262
+ let r = await tlogon(baseAppEnvContext);
263
+ console.error(r);
264
+ return res.status(200).json(r);
265
+ });
252
266
  app.get("/status", (_req, res) => {
253
267
  console.error("Received request for status endpoint. Current app status:", appStatus);
254
268
  if (appStatus === false) {
@@ -274,12 +288,12 @@ let appServer;
274
288
 
275
289
  // get TLS options
276
290
  if (appEnvBase.HTTPS === 'TRUE') {
277
- //appEnvBase.tlsOpts = getOpts(appEnvBase);
278
291
  if (appEnvBase.tlsOpts == null) {
279
292
  appEnvBase.tlsOpts = await getTls(appEnvBase);
280
293
  console.error(Object.keys(appEnvBase.tlsOpts));
281
294
  appEnvBase.tlsOpts.requestCert = false;
282
295
  appEnvBase.tlsOpts.rejectUnauthorized = false;
296
+ appEnvBase.contexts.appCert = appEnvBase.tlsOpts; /* just for completeness */
283
297
  }
284
298
 
285
299
  cache.set("appEnvBase", appEnvBase);
@@ -126,6 +126,44 @@ async function hapiMcpServer(mcpServer, cache, baseAppEnvContext) {
126
126
  tags: ["mcp"],
127
127
  }
128
128
  },
129
+ {
130
+ method: ["GET"],
131
+ path: "/startz",
132
+ options: {
133
+ handler: async (req, h) => {
134
+ let status = { status: (process.env.HEALTH === 'true') ? 'ready' : 'starting' };
135
+ console.error("startz check requested, returning:", status);
136
+ if (process.env.HEALTH !== 'true') {
137
+ return h.response(status).code(200).type('application/json');
138
+ } else {
139
+ return h.response(status).code(503).type('application/json');
140
+ }
141
+ },
142
+ auth: false,
143
+ description: "Help",
144
+ notes: "Help",
145
+ tags: ["mcp"],
146
+ }
147
+ },
148
+ {
149
+ method: ["GET"],
150
+ path: "/readyz",
151
+ options: {
152
+ handler: async (req, h) => {
153
+ let status = { status: (process.env.HEALTH === 'true') ? 'ready' : 'starting' };
154
+ console.error("readyz check requested, returning:", status);
155
+ if (process.env.HEALTH !== 'true') {
156
+ return h.response(status).code(200).type('application/json');
157
+ } else {
158
+ return h.response(status).code(503).type('application/json');
159
+ }
160
+ },
161
+ auth: false,
162
+ description: "Help",
163
+ notes: "Help",
164
+ tags: ["mcp"],
165
+ }
166
+ },
129
167
  {
130
168
  method: ["GET"],
131
169
  path: "/apiMeta",
@@ -1,176 +1,176 @@
1
- /*
2
- * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- function openAPIJson(version) {
6
- let spec = {
7
- "swagger": "2.0",
8
- "info": {
9
- "title": "sas-score-mcp-serverjs API",
10
- "version": "1.0.0",
11
- "description": "sas-score-mcp-serverjs is a mcp server for SAS Viya"
12
- },
13
- "host": "localhost:8080",
14
- "basePath": "/",
15
- "schemes": ["http", "https"],
16
- "consumes": ["application/json"],
17
- "produces": ["application/json"],
18
- "paths": {
19
- "/health": {
20
- "get": {
21
- "summary": "Health check",
22
- "operationId": "getHealth",
23
- "description": "Returns health and version information.",
24
- "responses": {
25
- "200": {
26
- "description": "Health information",
27
- "schema": {
28
- "type": "object",
29
- "properties": {
30
- "name": { "type": "string" },
31
- "version": { "type": "string" },
32
- "description": { "type": "string" },
33
- "endpoints": { "type": "object" },
34
- "usage": { "type": "string" }
35
- }
36
- }
37
- }
38
- }
39
- }
40
- },
41
- "/apiMeta": {
42
- "get": {
43
- "summary": "API metadata using apiMeta",
44
- "operationId": "GetApiMeta",
45
- "responses": {
46
- "200": {
47
- "description": "OpenAPI document",
48
- "schema": { "type": "object" }
49
- }
50
- }
51
- }
52
- },
53
- "/openapi.json": {
54
- "get": {
55
- "summary": "API metadata using openapi.json",
56
- "operationId": "GetOpenApiJson",
57
- "description": "Returns the OpenAPI specification for this server.",
58
- "responses": {
59
- "200": {
60
- "description": "OpenAPI document",
61
- "schema": { "type": "object" }
62
- }
63
- }
64
- }
65
- },
66
- "/mcp": {
67
- "options": {
68
- "summary": "CORS preflight",
69
- "operationId": "OptionsMcp",
70
- "description": "CORS preflight endpoint.",
71
- "responses": {
72
- "204": { "description": "No Content" }
73
- }
74
- },
75
- "post": {
76
- "summary": "MCP request",
77
- "operationId": "PostMcp",
78
- "parameters": [
79
- {
80
- "name": "body",
81
- "in": "body",
82
- "required": true,
83
- "schema": { "type": "object" }
84
- },
85
- {
86
- "name": "Authorization",
87
- "in": "header",
88
- "required": false,
89
- "type": "string",
90
- "description": "Bearer token for authentication"
91
- },
92
- {
93
- "name": "X-VIYA-SERVER",
94
- "in": "header",
95
- "required": false,
96
- "type": "string",
97
- "description": "Override VIYA server"
98
- },
99
- {
100
- "name": "X-REFRESH-TOKEN",
101
- "in": "header",
102
- "required": false,
103
- "type": "string",
104
- "description": "Refresh token for authentication"
105
- },
106
- {
107
- "name": "mcp-session-id",
108
- "in": "header",
109
- "required": false,
110
- "type": "string",
111
- "description": "Session ID"
112
- }
113
- ],
114
- "responses": {
115
- "200": {
116
- "description": "MCP response",
117
- "schema": { "type": "object" }
118
- },
119
- "500": {
120
- "description": "Server error",
121
- "schema": { "type": "object" }
122
- }
123
- }
124
- },
125
- "get": {
126
- "summary": "get MCP session",
127
- "operationId": "GetMcp",
128
- "description": "Retrieves information for an MCP session.",
129
- "parameters": [
130
- {
131
- "name": "mcp-session-id",
132
- "in": "header",
133
- "required": true,
134
- "type": "string",
135
- "description": "Session ID"
136
- }
137
- ],
138
- "responses": {
139
- "200": {
140
- "description": "Session information",
141
- "schema": { "type": "object" }
142
- },
143
- "400": {
144
- "description": "Invalid or missing session ID"
145
- }
146
- }
147
- },
148
- "delete": {
149
- "summary": "Delete MCP session",
150
- "operationId": "DeleteMcp",
151
- "parameters": [
152
- {
153
- "name": "mcp-session-id",
154
- "in": "header",
155
- "required": true,
156
- "type": "string",
157
- "description": "Session ID"
158
- }
159
- ],
160
- "responses": {
161
- "200": {
162
- "description": "Session deleted",
163
- "schema": { "type": "object" }
164
- },
165
- "400": {
166
- "description": "Invalid or missing session ID"
167
- }
168
- }
169
- }
170
- }
171
- }
172
- }
173
- spec.info.version = version;
174
- return spec;
175
- };
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ function openAPIJson(version) {
6
+ let spec = {
7
+ "swagger": "2.0",
8
+ "info": {
9
+ "title": "sas-score-mcp-serverjs API",
10
+ "version": "1.0.0",
11
+ "description": "sas-score-mcp-serverjs is a mcp server for SAS Viya"
12
+ },
13
+ "host": "localhost:8080",
14
+ "basePath": "/",
15
+ "schemes": ["http", "https"],
16
+ "consumes": ["application/json"],
17
+ "produces": ["application/json"],
18
+ "paths": {
19
+ "/health": {
20
+ "get": {
21
+ "summary": "Health check",
22
+ "operationId": "getHealth",
23
+ "description": "Returns health and version information.",
24
+ "responses": {
25
+ "200": {
26
+ "description": "Health information",
27
+ "schema": {
28
+ "type": "object",
29
+ "properties": {
30
+ "name": { "type": "string" },
31
+ "version": { "type": "string" },
32
+ "description": { "type": "string" },
33
+ "endpoints": { "type": "object" },
34
+ "usage": { "type": "string" }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ },
41
+ "/apiMeta": {
42
+ "get": {
43
+ "summary": "API metadata using apiMeta",
44
+ "operationId": "GetApiMeta",
45
+ "responses": {
46
+ "200": {
47
+ "description": "OpenAPI document",
48
+ "schema": { "type": "object" }
49
+ }
50
+ }
51
+ }
52
+ },
53
+ "/openapi.json": {
54
+ "get": {
55
+ "summary": "API metadata using openapi.json",
56
+ "operationId": "GetOpenApiJson",
57
+ "description": "Returns the OpenAPI specification for this server.",
58
+ "responses": {
59
+ "200": {
60
+ "description": "OpenAPI document",
61
+ "schema": { "type": "object" }
62
+ }
63
+ }
64
+ }
65
+ },
66
+ "/mcp": {
67
+ "options": {
68
+ "summary": "CORS preflight",
69
+ "operationId": "OptionsMcp",
70
+ "description": "CORS preflight endpoint.",
71
+ "responses": {
72
+ "204": { "description": "No Content" }
73
+ }
74
+ },
75
+ "post": {
76
+ "summary": "MCP request",
77
+ "operationId": "PostMcp",
78
+ "parameters": [
79
+ {
80
+ "name": "body",
81
+ "in": "body",
82
+ "required": true,
83
+ "schema": { "type": "object" }
84
+ },
85
+ {
86
+ "name": "Authorization",
87
+ "in": "header",
88
+ "required": false,
89
+ "type": "string",
90
+ "description": "Bearer token for authentication"
91
+ },
92
+ {
93
+ "name": "X-VIYA-SERVER",
94
+ "in": "header",
95
+ "required": false,
96
+ "type": "string",
97
+ "description": "Override VIYA server"
98
+ },
99
+ {
100
+ "name": "X-REFRESH-TOKEN",
101
+ "in": "header",
102
+ "required": false,
103
+ "type": "string",
104
+ "description": "Refresh token for authentication"
105
+ },
106
+ {
107
+ "name": "mcp-session-id",
108
+ "in": "header",
109
+ "required": false,
110
+ "type": "string",
111
+ "description": "Session ID"
112
+ }
113
+ ],
114
+ "responses": {
115
+ "200": {
116
+ "description": "MCP response",
117
+ "schema": { "type": "object" }
118
+ },
119
+ "500": {
120
+ "description": "Server error",
121
+ "schema": { "type": "object" }
122
+ }
123
+ }
124
+ },
125
+ "get": {
126
+ "summary": "get MCP session",
127
+ "operationId": "GetMcp",
128
+ "description": "Retrieves information for an MCP session.",
129
+ "parameters": [
130
+ {
131
+ "name": "mcp-session-id",
132
+ "in": "header",
133
+ "required": true,
134
+ "type": "string",
135
+ "description": "Session ID"
136
+ }
137
+ ],
138
+ "responses": {
139
+ "200": {
140
+ "description": "Session information",
141
+ "schema": { "type": "object" }
142
+ },
143
+ "400": {
144
+ "description": "Invalid or missing session ID"
145
+ }
146
+ }
147
+ },
148
+ "delete": {
149
+ "summary": "Delete MCP session",
150
+ "operationId": "DeleteMcp",
151
+ "parameters": [
152
+ {
153
+ "name": "mcp-session-id",
154
+ "in": "header",
155
+ "required": true,
156
+ "type": "string",
157
+ "description": "Session ID"
158
+ }
159
+ ],
160
+ "responses": {
161
+ "200": {
162
+ "description": "Session deleted",
163
+ "schema": { "type": "object" }
164
+ },
165
+ "400": {
166
+ "description": "Invalid or missing session ID"
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ spec.info.version = version;
174
+ return spec;
175
+ };
176
176
  export default openAPIJson;
@@ -13,7 +13,7 @@ async function getLogonPayload(_appContext) {
13
13
  }
14
14
 
15
15
  async function igetLogonPayload(_appContext) {
16
-
16
+ console.error('[Info] Getting logon payload...',_appContext.AUTHFLOW);
17
17
  // Use cached logonPayload if available
18
18
  // This will cause timeouts if the token expires
19
19
  if (_appContext.contexts.logonPayload != null && _appContext.tokenRefresh !== true) {
@@ -57,12 +57,12 @@ async function igetLogonPayload(_appContext) {
57
57
 
58
58
  // Use user supplied refresh token-
59
59
  if (_appContext.AUTHFLOW === "refresh") {
60
- console.error("[Note] Using user supplied refresh token");
61
- let token = await refreshToken(_appContext,{token: _appContext.refreshToken, host: _appContext.VIYA_SERVER});
60
+ console.error("[Note] Using user supplied refresh token", _appContext.REFRESH_TOKEN);
61
+ let token = await refreshToken(_appContext,{token: _appContext.REFRESH_TOKEN, host: _appContext.VIYA_SERVER});
62
62
  let logonPayload = {
63
63
  host: _appContext.VIYA_SERVER,
64
64
  authType: "server",
65
- token: token,
65
+ token: token,
66
66
  tokenType: "Bearer",
67
67
  };
68
68
 
@@ -97,8 +97,8 @@ async function igetLogonPayload(_appContext) {
97
97
  authType: "password",
98
98
  user: _appContext.USERNAME,
99
99
  password: _appContext.PASSWORD,
100
- clientID: _appContext.CLIENTIDPW,
101
- clientSecret: _appContext.CLIENTSECRETPW,
100
+ clientID: _appContext.CLIENTID,
101
+ clientSecret: _appContext.CLIENTSECRET,
102
102
  };
103
103
 
104
104
  return logonPayload;
@@ -2,11 +2,10 @@
2
2
  * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
- import getOptsViya from './getOptsViya.js';
6
5
 
7
6
  function getStoreOpts(_appContext) {
8
7
 
9
- let opts = getOptsViya(_appContext);
8
+ let opts = _appContext.contexts.viyaCert;
10
9
  let storeOpts = {
11
10
  casProxy: true,
12
11
  httpOptions: { ...opts, rejectUnauthorized: true }
@@ -15,7 +15,6 @@ async function getToken(_appContext) {
15
15
  let sep = (os.platform() === 'win32') ? '\\' : '/';
16
16
  let credentials = homedir + sep + '.sas' + sep + 'credentials.json';
17
17
  let url = homedir + sep + '.sas' + sep + 'config.json';
18
- console.error('[Note] Using credentials file: ' + credentials);
19
18
  console.error('[Note] Using config file: ' + url);
20
19
  let profile = (_appContext.SAS_CLI_PROFILE == null || _appContext.SAS_CLI_PROFILE.toLowerCase() === 'default')
21
20
  ? 'Default' : _appContext.SAS_CLI_PROFILE;
@@ -1,49 +1,51 @@
1
- /*
2
- * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- import { Agent, fetch } from 'undici';
6
- import getOpts from './getOpts.js';
7
- async function refreshToken(_appContext,params) {
8
- let {host, token} = params;
9
- const url = `${host}/SASLogon/oauth/token`;
10
- console.error('[Info] Refresh token...', token);
11
- let opts = getOpts(_appContext);
12
- console.error('opts: ', opts);
13
- const agent = new Agent({
14
- connect: opts
15
- });
16
- console.error('[Info] Refreshing token...', token);
17
- const body = new URLSearchParams({
18
- grant_type: 'refresh_token',
19
- refresh_token: token,
20
- client_id: 'sas.cli'
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { Agent, fetch } from 'undici';
6
+
7
+ async function refreshToken(_appContext, params) {
8
+ let { host, token } = params;
9
+ let url = `${host}/SASLogon/oauth/token`;
10
+
11
+ let aconnect = {
12
+ ca: _appContext.contexts.viyaCert.ca, // trust this CA
13
+ rejectUnauthorized: false // or false, if you really want to bypass checks
14
+ }
15
+
16
+ const agent = new Agent(aconnect);
17
+
18
+ console.error('[Info] Refreshing token...', token);
19
+ const ibody = {
20
+ grant_type: 'refresh_token',
21
+ refresh_token: token,
22
+ client_id: 'sas.cli'
23
+ };
24
+
25
+ let body = new URLSearchParams(ibody);
26
+ try {
27
+ const response = await fetch(url, {
28
+ method: 'POST',
29
+ headers: {
30
+ 'Accept': 'application/json',
31
+ 'Content-Type': 'application/x-www-form-urlencoded',
32
+ dispatcher: agent
33
+ },
34
+ body: body.toString()
21
35
  });
22
-
23
- try {
24
- const response = await fetch(url, {
25
- method: 'POST',
26
- headers: {
27
- 'Accept': 'application/json',
28
- 'Content-Type': 'application/x-www-form-urlencoded',
29
- dispatcher: agent
30
- },
31
- body: body.toString()
32
- });
33
-
34
- if (!response.ok) {
35
- const error = await response.text();
36
- console.error('[Error] Failed to refresh token: ', error);
37
- throw new Error(error);
38
- }
39
-
40
- const data = await response.json();
41
- console.error('[Info] Token refreshed successfully: ', data.access_token);
42
- return data.access_token;
43
- } catch (err) {
44
- console.error('[Error] Failed to refresh token: ', err);
45
- throw err;
36
+
37
+ if (!response.ok) {
38
+ const error = await response.text();
39
+ console.error('[Error] Failed to refresh token: ', error);
40
+ throw new Error(error);
46
41
  }
42
+
43
+ const data = await response.json();
44
+ return data.access_token;
45
+ } catch (err) {
46
+ console.error('[Error] Failed to refresh token: ', err);
47
+ throw err;
47
48
  }
48
-
49
- export default refreshToken;
49
+ }
50
+
51
+ export default refreshToken;
@@ -3,11 +3,11 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { Agent, fetch } from 'undici';
6
- import getOpts from './getOpts.js';
6
+ //import getOpts from './getOpts.js';
7
7
  async function refreshTokenOauth(_appContext, oauthInfo ){
8
8
 
9
9
  const url = `${process.env.VIYA_SERVER}/SASLogon/oauth/token`;
10
- let opts = getOpts(_appContext);
10
+ let opts = _appContext.contexts.appCert;
11
11
 
12
12
  const agent = new Agent({
13
13
  connect: opts
@@ -0,0 +1,9 @@
1
+ import getLogonPayload from "./getLogonPayload.js";
2
+
3
+ async function tlogon(_appContext) {
4
+ console.error('[Info] Starting tlogon...');
5
+ let r = await getLogonPayload(_appContext);
6
+ console.error('[Info] tlogon completed');
7
+ return r;
8
+ }
9
+ export default tlogon;
@@ -38,7 +38,7 @@ Disambiguation & Clarification
38
38
  Examples (→ mapped params)
39
39
  - "Calculate deva score for 5 and 10" → { a: 5, b: 10 } returns 630
40
40
  - "Score 1 and 2" → { a: 1, b: 2 } returns 126
41
- - For multiple numbers, chain calls: deva(1,2)→126, then deva(126,3)→5418
41
+ - For multiple numbers, chain calls: devaScore(1,2)→126, then devaScore(126,3)→5418
42
42
 
43
43
  Negative Examples (should NOT call deva-score)
44
44
  - "Score this customer with the credit model" (use model-score instead)
@@ -58,11 +58,11 @@ For sequences of numbers, use a left-to-right fold: call devaScore(first, second
58
58
  a: z.number(),
59
59
  b: z.number()
60
60
  },
61
- handler: async ({ a, b,_appContext }) => {
61
+ handler: async ({ a, b }) => {
62
62
  console.error( a, b);
63
- return { content: [{ type: 'text',
64
- text: String((a + b) * 42) }] }
63
+ return `MagicScore ${ (a + b) * 42 }`;
65
64
  }
65
+
66
66
  }
67
67
  return spec;
68
68
  }
@@ -1,51 +0,0 @@
1
- /**
2
- * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- /**
6
- * Helper function to get TLS options(for the app server) from specified directory
7
- * signed certificates
8
- * for testing you can use mkcert
9
- * if this function return a null, coreehttp will create unsigned certs
10
- * @param {Object} _appContext - Application context containing SSLCERT property
11
- */
12
-
13
- import readCerts from './readCerts.js';
14
- function getOpts(_appContext) {
15
-
16
- if (_appContext.tlsOpts != null) {
17
- return _appContext.tlsOpts;
18
- }
19
- let r = readCerts(_appContext.SSLCERT);
20
- _appContext.tlsOpts = r;
21
- return r;
22
-
23
-
24
- /*
25
- let tlsdir = _appContext.SSLCERT;
26
- if (tlsdir == null || tlsdir === 'NONE') {
27
- return null;
28
- }
29
-
30
- console.error("[Note] Using TLS dir: " + tlsdir);
31
- if (fs.existsSync(tlsdir) === false) {
32
- console.error("[Warning] Specified TLS dir does not exist: " + tlsdir);
33
- return null;
34
- }
35
-
36
- let listOfFiles = fs.readdirSync(tlsdir);
37
- console.error("[Note] TLS/SSL files found: " + listOfFiles);
38
- let options = {};
39
- for(let i=0; i < listOfFiles.length; i++) {
40
- let fname = listOfFiles[i];
41
- let name = tlsdir + '/' + listOfFiles[i];
42
- let key = fname.split('.')[0];
43
- options[key] = fs.readFileSync(name, { encoding: 'utf8' });
44
- }
45
- console.error('TLS FILES', Object.keys(options));
46
- _appContext.tlsOpts = options;
47
- return options;
48
- */
49
-
50
- }
51
- export default getOpts;
@@ -1,44 +0,0 @@
1
- /**
2
- * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- import readCerts from './readCerts.js';
6
- function getOptsViya(_appContext) {
7
-
8
- if (_appContext.contexts.viyaCert != null) {
9
- console.error('[Note] Using cached viyaOpts');
10
- return _appContext.contexts.viyaCert;
11
- }
12
- let r = readCerts(_appContext.VIYACERT);
13
- _appContext.contexts.viyaCert = r;
14
- return r;
15
-
16
- /*
17
- let tlsdir = _appContext.VIYACERT;
18
- if (tlsdir == null || tlsdir === 'NONE') {
19
- return {};
20
- }
21
-
22
- console.error(`[Note] Using VIYACERT dir: ` + tlsdir);
23
- if (fs.existsSync(tlsdir) === false) {
24
- console.error("[Warning] Specified VIYACERT dir does not exist: " + tlsdir);
25
- return {};
26
- }
27
-
28
- let listOfFiles = fs.readdirSync(tlsdir);
29
- console.error("[Note] TLS/SSL files found: " + listOfFiles);
30
- let options = {};
31
- for(let i=0; i < listOfFiles.length; i++) {
32
- let fname = listOfFiles[i];
33
- let name = tlsdir + '/' + listOfFiles[i];
34
- let key = fname.split('.')[0];
35
- console.error('Reading TLS file: ' + name + ' as key: ' + key);
36
- options[key] = fs.readFileSync(name, { encoding: 'utf8' });
37
- }
38
- console.error('VIYACERT FILES', Object.keys(options));
39
- _appContext.contexts.viyaCert = options;
40
- return options;
41
- */
42
-
43
- }
44
- export default getOptsViya;