@sassoftware/sas-score-mcp-serverjs 1.0.1-3 → 1.0.1-31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/.skills/agents/sas-score-mcp-serverjs-agent.md +190 -0
  2. package/.skills/copilot-instructions.md +241 -0
  3. package/.skills/skills/README.md +125 -0
  4. package/.skills/skills/detail-strategy/SKILL.md +272 -0
  5. package/.skills/skills/find-resources/SKILL.md +155 -0
  6. package/.skills/skills/list-resource/SKILL.md +258 -0
  7. package/.skills/skills/read-strategy/SKILL.md +137 -0
  8. package/.skills/skills/request-routing/SKILL.md +107 -0
  9. package/.skills/skills/score-strategy/SKILL.md +231 -0
  10. package/README.md +96 -54
  11. package/cli.js +37 -27
  12. package/openApi.yaml +121 -121
  13. package/package.json +14 -14
  14. package/scripts/docs/oauth-http-transport.md +2 -2
  15. package/scripts/refreshtoken.js +58 -0
  16. package/src/createMcpServer.js +0 -1
  17. package/src/expressMcpServer.js +47 -49
  18. package/src/oauthHandlers/authorize.js +4 -1
  19. package/src/oauthHandlers/baseUrl.js +4 -0
  20. package/src/oauthHandlers/callback.js +5 -1
  21. package/src/oauthHandlers/getMetadata.js +4 -0
  22. package/src/oauthHandlers/index.js +4 -0
  23. package/src/oauthHandlers/token.js +4 -0
  24. package/src/openApi.yaml +121 -121
  25. package/src/processHeaders.js +10 -7
  26. package/src/setupSkills.js +12 -7
  27. package/src/toolHelpers/_findJob.js +12 -0
  28. package/src/toolHelpers/_findJobdef.js +10 -0
  29. package/src/toolHelpers/_findLibrary.js +10 -0
  30. package/src/toolHelpers/_findModel.js +12 -0
  31. package/src/toolHelpers/_findTable.js +10 -0
  32. package/src/toolHelpers/_listJobs.js +2 -1
  33. package/src/toolHelpers/_listLibrary.js +1 -1
  34. package/src/toolHelpers/_listTables.js +1 -1
  35. package/src/toolHelpers/getLogonPayload.js +2 -2
  36. package/src/toolSet/devaScore.js +61 -61
  37. package/src/toolSet/findJob.js +2 -1
  38. package/src/toolSet/findJobdef.js +7 -7
  39. package/src/toolSet/findLibrary.js +68 -68
  40. package/src/toolSet/findModel.js +2 -2
  41. package/src/toolSet/findTable.js +2 -2
  42. package/src/toolSet/jobInfo.js +59 -0
  43. package/src/toolSet/jobdefInfo.js +59 -0
  44. package/src/toolSet/listJobdefs.js +61 -61
  45. package/src/toolSet/listJobs.js +61 -61
  46. package/src/toolSet/listLibraries.js +78 -78
  47. package/src/toolSet/listModels.js +56 -56
  48. package/src/toolSet/listTables.js +66 -66
  49. package/src/toolSet/makeTools.js +3 -0
  50. package/src/toolSet/modelInfo.js +1 -1
  51. package/src/toolSet/modelScore.js +23 -25
  52. package/src/toolSet/readTable.js +63 -63
  53. package/src/toolSet/runCasProgram.js +21 -10
  54. package/src/toolSet/runJob.js +15 -19
  55. package/src/toolSet/runJobdef.js +15 -19
  56. package/src/toolSet/runMacro.js +82 -82
  57. package/src/toolSet/sasQuery.js +77 -77
  58. package/src/toolSet/scrScore.js +60 -69
  59. package/src/toolSet/setContext.js +65 -65
  60. package/src/toolSet/superstat.js +61 -61
  61. package/src/toolSet/tableInfo.js +58 -58
  62. package/.skills_claude/README.md +0 -303
  63. package/.skills_claude/TESTING_GUIDE.md +0 -252
  64. package/.skills_claude/agents/sas-viya-scoring-expert.md +0 -58
  65. package/.skills_claude/claude-desktop-config.json +0 -16
  66. package/.skills_claude/claude-desktop-system-prompt.md +0 -127
  67. package/.skills_claude/copilot-instructions.md +0 -155
  68. package/.skills_claude/instructions.md +0 -184
  69. package/.skills_claude/skills/sas-find-library-smart/SKILL.md +0 -157
  70. package/.skills_claude/skills/sas-find-resource-strategy/SKILL.md +0 -105
  71. package/.skills_claude/skills/sas-list-resource-strategy/SKILL.md +0 -124
  72. package/.skills_claude/skills/sas-list-tables-smart/SKILL.md +0 -126
  73. package/.skills_claude/skills/sas-read-and-score/SKILL.md +0 -112
  74. package/.skills_claude/skills/sas-read-strategy/SKILL.md +0 -154
  75. package/.skills_claude/skills/sas-request-classifier/SKILL.md +0 -69
  76. package/.skills_claude/skills/sas-score-workflow/SKILL.md +0 -200
  77. package/.skills_claude/skills-index.md +0 -345
  78. package/.skills_github/agents/sas-viya-scoring-expert.md +0 -58
  79. package/.skills_github/copilot-instructions.md +0 -177
  80. package/.skills_github/skills/sas-find-library-smart/SKILL.md +0 -155
  81. package/.skills_github/skills/sas-find-resource-strategy/SKILL.md +0 -105
  82. package/.skills_github/skills/sas-list-resource-strategy/SKILL.md +0 -124
  83. package/.skills_github/skills/sas-list-tables-smart/SKILL.md +0 -128
  84. package/.skills_github/skills/sas-read-and-score/SKILL.md +0 -113
  85. package/.skills_github/skills/sas-read-strategy/SKILL.md +0 -154
  86. package/.skills_github/skills/sas-request-classifier/SKILL.md +0 -74
  87. package/.skills_github/skills/sas-score-workflow/SKILL.md +0 -314
  88. package/scripts/optimize_final.py +0 -140
  89. package/scripts/optimize_tools.py +0 -99
  90. package/scripts/setup-skills.js +0 -34
  91. package/scripts/update_descriptions.py +0 -46
  92. package/src/authpkce.js +0 -219
  93. package/src/handleGetDelete.js +0 -34
  94. package/src/handleRequest.js +0 -112
  95. package/src/hapiMcpServer.js +0 -241
  96. package/src/toolSet/.claude/settings.local.json +0 -13
@@ -9,6 +9,7 @@ import cors from "cors";
9
9
  import bodyParser from "body-parser";
10
10
  import selfsigned from "selfsigned";
11
11
  import openAPIJson from "./openAPIJson.js";
12
+ import createMcpServer from "./createMcpServer.js";
12
13
 
13
14
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
14
15
  import { randomUUID } from "node:crypto";
@@ -21,7 +22,7 @@ import processHeaders from "./processHeaders.js";
21
22
 
22
23
  // setup express server
23
24
 
24
- async function expressMcpServer(mcpServer, cache, baseAppEnvContext) {
25
+ async function expressMcpServer(_mcpServer, cache, baseAppEnvContext) {
25
26
  // setup for change to persistence session
26
27
  cache.del("headerCache");
27
28
  const app = express();
@@ -50,6 +51,10 @@ async function expressMcpServer(mcpServer, cache, baseAppEnvContext) {
50
51
  const pkceStore = new Map(); // ourState -> { codeVerifier, clientRedirectUri, clientState }
51
52
  const codeStore = new Map(); // ourCode -> { access_token, refresh_token, expires_in }
52
53
 
54
+ // Per-session transports — each initialize creates its own transport
55
+ const transports = new Map(); // sessionId -> transport
56
+ cache.set("transports", transports);
57
+
53
58
  app.get('/.well-known/oauth-protected-resource', (req, res) => {
54
59
  let payload = {
55
60
  resource: `${baseAppEnvContext.mcpHost}/mcp`,
@@ -152,67 +157,54 @@ async function expressMcpServer(mcpServer, cache, baseAppEnvContext) {
152
157
 
153
158
  // process mcp endpoint requests
154
159
  const handleRequest = async (req, res) => {
155
- let transport = null;
156
- let transports = cache.get("transports");
157
160
  console.error("=========================================================");
158
161
  console.error("Processing POST /mcp request");
159
- if (transports == null) {
160
- console.error("[Error] ***** transports cache is null. This is an error");
161
- transports = {};
162
- cache.set("transports", transports);
163
- }
164
-
165
- console.error("current transports in cache:", Object.keys(transports));
162
+ console.error("current active sessions:", transports.size);
166
163
  try {
167
164
 
168
165
  let sessionId = req.headers["mcp-session-id"];
169
166
  console.error("[Note]Incoming session ID:", sessionId);
170
167
  let body = (req.body == null) ? 'no body' : JSON.stringify(req.body);
171
168
  console.error('[Note] Payload is ', body);
172
- if (/*!sessionId &&*/ isInitializeRequest(req.body)) {
173
- // create transport
174
- console.error("[Note] Initializing new transport for MCP session...");
175
-
176
- transport = new StreamableHTTPServerTransport({
169
+
170
+ if (isInitializeRequest(req.body)) {
171
+ console.error("[Note]>>>>>>>>>>>>>>>>>>>>>>>>> Creating new MCP server");
172
+ let mcpServer = await createMcpServer(cache, baseAppEnvContext);
173
+ // New session — create a dedicated transport
174
+ console.error("[Note] Initializing new session with fresh transport...");
175
+ const isInitRequest = req.body?.method === 'initialize';
176
+
177
+ const transport = new StreamableHTTPServerTransport({
177
178
  sessionIdGenerator: () => randomUUID(),
178
179
  enableJsonResponse: true,
179
- enableDnsRebindingProtection: true,
180
- onsessioninitialized: (sessionId) => {
181
- // Store the transport by session ID
182
- console.error('Session initialized');
183
- console.error("[Note] Transport initialized with ID:", sessionId);
184
- transports[sessionId] = transport;
180
+ onsessioninitialized: (newSessionId) => {
181
+ console.error("[Note] Session initialized with ID:", newSessionId);
182
+ transports.set(newSessionId, transport);
183
+ cache.set("transports", transports);
184
+ console.error("[Note] Total active sessions:", Object.keys(transports));
185
185
  },
186
186
  });
187
- // Clean up transport when closed
188
187
  transport.onclose = () => {
189
- if (transport.sessionId && transports[transport.sessionId]) {
190
- delete transports[transport.sessionId];
188
+ if (transport.sessionId) {
189
+ console.error("[Note] Session closed, removing transport:", transport.sessionId);
190
+ transports.delete(transport.sessionId);
191
+ cache.del(transport.sessionId);
191
192
  }
192
193
  };
193
- console.error("[Note] Connecting mcpServer to new transport...");
194
194
  await mcpServer.connect(transport);
195
-
196
- // Save transport data and app context for use in tools
197
- console.error('[Note] Connected to mcpServer');
198
- cache.set("transports", transports);
199
195
  console.error("=======================================================");
200
196
  return await transport.handleRequest(req, res, req.body);
201
197
 
202
- // cache transport
203
-
204
198
  } else if (sessionId != null) {
205
- console.error('[Note] Incoming session ID:', sessionId);
206
- transport = transports[sessionId];
207
- console.error("[Note] Found transport:", transport != null);
208
- if (transport == null) {
209
- // this can happen if client is holding on to old session id
210
- console.error("[Error] No transport found for session ID:", sessionId, "Returning a 404 error with instructions for the user");
211
- res.status(404).send(`Invalid or missing session ID ${sessionId}. Please ensure your MCP client is configured to use the correct session ID returned in the 'mcp-session-id' header of the response from the /mcp endpoint.`);
212
- return;
199
+ const transport = transports.get(sessionId);
200
+ if (!transport) {
201
+ console.error("[Note] Unknown session ID:", sessionId);
202
+ return res.status(404).json({ jsonrpc: "2.0", error: { code: -32000, message: "Session not found. Please re-initialize." }, id: null });
213
203
  }
204
+ console.error('[Note] Incoming session ID:', sessionId);
205
+ console.error("[Note] Using transport for session ID:", sessionId);
214
206
 
215
- // post the curren session - used to pass _appContext to tools
207
+ // post the current session - used to pass _appContext to tools
216
208
  cache.set("currentId", sessionId);
217
209
 
218
210
  // get app context for session
@@ -230,7 +222,6 @@ async function expressMcpServer(mcpServer, cache, baseAppEnvContext) {
230
222
  _appContext = Object.assign(_appContext, headerCache);
231
223
  cache.set(sessionId, _appContext);
232
224
  }
233
- console.error("[Note] Using existing transport for session ID:", sessionId);
234
225
  console.error("==========================================================");
235
226
  await transport.handleRequest(req, res, req.body);
236
227
  return;
@@ -260,21 +251,28 @@ async function expressMcpServer(mcpServer, cache, baseAppEnvContext) {
260
251
  const sessionId = req.headers["mcp-session-id"];
261
252
  console.error("[Note] SessionId:", sessionId);
262
253
 
263
- let transports = cache.get("transports");
264
- let transport = (sessionId == null) ? null : transports[sessionId];
265
- console.error("[Note] Transport found:", transport != null);
266
- if (!sessionId || transport == null) {
267
- res.status(404).send(`[Error] In ${req.method}: Invalid or missing session ID ${sessionId}`);
268
- return;
254
+ if (!sessionId) {
255
+ console.error("[Note] No session ID on /DELETE rejecting");
256
+ return res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request: Mcp-Session-Id header is required" }, id: null });
269
257
  }
258
+
259
+ const transport = transports.get(sessionId);
260
+ if (!transport) {
261
+ console.error("[Note] Unknown session ID:", sessionId);
262
+ return res.status(404).json({ jsonrpc: "2.0", error: { code: -32000, message: "Session not found. Please re-initialize." }, id: null });
263
+ }
264
+
270
265
  if (req.method === "GET") {
266
+ console.error("[Note] calling transport.handleRequest for GET /mcp");
271
267
  await transport.handleRequest(req, res);
272
268
  return;
273
269
  }
274
- if (req.method === "DELETE" && sessionId != null) {
270
+ if (req.method === "DELETE") {
275
271
  console.error("[Note] Deleting transport and cache for session ID:", sessionId);
276
- delete transports[sessionId];
272
+ transports.delete(sessionId);
277
273
  cache.del(sessionId);
274
+ console.error("[Note] Deleted session ID:", sessionId);
275
+ console.error("[Note] Total active sessions:", Object.keys(transports));
278
276
  res.status(201).send(`[Info] Deleted session ${sessionId}`);
279
277
  }
280
278
  }
@@ -1,4 +1,7 @@
1
- //authorize
1
+ /*
2
+ * Copyright © 2026, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
2
5
  import { randomBytes, createHash } from "node:crypto";
3
6
  import baseUrl from "./baseUrl.js";
4
7
 
@@ -1,3 +1,7 @@
1
+ /*
2
+ * Copyright © 2026, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
1
5
  function baseUrl(appContext) {
2
6
  const protocol = appContext.HTTPS === "TRUE" ? "https" : "http";
3
7
  //const host = "localhost";
@@ -1,3 +1,7 @@
1
+ /*
2
+ * Copyright © 2026, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
1
5
  import baseUrl from "./baseUrl.js";
2
6
  import { Agent, fetch as undiciFetch } from "undici";
3
7
  import { randomUUID } from "node:crypto";
@@ -75,7 +79,7 @@ async function callback(req, res, pkceStore, codeStore, appContext) {
75
79
  // which was part of the payload from the client to /oauth/authorize
76
80
  // we trust since it was associated with the valid PKCE state
77
81
  console.error("[Note] OAuth callback complete, redirecting to MCP client");
78
- console.log(pending.clientRedirectUri.toString())
82
+ console.error(pending.clientRedirectUri.toString())
79
83
  return res.redirect(`${pending.clientRedirectUri}?${redirectParams}`);
80
84
  } catch (err) {
81
85
  console.error("[Error] OAuth callback handler error:", err);
@@ -1,3 +1,7 @@
1
+ /*
2
+ * Copyright © 2026, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
1
5
  import baseUrl from "./baseUrl.js";
2
6
  function getMetadata(req, res, appEnvContext) {
3
7
  let base = '';
@@ -1,3 +1,7 @@
1
+ /*
2
+ * Copyright © 2026, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
1
5
  import authorize from "./authorize.js";
2
6
  import callback from "./callback.js";
3
7
  import token from "./token.js";
@@ -1,3 +1,7 @@
1
+ /*
2
+ * Copyright © 2026, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
1
5
  function token(req, res, appContext, codeStore, cache) {
2
6
  console.error("===============================================================");
3
7
  console.error("[Note] at /token endpoint");
package/src/openApi.yaml CHANGED
@@ -1,121 +1,121 @@
1
- swagger: "2.0"
2
- info:
3
- title: SAS Viya Sample MCP Server API
4
- version: "1.0.0"
5
- description: API for interacting with the SAS Viya Sample MCP Server.
6
- host: localhost:8080
7
- basePath: /
8
- schemes:
9
- - http
10
- - https
11
- consumes:
12
- - application/json
13
- produces:
14
- - application/json
15
- paths:
16
- /health:
17
- get:
18
- summary: Health check
19
- description: Returns health and version information.
20
- responses:
21
- 200:
22
- description: Health information
23
- schema:
24
- type: object
25
- properties:
26
- name:
27
- type: string
28
- version:
29
- type: string
30
- description:
31
- type: string
32
- endpoints:
33
- type: object
34
- usage:
35
- type: string
36
- /apiMeta:
37
- get:
38
- summary: API metadata
39
- description: Returns the OpenAPI specification for this server.
40
- responses:
41
- 200:
42
- description: OpenAPI document
43
- schema:
44
- type: object
45
- /mcp:
46
- options:
47
- summary: CORS preflight
48
- description: CORS preflight endpoint.
49
- responses:
50
- 204:
51
- description: No Content
52
- post:
53
- summary: MCP request
54
- description: Handles MCP JSON-RPC requests.
55
- parameters:
56
- - name: body
57
- in: body
58
- required: true
59
- schema:
60
- type: object
61
- - name: Authorization
62
- in: header
63
- required: false
64
- type: string
65
- description: Bearer token for authentication
66
- - name: X-VIYA-SERVER
67
- in: header
68
- required: false
69
- type: string
70
- description: Override VIYA server
71
- - name: X-REFRESH-TOKEN
72
- in: header
73
- required: false
74
- type: string
75
- description: Refresh token for authentication
76
- - name: mcp-session-id
77
- in: header
78
- required: false
79
- type: string
80
- description: Session ID
81
- responses:
82
- 200:
83
- description: MCP response
84
- schema:
85
- type: object
86
- 500:
87
- description: Server error
88
- schema:
89
- type: object
90
- get:
91
- summary: Get MCP session
92
- description: Retrieves information for an MCP session.
93
- parameters:
94
- - name: mcp-session-id
95
- in: header
96
- required: true
97
- type: string
98
- description: Session ID
99
- responses:
100
- 200:
101
- description: Session information
102
- schema:
103
- type: object
104
- 400:
105
- description: Invalid or missing session ID
106
- delete:
107
- summary: Delete MCP session
108
- description: Deletes an MCP session.
109
- parameters:
110
- - name: mcp-session-id
111
- in: header
112
- required: true
113
- type: string
114
- description: Session ID
115
- responses:
116
- 200:
117
- description: Session deleted
118
- schema:
119
- type: object
120
- 400:
121
- description: Invalid or missing session ID
1
+ swagger: "2.0"
2
+ info:
3
+ title: SAS Viya Sample MCP Server API
4
+ version: "1.0.0"
5
+ description: API for interacting with the SAS Viya Sample MCP Server.
6
+ host: localhost:8080
7
+ basePath: /
8
+ schemes:
9
+ - http
10
+ - https
11
+ consumes:
12
+ - application/json
13
+ produces:
14
+ - application/json
15
+ paths:
16
+ /health:
17
+ get:
18
+ summary: Health check
19
+ description: Returns health and version information.
20
+ responses:
21
+ 200:
22
+ description: Health information
23
+ schema:
24
+ type: object
25
+ properties:
26
+ name:
27
+ type: string
28
+ version:
29
+ type: string
30
+ description:
31
+ type: string
32
+ endpoints:
33
+ type: object
34
+ usage:
35
+ type: string
36
+ /apiMeta:
37
+ get:
38
+ summary: API metadata
39
+ description: Returns the OpenAPI specification for this server.
40
+ responses:
41
+ 200:
42
+ description: OpenAPI document
43
+ schema:
44
+ type: object
45
+ /mcp:
46
+ options:
47
+ summary: CORS preflight
48
+ description: CORS preflight endpoint.
49
+ responses:
50
+ 204:
51
+ description: No Content
52
+ post:
53
+ summary: MCP request
54
+ description: Handles MCP JSON-RPC requests.
55
+ parameters:
56
+ - name: body
57
+ in: body
58
+ required: true
59
+ schema:
60
+ type: object
61
+ - name: Authorization
62
+ in: header
63
+ required: false
64
+ type: string
65
+ description: Bearer token for authentication
66
+ - name: X-VIYA-SERVER
67
+ in: header
68
+ required: false
69
+ type: string
70
+ description: Override VIYA server
71
+ - name: X-REFRESH-TOKEN
72
+ in: header
73
+ required: false
74
+ type: string
75
+ description: Refresh token for authentication
76
+ - name: mcp-session-id
77
+ in: header
78
+ required: false
79
+ type: string
80
+ description: Session ID
81
+ responses:
82
+ 200:
83
+ description: MCP response
84
+ schema:
85
+ type: object
86
+ 500:
87
+ description: Server error
88
+ schema:
89
+ type: object
90
+ get:
91
+ summary: Get MCP session
92
+ description: Retrieves information for an MCP session.
93
+ parameters:
94
+ - name: mcp-session-id
95
+ in: header
96
+ required: true
97
+ type: string
98
+ description: Session ID
99
+ responses:
100
+ 200:
101
+ description: Session information
102
+ schema:
103
+ type: object
104
+ 400:
105
+ description: Invalid or missing session ID
106
+ delete:
107
+ summary: Delete MCP session
108
+ description: Deletes an MCP session.
109
+ parameters:
110
+ - name: mcp-session-id
111
+ in: header
112
+ required: true
113
+ type: string
114
+ description: Session ID
115
+ responses:
116
+ 200:
117
+ description: Session deleted
118
+ schema:
119
+ type: object
120
+ 400:
121
+ description: Invalid or missing session ID
@@ -1,5 +1,3 @@
1
- import { start } from "node:repl";
2
-
3
1
  /*
4
2
  * Copyright © 2026, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
5
3
  * SPDX-License-Identifier: Apache-2.0
@@ -32,20 +30,25 @@ function processHeaders(req, res, next, cache, appContext) {
32
30
  const hdr = req.header("Authorization");
33
31
  //for now, ignore Authorization if authflow is not bearer
34
32
  let token = (hdr != null) ? hdr.slice(7) : null;
35
- //console.error("[Note] Authorization token", token);
36
33
  debugger;
37
- console.log('>>>',appContext.AUTHFLOW);
34
+ console.error('[Note} AUTHFLOW=', appContext.AUTHFLOW);
35
+ console.error("[Note] External authorization :", appContext.AUTHEXTERNAL);
38
36
  if (appContext.AUTHFLOW === 'bearer') {
39
37
  debugger;
40
38
  let startAuth = false;
41
- console.error("[Note] appContext.AUTHEXTERNAL:", appContext.AUTHEXTERNAL);
39
+
42
40
  if (appContext.AUTHEXTERNAL === true) {
43
41
  console.error("[Note] Expecting external authorization");
44
42
  if (token != null) {
45
43
  console.error("[Note] Using user supplied token for authorization");
46
44
  headerCache.bearerToken = token;
47
45
  } else {
48
- startAuth = true;
46
+ console.error("[Note] No Authorization token provided in header for external authorization.");
47
+ console.error("[Note] Returning 404 since we are configured for external token and no token provided in header.");
48
+ return res.status(404).json({
49
+ error: "unauthorized",
50
+ error_description: "[Error] Missing token for external authorization."
51
+ });
49
52
  }
50
53
  } else if (token == null) {
51
54
  console.error("[Note] No Authorization token provided in header.");
@@ -55,7 +58,7 @@ function processHeaders(req, res, next, cache, appContext) {
55
58
  let tokenlist = cache.get("tokenlist");
56
59
  let tokenData = tokenlist[token];
57
60
  if (tokenData == null) {
58
- return res.status(403).json({
61
+ return res.status(401).json({
59
62
  error: "unauthorized",
60
63
  error_description: "[Error] Expired token. Clear token and try again."
61
64
  });
@@ -4,7 +4,7 @@ import path from 'path';
4
4
  import os from 'os';
5
5
  import { fileURLToPath } from 'url';
6
6
 
7
- function setupSkills(clientName) {
7
+ function setupSkills(clientName,agentFolder) {
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
9
  // Paths
10
10
  let destination;
@@ -13,10 +13,13 @@ function setupSkills(clientName) {
13
13
  destination = path.join(process.cwd(), clientName);
14
14
  clientName = clientName.slice(1);
15
15
  } else {
16
- destination = path.join(os.homedir(), '.' + clientName);
16
+ destination = path.join(os.homedir(), '.' + clientName );
17
17
  }
18
18
 
19
- const source = path.join(__dirname, `../.skills` + '_' + clientName.toLowerCase());
19
+ if (agentFolder) {
20
+ destination = path.join(destination, agentFolder);
21
+ }
22
+ const source = path.join(__dirname, `../.skills`);
20
23
  console.error("==================================================================");
21
24
  console.error(` Copying ${source} to ${destination}...`);
22
25
 
@@ -25,22 +28,24 @@ function setupSkills(clientName) {
25
28
  if (!fs.existsSync(to)) fs.mkdirSync(to, { recursive: true });;
26
29
  fs.readdirSync(from).forEach(element => {
27
30
  const fromPath = path.join(from, element);
28
- const toPath = path.join(to, element);
31
+ let toPath = path.join(to, element);
32
+ if (clientName === 'claude' && element === 'copilot-instructions.md') {
33
+ toPath = path.join(to, 'CLAUDE.md');
34
+ }
29
35
  if (fs.lstatSync(fromPath).isFile()) {
30
36
  console.error(` 📄 Copying file: ${element}`);
31
37
  fs.copyFileSync(fromPath, toPath);
32
38
  } else if (fs.lstatSync(fromPath).isDirectory()) {
33
39
  console.error(`📂 Copying folder: ${element}`);
34
- copyFolderSync(fromPath, toPath) ;
40
+ copyFolderSync(fromPath, toPath);
35
41
  }
36
42
  });
37
43
  }
38
-
39
44
  function listExpandedFolder(dir, indent = "") {
40
45
  const entries = fs.readdirSync(dir, { withFileTypes: true });
41
46
 
42
47
  for (const entry of entries) {
43
- console.log(indent + entry.name);
48
+ console.error(indent + entry.name);
44
49
 
45
50
  if (entry.isDirectory()) {
46
51
  listExpandedFolder(path.join(dir, entry.name), indent + " ");
@@ -0,0 +1,12 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import _listJobs from './_listJobs.js';
7
+ async function _findJob(params) {
8
+ let r = await _listJobs(params);
9
+ console.log ("findJob result:" , r);
10
+ return r;
11
+ }
12
+ export default _findJob;
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import _listJobdefs from './_listJobdefs.js';
7
+ async function _findJobdef(params) {
8
+ return await _listJobdefs(params);
9
+ }
10
+ export default _findJobdef;
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import _listLibrary from './_listLibrary.js';
7
+ async function _findLibrary(params) {
8
+ return await _listLibrary(params);
9
+ }
10
+ export default _findLibrary;
@@ -0,0 +1,12 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import _listModels from './_listModels.js';
7
+ async function _findModel(params) {
8
+ let r = await _listModels(params);
9
+ console.log ("findModel result:" , r);
10
+ return r;
11
+ }
12
+ export default _findModel;
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import _listTables from './_listTables.js';
7
+ async function _findTable(params) {
8
+ return await _listTables(params);
9
+ }
10
+ export default _findTable;
@@ -42,8 +42,9 @@ async function _listJobs(params) {
42
42
  names[jname] = {parameters: (value == null) ? {} : value.toJS() };
43
43
  }
44
44
  });
45
-
45
+ console.error('parameters', JSON.stringify(names, null, 2));
46
46
  let response = {jobs: Object.keys(names)};
47
+ console.error('response', JSON.stringify(response, null, 2));
47
48
 
48
49
  if (name != null) {
49
50
  response = { name: name, parameters: names[name].parameters };
@@ -54,7 +54,7 @@ async function _listLibrary(params) {
54
54
  if (appControl != null) {
55
55
  await deleteSession(appControl);
56
56
  }
57
- return { isError: true, content: [{ type: 'text', text: JSON.stringify(err) }] };
57
+ return { isError: true, content: [{ type: 'text', text: (typeof err === 'string') ? err : JSON.stringify(err) }] };
58
58
  }
59
59
  }
60
60