@sassoftware/sas-score-mcp-serverjs 0.4.1 → 1.0.1-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 +155 -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 +46 -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,95 +1,66 @@
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 debug from 'debug';
8
- import _listTables from '../toolHelpers/_listTables.js';
9
-
10
-
11
- function listTables(_appContext) {
12
- const log = debug('tools');
13
-
14
- let description = `
15
- ## list-tables — enumerate tables within a specific CAS or SAS library
16
-
17
- LLM Invocation Guidance (When to use)
18
- Use THIS tool when the user explicitly wants the tables inside ONE library:
19
- - "list tables in Samples"
20
- - "show tables in sashelp"
21
- - "list cas tables in Public"
22
- - "list 25 tables in Samples"
23
- - "next tables" (after a prior listTables call)
24
-
25
- Do NOT use this tool to list the following
26
- - lib -> use list-libraries
27
- - list models -> use list-models
28
- - list jobs -> use list-jobs
29
- - list jobdefs -> use list-jobdefs
30
- - Finding whether a library exists (use find-library)
31
- - Describing a single table's columns or metadata (use table-info)
32
- - Reading table data rows (use read-table)
33
- - Listing jobs/models (other specialized tools)
34
-
35
- Purpose
36
- Return the names (and possibly lightweight metadata) of tables contained in a specified library (CAS caslib or SAS libref).
37
-
38
- Parameters
39
- - lib (string, required): Library to inspect (e.g. "Samples", "sashelp").
40
- - server (cas|sas, default 'cas'): Target environment; default when unspecified is CAS.
41
- - limit (number, default 10): Page size.
42
- - start (number, default 1): 1-based offset for pagination.
43
- - where (string, optional): Filter expression (if supported by backend) or ignored safely.
44
-
45
- Response Contract
46
- - JSON: { tables: string[] [, start:number]? }
47
- - tables array is empty when no matches.
48
- - Include start = start + limit when length === limit (possible more pages).
49
-
50
- Pagination Examples
51
- - First page: { lib:'Samples', start:1, limit:10 }
52
- - Next page: { lib:'Samples', start:11, limit:10 }
53
-
54
- Disambiguation & Clarification
55
- - Missing library name → ask: "Which library do you want to list tables from?"
56
- - Input only "list tables" → ask for the library unless prior context supplies one.
57
- - If user mentions multiple libs ("tables in Public and Samples") request a single library.
58
-
59
- Negative Examples (should NOT call list-tables)
60
- - "list libs" (list-libraries)
61
- - "find lib Public" (find-library)
62
- - "describe table cars" (table-info)
63
- - "read table cars from sashelp" (read-table)
64
-
65
- Usage Tips
66
- - After listing, call table-info to inspect structure or read-table for sample data.
67
- - Keep limit moderate; page for very large libraries.
68
-
69
- Examples (→ mapped params)
70
- - "list tables in samples" → { lib:"samples", start:1, limit:10 }
71
- - "show 25 tables in sashelp" → { lib:"sashelp", limit:25, start:1 }
72
- - "next tables" (after previous {start:1,limit:10}) → { start:11, limit:10, lib:<previousLib> }
73
- `;
74
-
75
- let spec = {
76
- name: 'list-tables',
77
- aliases: ['listTables','list tables','list_tables'],
78
- description: description,
79
-
80
- schema: {
81
- 'lib': z.string(),
82
- 'server': z.string().default('cas'),
83
- 'limit': z.number().default(10),
84
- 'start': z.number().default(1)
85
- },
86
- required: ['lib'],
87
- handler: async (params) => {
88
- let r = await _listTables(params);
89
- return r;
90
- }
91
- }
92
- return spec;
93
- }
94
-
95
- export default listTables;
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 debug from 'debug';
8
+ import _listTables from '../toolHelpers/_listTables.js';
9
+
10
+
11
+ function listTables(_appContext) {
12
+ const log = debug('tools');
13
+
14
+ let description = `
15
+ list-tables — enumerate tables within a library.
16
+
17
+ USE when: list tables in <lib>, show tables in <lib>, next page
18
+ DO NOT USE for: find table, list libraries, get table structure (use table-info), read data (use read-table)
19
+
20
+ PARAMETERS
21
+ - lib: string library to inspect (required)
22
+ - server: string (default: 'cas') — 'cas' or 'sas'
23
+ - limit: number (default: 10) page size
24
+ - start: number (default: 1) — 1-based offset
25
+ - where: string optional filter expression
26
+
27
+ ROUTING RULES
28
+ - "list tables in Samples" → { lib: "Samples", start: 1, limit: 10 }
29
+ - "list 25 tables in sashelp" → { lib: "sashelp", limit: 25, start: 1 }
30
+ - "list cas tables in Public" { lib: "Public", server: "cas", start: 1, limit: 10 }
31
+
32
+ EXAMPLES
33
+ - "list tables in Samples" → { lib: "Samples", start: 1, limit: 10 }
34
+ - "show 25 tables in sashelp" → { lib: "sashelp", limit: 25, start: 1 }
35
+
36
+ NEGATIVE EXAMPLES (do not route here)
37
+ - "list libs" (use list-libraries)
38
+ - "find lib Public" (use find-library)
39
+ - "describe table cars" (use table-info)
40
+ - "read table cars" (use read-table)
41
+
42
+ ERRORS
43
+ Returns empty array if no tables found.
44
+ `;
45
+
46
+ let spec = {
47
+ name: 'list-tables',
48
+ description: description,
49
+
50
+ inputSchema: z.object({
51
+ lib: z.string(),
52
+ server: z.string().optional(),
53
+ limit: z.number().optional(),
54
+ start: z.number().optional(),
55
+ where: z.string().optional()
56
+ }),
57
+ handler: async (params) => {
58
+ let r = await _listTables(params);
59
+ return r;
60
+ }
61
+ }
62
+ return spec;
63
+ }
64
+
65
+ export default listTables;
66
+
@@ -31,6 +31,7 @@ import findJobdef from './findJobdef.js';
31
31
  import sasQuery from './sasQuery.js';
32
32
  import setContext from './setContext.js';
33
33
 
34
+
34
35
  function makeTools(_appContext) {
35
36
  // wrap all tools with
36
37
  let customTools = [];
@@ -10,72 +10,39 @@ const log = debug('tools');
10
10
 
11
11
  function modelInfo(_appContext) {
12
12
  let description = `
13
- ## model-info — retrieve detailed metadata for a deployed model
13
+ model-info — retrieve detailed metadata for a deployed model.
14
14
 
15
- LLM Invocation Guidance (When to use)
16
- Use THIS tool when:
17
- - User wants input/output variable information: "What inputs does model X need?"
18
- - User wants to understand model variables: "Describe model myModel"
19
- - User wants data types and roles: "Show me the variables for myModel"
20
- - User wants to prepare data for scoring: "What are the required inputs for model sales_forecast?"
15
+ USE when: what inputs does model need, describe model, show variables for model, model inputs/outputs
16
+ DO NOT USE for: find model, list models, score model, table/job operations
21
17
 
22
- Do NOT use this tool for:
23
- - Checking if a model exists (use find-model)
24
- - Listing available models (use list-models)
25
- - Scoring a model (use model-score)
26
- - Looking up tables or jobs (use respective find/list tools)
18
+ PARAMETERS
19
+ - model: string model name (required, exact match)
27
20
 
28
- Purpose
29
- Retrieve detailed metadata for a model deployed to MAS (Model Aggregation Service). This includes input variable names, data types, roles/usage, ranges, and output variable information. Use this before scoring to understand what data the model requires.
21
+ ROUTING RULES
22
+ - "what inputs does model X need?" { model: "X" }
23
+ - "describe model Y" → { model: "Y" }
24
+ - "show variables for Z" → { model: "Z" }
30
25
 
31
- Parameters
32
- - model (string, required): The name of the model as published to MAS. Must be exact match.
33
-
34
- Response Contract
35
- Returns a JSON object containing model metadata, typically including:
36
- - name: The model name
37
- - inputs: Array of input variable objects with:
38
- - name: Variable name
39
- - type: Data type (numeric, character, etc.)
40
- - role: Role in model (input, key, etc.)
41
- - allowed_values or range: Optional constraints
42
- - outputs: Array of output/prediction objects with:
43
- - name: Prediction/output variable name
44
- - type: Data type
45
- - possible_values: For classification models, the class labels
46
- - model_type: Type of model (regression, classification, etc.)
47
- - description: Optional model description
48
-
49
- Disambiguation & Clarification
50
- - If model name is missing: ask "Which model would you like to see information for?"
51
- - If user says "variables" without specifying model: ask "Which model would you like the variables for?"
52
- - Multiple model requests: handle one at a time
53
-
54
- Examples (→ mapped params)
26
+ EXAMPLES
55
27
  - "What inputs does model churnRisk need?" → { model: "churnRisk" }
56
28
  - "Describe model creditScore" → { model: "creditScore" }
57
- - "Show the variables for myModel" → { model: "myModel" }
58
- - "What are the outputs of model sales_forecast?" → { model: "sales_forecast" }
29
+ - "Show variables for myModel" → { model: "myModel" }
59
30
 
60
- Negative Examples (should NOT call model-info)
61
- - "List all available models" (use list-models instead)
62
- - "Find model cancer" (use find-model instead)
63
- - "Score this customer with model churnRisk" (use model-score instead)
31
+ NEGATIVE EXAMPLES (do not route here)
32
+ - "list models" (use list-models)
33
+ - "find model X" (use find-model)
34
+ - "score with model X" (use model-score)
64
35
 
65
- Related Tools
66
- - list-models find-model model-info model-score (typical workflow)
67
- - find-model — to check if a specific model exists
68
- - model-score — to score data using the model
69
- `;
36
+ ERRORS
37
+ Returns model metadata: inputs (name, type, role), outputs (name, type, possible_values), model_type, description.
38
+ `;
70
39
 
71
40
  let spec = {
72
41
  name: 'model-info',
73
- aliases: ['modelInfo','model info','model_info'],
74
42
  description: description,
75
- schema: {
76
- 'model': z.string()
77
- },
78
- required: ['model'],
43
+ inputSchema: z.object({
44
+ model: z.string()
45
+ }),
79
46
  handler: async (params) => {
80
47
  let r = await _masDescribe(params);
81
48
  return r;
@@ -85,3 +52,4 @@ Related Tools
85
52
  }
86
53
 
87
54
  export default modelInfo;
55
+
@@ -10,93 +10,44 @@ const log = debug('tools');
10
10
 
11
11
  function modelScore(_appContext) {
12
12
  let description = `
13
- ## model-score — score data using a deployed model on MAS
13
+ mas-score — score data using a deployed model on MAS.
14
14
 
15
- LLM Invocation Guidance (When to use)
16
- Use THIS tool when:
17
- - User wants to score data with a model: "score this customer with model churnRisk"
18
- - User provides input values for prediction: "predict using model creditScore with age=45, income=60000"
19
- - User wants batch scoring: "score these 10 customers with fraud model"
20
- - User asks for model predictions: "what does the sales forecast model predict for Q4?"
21
- - User wants to get predictions: "use the risk model to score this applicant"
22
- - User provides scenario data for a model: "run model cancer1 with age=45, sex=M"
15
+ USE when: score with model, predict using model, batch scoring, model predictions
16
+ DO NOT USE for: find model, model metadata, list models, run programs/jobs, query tables
23
17
 
24
- Do NOT use this tool for:
25
- - Checking if a model exists (use find-model)
26
- - Getting model metadata/variables (use model-info)
27
- - Listing available models (use list-models)
28
- - Running SAS programs (use run-sas-program)
29
- - Running jobs (use run-job)
30
- - Querying tables (use sas-query or read-table)
18
+ PARAMETERS
19
+ - model: string model name (required, exact match)
20
+ - scenario: string | object | array — input data (required). Accepts: "x=1, y=2", {x:1, y:2}, or array of objects
21
+ - uflag: boolean (default: false) — prefix model fields with underscore when true
31
22
 
32
- Purpose
33
- Score user-supplied scenario data using a model published to MAS (Model Aggregation Service) on SAS Viya. Returns predictions, probabilities, scores, and other model outputs merged with the input data.
23
+ ROUTING RULES
24
+ - "score with model X using a=1, b=2" { model: "X", scenario: {a:1, b:2}, uflag: false }
25
+ - "predict using model Y with age=45, income=60000" → { model: "Y", scenario: {age:45, income:60000}, uflag: false }
34
26
 
35
- Parameters
36
- - model (string, required): The name of the model as published to MAS. Must be exact match.
37
- - scenario (string | object | array, required): Data to score. Accepts:
38
- - A comma-separated key=value string (e.g., "x=1, y=2")
39
- - A plain object with field names and values (e.g., {x: 1, y: 2})
40
- - An array of objects for batch scoring
41
- - uflag (boolean, default false): When true, returned model field names will be prefixed with an underscore.
27
+ EXAMPLES
28
+ - "score with model churn using age=45, income=60000" { model: "churn", scenario: {age:45, income:60000}, uflag: false }
29
+ - "predict creditScore for credit=700, debt=20000" → { model: "creditScore", scenario: {credit:700, debt:20000}, uflag: false }
42
30
 
43
- Parsing & Behavior
44
- - If scenario is a string, the tool parses comma-separated key=value pairs into an object
45
- - If scenario is an array, supports batch scoring (processes multiple records)
46
- - Numeric values in strings remain as strings; validate and cast as needed before scoring
31
+ NEGATIVE EXAMPLES (do not route here)
32
+ - "find model X" (use find-model)
33
+ - "what inputs does model need" (use model-info)
34
+ - "list models" (use list-models)
35
+ - "run job X" (use run-job)
47
36
 
48
- Response Contract
49
- Returns a JSON object containing:
50
- - Input fields merged with scoring results
51
- - Prediction/score field(s) from the model (field names depend on model)
52
- - Probability fields for classification models (e.g., P_class1, P_class2)
53
- - Model metadata: model name, version if available
54
- - _variables_: Variable metadata if uflag=true
55
- - Error object if scoring fails with error message
56
-
57
- Disambiguation & Clarification
58
- - Missing model name: ask "Which model would you like to use for scoring?"
59
- - Missing scenario data: ask "What input values should I use for scoring?"
60
- - If model unknown: suggest "Use find-model to verify the model exists or list-models to see available models"
61
- - If unsure of inputs: suggest "Use model-info to see the required input variables first"
62
-
63
- Examples (→ mapped params)
64
- - "score with model churn using age=45, income=60000" → { model: "churn", scenario: { age: "45", income: "60000" }, uflag: false }
65
- - "predict creditScore for applicant: credit=700, debt=20000" → { model: "creditScore", scenario: { credit: "700", debt: "20000" }, uflag: false }
66
- - "use fraud model to score transaction amount=500, merchant=online" → { model: "fraud", scenario: { amount: "500", merchant: "online" }, uflag: false }
67
- - "run model cancer1 with age=45, sex=M, tumor=stage2" → { model: "cancer1", scenario: { age: "45", sex: "M", tumor: "stage2" }, uflag: false }
68
-
69
- Negative Examples (should NOT call model-score)
70
- - "find model churnRisk" (use find-model instead)
71
- - "what inputs does model need?" (use model-info instead)
72
- - "list all models" (use list-models instead)
73
- - "run job scoring_job" (use run-job instead)
74
- - "read table scores from Public" (use read-table instead)
75
-
76
- Usage Tips
77
- - Use model-info first to understand required input variables and data types
78
- - Verify model exists with find-model before attempting to score
79
- - For batch scoring, provide an array of objects as the scenario parameter
80
- - Ensure MAS connectivity and credentials are available
81
-
82
- Related Tools
83
- - list-models → find-model → model-info → model-score (typical workflow)
84
- - find-model — to verify model exists before scoring
85
- - model-info — to understand required inputs and expected outputs
86
- - list-models — to discover available models
87
- `;
37
+ ERRORS
38
+ Returns predictions, probabilities, scores merged with input data. Returns error if model not found or scoring fails.
39
+ `;
88
40
 
89
41
 
90
42
  let spec = {
91
- name: 'model-score',
92
- aliases: ['modelScore','model score','model_score'],
43
+ name: 'mas-score',
93
44
  description: description,
94
- schema: {
95
- 'model': z.string(),
96
- 'scenario': z.any(),
97
- 'uflag': z.boolean()
98
- },
99
- required: ['model', 'scenario'],
45
+ inputSchema:z.object({
46
+ model: z.string(),
47
+ scenario: z.string(),
48
+ uflag: z.boolean().optional()
49
+ }),
50
+
100
51
  handler: async (iparams) => {
101
52
  let params = {...iparams};
102
53
  let scenario = params.scenario;
@@ -119,6 +70,12 @@ Related Tools
119
70
  }, {});
120
71
  }
121
72
  params.scenario= scenarioObj;
73
+
74
+ // Drop model extension (e.g., .job, .model)
75
+ if (params.model && params.model.includes('.')) {
76
+ params.model = params.model.substring(0, params.model.lastIndexOf('.'));
77
+ }
78
+
122
79
  log('modelScore params', params);
123
80
  // Check if the params.scenario is a string and parse it
124
81
  let r = await _masScoring(params)
@@ -129,3 +86,4 @@ Related Tools
129
86
  }
130
87
 
131
88
  export default modelScore;
89
+
@@ -1,104 +1,63 @@
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 debug from 'debug';
7
-
8
- import _readTable from '../toolHelpers/_readTable.js';
9
- function readTable(_appContext) {
10
-
11
- let describe = `
12
- ## read-table — retrieve rows from a table in a CAS or SAS library
13
-
14
- LLM Invocation Guidance (When to use)
15
- Use THIS tool when:
16
- - User wants to read data from a table: "read table xyz.customers"
17
- - User wants sample rows: "show me 10 rows from lib.sales"
18
- - User wants filtered data: "read from mylib.orders where status = 'shipped'"
19
- - User wants from specific library: "read table cars in sashelp"
20
- - User wants from specific server: "read table from mylib.employees on sas"
21
-
22
- Do NOT use this tool for:
23
- - Listing tables in a library (use list-tables)
24
- - Getting table structure/metadata (use table-info)
25
- - Running SQL queries (use sas-query)
26
- - Executing SAS programs (use run-sas-program)
27
- - Running statistical analysis (use appropriate analytics tools)
28
-
29
- Purpose
30
- Read one or more rows from a specified table in a CAS caslib or SAS libref. Supports pagination, filtering with WHERE clauses, and formatted/raw value display. Use this to inspect table data, sample rows, or retrieve filtered subsets.
31
-
32
- Parameters
33
- - table (string, required): Table name to read.
34
- - lib (string, required): The caslib or libref containing the table.
35
- - server (string, default 'cas'): Target server: 'cas' or 'sas'.
36
- - start (number, default 1): 1-based row index to start reading from.
37
- - limit (number, default 10): Maximum number of rows to return (1-1000 recommended).
38
- - where (string, optional): SQL-style WHERE clause to filter rows (e.g., "age > 30 AND status = 'active'").
39
- - format (boolean, default true): When true, return formatted/labeled values; when false return raw values.
40
- - row (number, optional): Read a single specific row (sets start to this value and limit to 1).
41
-
42
- Response Contract
43
- Returns a JSON object containing:
44
- - rows: Array of row objects with column names as keys
45
- - total (optional): Total count of rows in table (if available)
46
- - filtered_count (optional): Count of rows matching WHERE clause (if WHERE used)
47
- - columns (optional): Column metadata including names and types
48
- - Empty array if no rows match the criteria
49
-
50
- Pagination & Filtering
51
- - First page default: { start: 1, limit: 10 }
52
- - To get next page: increment start by limit (e.g., { start: 11, limit: 10 })
53
- - Use WHERE clause for server-side filtering: { where: "age > 30" }
54
-
55
- Disambiguation & Clarification
56
- - Missing library: ask "Which library contains the table you want to read?"
57
- - Missing table: ask "Which table would you like to read?"
58
- - Ambiguous lib.table format: parse and use as separate parameters
59
- - Multiple tables: clarify which one (readTable handles one table at a time)
60
-
61
- Examples (→ mapped params)
62
- - "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
63
- - "show 25 rows from customers" → { table: "customers", lib: <current_lib>, limit: 25, start: 1 }
64
- - "read orders where status = 'shipped' limit 50" → { table: "orders", lib: <lib>, where: "status = 'shipped'", limit: 50, start: 1 }
65
- - "read row 15 from employees in mylib on sas" → { table: "employees", lib: "mylib", server: "sas", row: 15 }
66
- - "get next 10 rows" (after previous {start:1,limit:10}) → { table: <same>, lib: <same>, start: 11, limit: 10 }
67
-
68
- Negative Examples (should NOT call read-table)
69
- - "list tables in Samples" (use list-tables instead)
70
- - "what columns are in the cars table?" (use table-info instead)
71
- - "execute this SQL query" (use sas-query instead)
72
- - "run this SAS code" (use run-sas-program instead)
73
-
74
- Related Tools
75
- - list-tables — to browse available tables in a library
76
- - table-info — to inspect table structure, columns, and metadata
77
- - find-table — to check if a table exists in a library
78
- - list-libraries — to browse available libraries
79
- - sas-query — to run complex SQL queries across tables
80
- `;
81
-
82
- let specs = {
83
- name: 'read-table',
84
- aliases: ['readTable','read table','read_table'],
85
- description: describe,
86
- schema: {
87
- table: z.string(),
88
- lib: z.string(),
89
- start: z.number(),
90
- limit: z.number().default(10),
91
- server: z.string().default('cas'),
92
- where: z.string().default(''),
93
- format: z.boolean().default(true)
94
-
95
- },
96
- required: ['table', 'lib'],
97
- handler: async (params) => {
98
- let r = await _readTable(params,'query');
99
- return r;
100
- }
101
- }
102
- return specs;
103
- }
104
- export default readTable;
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 debug from 'debug';
7
+
8
+ import _readTable from '../toolHelpers/_readTable.js';
9
+ function readTable(_appContext) {
10
+
11
+ let describe = `
12
+ read-table — retrieve rows from a table in a CAS or SAS library.
13
+
14
+ USE when: read table, show rows, read from library, filtered data with WHERE
15
+ DO NOT USE for: list tables, table structure (use table-info), SQL queries (use sas-query), SAS programs
16
+
17
+ PARAMETERS
18
+ - table: string table name (required)
19
+ - lib: string caslib or libref (required)
20
+ - server: string (default: 'cas') 'cas' or 'sas'
21
+ - start: number (default: 1) — 1-based row index
22
+ - limit: number (default: 10) — max rows (1-1000)
23
+ - where: string SQL WHERE clause filter
24
+ - format: boolean (default: true) — formatted or raw values
25
+
26
+ ROUTING RULES
27
+ - "read table cars in Samples" { table: "cars", lib: "Samples", start: 1, limit: 10 }
28
+ - "show 25 rows from customers" → { table: "customers", lib: "<lib>", limit: 25, start: 1 }
29
+ - "read from mylib.orders where status='shipped'" → { table: "orders", lib: "mylib", where: "status='shipped'", start: 1, limit: 10 }
30
+
31
+ EXAMPLES
32
+ - "read table cars in Samples" → { table: "cars", lib: "Samples", start: 1, limit: 10 }
33
+ - "show 25 rows from customers" → { table: "customers", lib: "mylib", limit: 25, start: 1 }
34
+
35
+ NEGATIVE EXAMPLES (do not route here)
36
+ - "list tables in Samples" (use list-tables)
37
+ - "what columns are in cars" (use table-info)
38
+ - "execute SQL query" (use sas-query)
39
+ - "run SAS code" (use run-sas-program)
40
+
41
+ ERRORS
42
+ Returns rows array, total count, filtered_count, columns metadata. Empty array if no matches.
43
+ `;
44
+
45
+ let specs = {
46
+ name: 'read-table',
47
+ description: describe,
48
+ inputSchema: z.object({
49
+ table: z.string(),
50
+ lib: z.string().optional(),
51
+ start: z.number().optional(),
52
+ limit: z.number().optional(),
53
+ server: z.string().optional(),
54
+ where: z.string().optional()
55
+ }),
56
+ handler: async (params) => {
57
+ let r = await _readTable(params,'query');
58
+ return r;
59
+ }
60
+ }
61
+ return specs;
62
+ }
63
+ export default readTable;