@sassoftware/sas-score-mcp-serverjs 0.4.1 → 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.
Files changed (72) hide show
  1. package/.skills/agents/sas-viya-scoring-expert.md +58 -0
  2. package/.skills/copilot-instructions.md +147 -0
  3. package/.skills/skills/sas-find-library-smart/SKILL.md +154 -0
  4. package/.skills/skills/sas-list-tables-smart/SKILL.md +127 -0
  5. package/.skills/skills/sas-read-and-score/SKILL.md +111 -0
  6. package/.skills/skills/sas-read-strategy/SKILL.md +156 -0
  7. package/.skills/skills/sas-request-classifier/SKILL.md +69 -0
  8. package/.skills/skills/sas-score-workflow/SKILL.md +314 -0
  9. package/cli.js +311 -70
  10. package/package.json +7 -7
  11. package/scripts/docs/SCORE_SKILL_REFERENCE.md +142 -0
  12. package/scripts/docs/TOOL_DESCRIPTION_TEMPLATE.md +157 -0
  13. package/scripts/docs/TOOL_UPDATES_SUMMARY.md +208 -0
  14. package/scripts/docs/mcp-localhost-config-guide.md +184 -0
  15. package/scripts/docs/oauth-http-transport.md +96 -0
  16. package/scripts/docs/sas-mcp-tools-reference.md +600 -0
  17. package/scripts/getViyaca.sh +1 -0
  18. package/scripts/optimize_final.py +140 -0
  19. package/scripts/optimize_tools.py +99 -0
  20. package/scripts/setup-skills.js +34 -0
  21. package/scripts/update_descriptions.py +46 -0
  22. package/scripts/viyatls.sh +3 -0
  23. package/src/authpkce.js +219 -0
  24. package/src/createMcpServer.js +16 -5
  25. package/src/expressMcpServer.js +350 -308
  26. package/src/handleGetDelete.js +6 -3
  27. package/src/hapiMcpServer.js +10 -18
  28. package/src/oauthHandlers/authorize.js +46 -0
  29. package/src/oauthHandlers/baseUrl.js +8 -0
  30. package/src/oauthHandlers/callback.js +96 -0
  31. package/src/oauthHandlers/getMetadata.js +27 -0
  32. package/src/oauthHandlers/index.js +7 -0
  33. package/src/oauthHandlers/token.js +37 -0
  34. package/src/processHeaders.js +88 -0
  35. package/src/setupSkills.js +37 -0
  36. package/src/toolHelpers/_jobSubmit.js +2 -0
  37. package/src/toolHelpers/_listLibrary.js +55 -39
  38. package/src/toolHelpers/getLogonPayload.js +7 -1
  39. package/src/toolHelpers/readCerts.js +4 -4
  40. package/src/toolHelpers/refreshToken.js +3 -2
  41. package/src/toolHelpers/refreshTokenOauth.js +3 -3
  42. package/src/toolSet/.claude/settings.local.json +13 -0
  43. package/src/toolSet/devaScore.js +61 -69
  44. package/src/toolSet/findJob.js +38 -71
  45. package/src/toolSet/findJobdef.js +28 -59
  46. package/src/toolSet/findLibrary.js +68 -100
  47. package/src/toolSet/findModel.js +35 -58
  48. package/src/toolSet/findTable.js +31 -60
  49. package/src/toolSet/getEnv.js +30 -45
  50. package/src/toolSet/listJobdefs.js +61 -96
  51. package/src/toolSet/listJobs.js +61 -110
  52. package/src/toolSet/listLibraries.js +78 -90
  53. package/src/toolSet/listModels.js +56 -83
  54. package/src/toolSet/listTables.js +66 -95
  55. package/src/toolSet/makeTools.js +1 -0
  56. package/src/toolSet/modelInfo.js +22 -54
  57. package/src/toolSet/modelScore.js +35 -77
  58. package/src/toolSet/readTable.js +63 -104
  59. package/src/toolSet/runCasProgram.js +32 -52
  60. package/src/toolSet/runJob.js +24 -24
  61. package/src/toolSet/runJobdef.js +26 -29
  62. package/src/toolSet/runMacro.js +82 -82
  63. package/src/toolSet/runProgram.js +32 -84
  64. package/src/toolSet/sasQuery.js +77 -126
  65. package/src/toolSet/sasQueryTemplate.js +4 -5
  66. package/src/toolSet/sasQueryTemplate2.js +4 -5
  67. package/src/toolSet/scrInfo.js +4 -7
  68. package/src/toolSet/scrScore.js +69 -70
  69. package/src/toolSet/searchAssets.js +5 -6
  70. package/src/toolSet/setContext.js +65 -92
  71. package/src/toolSet/superstat.js +61 -60
  72. package/src/toolSet/tableInfo.js +58 -102
@@ -1,126 +1,77 @@
1
- /*
2
- * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- import {z} from 'zod';
6
- import _jobSubmit from '../toolHelpers/_jobSubmit.js';
7
-
8
-
9
- function sasQuery() {
10
-
11
- let description = `
12
- ## sas-query — convert natural language questions into SQL queries and execute them
13
-
14
- LLM Invocation Guidance (When to use)
15
- Use THIS tool when:
16
- - User asks a natural language question about table data: "how many customers by region?"
17
- - User wants aggregated analytics: "show total sales by year"
18
- - User needs complex filtering: "find all orders over $1000 from last month"
19
- - User requests joined data: "show products with their category names"
20
- - User wants statistical summaries: "average, min, max salary by department"
21
- - User asks for specific calculations: "percentage of customers by state"
22
-
23
- Do NOT use this tool for:
24
- - Reading raw table data without filtering (use read-table)
25
- - Getting table structure or column info (use table-info)
26
- - Running pre-written SAS programs (use run-sas-program)
27
- - Running jobs or job definitions (use run-job or run-jobdef)
28
- - Executing macros (use run-macro)
29
- - Simple table reads with no aggregation (use read-table)
30
-
31
- Purpose
32
- Convert natural language queries into SAS PROC SQL SELECT statements and execute them to retrieve analyzed data. The LLM generates the SQL from the natural language query, and this tool executes it against the specified table.
33
-
34
- Parameters
35
- - table (string, required): Table name in lib.table format (e.g., "Public.cars", "sashelp.class")
36
- - query (string, required): Natural language description of what data you want
37
- - sql (string, optional): Pre-generated SQL SELECT statement (LLM should generate this from the query)
38
- - job (string, default 'program'): Job name to execute the query (default is 'program')
39
-
40
- Behavior & Processing
41
- - LLM converts the natural language query into a valid SAS PROC SQL SELECT statement
42
- - Do not add semicolons to the end of SQL statements
43
- - SQL reference: https://go.documentation.sas.com/doc/en/pgmsascdc/v_067/sqlproc/n0w2pkrm208upln11i9r4ogwyvow.htm
44
- - Tool executes the generated SQL against the specified table
45
- - Returns data in JSON format
46
-
47
- Response Contract
48
- Returns a JSON object containing:
49
- - rows: Array of row objects with query results
50
- - columns: Column metadata from the query result
51
- - log: Execution log if available
52
- - If error: structured error message
53
- - If more than 10 rows: only first 10 displayed (ask user if they want more)
54
-
55
- Disambiguation & Clarification
56
- - If table missing: ask "Which table should I query (format: lib.tablename)?"
57
- - If query too vague: ask "Can you be more specific about what data or calculation you want?"
58
- - If table format unclear: ask "Please specify table as library.tablename (e.g., Public.cars)"
59
- - If ambiguous calculation: ask for clarification on what to aggregate or filter
60
-
61
- Examples (→ mapped params)
62
- - "how many cars by make in sashelp.cars" → { table: "sashelp.cars", query: "how many cars by make", sql: "SELECT make, COUNT(*) AS count FROM sashelp.cars GROUP BY make" }
63
- - "average horsepower by origin" → { table: "sashelp.cars", query: "average horsepower by origin", sql: "SELECT origin, AVG(horsepower) AS avg_hp FROM sashelp.cars GROUP BY origin" }
64
- - "total sales over $1000 by region" → { table: "mylib.sales", query: "total sales over $1000 by region", sql: "SELECT region, SUM(amount) AS total FROM mylib.sales WHERE amount > 1000 GROUP BY region" }
65
- - "percentage of students by year in Public.students" → { table: "Public.students", query: "percentage by year", sql: "SELECT year, COUNT(*) * 100.0 / (SELECT COUNT(*) FROM Public.students) AS pct FROM Public.students GROUP BY year" }
66
-
67
- Negative Examples (should NOT call sas-query)
68
- - "read table cars from sashelp" (use read-table instead)
69
- - "show me 10 rows from customers" (use read-table instead)
70
- - "what columns are in the sales table?" (use table-info instead)
71
- - "run this SAS code: proc sql; select * from..." (use run-sas-program instead)
72
- - "execute job monthly_report" (use run-job instead)
73
- - "run macro summarize_data" (use run-macro instead)
74
-
75
- Usage Tips
76
- - Ensure table is specified in lib.tablename format
77
- - Be specific in natural language queries for better SQL generation
78
- - Use table-info first to understand column names and types
79
- - For simple reads without filtering/aggregation, prefer read-table
80
-
81
- Related Tools
82
- - read-table — for simple data reading without SQL queries
83
- - table-info — to inspect table schema before querying
84
- - run-sas-program — for executing pre-written SAS/SQL code
85
- - find-table — to verify table exists before querying
86
- `;
87
-
88
-
89
- let spec = {
90
- name: 'sas-query',
91
- aliases: ['sasQuery','sas query','sas_query'],
92
- description: description,
93
- schema: {
94
- query: z.string(),
95
- table: z.string(),
96
- sql: z.string().optional(),
97
- job: z.string().default('program')
98
- },
99
- required: ['query', 'table'],
100
- handler: async (params) => {
101
- let {table,query, sql, job, _appContext} = params;
102
- let sqlinput = (sql == null) ? ' ' : sql.replaceAll(';', ' ').replaceAll('\n', ' ').replaceAll('\r', ' ');
103
-
104
- let iparams = {
105
- scenario: {
106
- table: table,
107
- prompt: query,
108
- sql: sqlinput
109
- },
110
- name: (job == null) ? 'program' : job,
111
- type: 'job',
112
- query: true,
113
- _appContext: _appContext
114
- };
115
- if (sql == null || sql.trim().length === 0) {
116
- return { content: [{ type: 'text', text: 'Error: The SQL statement generated is blank. Please provide a valid natural language query that can be converted to SQL.' }] };
117
- }
118
-
119
- let r = await _jobSubmit(iparams);
120
- return r;
121
-
122
- }
123
- }
124
- return spec;
125
- }
126
- export default sasQuery;
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import {z} from 'zod';
6
+ import _jobSubmit from '../toolHelpers/_jobSubmit.js';
7
+
8
+ function sasQuery() {
9
+
10
+ let description = `
11
+ sas-query convert natural language questions into SQL queries and execute them.
12
+
13
+ USE when: how many/count/total/average by, aggregated analytics, complex filtering, statistical summaries
14
+ DO NOT USE for: raw reads without filtering (use read-table), table structure (use table-info), SAS programs (use run-sas-program), jobs/jobdefs
15
+
16
+ PARAMETERS
17
+ - table: string table in lib.table format (required), e.g. "Public.cars" or "sashelp.class"
18
+ - query: string natural language question (required)
19
+ - sql: string pre-generated SQL SELECT (optional, LLM generates)
20
+ - job: string (default: 'program') job name to execute
21
+
22
+ ROUTING RULES
23
+ - "how many cars by make in sashelp.cars" → { table: "sashelp.cars", query: "how many cars by make", sql: "SELECT make, COUNT(*) FROM sashelp.cars GROUP BY make" }
24
+ - "average salary by department in Public.employees" → { table: "Public.employees", query: "average salary by department", sql: "SELECT department, AVG(salary) FROM Public.employees GROUP BY department" }
25
+
26
+ EXAMPLES
27
+ - "how many cars by make in sashelp.cars" → { table: "sashelp.cars", query: "how many cars by make", sql: "SELECT make, COUNT(*) FROM sashelp.cars GROUP BY make" }
28
+ - "total sales by region from mylib.sales" → { table: "mylib.sales", query: "total sales by region", sql: "SELECT region, SUM(amount) FROM mylib.sales GROUP BY region" }
29
+
30
+ NEGATIVE EXAMPLES (do not route here)
31
+ - "read table cars" (use read-table)
32
+ - "show 10 rows" (use read-table)
33
+ - "table structure" (use table-info)
34
+ - "run SAS code" (use run-sas-program)
35
+ - "run job/macro" (use run-job/run-macro)
36
+
37
+ ERRORS
38
+ Returns rows array, columns metadata, log. Returns error if SQL invalid or table not found.
39
+ `;
40
+
41
+
42
+ let spec = {
43
+ name: 'sas-query',
44
+ description: description,
45
+ inputSchema: z.object({
46
+ query: z.string(),
47
+ table: z.string(),
48
+ sql: z.string().optional()
49
+ }),
50
+ handler: async (params) => {
51
+ let {table,query, sql, job, _appContext} = params;
52
+ let sqlinput = (sql == null) ? ' ' : sql.replaceAll(';', ' ').replaceAll('\n', ' ').replaceAll('\r', ' ');
53
+
54
+ let iparams = {
55
+ scenario: {
56
+ table: table,
57
+ prompt: query,
58
+ sql: sqlinput
59
+ },
60
+ name: (job == null) ? 'program' : job,
61
+ type: 'job',
62
+ query: true,
63
+ _appContext: _appContext
64
+ };
65
+ if (sql == null || sql.trim().length === 0) {
66
+ return { content: [{ type: 'text', text: 'Error: The SQL statement generated is blank. Please provide a valid natural language query that can be converted to SQL.' }] };
67
+ }
68
+
69
+ let r = await _jobSubmit(iparams);
70
+ return r;
71
+
72
+ }
73
+ }
74
+ return spec;
75
+ }
76
+ export default sasQuery;
77
+
@@ -108,13 +108,12 @@ async function sasQueryTemplate(uparams) {
108
108
  name: toolname,
109
109
  description: description,
110
110
 
111
- schema: {
111
+ inputSchema: z.object({
112
112
  query: z.string(),
113
113
  table: z.string().default(uTable),
114
- sql: z.string().optional()
115
- },
116
- required: ['query', 'table'],
117
- handler: async (params) => {
114
+ sql: z.string()
115
+ }),
116
+ handler: async (params) => {
118
117
 
119
118
 
120
119
  let { table, query, sql, _appContext} = params;
@@ -102,14 +102,13 @@ async function sasQueryTemplate2(uparams) {
102
102
  name: toolname,
103
103
  description: description,
104
104
 
105
- schema: {
105
+ inputSchema: z.object({
106
106
  query: z.string(),
107
107
  table: z.string().default(uTable),
108
- sql: z.string().optional(),
108
+ sql: z.string(),
109
109
 
110
- },
111
- required: ['query', 'table'],
112
- handler: async (params) => {
110
+ }),
111
+ handler: async (params) => {
113
112
 
114
113
  let { table, query, sql } = params;
115
114
  let sqlinput = (sql == null) ? ' ' : sql.replaceAll(';', ' ').replaceAll('\n', ' ').replaceAll('\r', ' ');
@@ -15,7 +15,7 @@ Purpose
15
15
  Return the input/output schema and metadata for an SCR (Score Code Runtime) model.
16
16
 
17
17
  Inputs
18
- - name (string): The SCR model identifier.
18
+ - url (string): The SCR model identifier.
19
19
  What it returns
20
20
  - A JSON object describing the model's interface, typically including:
21
21
  - Input variables (names, types, required/optional)
@@ -23,7 +23,6 @@ What it returns
23
23
 
24
24
 
25
25
  Usage notes
26
- - If no local mapping exists and \`name\` looks like a URL, the tool will attempt to fetch the schema from that URL.
27
26
  - Ensure network connectivity and credentials for the remote SCR service when needed.
28
27
  - Use scr-score to score data after inspecting the schema.
29
28
 
@@ -34,12 +33,10 @@ Examples
34
33
 
35
34
  let spec = {
36
35
  name: 'scr-info',
37
- aliases: ['scrInfo','scr info','scr_info'],
38
36
  description: description,
39
- schema: {
40
- name: z.string(),
41
- },
42
- required: ['name'],
37
+ inputSchema: z.object({
38
+ url: z.string()
39
+ }),
43
40
  handler: async (params) => {
44
41
  let {url, _appContext} = params;
45
42
  if (url === null) {
@@ -1,71 +1,70 @@
1
- /*
2
- * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
-
6
- import { z } from 'zod';
7
- import _scrScore from '../toolHelpers/_scrScore.js';
8
-
9
- function scrScore(_appContext) {
10
- let description = `
11
- ## scr-score
12
-
13
- Purpose
14
- Score a scenario using a model deployed as a SCR container in Azure or another host).
15
-
16
- Inputs
17
- - url (string, required): SCR model identifier (URL)
18
- - scenario (string | object | array, optional): Input values to score. Accepts:
19
- - a comma-separated key=value string (e.g. "x=1, y=2"),
20
- - a JSON object with field names and values (recommended for typed inputs),
21
- - an array of objects for batch scoring. If omitted, the tool will return the model's input variable definitions.
22
-
23
- What it returns
24
- - When scoring: the SCR endpoint response (predictions, probabilities, scores) merged with or alongside the supplied inputs.
25
- - When \`scenario\` is omitted: metadata describing the model's input variables (names, types, required/optional).
26
-
27
- Usage notes
28
- - Run \`scr-info\` first to inspect the expected input variables and types.
29
- - Prefer structured objects for numeric/date values to avoid type ambiguity; the simple string parser keeps values as strings.
30
- - Ensure network connectivity and any required credentials for the target SCR service.
31
-
32
- Examples
33
- - scrScore with url="loan" and scenario="age=45, income=60000"
34
- - scrScore with url="https://scr-host/models/loan" and scenario={age:45, income:60000}
35
- `;
36
-
37
- let spec = {
38
- name: 'scr-score',
39
- aliases: ['scrScore','scr score','scr_score'],
40
- description: description,
41
- schema: {
42
- url: z.string(),
43
- scenario: z.any()
44
- },
45
- required: ['url'],
46
- handler: async (params) => {
47
- let {url, scenario,_appContext} = params;
48
-
49
- if (url === null) {
50
- return { status: { statusCode: 2, msg: `SCR model ${url} was not specified` }, results: {} };
51
- }
52
-
53
- // Normalize simple string scenarios like "x=1, y=2" into an object
54
- if (typeof scenario === 'string' && scenario.includes('=')) {
55
- scenario = scenario.split(',').reduce((acc, pair) => {
56
- const [k, ...rest] = pair.split('=');
57
- if (!k) return acc;
58
- acc[k.trim()] = rest.join('=').trim();
59
- return acc;
60
- }, {});
61
- }
62
-
63
- let r = await _scrScore({ url: url, scenario: scenario , _appContext: _appContext});
64
- return r;
65
- }
66
- }
67
-
68
- return spec;
69
- }
70
-
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import { z } from 'zod';
7
+ import _scrScore from '../toolHelpers/_scrScore.js';
8
+
9
+ function scrScore(_appContext) {
10
+ let description = `
11
+ ## scr-score
12
+
13
+ Purpose
14
+ Score a scenario using a model deployed as a SCR container in Azure or another host).
15
+
16
+ Inputs
17
+ - url (string, required): SCR model identifier (URL)
18
+ - scenario (string | object | array, optional): Input values to score. Accepts:
19
+ - a comma-separated key=value string (e.g. "x=1, y=2"),
20
+ - a JSON object with field names and values (recommended for typed inputs),
21
+ - an array of objects for batch scoring. If omitted, the tool will return the model's input variable definitions.
22
+
23
+ What it returns
24
+ - When scoring: the SCR endpoint response (predictions, probabilities, scores) merged with or alongside the supplied inputs.
25
+ - When \`scenario\` is omitted: metadata describing the model's input variables (names, types, required/optional).
26
+
27
+ Usage notes
28
+ - Run \`scr-info\` first to inspect the expected input variables and types.
29
+ - Prefer structured objects for numeric/date values to avoid type ambiguity; the simple string parser keeps values as strings.
30
+ - Ensure network connectivity and any required credentials for the target SCR service.
31
+
32
+ Examples
33
+ - scrScore with url="loan" and scenario="age=45, income=60000"
34
+ - scrScore with url="https://scr-host/models/loan" and scenario={age:45, income:60000}
35
+ `;
36
+
37
+ let spec = {
38
+ name: 'scr-score',
39
+ description: description,
40
+ inputSchema: z.object({
41
+ url: z.string(),
42
+ scenario: z.string().optional()
43
+ }),
44
+
45
+ handler: async (params) => {
46
+ let {url, scenario,_appContext} = params;
47
+
48
+ if (url === null) {
49
+ return { status: { statusCode: 2, msg: `SCR model ${url} was not specified` }, results: {} };
50
+ }
51
+
52
+ // Normalize simple string scenarios like "x=1, y=2" into an object
53
+ if (typeof scenario === 'string' && scenario.includes('=')) {
54
+ scenario = scenario.split(',').reduce((acc, pair) => {
55
+ const [k, ...rest] = pair.split('=');
56
+ if (!k) return acc;
57
+ acc[k.trim()] = rest.join('=').trim();
58
+ return acc;
59
+ }, {});
60
+ }
61
+
62
+ let r = await _scrScore({ url: url, scenario: scenario , _appContext: _appContext});
63
+ return r;
64
+ }
65
+ }
66
+
67
+ return spec;
68
+ }
69
+
71
70
  export default scrScore;
@@ -30,16 +30,15 @@ function searchAssets(_appContext) {
30
30
  `;
31
31
 
32
32
  let specs = {
33
- name: 'searchAssets',
33
+ name: 'search-assets',
34
34
  description: description,
35
- schema: {
35
+ inputSchema: z.object({
36
36
  searchstring: z.string(),
37
37
  assetType: z.string(),
38
- start: z.number().default(0),
39
- limit: z.number().default(10),
38
+ start: z.number(),
39
+ limit: z.number()
40
40
 
41
- },
42
- required: ['assetType'],
41
+ }),
43
42
  handler: async (params) => {
44
43
  log('searchAssets params', params);
45
44
  return await _catalogSearch(params, 'search');
@@ -1,92 +1,65 @@
1
- /*
2
- * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
-
6
- import {z} from 'zod';
7
-
8
- function setContext(_appContext) {
9
- let description = `
10
- ## set-context — set the CAS and SAS server contexts for subsequent tool calls
11
-
12
- LLM Invocation Guidance (When to use)
13
- Use THIS tool when:
14
- - User wants to switch to a different CAS server: "Use the finance-cas-server"
15
- - User wants to change the compute context: "Switch to 'SAS Studio Compute Context'"
16
- - User wants to check current context: "What context am I using?"
17
- - User wants to set both: "Use finance-cas-server for CAS and my-compute for SAS"
18
-
19
- Do NOT use this tool for:
20
- - Retrieving variable values (use get-env)
21
- - Reading table data (use read-table)
22
- - Running programs or queries (use run-sas-program or sas-query)
23
- - Listing available servers or contexts (no tool for this; would require backend support)
24
-
25
- Purpose
26
- Set the active CAS server and/or SAS compute context for all subsequent tool calls in this session. This allows switching between different server environments. If neither parameter is provided, the tool returns the current context values.
27
-
28
- Parameters
29
- - cas (string, optional): The name of the CAS server to use for subsequent CAS operations. Examples: 'cas-shared-default', 'finance-cas-server', 'analytics-cas'
30
- - sas (string, optional): The name of the SAS compute context to use for subsequent SAS operations. Examples: 'SAS Studio Compute Context', 'my-compute', 'batch-compute'
31
-
32
- Response Contract
33
- Returns a JSON object containing:
34
- - cas: The current/new CAS server name (string or null)
35
- - sas: The current/new SAS compute context name (string or null)
36
- - If no parameters provided, returns the current context values
37
- - If parameters provided, updates and returns the new context values
38
- - On error: error message if context cannot be set (e.g., invalid server name)
39
-
40
- Disambiguation & Clarification
41
- - If user says "switch servers" without specifying which: ask "Which server would you like to use: CAS, SAS, or both?"
42
- - If user provides a server name that may not exist: proceed with setting it (the backend will validate)
43
- - If user says "reset context": ask "Should I reset the CAS context, SAS context, or both?"
44
- - If user only says "context": ask "Would you like to check the current context or set a new one?"
45
-
46
- Examples ( mapped params)
47
- - "Use the finance-cas-server" → { cas: "finance-cas-server" }
48
- - "Switch to SAS Studio Compute Context" → { sas: "SAS Studio Compute Context" }
49
- - "Set CAS to prod-cas and SAS to batch-compute" → { cas: "prod-cas", sas: "batch-compute" }
50
- - "What's my current context?" → { } (no parameters returns current context)
51
- - "Show me the active CAS server" → { } (no parameters returns current context)
52
-
53
- Negative Examples (should NOT call set-context)
54
- - "Read 10 rows from the customers table" (use read-table instead)
55
- - "What's the value of myVariable?" (use get-env instead)
56
- - "Run this SAS program" (use run-sas-program instead)
57
-
58
- Related Tools
59
- - get-env — to retrieve individual environment variable values
60
- - read-table — to read data using the current context
61
- - run-sas-program — to execute SAS programs in the current context
62
- - sas-query — to execute SQL queries in the current context
63
- `;
64
-
65
- let spec = {
66
- name: 'set-context',
67
- aliases: ['setContext','set context','set_context'],
68
- description: description,
69
- schema: {
70
- cas: z.string().optional(),
71
- sas: z.string().optional()
72
- },
73
-
74
- handler: async (params) => {
75
-
76
- let {cas, sas} = params;
77
- if (typeof cas === 'string' && cas.trim().length > 0) {
78
- _appContext.contexts.cas = cas.trim();
79
- }
80
- if (typeof sas === 'string' && sas.trim().length > 0) {
81
- _appContext.contexts.sas = sas.trim();
82
- }
83
- // Return a structured response without extraneous keys
84
- return {
85
- content: [{ type: 'text', text: JSON.stringify(_appContext.contexts) }],
86
- structuredContent: _appContext.contexts
87
- };
88
- }
89
- }
90
- return spec;
91
- }
92
- export default setContext;
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import {z} from 'zod';
7
+
8
+ function setContext(_appContext) {
9
+ let description = `
10
+ set-context — set the CAS and SAS server contexts for subsequent tool calls.
11
+
12
+ USE when: switch to CAS server, change compute context, check current context, set both
13
+ DO NOT USE for: get variables (use get-env), read data (use read-table), run programs (use run-sas-program)
14
+
15
+ PARAMETERS
16
+ - cas: string CAS server name (optional), e.g. 'cas-shared-default', 'finance-cas-server'
17
+ - sas: string SAS compute context (optional), e.g. 'SAS Studio Compute Context', 'batch-compute'
18
+
19
+ ROUTING RULES
20
+ - "use finance-cas-server" { cas: "finance-cas-server" }
21
+ - "switch to SAS Studio Compute Context" → { sas: "SAS Studio Compute Context" }
22
+ - "use finance-cas for CAS and batch-compute for SAS" → { cas: "finance-cas", sas: "batch-compute" }
23
+ - "what context am I using" { } (no params, returns current)
24
+
25
+ EXAMPLES
26
+ - "use finance-cas-server" { cas: "finance-cas-server" }
27
+ - "switch to batch-compute" → { sas: "batch-compute" }
28
+ - "what's my current context" → { }
29
+
30
+ NEGATIVE EXAMPLES (do not route here)
31
+ - "read table cars" (use read-table)
32
+ - "what's the value of X" (use get-env)
33
+ - "run program" (use run-sas-program)
34
+
35
+ ERRORS
36
+ Returns current or updated context values {cas, sas}. Error if server not found or invalid name.
37
+ `;
38
+
39
+ let spec = {
40
+ name: 'set-context',
41
+ description: description,
42
+ inputSchema: z.object({
43
+ cas: z.string().optional(),
44
+ sas: z.string().optional()
45
+ }),
46
+ handler: async (params) => {
47
+
48
+ let {cas, sas} = params;
49
+ if (typeof cas === 'string' && cas.trim().length > 0) {
50
+ _appContext.contexts.cas = cas.trim();
51
+ }
52
+ if (typeof sas === 'string' && sas.trim().length > 0) {
53
+ _appContext.contexts.sas = sas.trim();
54
+ }
55
+ // Return a structured response without extraneous keys
56
+ return {
57
+ content: [{ type: 'text', text: JSON.stringify(_appContext.contexts) }],
58
+ structuredContent: _appContext.contexts
59
+ };
60
+ }
61
+ }
62
+ return spec;
63
+ }
64
+ export default setContext;
65
+