@sassoftware/sas-score-mcp-serverjs 0.4.1-1 → 0.4.1-17

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 (61) hide show
  1. package/cli.js +210 -108
  2. package/package.json +6 -4
  3. package/scripts/docs/SCORE_SKILL_REFERENCE.md +142 -0
  4. package/scripts/docs/TOOL_DESCRIPTION_TEMPLATE.md +157 -0
  5. package/scripts/docs/TOOL_UPDATES_SUMMARY.md +208 -0
  6. package/scripts/docs/mcp-localhost-config-guide.md +184 -0
  7. package/scripts/docs/oauth-http-transport.md +96 -0
  8. package/scripts/docs/sas-mcp-tools-reference.md +600 -0
  9. package/scripts/getViyaca.sh +1 -0
  10. package/scripts/optimize_final.py +140 -0
  11. package/scripts/optimize_tools.py +99 -0
  12. package/scripts/setup-skills.js +78 -0
  13. package/scripts/update_descriptions.py +46 -0
  14. package/scripts/viyatls.sh +3 -0
  15. package/skills/sas-find-library-smart/SKILL.md +154 -0
  16. package/skills/sas-list-tables-smart/SKILL.md +127 -0
  17. package/skills/sas-read-and-score/SKILL.md +71 -51
  18. package/skills/sas-read-strategy/SKILL.md +43 -30
  19. package/skills/sas-score-workflow/SKILL.md +65 -34
  20. package/skills/sas-spec-migration/SKILL.md +303 -0
  21. package/src/authpkce.js +219 -0
  22. package/src/createMcpServer.js +11 -8
  23. package/src/expressMcpServer.js +354 -338
  24. package/src/oauthHandlers/authorize.js +46 -0
  25. package/src/oauthHandlers/baseUrl.js +8 -0
  26. package/src/oauthHandlers/callback.js +96 -0
  27. package/src/oauthHandlers/getMetadata.js +27 -0
  28. package/src/oauthHandlers/index.js +7 -0
  29. package/src/oauthHandlers/token.js +37 -0
  30. package/src/processHeaders.js +88 -0
  31. package/src/toolHelpers/_listLibrary.js +0 -1
  32. package/src/toolHelpers/getLogonPayload.js +5 -1
  33. package/src/toolHelpers/refreshToken.js +3 -2
  34. package/src/toolHelpers/refreshTokenOauth.js +3 -3
  35. package/src/toolSet/.claude/settings.local.json +13 -0
  36. package/src/toolSet/devaScore.js +61 -61
  37. package/src/toolSet/findJob.js +1 -1
  38. package/src/toolSet/findJobdef.js +2 -2
  39. package/src/toolSet/findLibrary.js +68 -67
  40. package/src/toolSet/findModel.js +2 -2
  41. package/src/toolSet/findTable.js +3 -2
  42. package/src/toolSet/getEnv.js +8 -4
  43. package/src/toolSet/listJobdefs.js +61 -61
  44. package/src/toolSet/listJobs.js +61 -61
  45. package/src/toolSet/listLibraries.js +78 -78
  46. package/src/toolSet/listModels.js +56 -56
  47. package/src/toolSet/listTables.js +66 -65
  48. package/src/toolSet/modelInfo.js +2 -2
  49. package/src/toolSet/modelScore.js +6 -5
  50. package/src/toolSet/readTable.js +63 -65
  51. package/src/toolSet/runCasProgram.js +7 -6
  52. package/src/toolSet/runJob.js +81 -81
  53. package/src/toolSet/runJobdef.js +82 -82
  54. package/src/toolSet/runMacro.js +81 -80
  55. package/src/toolSet/runProgram.js +4 -8
  56. package/src/toolSet/sasQuery.js +77 -78
  57. package/src/toolSet/scrInfo.js +1 -1
  58. package/src/toolSet/scrScore.js +69 -68
  59. package/src/toolSet/setContext.js +65 -65
  60. package/src/toolSet/superstat.js +61 -59
  61. package/src/toolSet/tableInfo.js +58 -57
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env python3
2
+ import re
3
+
4
+ # Read and replace listJobs
5
+ with open('src/toolSet/listJobs.js', 'r') as f:
6
+ content = f.read()
7
+
8
+ # Find and replace listJobs description - find the full description block
9
+ pattern = r'let description = `\n ## list-jobs.*? `;'
10
+ replacement = r'''let description = `
11
+ list-jobs — enumerate SAS Viya job assets.
12
+
13
+ USE when: list jobs, show jobs, browse jobs, list available jobs, next page of jobs
14
+ DO NOT USE for: find single job (use find-job), execute job (use run-job), run jobdef (use run-jobdef), sas code (use run-sas-program)
15
+
16
+ PARAMETERS
17
+ - limit: number (default: 10) — number of jobs per page
18
+ - start: number (default: 1) — 1-based page offset
19
+ - where: string (default: '') — optional filter expression
20
+
21
+ ROUTING RULES
22
+ - "list jobs" → { start: 1, limit: 10 }
23
+ - "show me 25 jobs" → { start: 1, limit: 25 }
24
+ - "list jobs limit 50" → { start: 1, limit: 50 }
25
+ - "next jobs" (after prior page) → { start: previousStart + previousLimit, limit: previousLimit }
26
+
27
+ EXAMPLES
28
+ - "list jobs" → { start: 1, limit: 10 }
29
+ - "list 25 jobs" → { start: 1, limit: 25 }
30
+ - "next jobs" → { start: 11, limit: 10 }
31
+
32
+ NEGATIVE EXAMPLES (do not route here)
33
+ - "find job abc" (use find-job)
34
+ - "run job abc" (use run-job)
35
+ - "list models" (use list-models)
36
+
37
+ PAGINATION
38
+ If returned length === limit, hint: next start = start + limit. Empty result with start > 1 means paged past end.
39
+
40
+ ERRORS
41
+ Surface backend error directly; never fabricate job names.
42
+ `;'''
43
+
44
+ if re.search(pattern, content, re.DOTALL):
45
+ content = re.sub(pattern, replacement, content, flags=re.DOTALL)
46
+ with open('src/toolSet/listJobs.js', 'w') as f:
47
+ f.write(content)
48
+ print("✓ Updated listJobs.js")
49
+ else:
50
+ print("✗ Pattern not found in listJobs.js")
51
+
52
+ # Read and replace listJobdefs
53
+ with open('src/toolSet/listJobdefs.js', 'r') as f:
54
+ content = f.read()
55
+
56
+ pattern = r'let description = `\n ## list-jobdefs.*? `;'
57
+ replacement = r'''let description = `
58
+ list-jobdefs — enumerate SAS Viya job definitions (jobdefs) assets.
59
+
60
+ USE when: list jobdefs, show jobdefs, browse jobdefs, list available jobdefs, next page
61
+ DO NOT USE for: find single jobdef (use find-jobdef), execute jobdef (use run-jobdef), find job (use find-job), sas code (use run-sas-program)
62
+
63
+ PARAMETERS
64
+ - limit: number (default: 10) — number of jobdefs per page
65
+ - start: number (default: 1) — 1-based page offset
66
+ - where: string (default: '') — optional filter expression
67
+
68
+ ROUTING RULES
69
+ - "list jobdefs" → { start: 1, limit: 10 }
70
+ - "show me 25 jobdefs" → { start: 1, limit: 25 }
71
+ - "next jobdefs" → { start: previousStart + previousLimit, limit: previousLimit }
72
+
73
+ EXAMPLES
74
+ - "list jobdefs" → { start: 1, limit: 10 }
75
+ - "list 25 jobdefs" → { start: 1, limit: 25 }
76
+ - "next jobdefs" → { start: 11, limit: 10 }
77
+
78
+ NEGATIVE EXAMPLES (do not route here)
79
+ - "find jobdef abc" (use find-jobdef)
80
+ - "list jobs" (use list-jobs)
81
+ - "run jobdef abc" (use run-jobdef)
82
+
83
+ PAGINATION
84
+ If returned length === limit, hint: next start = start + limit.
85
+
86
+ ERRORS
87
+ Surface backend error directly; never fabricate jobdef names.
88
+ `;'''
89
+
90
+ if re.search(pattern, content, re.DOTALL):
91
+ content = re.sub(pattern, replacement, content, flags=re.DOTALL)
92
+ with open('src/toolSet/listJobdefs.js', 'w') as f:
93
+ f.write(content)
94
+ print("✓ Updated listJobdefs.js")
95
+ else:
96
+ print("✗ Pattern not found in listJobdefs.js")
97
+
98
+ # Read and replace runCasProgram
99
+ with open('src/toolSet/runCasProgram.js', 'r') as f:
100
+ content = f.read()
101
+
102
+ pattern = r'let description = `\n## run-cas-program.*?Response\n`'
103
+ replacement = r'''let description = `
104
+ run-cas-program — execute a CAS program on SAS Viya server.
105
+
106
+ USE when: run cas program, execute cas, submit cas, run cas code, cas action
107
+ DO NOT USE for: macros (use run-macro), sas code (use run-sas-program), jobs (use run-job/find-job), jobdefs (use run-jobdef/find-jobdef), models (use find-model)
108
+
109
+ PARAMETERS
110
+ - src: string (required) — CAS program code to execute verbatim
111
+ - scenario: string | object (optional) — input parameters. Accepts: "x=1, y=2" or {x:1, y:2}
112
+
113
+ ROUTING RULES
114
+ - "run cas program 'action echo / code=\"xyz\"'" → { src: "action echo / code=\"xyz\"" }
115
+ - "submit cas action echo" → { src: "action echo" }
116
+ - "cas program with param1=10" → { src: "...", scenario: {param1: 10} }
117
+
118
+ EXAMPLES
119
+ - "run cas program 'action echo / code=\"hello\"'" → { src: "action echo / code=\"hello\"" }
120
+ - "execute cas action simple.summary" → { src: "action simple.summary" }
121
+
122
+ NEGATIVE EXAMPLES (do not route here)
123
+ - "run sas macro" (use run-macro)
124
+ - "submit sas code" (use run-sas-program)
125
+ - "run job X" (use run-job)
126
+
127
+ NOTES
128
+ Sends src verbatim without validation. For SAS macros use run-macro. For arbitrary SAS code use run-sas-program.
129
+
130
+ RESPONSE
131
+ Log output, listings, tables from CAS execution. Error if execution fails.
132
+ `'''
133
+
134
+ if re.search(pattern, content, re.DOTALL):
135
+ content = re.sub(pattern, replacement, content, flags=re.DOTALL)
136
+ with open('src/toolSet/runCasProgram.js', 'w') as f:
137
+ f.write(content)
138
+ print("✓ Updated runCasProgram.js")
139
+ else:
140
+ print("✗ Pattern not found in runCasProgram.js")
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env python3
2
+ import re
3
+
4
+ # DevaScore optimization
5
+ deva_old = ''' let description = `
6
+ ## deva-score — compute a numeric score based on two input values
7
+
8
+ LLM Invocation Guidance (When to use)
9
+ Use THIS tool when:
10
+ - User wants to calculate the deva score: "Calculate deva score for 5 and 10"
11
+ - User provides two numbers for scoring: "Score these values: 3 and 7"
12
+ - User wants to compute a score in a series: "Calculate scores for [list of numbers]"
13
+
14
+ Do NOT use this tool for:
15
+ - Scoring models (use model-score)
16
+ - Statistical calculations beyond deva scoring
17
+ - Looking up data or metadata
18
+
19
+ Purpose
20
+ Compute a numeric deva score by applying the formula (a + b) * 42 to two input numbers. For scoring more than two numbers, call this tool multiple times using the previous result as the first input (left-to-right fold).
21
+
22
+ Parameters
23
+ - a (number, required): First numeric input value
24
+ - b (number, required): Second numeric input value
25
+
26
+ Response Contract
27
+ Returns a numeric result: (a + b) * 42
28
+ The result is always a number representing the computed deva score.
29
+
30
+ Disambiguation & Clarification
31
+ - If user provides more than two numbers without clear instructions: "Do you want to calculate the deva score by combining these numbers left-to-right?"
32
+ - If user provides non-numeric input: "Please provide numeric values"
33
+
34
+ Examples (→ mapped params)
35
+ - "Calculate deva score for 5 and 10" → { a: 5, b: 10 } returns 630
36
+ - "Score 1 and 2" → { a: 1, b: 2 } returns 126
37
+ - For multiple numbers, chain calls: devaScore(1,2)→126, then devaScore(126,3)→5418
38
+
39
+ Negative Examples (should NOT call deva-score)
40
+ - "Score this customer with the credit model" (use model-score instead)
41
+ - "Calculate the mean of these values" (use run-sas-program or sas-query instead)
42
+
43
+ Related Tools
44
+ - None directly related (this is a specialized scoring tool)
45
+
46
+ Notes
47
+ For sequences of numbers, use a left-to-right fold: call devaScore(first, second), then use that result as the first parameter for devaScore(result, third), and so on.
48
+ `;'''
49
+
50
+ deva_new = ''' let description = `
51
+ deva-score — compute a numeric score based on two input values.
52
+
53
+ USE when: calculate deva score, score these values, compute score for numbers
54
+ DO NOT USE for: model scoring (use model-score), statistical calculations, data lookup
55
+
56
+ PARAMETERS
57
+ - a: number (required) — first input value
58
+ - b: number (required) — second input value
59
+
60
+ FORMULA: (a + b) * 42
61
+
62
+ ROUTING RULES
63
+ - "calculate deva score for 5 and 10" → { a: 5, b: 10 }
64
+ - "score 1 and 2" → { a: 1, b: 2 }
65
+ - "deva score a=3, b=7" → { a: 3, b: 7 }
66
+ - Multiple numbers → chain calls left-to-right: call(first, second), then call(result, third)
67
+
68
+ EXAMPLES
69
+ - "Calculate deva score for 5 and 10" → { a: 5, b: 10 } returns 630
70
+ - "Score 1 and 2" → { a: 1, b: 2 } returns 126
71
+ - "Deva score 20 and 30" → { a: 20, b: 30 } returns 2100
72
+
73
+ NEGATIVE EXAMPLES (do not route here)
74
+ - "Score this customer with credit model" (use model-score)
75
+ - "Calculate the mean of these values" (use run-sas-program or sas-query)
76
+ - "Statistical analysis of numbers" (use sas-query)
77
+
78
+ RESPONSE
79
+ Returns { score: (a + b) * 42 }
80
+ `;'''
81
+
82
+ files = {
83
+ 'src/toolSet/devaScore.js': (deva_old, deva_new),
84
+ }
85
+
86
+ for filepath, (old, new) in files.items():
87
+ try:
88
+ with open(filepath, 'r', encoding='utf-8') as f:
89
+ content = f.read()
90
+
91
+ if old in content:
92
+ content = content.replace(old, new)
93
+ with open(filepath, 'w', encoding='utf-8') as f:
94
+ f.write(content)
95
+ print(f"✓ Updated {filepath}")
96
+ else:
97
+ print(f"✗ Pattern not found in {filepath}")
98
+ except Exception as e:
99
+ print(f"✗ Error updating {filepath}: {e}")
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { parseArgs } from "node:util";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ // Recursive copy function
12
+ function copyRecursiveSync(src, dest) {
13
+ const stat = fs.statSync(src);
14
+
15
+ if (stat.isDirectory()) {
16
+ if (!fs.existsSync(dest)) {
17
+ fs.mkdirSync(dest, { recursive: true });
18
+ }
19
+ const files = fs.readdirSync(src);
20
+ files.forEach(file => {
21
+ copyRecursiveSync(path.join(src, file), path.join(dest, file));
22
+ });
23
+ } else {
24
+ fs.copyFileSync(src, dest);
25
+ }
26
+ }
27
+
28
+ // Get folder name from command line arguments or environment variable
29
+ const args = parseArgs({
30
+ options: {
31
+ 'skills-folder': {
32
+ type: 'string',
33
+ short: 'f',
34
+ description: 'Skills folder name'
35
+ }
36
+ }});
37
+
38
+ let folderName = args.values['skills-folder'] || process.env.SKILLS_FOLDER_NAME;
39
+ if (folderName == null) {
40
+ folderName = '.github';
41
+ }
42
+ folderName = folderName + '/skills';
43
+ // If no folder name provided, show usage
44
+ if (!folderName) {
45
+ console.error('Usage:');
46
+ console.error(' npm run setup-skills -f <folder-name>');
47
+ console.error(' or set SKILLS_FOLDER_NAME environment variable');
48
+ console.error('\nExample:');
49
+ console.error(' npm run setup-skills -f my-skills-folder');
50
+ process.exit(0);
51
+ }
52
+ console.error(`Setting up skills in folder: ${folderName}`);
53
+ // Resolve paths
54
+ const skillsSourcePath = path.join(__dirname, '../skills');
55
+ const targetPath = path.join(process.cwd(), folderName);
56
+
57
+ // Check if skills folder exists
58
+ if (!fs.existsSync(skillsSourcePath)) {
59
+ console.error(`Error: Skills folder not found at ${skillsSourcePath}`);
60
+ process.exit(1);
61
+ }
62
+
63
+ try {
64
+ // Create target directory if it doesn't exist
65
+ if (!fs.existsSync(targetPath)) {
66
+ fs.mkdirSync(targetPath, { recursive: true });
67
+ console.error(`✓ Created folder: ${targetPath}`);
68
+ } else {
69
+ console.error(`✓ Folder already exists: ${targetPath}`);
70
+ }
71
+
72
+ // Copy skills folder contents to target
73
+ copyRecursiveSync(skillsSourcePath, targetPath);
74
+ console.error(`✓ Successfully copied skills to: ${targetPath}`);
75
+ } catch (error) {
76
+ console.error('Error during setup:', error.message);
77
+ process.exit(1);
78
+ }
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env python3
2
+ import re
3
+ import os
4
+
5
+ os.chdir('c:/dev/github/sas-score-mcp-serverjs')
6
+
7
+ # Update runJob.js
8
+ file_path = 'src/toolSet/runJob.js'
9
+ with open(file_path, 'r', encoding='utf-8') as f:
10
+ content = f.read()
11
+
12
+ new_desc = """run-job — execute a deployed SAS Viya job.
13
+
14
+ USE when: run job, execute job, run with parameters
15
+ DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro), list/find jobs
16
+
17
+ PARAMETERS
18
+ - name: string — job name (required)
19
+ - scenario: string | object — input parameters. Accepts: "x=1, y=2" or {x:1, y:2}
20
+
21
+ ROUTING RULES
22
+ - "run job xyz" → { name: "xyz" }
23
+ - "run job xyz with param1=10, param2=val2" → { name: "xyz", scenario: {param1:10, param2:"val2"} }
24
+
25
+ EXAMPLES
26
+ - "run job xyz" → { name: "xyz" }
27
+ - "run job monthly_etl with month=10, year=2025" → { name: "monthly_etl", scenario: {month:10, year:2025} }
28
+
29
+ NEGATIVE EXAMPLES (do not route here)
30
+ - "run SAS code" (use run-sas-program)
31
+ - "run macro X" (use run-macro)
32
+ - "list jobs" (use list-jobs)
33
+ - "find job X" (use find-job)
34
+
35
+ ERRORS
36
+ Returns log output, listings, tables from job. Error if job not found."""
37
+
38
+ # Use a more flexible pattern
39
+ pattern = r'let description = `\n## run-job.*?`;\n'
40
+ replacement = f'let description = `\n{new_desc}\n`;\n'
41
+ updated = re.sub(pattern, replacement, content, flags=re.DOTALL)
42
+
43
+ with open(file_path, 'w', encoding='utf-8') as f:
44
+ f.write(updated)
45
+
46
+ print(f"✓ Updated {file_path}")
@@ -0,0 +1,3 @@
1
+ kubectl cp $(kubectl get pod | grep "sas-consul-server-0" | awk -F" " '{print $1}'):security/ca.crt ./ca.crt
2
+ kubectl cp $(kubectl get pod | grep "sas-consul-server-0" | awk -F" " '{print $1}'):security/tls.crt ./tls.crt
3
+ kubectl cp $(kubectl get pod | grep "sas-consul-server-0" | awk -F" " '{print $1}'):security/tls.key ./tls.key
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: sas-find-library-smart
3
+ description: >
4
+ Find a SAS Viya library (libref or caslib) with intelligent server detection. Automatically checks
5
+ CAS first, then SAS if not found. Use this skill when the user needs to verify a library exists,
6
+ before accessing tables within it. Trigger phrases include: "find library", "does library exist",
7
+ "check if library", "locate library", "is there a library named", "verify library", or any request
8
+ to confirm a library's availability across servers.
9
+ ---
10
+
11
+ # Smart Library Lookup (Find Library)
12
+
13
+ Intelligently locates a SAS Viya library by checking CAS first, then SAS if the library is not found
14
+ in CAS. Provides the user with clear information about library availability and location.
15
+
16
+ **If the user specifies the server explicitly** (e.g., "find library Public in cas"):
17
+ - Use the specified server: `server: "cas"` or `server: "sas"`
18
+ - Proceed directly to finding the library
19
+
20
+ **If the server is NOT specified:**
21
+ 1. **First attempt**: Check CAS (`server: "cas"`)
22
+ 2. **If not found in CAS**: Check SAS with uppercase library name (`server: "sas"`)
23
+ 3. **If not found in either**:
24
+ - Inform user: *"The library '&lt;lib&gt;' was not found in CAS or SAS servers. Please verify the library name."*
25
+ - Suggest: *"Would you like to list available libraries?"* (suggest `sas-score-list-libraries`)
26
+ 4. **If found**:
27
+ - Inform user which server contains the library: *"Found library '&lt;lib&gt;' in CAS"* or *"Found library '&lt;lib&gt;' in SAS"*
28
+ - Offer next steps: *"Would you like to list tables in this library?"* (suggest `sas-score-list-tables`)
29
+
30
+ ---
31
+
32
+ ## Using sas-score-find-library
33
+
34
+ **When:**
35
+ - User wants to verify a library exists
36
+ - User needs to determine which server contains a library
37
+ - User wants to check library availability before accessing it
38
+ - User wants to explore available libraries (before querying)
39
+
40
+ **How:**
41
+ ```
42
+ sas-score-find-library({
43
+ name: "libraryname", // required
44
+ server: "cas" or "sas" // optional; determined by server check if not specified
45
+ })
46
+ ```
47
+
48
+ **Rules:**
49
+ - Always determine the correct server first (cas → sas → neither)
50
+ - **For SAS server: always uppercase the library name** (e.g., "public" → "PUBLIC")
51
+ - If library name is missing, ask: *"Which library name would you like to find?"*
52
+ - Return the server where the library was found
53
+ - If not found in either server, clearly inform the user and offer to list available libraries
54
+ - Do not proceed with table access until library existence is confirmed
55
+
56
+ ---
57
+
58
+ ## Smart server detection logic
59
+
60
+ ```
61
+ IF server specified by user
62
+ → IF server is "sas"
63
+ → uppercase lib
64
+ → use that server, call sas-score-find-library
65
+ ELSE
66
+ → TRY sas-score-find-library(lib, server="cas")
67
+ IF library found
68
+ → success, inform user: library found in CAS
69
+ ELSE
70
+ → uppercase lib
71
+ → TRY sas-score-find-library(lib.toUpperCase(), server="sas")
72
+ IF library found
73
+ → success, inform user: library found in SAS
74
+ ELSE
75
+ → inform user library not found in either server
76
+ → offer to list available libraries
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Common patterns
82
+
83
+ **Pattern 1 — Find library, server unspecified**
84
+ > "Find library Public"
85
+
86
+ 1. Try CAS: `sas-score-find-library({ name: "Public", server: "cas" })`
87
+ 2. If not found, try SAS with uppercase: `sas-score-find-library({ name: "PUBLIC", server: "sas" })`
88
+ 3. If found in CAS → *"Found library 'Public' in CAS. Would you like to list tables in it?"*
89
+ 4. If found in SAS → *"Found library 'PUBLIC' in SAS. Would you like to list tables in it?"*
90
+ 5. If not found → *"The library 'Public' was not found in CAS or SAS. Would you like to list available libraries?"*
91
+
92
+ **Pattern 2 — Find library with explicit server (CAS)**
93
+ > "Find library MyData in cas"
94
+
95
+ 1. Skip server detection
96
+ 2. Call: `sas-score-find-library({ name: "MyData", server: "cas" })`
97
+ 3. Result → *"Found library 'MyData' in CAS"* or *"Library 'MyData' not found in CAS"*
98
+
99
+ **Pattern 3 — Find library with explicit server (SAS)**
100
+ > "Does library SASHELP exist in sas"
101
+
102
+ 1. Skip server detection
103
+ 2. Uppercase lib: `sas-score-find-library({ name: "SASHELP", server: "sas" })`
104
+ 3. Result → *"Found library 'SASHELP' in SAS"* or *"Library 'SASHELP' not found in SAS"*
105
+
106
+ **Pattern 4 — Library not found, offer next steps**
107
+ > "Check if library staging exists"
108
+
109
+ 1. Try CAS: `sas-score-find-library({ name: "staging", server: "cas" })` → not found
110
+ 2. Try SAS: `sas-score-find-library({ name: "STAGING", server: "sas" })` → not found
111
+ 3. Respond:
112
+ - *"The library 'staging' was not found in CAS or SAS."*
113
+ - *"Would you like to:"*
114
+ - *"List all available libraries? (use `sas-score-list-libraries`))"*
115
+ - *"Check a different library name?"*
116
+
117
+ **Pattern 5 — Library found, follow-up action**
118
+ > "Verify library samples exists"
119
+
120
+ 1. Try CAS: `sas-score-find-library({ name: "samples", server: "cas" })` → found
121
+ 2. Respond:
122
+ - *"Found library 'samples' in CAS."*
123
+ - *"Would you like to list tables in this library? (use `sas-score-list-tables`))"*
124
+
125
+ ---
126
+
127
+ ## Output presentation
128
+
129
+ **When library is found:**
130
+ ```
131
+ ✓ Found library '<lib>' in <SERVER>
132
+
133
+ Would you like to:
134
+ • List tables in this library (use sas-list-tables-smart skill)
135
+ • Read data from a specific table (use sas-read-strategy skill)
136
+ ```
137
+
138
+ **When library is not found:**
139
+ ```
140
+ ✗ Library '<lib>' not found in either CAS or SAS
141
+
142
+ Suggestions:
143
+ • Check the spelling of the library name
144
+ • List available libraries (use list-libraries tool)
145
+ • Try a different library name
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Integration with other skills
151
+
152
+ - **After finding library → List tables**: Use `sas-list-tables-smart` skill to browse available tables
153
+ - **After finding library → Read data**: Use `sas-read-strategy` skill to retrieve data from tables
154
+ - **Library not found → Explore**: Use `sas-score-list-libraries` tool to see all available libraries
@@ -0,0 +1,127 @@
1
+ ---
2
+ name: sas-list-tables-smart
3
+ description: >
4
+ List all tables in a SAS Viya library with intelligent server detection. When the server is not
5
+ specified, automatically checks CAS first, then SAS if not found. Informs the user if the library
6
+ does not exist in either server. Use this skill when the user wants to browse or explore available
7
+ tables. Trigger phrases include: "list tables in", "show tables in", "what tables are in",
8
+ "browse tables in", "tables in library", "enumerate tables", or any request to explore data sources.
9
+ ---
10
+
11
+ # Smart Data Access in SAS Library (List, Read, Query)
12
+
13
+ Intelligently enumerates tables in a SAS Viya library, automatically determining the correct server
14
+ when not explicitly specified.
15
+
16
+ > **Pre-flight check**: Before listing tables, verify the library exists using the `sas-find-library-smart` skill.
17
+ > This ensures consistent server detection across all data operations.
18
+
19
+ **If the user specifies the server explicitly** (e.g., "list tables in Public in cas"):
20
+ - Use the specified server: `server: "cas"` or `server: "sas"`
21
+ - Proceed directly to listing tables
22
+
23
+ **If the server is NOT specified:**
24
+ 1. **First attempt**: Check CAS (`server: "cas"`)
25
+ 2. **If no tables found in CAS**: Check SAS (`server: "sas"`)
26
+ 3. **If no tables found in either**:
27
+ - Inform user: *"The library '&lt;lib&gt;' was not found in CAS or SAS. Please verify the library name is correct."*
28
+ - Ask: *"Would you like to list available libraries?"* (suggest `sas-score-list-libraries`)
29
+
30
+ ---
31
+
32
+ ## Using sas-score-list-tables
33
+
34
+ **When:**
35
+ - User wants to browse all tables in a library
36
+ - User wants to see what data is available
37
+ - User wants to explore library contents before querying
38
+
39
+ **How:**
40
+ ```
41
+ sas-score-list-tables({
42
+ lib: "libraryname", // required
43
+ server: "cas" or "sas", // required; determined by server check
44
+ limit: 10, // optional; default 10, adjust for pagination
45
+ start: 1 // optional; default 1, use for pagination
46
+ })
47
+ ```
48
+
49
+ **Rules:**
50
+ - Always determine the correct server first (cas → sas → neither)
51
+ - **For SAS server: always uppercase the library name** (e.g., "maps" → "MAPS")
52
+ - If library name is missing, ask: *"Which library should I list tables from?"*
53
+ - Default page size is 10; adjust based on user request ("show me all", "25 tables", etc.)
54
+ - If returned table count equals the limit, suggest pagination: *"There may be more tables. Use `start: {next_offset}` to see more."*
55
+ - If no tables are found despite library existing, report: *"No tables found in {lib} on {server} server."*
56
+ - Return table names only; do not fetch table metadata unless explicitly requested
57
+
58
+ ---
59
+
60
+ ## Smart server detection logic
61
+
62
+ ```
63
+ IF server specified by user
64
+ → IF server is "sas"
65
+ → uppercase lib
66
+ → use that server
67
+ ELSE
68
+ → TRY sas-score-list-tables(lib, server="cas")
69
+ IF tables found
70
+ → success, return tables
71
+ ELSE
72
+ → uppercase lib
73
+ → TRY sas-score-list-tables(lib.toUpperCase(), server="sas")
74
+ IF tables found
75
+ → success, return tables
76
+ ELSE
77
+ → inform user library not found in either server
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Common patterns
83
+
84
+ **Pattern 1 — List tables, server unspecified**
85
+ > "List tables in Public"
86
+
87
+ 1. Try CAS: `sas-score-list-tables({ lib: "Public", server: "cas" })`
88
+ 2. If empty, try SAS with uppercase: `sas-score-list-tables({ lib: "PUBLIC", server: "sas" })`
89
+ 3. If still empty → inform user
90
+
91
+ **Pattern 2 — List tables with explicit server (SAS)**
92
+ > "List tables in sashelp in sas"
93
+
94
+ 1. Skip server detection
95
+ 2. Call with uppercase lib: `sas-score-list-tables({ lib: "SASHELP", server: "sas" })`
96
+
97
+ **Pattern 3 — List tables with explicit server (CAS)**
98
+ > "List tables in Public in cas"
99
+
100
+ 1. No uppercase needed for CAS
101
+ 2. Call: `sas-score-list-tables({ lib: "Public", server: "cas" })`
102
+
103
+ **Pattern 4 — Pagination**
104
+ > "Show me 25 tables in Samples, then the next batch"
105
+
106
+ 1. First call: `sas-score-list-tables({ lib: "Samples", limit: 25, start: 1 })`
107
+ 2. Next call: `sas-score-list-tables({ lib: "Samples", limit: 25, start: 26 })`
108
+
109
+ **Pattern 5 — Library not found**
110
+ > "List tables in foo"
111
+
112
+ 1. Try CAS: empty
113
+ 2. Try SAS with uppercase: empty
114
+ 3. Response: *"The library 'foo' was not found in CAS or SAS. Please verify the library name."*
115
+
116
+ ---
117
+
118
+ ## Error handling
119
+
120
+ | Scenario | Action |
121
+ |---|---|
122
+ | Library not found in either server | Inform user and ask to verify library name |
123
+ | Empty result on first server | Automatically check second server |
124
+ | User specifies invalid server | Return error; ask user to clarify: `"cas"` or `"sas"` |
125
+ | Missing library name | Ask: *"Which library should I list tables from?"* |
126
+ | Library verification needed | Use `sas-find-library-smart` skill to verify library exists first |
127
+