@sassoftware/sas-score-mcp-serverjs 0.4.1-7 → 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 (46) 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/skills}/sas-read-strategy/SKILL.md +43 -30
  7. package/.skills/skills/sas-request-classifier/SKILL.md +69 -0
  8. package/{skills → .skills/skills}/sas-score-workflow/SKILL.md +49 -35
  9. package/cli.js +222 -140
  10. package/package.json +5 -4
  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 -220
  24. package/src/createMcpServer.js +10 -5
  25. package/src/expressMcpServer.js +54 -186
  26. package/src/oauthHandlers/authorize.js +46 -0
  27. package/src/oauthHandlers/baseUrl.js +8 -0
  28. package/src/oauthHandlers/callback.js +96 -0
  29. package/src/oauthHandlers/getMetadata.js +27 -0
  30. package/src/oauthHandlers/index.js +7 -0
  31. package/src/oauthHandlers/token.js +37 -0
  32. package/src/processHeaders.js +88 -0
  33. package/src/setupSkills.js +37 -0
  34. package/src/toolHelpers/_listLibrary.js +0 -1
  35. package/src/toolHelpers/getLogonPayload.js +5 -1
  36. package/src/toolHelpers/refreshToken.js +3 -2
  37. package/src/toolHelpers/refreshTokenOauth.js +3 -3
  38. package/src/toolSet/.claude/settings.local.json +2 -1
  39. package/src/toolSet/findModel.js +1 -1
  40. package/src/toolSet/findTable.js +3 -3
  41. package/src/toolSet/modelScore.js +2 -2
  42. package/src/toolSet/runJob.js +81 -81
  43. package/src/toolSet/runJobdef.js +82 -82
  44. package/skills/mcp-tool-description-optimizer/SKILL.md +0 -129
  45. package/skills/mcp-tool-description-optimizer/references/examples.md +0 -123
  46. package/skills/sas-read-and-score/SKILL.md +0 -91
@@ -53,7 +53,6 @@ async function igetLogonPayload(_appContext) {
53
53
  token: _appContext.bearerToken,
54
54
  tokenType: "Bearer",
55
55
  };
56
- console.error("[Note] Bearer token in logonPayload ", _appContext.bearerToken);
57
56
  return logonPayload;
58
57
  }
59
58
 
@@ -122,4 +121,9 @@ async function igetLogonPayload(_appContext) {
122
121
  return null;
123
122
  }
124
123
  }
124
+
125
+ function isExpired(expiresAt, skewSeconds = 60) {
126
+ return Date.now() >= (expiresAt - skewSeconds * 1000);
127
+ }
128
+
125
129
  export default getLogonPayload;
@@ -9,10 +9,11 @@ async function refreshToken(_appContext, params) {
9
9
  let url = `${host}/SASLogon/oauth/token`;
10
10
 
11
11
  let aconnect = {
12
- ca: _appContext.contexts.viyaCert.ca, // trust this CA
13
12
  rejectUnauthorized: false // or false, if you really want to bypass checks
14
13
  }
15
-
14
+ if (_appContext.contexts.viyaCert != null && _appContext.contexts.viyaCert.ca != null) {
15
+ aconnect.ca = _appContext.contexts.viyaCert.ca;
16
+ }
16
17
  const agent = new Agent(aconnect);
17
18
 
18
19
  console.error('[Info] Refreshing token...', token);
@@ -6,7 +6,7 @@
6
6
  //import getOpts from './getOpts.js';
7
7
  async function refreshTokenOauth(_appContext, oauthInfo ){
8
8
 
9
- const url = `${process.env.VIYA_SERVER}/SASLogon/oauth/token`;
9
+ const url = `${_appContext.VIYA_SERVER}/SASLogon/oauth/token`;
10
10
  let opts = _appContext.contexts.appCert;
11
11
 
12
12
  const agent = new Agent({
@@ -23,10 +23,10 @@
23
23
  try {
24
24
  const response = await fetch(url, {
25
25
  method: 'POST',
26
+ dispatcher: agent,
26
27
  headers: {
27
28
  'Accept': 'application/json',
28
- 'Content-Type': 'application/x-www-form-urlencoded',
29
- dispatcher: agent
29
+ 'Content-Type': 'application/x-www-form-urlencoded'
30
30
  },
31
31
  body: body.toString()
32
32
  });
@@ -6,7 +6,8 @@
6
6
  "mcp__sasmcp__sas-score-model-info",
7
7
  "Bash(cp \"C:/dev/github/7-skill-integration/.claude/skills/sas-read-and-score/SKILL.md\" \"C:/dev/github/7-skill-integration/skills/sas-read-and-score/SKILL.md\")",
8
8
  "Bash(cp \"C:/dev/github/7-skill-integration/.claude/skills/sas-read-strategy/SKILL.md\" \"C:/dev/github/7-skill-integration/skills/sas-read-strategy/SKILL.md\")",
9
- "Bash(cp \"C:/dev/github/7-skill-integration/.claude/skills/sas-score-workflow/SKILL.md\" \"C:/dev/github/7-skill-integration/skills/sas-score-workflow/SKILL.md\")"
9
+ "Bash(cp \"C:/dev/github/7-skill-integration/.claude/skills/sas-score-workflow/SKILL.md\" \"C:/dev/github/7-skill-integration/skills/sas-score-workflow/SKILL.md\")",
10
+ "WebSearch"
10
11
  ]
11
12
  }
12
13
  }
@@ -9,7 +9,7 @@ import _listModels from '../toolHelpers/_listModels.js';
9
9
 
10
10
  function findModel(_appContext) {
11
11
  let description = `
12
- find-model — locate a specific model deployed to MAS (Model Aggregation Service).
12
+ find-model — locate a specific MAS model deployed to MAS server
13
13
 
14
14
  USE when: find model, does model exist, is model deployed, lookup model, verify model exists
15
15
  DO NOT USE for: list models (use list-models), model info/variables (use model-info), score model (use model-score), find table/job/lib (use respective tools), scr models (use scr-info/scr-score)
@@ -17,7 +17,7 @@ DO NOT USE for: list tables (use list-tables), table schema/columns (use table-i
17
17
  PARAMETERS
18
18
  - lib: string (required) — library name (e.g., 'Public', 'sashelp')
19
19
  - name: string (required) — table name to locate
20
- - server: 'cas' | 'sas' (default: 'cas') — target environment
20
+ - server: 'cas' | 'sas' . If not specified set it to 'cas' — target environment
21
21
 
22
22
  ROUTING RULES
23
23
  - "find table <name> in <lib>" → { lib: "<lib>", name: "<name>", server: "cas" }
@@ -47,9 +47,9 @@ Returns { tables: [] } if not found; { tables: [name, ...] } if found. Never hal
47
47
  name: 'find-table',
48
48
  description: description,
49
49
  inputSchema: z.object({
50
- name: z.string(),
51
50
  lib: z.string(),
52
- server: z.string().optional() // default server is 'cas'
51
+ name: z.string(),
52
+ server: z.string()
53
53
  }),
54
54
 
55
55
  handler: async (params) => {
@@ -10,7 +10,7 @@ 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
15
  USE when: score with model, predict using model, batch scoring, model predictions
16
16
  DO NOT USE for: find model, model metadata, list models, run programs/jobs, query tables
@@ -40,7 +40,7 @@ Returns predictions, probabilities, scores merged with input data. Returns error
40
40
 
41
41
 
42
42
  let spec = {
43
- name: 'model-score',
43
+ name: 'mas-score',
44
44
  description: description,
45
45
  inputSchema:z.object({
46
46
  model: z.string(),
@@ -1,81 +1,81 @@
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
8
-
9
- function runJob(_appContext) {
10
-
11
- let description = `
12
- 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
-
39
- let spec = {
40
- name: 'run-job',
41
- description: description,
42
- inputSchema: z.object({
43
- name: z.string(),
44
- scenario: z.string().optional()
45
- }),
46
- handler: async (params) => {
47
- let scenario = params.scenario;
48
- let scenarioObj = {};
49
- let count = 0;
50
- //
51
- if (scenario == null) {
52
- scenarioObj = {};
53
- } else if (typeof scenario === 'object') {
54
- scenarioObj = scenario;
55
- } else if (Array.isArray(scenario)) {
56
- scenarioObj = scenario[0];
57
- } else if (typeof scenario === 'string') {
58
- if (scenario.trim() === '') {
59
- scenarioObj = {};
60
- } else {
61
- // console.error('Incoming scenario', scenario);
62
- scenarioObj = scenario.split(',').reduce((acc, pair) => {
63
- let [key, value] = pair.split('=');
64
- acc[key.trim()] = value;
65
- count++;
66
- return acc;
67
- }, {});
68
- }
69
- }
70
- params.type = 'job';
71
- params.scenario = scenarioObj;
72
- // Provide runtime context for auth and server settings
73
- let r = await _jobSubmit(params);
74
- return r;
75
- }
76
- };
77
- return spec;
78
- }
79
-
80
- export default runJob;
81
-
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
8
+
9
+ function runJob(_appContext) {
10
+
11
+ let description = `
12
+ run-job — score with a deployed SAS Viya job.
13
+
14
+ USE when: score with job, run job, execute job
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
+
39
+ let spec = {
40
+ name: 'run-job',
41
+ description: description,
42
+ inputSchema: z.object({
43
+ name: z.string(),
44
+ scenario: z.string().optional()
45
+ }),
46
+ handler: async (params) => {
47
+ let scenario = params.scenario;
48
+ let scenarioObj = {};
49
+ let count = 0;
50
+ //
51
+ if (scenario == null) {
52
+ scenarioObj = {};
53
+ } else if (typeof scenario === 'object') {
54
+ scenarioObj = scenario;
55
+ } else if (Array.isArray(scenario)) {
56
+ scenarioObj = scenario[0];
57
+ } else if (typeof scenario === 'string') {
58
+ if (scenario.trim() === '') {
59
+ scenarioObj = {};
60
+ } else {
61
+ // console.error('Incoming scenario', scenario);
62
+ scenarioObj = scenario.split(',').reduce((acc, pair) => {
63
+ let [key, value] = pair.split('=');
64
+ acc[key.trim()] = value;
65
+ count++;
66
+ return acc;
67
+ }, {});
68
+ }
69
+ }
70
+ params.type = 'job';
71
+ params.scenario = scenarioObj;
72
+ // Provide runtime context for auth and server settings
73
+ let r = await _jobSubmit(params);
74
+ return r;
75
+ }
76
+ };
77
+ return spec;
78
+ }
79
+
80
+ export default runJob;
81
+
@@ -1,82 +1,82 @@
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
8
-
9
- function runJobdef(_appContext) {
10
- // JSON object for LLM/tooling
11
-
12
- let description = `
13
- run-jobdef — execute a SAS Viya job definition.
14
-
15
- USE when: run jobdef, execute jobdef, run with parameters
16
- DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro), list/find jobdefs
17
-
18
- PARAMETERS
19
- - name: string — jobdef name (required)
20
- - scenario: string | object — input parameters. Accepts: "x=1, y=2" or {x:1, y:2}
21
-
22
- ROUTING RULES
23
- - "run jobdef xyz" → { name: "xyz" }
24
- - "run jobdef xyz with param1=10, param2=val2" → { name: "xyz", scenario: {param1:10, param2:"val2"} }
25
-
26
- EXAMPLES
27
- - "run jobdef xyz" → { name: "xyz" }
28
- - "run jobdef monthly_report with month=10, year=2025" → { name: "monthly_report", scenario: {month:10, year:2025} }
29
-
30
- NEGATIVE EXAMPLES (do not route here)
31
- - "run SAS code" (use run-sas-program)
32
- - "run macro X" (use run-macro)
33
- - "list jobdefs" (use list-jobdefs)
34
- - "find jobdef X" (use find-jobdef)
35
-
36
- ERRORS
37
- Returns log output, listings, tables from jobdef. Error if jobdef not found.
38
- `;
39
-
40
- let spec = {
41
- name: 'run-jobdef',
42
- description: description,
43
- inputSchema: z.object({
44
- name: z.string(),
45
- scenario: z.string().optional()
46
- }),
47
- handler: async (params) => {
48
- let scenario = params.scenario;
49
- let scenarioObj = {};
50
- let count = 0;
51
- //
52
- if (scenario == null) {
53
- scenarioObj = {};
54
- } else if (typeof scenario === 'object') {
55
- scenarioObj = scenario;
56
- } else if (Array.isArray(scenario)) {
57
- scenarioObj = scenario[0];
58
- } else if (typeof scenario === 'string') {
59
- if (scenario.trim() === '') {
60
- scenarioObj = {};
61
- } else {
62
- // console.error('Incoming scenario', scenario);
63
- scenarioObj = scenario.split(',').reduce((acc, pair) => {
64
- let [key, value] = pair.split('=');
65
- acc[key.trim()] = value;
66
- count++;
67
- return acc;
68
- }, {});
69
- }
70
- }
71
- params.type = 'def';
72
- params.scenario = scenarioObj;
73
- // Provide runtime context for auth and server settings
74
- let r = await _jobSubmit(params);
75
- return r;
76
- }
77
- };
78
- return spec;
79
- }
80
-
81
- export default runJobdef;
82
-
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 _jobSubmit from '../toolHelpers/_jobSubmit.js';
8
+
9
+ function runJobdef(_appContext) {
10
+ // JSON object for LLM/tooling
11
+
12
+ let description = `
13
+ run-jobdef — score with a deployed SAS Viya job definition.
14
+
15
+ USE when: score with jobdef, run jobdef, execute jobdef
16
+ DO NOT USE for: arbitrary SAS code (use run-sas-program), macros (use run-macro), list/find jobdefs
17
+
18
+ PARAMETERS
19
+ - name: string — jobdef name (required)
20
+ - scenario: string | object — input parameters. Accepts: "x=1, y=2" or {x:1, y:2}
21
+
22
+ ROUTING RULES
23
+ - "run jobdef xyz" → { name: "xyz" }
24
+ - "run jobdef xyz with param1=10, param2=val2" → { name: "xyz", scenario: {param1:10, param2:"val2"} }
25
+
26
+ EXAMPLES
27
+ - "run jobdef xyz" → { name: "xyz" }
28
+ - "run jobdef monthly_report with month=10, year=2025" → { name: "monthly_report", scenario: {month:10, year:2025} }
29
+
30
+ NEGATIVE EXAMPLES (do not route here)
31
+ - "run SAS code" (use run-sas-program)
32
+ - "run macro X" (use run-macro)
33
+ - "list jobdefs" (use list-jobdefs)
34
+ - "find jobdef X" (use find-jobdef)
35
+
36
+ ERRORS
37
+ Returns log output, listings, tables from jobdef. Error if jobdef not found.
38
+ `;
39
+
40
+ let spec = {
41
+ name: 'run-jobdef',
42
+ description: description,
43
+ inputSchema: z.object({
44
+ name: z.string(),
45
+ scenario: z.string().optional()
46
+ }),
47
+ handler: async (params) => {
48
+ let scenario = params.scenario;
49
+ let scenarioObj = {};
50
+ let count = 0;
51
+ //
52
+ if (scenario == null) {
53
+ scenarioObj = {};
54
+ } else if (typeof scenario === 'object') {
55
+ scenarioObj = scenario;
56
+ } else if (Array.isArray(scenario)) {
57
+ scenarioObj = scenario[0];
58
+ } else if (typeof scenario === 'string') {
59
+ if (scenario.trim() === '') {
60
+ scenarioObj = {};
61
+ } else {
62
+ // console.error('Incoming scenario', scenario);
63
+ scenarioObj = scenario.split(',').reduce((acc, pair) => {
64
+ let [key, value] = pair.split('=');
65
+ acc[key.trim()] = value;
66
+ count++;
67
+ return acc;
68
+ }, {});
69
+ }
70
+ }
71
+ params.type = 'def';
72
+ params.scenario = scenarioObj;
73
+ // Provide runtime context for auth and server settings
74
+ let r = await _jobSubmit(params);
75
+ return r;
76
+ }
77
+ };
78
+ return spec;
79
+ }
80
+
81
+ export default runJobdef;
82
+
@@ -1,129 +0,0 @@
1
- ---
2
- name: mcp-tool-description-optimizer
3
- description: >
4
- Optimize MCP (Model Context Protocol) tool descriptions for token efficiency and LLM routing accuracy.
5
- Use this skill whenever a user shares a raw, verbose, or poorly structured MCP tool description and wants
6
- it improved, rewritten, or reviewed. Trigger on phrases like: "optimize this tool description",
7
- "rewrite my MCP tool description", "make this tool description more efficient", "clean up my tool spec",
8
- "improve how Claude picks my tool", or when a user pastes a JavaScript/TypeScript tool description string
9
- and asks for help with it. Also trigger when the user mentions token efficiency, tool routing,
10
- or LLM disambiguation in the context of MCP servers.
11
- ---
12
-
13
- # MCP Tool Description Optimizer
14
-
15
- Rewrites verbose or poorly structured MCP tool descriptions into compact, signal-rich versions that
16
- improve LLM tool selection accuracy while reducing token usage.
17
-
18
- ## Why this matters
19
-
20
- Claude and other LLMs select MCP tools based entirely on the `description` field. Descriptions that are
21
- too long, redundant, or badly structured waste context tokens and reduce routing precision.
22
- A well-optimized description:
23
- - States what the tool does and when to use it upfront
24
- - Eliminates redundancy (same info repeated across sections)
25
- - Uses a compact, scannable format (labeled blocks, not nested markdown)
26
- - Includes clear negative examples to prevent mis-routing
27
- - Keeps parameters terse — name, type, default, one-line purpose
28
-
29
- ---
30
-
31
- ## Optimization Process
32
-
33
- ### Step 1 — Analyze the input description
34
-
35
- Before rewriting, identify these problems in the original:
36
-
37
- | Problem | Example |
38
- |---|---|
39
- | **Redundancy** | Trigger phrases listed in 3+ places |
40
- | **Filler sections** | "Rationale", "Behavior Summary", "Response Contract" with no routing signal |
41
- | **Orphaned syntax** | Arrows (`→`) or bullets with no target |
42
- | **Overlong examples** | Long prose examples when one-liners suffice |
43
- | **Heavy markdown** | `##` headers for every minor point |
44
- | **Duplicated parameter docs** | Same param described in both a table and prose |
45
-
46
- Call out 2–4 of the most impactful issues before writing the new version.
47
-
48
- ### Step 2 — Rewrite using the standard template
49
-
50
- Use this exact block structure for the output. Omit blocks that don't apply.
51
-
52
- ```
53
- <tool-name> — <one-line purpose>.
54
-
55
- USE when: <comma-separated user intents or trigger phrases>
56
- DO NOT USE for: <comma-separated anti-patterns with → redirect where applicable>
57
-
58
- PARAMETERS
59
- - <name>: <type> (default: <val>) — <one-line purpose>
60
- ...
61
-
62
- ROUTING RULES
63
- - "<trigger phrase>" → { param: value }
64
- - "<trigger phrase>" → { param: value }
65
- - <ambiguous case> → <ask for clarification | default behavior>
66
-
67
- EXAMPLES
68
- - "<user utterance>" → { param: value, ... }
69
- - "<user utterance>" → { param: value, ... }
70
-
71
- NEGATIVE EXAMPLES (do not route here)
72
- - "<user utterance>" → <correct tool>
73
-
74
- PAGINATION (include only if tool is paginated)
75
- If returned count === limit → hint: next start = start + limit.
76
- If start > 1 and result empty → note paging may exceed available items.
77
-
78
- ERRORS
79
- <One or two lines: return structure, hallucination policy>
80
- ```
81
-
82
- ### Step 3 — Apply these rules consistently
83
-
84
- **USE/DO NOT USE block**
85
- - Write as comma-separated inline list, not a bullet list
86
- - DO NOT USE entries should name the redirect tool in parentheses where known
87
-
88
- **ROUTING RULES block**
89
- - One rule per line; quote the trigger phrase; use `→ { }` for param mapping
90
- - Consolidate synonyms on one line: `"cas libs / cas libraries / in cas" → { server: 'cas' }`
91
- - List the default/fallback rule last
92
-
93
- **PARAMETERS block**
94
- - One line per param: `- name: type (default: val) — purpose`
95
- - Skip obvious params (e.g. don't explain what `limit` means if it's standard pagination)
96
-
97
- **EXAMPLES block**
98
- - Each example fits on one line
99
- - For "next page" examples, include the prior call's state inline: `"next" (prev: start:1, limit:10) → { start: 11, limit: 10 }`
100
-
101
- **NEGATIVE EXAMPLES block**
102
- - Only include when mis-routing is a real risk
103
- - Format: `"<utterance>" → <correct-tool-name>`
104
-
105
- **Tone**
106
- - Imperative, terse. No filler words ("Please note that...", "It is important to...")
107
- - Never include a "Rationale" or "Behavior Summary" section — if behavior matters, encode it as a rule
108
-
109
- ### Step 4 — Validate before returning
110
-
111
- Check the rewritten description against this list:
112
-
113
- - [ ] No trigger phrase appears in more than one block
114
- - [ ] No orphaned `→` arrows or dangling bullets
115
- - [ ] Parameter defaults are stated explicitly
116
- - [ ] Negative examples cover the tool's most common mis-routing risks
117
- - [ ] Total length is ≤ 50% of the original (target: 30–40% reduction)
118
-
119
- ---
120
-
121
- ## Output format
122
-
123
- Always return:
124
-
125
- 1. **Analysis** — 2–4 bullet points naming the key issues found in the original
126
- 2. **Rewritten description** — inside a JavaScript code block (matching the user's original code style)
127
- 3. **Change summary** — a short table or bullet list of what changed and why
128
-
129
- See `references/examples.md` for before/after examples of real tool descriptions.