@skillsmith/mcp-server 0.3.3 → 0.3.5

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 (261) hide show
  1. package/README.md +5 -3
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/src/__tests__/LocalIndexer.test.d.ts +5 -0
  4. package/dist/src/__tests__/LocalIndexer.test.d.ts.map +1 -0
  5. package/dist/src/__tests__/LocalIndexer.test.js +396 -0
  6. package/dist/src/__tests__/LocalIndexer.test.js.map +1 -0
  7. package/dist/src/__tests__/context.test.d.ts +10 -0
  8. package/dist/src/__tests__/context.test.d.ts.map +1 -0
  9. package/dist/src/__tests__/context.test.js +345 -0
  10. package/dist/src/__tests__/context.test.js.map +1 -0
  11. package/dist/src/__tests__/get-skill.test.d.ts +1 -0
  12. package/dist/src/__tests__/get-skill.test.d.ts.map +1 -1
  13. package/dist/src/__tests__/get-skill.test.js +279 -0
  14. package/dist/src/__tests__/get-skill.test.js.map +1 -1
  15. package/dist/src/__tests__/index-local.test.d.ts +5 -0
  16. package/dist/src/__tests__/index-local.test.d.ts.map +1 -0
  17. package/dist/src/__tests__/index-local.test.js +203 -0
  18. package/dist/src/__tests__/index-local.test.js.map +1 -0
  19. package/dist/src/__tests__/middleware/license.test.js +180 -78
  20. package/dist/src/__tests__/middleware/license.test.js.map +1 -1
  21. package/dist/src/__tests__/recommend.test.d.ts +7 -0
  22. package/dist/src/__tests__/recommend.test.d.ts.map +1 -0
  23. package/dist/src/__tests__/recommend.test.js +277 -0
  24. package/dist/src/__tests__/recommend.test.js.map +1 -0
  25. package/dist/src/__tests__/search.test.js +140 -1
  26. package/dist/src/__tests__/search.test.js.map +1 -1
  27. package/dist/src/__tests__/utils/validation.test.d.ts +7 -0
  28. package/dist/src/__tests__/utils/validation.test.d.ts.map +1 -0
  29. package/dist/src/__tests__/utils/validation.test.js +82 -0
  30. package/dist/src/__tests__/utils/validation.test.js.map +1 -0
  31. package/dist/src/context.d.ts +22 -0
  32. package/dist/src/context.d.ts.map +1 -1
  33. package/dist/src/context.js +63 -9
  34. package/dist/src/context.js.map +1 -1
  35. package/dist/src/index.js +15 -1
  36. package/dist/src/index.js.map +1 -1
  37. package/dist/src/indexer/FrontmatterParser.d.ts +30 -0
  38. package/dist/src/indexer/FrontmatterParser.d.ts.map +1 -0
  39. package/dist/src/indexer/FrontmatterParser.js +109 -0
  40. package/dist/src/indexer/FrontmatterParser.js.map +1 -0
  41. package/dist/src/indexer/LocalIndexer.d.ts +136 -0
  42. package/dist/src/indexer/LocalIndexer.d.ts.map +1 -0
  43. package/dist/src/indexer/LocalIndexer.js +271 -0
  44. package/dist/src/indexer/LocalIndexer.js.map +1 -0
  45. package/dist/src/indexer/index.d.ts +8 -0
  46. package/dist/src/indexer/index.d.ts.map +1 -0
  47. package/dist/src/indexer/index.js +8 -0
  48. package/dist/src/indexer/index.js.map +1 -0
  49. package/dist/src/llm/failover.d.ts +210 -0
  50. package/dist/src/llm/failover.d.ts.map +1 -0
  51. package/dist/src/llm/failover.js +329 -0
  52. package/dist/src/llm/failover.js.map +1 -0
  53. package/dist/src/middleware/errorFormatter.d.ts +27 -0
  54. package/dist/src/middleware/errorFormatter.d.ts.map +1 -1
  55. package/dist/src/middleware/errorFormatter.js +95 -0
  56. package/dist/src/middleware/errorFormatter.js.map +1 -1
  57. package/dist/src/middleware/index.d.ts +1 -1
  58. package/dist/src/middleware/index.d.ts.map +1 -1
  59. package/dist/src/middleware/index.js.map +1 -1
  60. package/dist/src/middleware/license.d.ts +5 -0
  61. package/dist/src/middleware/license.d.ts.map +1 -1
  62. package/dist/src/middleware/license.js +2 -1
  63. package/dist/src/middleware/license.js.map +1 -1
  64. package/dist/src/middleware/quota-helpers.d.ts +40 -0
  65. package/dist/src/middleware/quota-helpers.d.ts.map +1 -0
  66. package/dist/src/middleware/quota-helpers.js +90 -0
  67. package/dist/src/middleware/quota-helpers.js.map +1 -0
  68. package/dist/src/middleware/quota-types.d.ts +105 -0
  69. package/dist/src/middleware/quota-types.d.ts.map +1 -0
  70. package/dist/src/middleware/quota-types.js +4 -0
  71. package/dist/src/middleware/quota-types.js.map +1 -0
  72. package/dist/src/middleware/quota.d.ts +3 -97
  73. package/dist/src/middleware/quota.d.ts.map +1 -1
  74. package/dist/src/middleware/quota.js +2 -87
  75. package/dist/src/middleware/quota.js.map +1 -1
  76. package/dist/src/tools/LocalSkillSearch.d.ts +32 -0
  77. package/dist/src/tools/LocalSkillSearch.d.ts.map +1 -0
  78. package/dist/src/tools/LocalSkillSearch.js +74 -0
  79. package/dist/src/tools/LocalSkillSearch.js.map +1 -0
  80. package/dist/src/tools/compare.d.ts +3 -102
  81. package/dist/src/tools/compare.d.ts.map +1 -1
  82. package/dist/src/tools/compare.helpers.d.ts +36 -0
  83. package/dist/src/tools/compare.helpers.d.ts.map +1 -0
  84. package/dist/src/tools/compare.helpers.js +252 -0
  85. package/dist/src/tools/compare.helpers.js.map +1 -0
  86. package/dist/src/tools/compare.js +6 -288
  87. package/dist/src/tools/compare.js.map +1 -1
  88. package/dist/src/tools/compare.types.d.ts +134 -0
  89. package/dist/src/tools/compare.types.d.ts.map +1 -0
  90. package/dist/src/tools/compare.types.js +47 -0
  91. package/dist/src/tools/compare.types.js.map +1 -0
  92. package/dist/src/tools/get-skill.d.ts.map +1 -1
  93. package/dist/src/tools/get-skill.js +38 -2
  94. package/dist/src/tools/get-skill.js.map +1 -1
  95. package/dist/src/tools/index-local.d.ts +117 -0
  96. package/dist/src/tools/index-local.d.ts.map +1 -0
  97. package/dist/src/tools/index-local.js +126 -0
  98. package/dist/src/tools/index-local.js.map +1 -0
  99. package/dist/src/tools/index.d.ts +2 -0
  100. package/dist/src/tools/index.d.ts.map +1 -1
  101. package/dist/src/tools/index.js +2 -0
  102. package/dist/src/tools/index.js.map +1 -1
  103. package/dist/src/tools/install.conflict-helpers.d.ts +64 -0
  104. package/dist/src/tools/install.conflict-helpers.d.ts.map +1 -0
  105. package/dist/src/tools/install.conflict-helpers.js +165 -0
  106. package/dist/src/tools/install.conflict-helpers.js.map +1 -0
  107. package/dist/src/tools/install.conflict-helpers.test.d.ts +15 -0
  108. package/dist/src/tools/install.conflict-helpers.test.d.ts.map +1 -0
  109. package/dist/src/tools/install.conflict-helpers.test.js +243 -0
  110. package/dist/src/tools/install.conflict-helpers.test.js.map +1 -0
  111. package/dist/src/tools/install.conflict.d.ts +58 -0
  112. package/dist/src/tools/install.conflict.d.ts.map +1 -0
  113. package/dist/src/tools/install.conflict.js +160 -0
  114. package/dist/src/tools/install.conflict.js.map +1 -0
  115. package/dist/src/tools/install.conflict.test.d.ts +15 -0
  116. package/dist/src/tools/install.conflict.test.d.ts.map +1 -0
  117. package/dist/src/tools/install.conflict.test.js +354 -0
  118. package/dist/src/tools/install.conflict.test.js.map +1 -0
  119. package/dist/src/tools/install.d.ts +11 -60
  120. package/dist/src/tools/install.d.ts.map +1 -1
  121. package/dist/src/tools/install.helpers.d.ts +83 -0
  122. package/dist/src/tools/install.helpers.d.ts.map +1 -0
  123. package/dist/src/tools/install.helpers.js +333 -0
  124. package/dist/src/tools/install.helpers.js.map +1 -0
  125. package/dist/src/tools/install.js +199 -255
  126. package/dist/src/tools/install.js.map +1 -1
  127. package/dist/src/tools/install.types.d.ts +178 -0
  128. package/dist/src/tools/install.types.d.ts.map +1 -0
  129. package/dist/src/tools/install.types.js +105 -0
  130. package/dist/src/tools/install.types.js.map +1 -0
  131. package/dist/src/tools/merge.d.ts +53 -0
  132. package/dist/src/tools/merge.d.ts.map +1 -0
  133. package/dist/src/tools/merge.js +276 -0
  134. package/dist/src/tools/merge.js.map +1 -0
  135. package/dist/src/tools/merge.test.d.ts +11 -0
  136. package/dist/src/tools/merge.test.d.ts.map +1 -0
  137. package/dist/src/tools/merge.test.js +224 -0
  138. package/dist/src/tools/merge.test.js.map +1 -0
  139. package/dist/src/tools/recommend.d.ts +3 -148
  140. package/dist/src/tools/recommend.d.ts.map +1 -1
  141. package/dist/src/tools/recommend.helpers.d.ts +42 -0
  142. package/dist/src/tools/recommend.helpers.d.ts.map +1 -0
  143. package/dist/src/tools/recommend.helpers.js +155 -0
  144. package/dist/src/tools/recommend.helpers.js.map +1 -0
  145. package/dist/src/tools/recommend.js +216 -154
  146. package/dist/src/tools/recommend.js.map +1 -1
  147. package/dist/src/tools/recommend.types.d.ts +164 -0
  148. package/dist/src/tools/recommend.types.d.ts.map +1 -0
  149. package/dist/src/tools/recommend.types.js +87 -0
  150. package/dist/src/tools/recommend.types.js.map +1 -0
  151. package/dist/src/tools/search.d.ts +18 -4
  152. package/dist/src/tools/search.d.ts.map +1 -1
  153. package/dist/src/tools/search.js +91 -16
  154. package/dist/src/tools/search.js.map +1 -1
  155. package/dist/src/tools/validate.d.ts +3 -70
  156. package/dist/src/tools/validate.d.ts.map +1 -1
  157. package/dist/src/tools/validate.helpers.d.ts +22 -0
  158. package/dist/src/tools/validate.helpers.d.ts.map +1 -0
  159. package/dist/src/tools/validate.helpers.js +276 -0
  160. package/dist/src/tools/validate.helpers.js.map +1 -0
  161. package/dist/src/tools/validate.js +4 -337
  162. package/dist/src/tools/validate.js.map +1 -1
  163. package/dist/src/tools/validate.types.d.ts +96 -0
  164. package/dist/src/tools/validate.types.d.ts.map +1 -0
  165. package/dist/src/tools/validate.types.js +71 -0
  166. package/dist/src/tools/validate.types.js.map +1 -0
  167. package/dist/src/utils/validation.d.ts +6 -2
  168. package/dist/src/utils/validation.d.ts.map +1 -1
  169. package/dist/src/utils/validation.js +11 -2
  170. package/dist/src/utils/validation.js.map +1 -1
  171. package/dist/src/webhooks/index.d.ts +1 -0
  172. package/dist/src/webhooks/index.d.ts.map +1 -1
  173. package/dist/src/webhooks/index.js +2 -0
  174. package/dist/src/webhooks/index.js.map +1 -1
  175. package/dist/src/webhooks/stripe-webhook-endpoint.d.ts +68 -0
  176. package/dist/src/webhooks/stripe-webhook-endpoint.d.ts.map +1 -0
  177. package/dist/src/webhooks/stripe-webhook-endpoint.js +213 -0
  178. package/dist/src/webhooks/stripe-webhook-endpoint.js.map +1 -0
  179. package/dist/src/webhooks/webhook-endpoint.d.ts +4 -49
  180. package/dist/src/webhooks/webhook-endpoint.d.ts.map +1 -1
  181. package/dist/src/webhooks/webhook-endpoint.js +3 -125
  182. package/dist/src/webhooks/webhook-endpoint.js.map +1 -1
  183. package/dist/src/webhooks/webhook-helpers.d.ts +69 -0
  184. package/dist/src/webhooks/webhook-helpers.d.ts.map +1 -0
  185. package/dist/src/webhooks/webhook-helpers.js +146 -0
  186. package/dist/src/webhooks/webhook-helpers.js.map +1 -0
  187. package/dist/tests/e2e/conflict-resolution.e2e.test.d.ts +12 -0
  188. package/dist/tests/e2e/conflict-resolution.e2e.test.d.ts.map +1 -0
  189. package/dist/tests/e2e/conflict-resolution.e2e.test.js +343 -0
  190. package/dist/tests/e2e/conflict-resolution.e2e.test.js.map +1 -0
  191. package/dist/tests/integration/fixtures/development-skills.d.ts +22 -0
  192. package/dist/tests/integration/fixtures/development-skills.d.ts.map +1 -0
  193. package/dist/tests/integration/fixtures/development-skills.js +165 -0
  194. package/dist/tests/integration/fixtures/development-skills.js.map +1 -0
  195. package/dist/tests/integration/fixtures/devops-skills.d.ts +10 -0
  196. package/dist/tests/integration/fixtures/devops-skills.d.ts.map +1 -0
  197. package/dist/tests/integration/fixtures/devops-skills.js +70 -0
  198. package/dist/tests/integration/fixtures/devops-skills.js.map +1 -0
  199. package/dist/tests/integration/fixtures/experimental-skills.d.ts +14 -0
  200. package/dist/tests/integration/fixtures/experimental-skills.d.ts.map +1 -0
  201. package/dist/tests/integration/fixtures/experimental-skills.js +255 -0
  202. package/dist/tests/integration/fixtures/experimental-skills.js.map +1 -0
  203. package/dist/tests/integration/fixtures/skill-types.d.ts +18 -0
  204. package/dist/tests/integration/fixtures/skill-types.d.ts.map +1 -0
  205. package/dist/tests/integration/fixtures/skill-types.js +6 -0
  206. package/dist/tests/integration/fixtures/skill-types.js.map +1 -0
  207. package/dist/tests/integration/fixtures/test-skills.d.ts +10 -14
  208. package/dist/tests/integration/fixtures/test-skills.d.ts.map +1 -1
  209. package/dist/tests/integration/fixtures/test-skills.js +23 -590
  210. package/dist/tests/integration/fixtures/test-skills.js.map +1 -1
  211. package/dist/tests/integration/fixtures/testing-skills.d.ts +10 -0
  212. package/dist/tests/integration/fixtures/testing-skills.d.ts.map +1 -0
  213. package/dist/tests/integration/fixtures/testing-skills.js +70 -0
  214. package/dist/tests/integration/fixtures/testing-skills.js.map +1 -0
  215. package/dist/tests/integration/fixtures/verified-skills.d.ts +11 -0
  216. package/dist/tests/integration/fixtures/verified-skills.d.ts.map +1 -0
  217. package/dist/tests/integration/fixtures/verified-skills.js +91 -0
  218. package/dist/tests/integration/fixtures/verified-skills.js.map +1 -0
  219. package/dist/tests/integration/install-conflict.integration.test.d.ts +8 -0
  220. package/dist/tests/integration/install-conflict.integration.test.d.ts.map +1 -0
  221. package/dist/tests/integration/install-conflict.integration.test.js +384 -0
  222. package/dist/tests/integration/install-conflict.integration.test.js.map +1 -0
  223. package/dist/tests/integration/install-trust-parsing.integration.test.d.ts +13 -0
  224. package/dist/tests/integration/install-trust-parsing.integration.test.d.ts.map +1 -0
  225. package/dist/tests/integration/install-trust-parsing.integration.test.js +290 -0
  226. package/dist/tests/integration/install-trust-parsing.integration.test.js.map +1 -0
  227. package/dist/tests/integration/install.integration.test.js +151 -1
  228. package/dist/tests/integration/install.integration.test.js.map +1 -1
  229. package/dist/tests/integration/recommend.integration.test.js +2 -1
  230. package/dist/tests/integration/recommend.integration.test.js.map +1 -1
  231. package/dist/tests/llm/failover.test.d.ts +13 -0
  232. package/dist/tests/llm/failover.test.d.ts.map +1 -0
  233. package/dist/tests/llm/failover.test.js +250 -0
  234. package/dist/tests/llm/failover.test.js.map +1 -0
  235. package/dist/tests/recommend.test.js +133 -1
  236. package/dist/tests/recommend.test.js.map +1 -1
  237. package/dist/tests/tools.test.d.ts +1 -0
  238. package/dist/tests/tools.test.d.ts.map +1 -1
  239. package/dist/tests/tools.test.js +59 -1
  240. package/dist/tests/tools.test.js.map +1 -1
  241. package/dist/tests/unit/compare-helpers.test.d.ts +8 -0
  242. package/dist/tests/unit/compare-helpers.test.d.ts.map +1 -0
  243. package/dist/tests/unit/compare-helpers.test.js +224 -0
  244. package/dist/tests/unit/compare-helpers.test.js.map +1 -0
  245. package/dist/tests/unit/install-helpers.test.d.ts +8 -0
  246. package/dist/tests/unit/install-helpers.test.d.ts.map +1 -0
  247. package/dist/tests/unit/install-helpers.test.js +460 -0
  248. package/dist/tests/unit/install-helpers.test.js.map +1 -0
  249. package/dist/tests/unit/recommend-helpers.test.d.ts +8 -0
  250. package/dist/tests/unit/recommend-helpers.test.d.ts.map +1 -0
  251. package/dist/tests/unit/recommend-helpers.test.js +117 -0
  252. package/dist/tests/unit/recommend-helpers.test.js.map +1 -0
  253. package/dist/tests/unit/validate-helpers.test.d.ts +8 -0
  254. package/dist/tests/unit/validate-helpers.test.d.ts.map +1 -0
  255. package/dist/tests/unit/validate-helpers.test.js +243 -0
  256. package/dist/tests/unit/validate-helpers.test.js.map +1 -0
  257. package/package.json +7 -6
  258. package/src/assets/docs/USER_GUIDE.md +0 -220
  259. package/src/assets/skills/skillsmith/docs/QUOTAS.md +0 -182
  260. package/src/assets/skills/skillsmith/docs/SECURITY.md +0 -174
  261. package/src/assets/skills/skillsmith/docs/TRUST_TIERS.md +0 -142
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Validate Tool Helper Functions
3
+ * @module @skillsmith/mcp-server/tools/validate.helpers
4
+ */
5
+ import type { ValidationError } from './validate.types.js';
6
+ /**
7
+ * Parse YAML frontmatter from markdown content
8
+ */
9
+ export declare function parseYamlFrontmatter(content: string): Record<string, unknown> | null;
10
+ /**
11
+ * Check for SSRF patterns in a URL
12
+ */
13
+ export declare function hasSsrfPattern(url: string): boolean;
14
+ /**
15
+ * Check for path traversal patterns
16
+ */
17
+ export declare function hasPathTraversal(path: string): boolean;
18
+ /**
19
+ * Validate skill metadata
20
+ */
21
+ export declare function validateMetadata(metadata: Record<string, unknown>, strict: boolean): ValidationError[];
22
+ //# sourceMappingURL=validate.helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.helpers.d.ts","sourceRoot":"","sources":["../../../src/tools/validate.helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAG1D;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAqFpF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,MAAM,EAAE,OAAO,GACd,eAAe,EAAE,CA4KnB"}
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Validate Tool Helper Functions
3
+ * @module @skillsmith/mcp-server/tools/validate.helpers
4
+ */
5
+ import { FIELD_LIMITS, SSRF_PATTERNS, PATH_TRAVERSAL_PATTERNS } from './validate.types.js';
6
+ /**
7
+ * Parse YAML frontmatter from markdown content
8
+ */
9
+ export function parseYamlFrontmatter(content) {
10
+ const trimmed = content.trim();
11
+ if (!trimmed.startsWith('---')) {
12
+ return null;
13
+ }
14
+ const endIndex = trimmed.indexOf('---', 3);
15
+ if (endIndex === -1) {
16
+ return null;
17
+ }
18
+ const yamlContent = trimmed.slice(3, endIndex).trim();
19
+ const result = {};
20
+ const lines = yamlContent.split('\n');
21
+ let currentKey = null;
22
+ let arrayBuffer = [];
23
+ let inArray = false;
24
+ for (const line of lines) {
25
+ const trimmedLine = line.trim();
26
+ if (!trimmedLine || trimmedLine.startsWith('#')) {
27
+ continue;
28
+ }
29
+ if (trimmedLine.startsWith('- ')) {
30
+ if (currentKey && inArray) {
31
+ const value = trimmedLine
32
+ .slice(2)
33
+ .trim()
34
+ .replace(/^["']|["']$/g, '');
35
+ arrayBuffer.push(value);
36
+ }
37
+ continue;
38
+ }
39
+ const colonIndex = trimmedLine.indexOf(':');
40
+ if (colonIndex > 0) {
41
+ if (currentKey && inArray && arrayBuffer.length > 0) {
42
+ result[currentKey] = arrayBuffer;
43
+ arrayBuffer = [];
44
+ }
45
+ const key = trimmedLine.slice(0, colonIndex).trim();
46
+ const value = trimmedLine.slice(colonIndex + 1).trim();
47
+ if (value === '' || value === '|' || value === '>') {
48
+ currentKey = key;
49
+ inArray = true;
50
+ arrayBuffer = [];
51
+ }
52
+ else {
53
+ currentKey = null;
54
+ inArray = false;
55
+ let parsedValue = value;
56
+ if ((value.startsWith('"') && value.endsWith('"')) ||
57
+ (value.startsWith("'") && value.endsWith("'"))) {
58
+ parsedValue = value.slice(1, -1);
59
+ }
60
+ else if (value === 'true') {
61
+ parsedValue = true;
62
+ }
63
+ else if (value === 'false') {
64
+ parsedValue = false;
65
+ }
66
+ else if (/^-?\d+(\.\d+)?$/.test(value)) {
67
+ parsedValue = parseFloat(value);
68
+ }
69
+ else if (value.startsWith('[') && value.endsWith(']')) {
70
+ parsedValue = value
71
+ .slice(1, -1)
72
+ .split(',')
73
+ .map((item) => item.trim().replace(/^["']|["']$/g, ''))
74
+ .filter((item) => item.length > 0);
75
+ }
76
+ result[key] = parsedValue;
77
+ }
78
+ }
79
+ }
80
+ if (currentKey && inArray && arrayBuffer.length > 0) {
81
+ result[currentKey] = arrayBuffer;
82
+ }
83
+ return result;
84
+ }
85
+ /**
86
+ * Check for SSRF patterns in a URL
87
+ */
88
+ export function hasSsrfPattern(url) {
89
+ return SSRF_PATTERNS.some((pattern) => pattern.test(url));
90
+ }
91
+ /**
92
+ * Check for path traversal patterns
93
+ */
94
+ export function hasPathTraversal(path) {
95
+ return PATH_TRAVERSAL_PATTERNS.some((pattern) => pattern.test(path));
96
+ }
97
+ /**
98
+ * Validate skill metadata
99
+ */
100
+ export function validateMetadata(metadata, strict) {
101
+ const errors = [];
102
+ // Required fields - name
103
+ if (!metadata.name) {
104
+ errors.push({
105
+ field: 'name',
106
+ message: 'Required field "name" is missing',
107
+ severity: 'error',
108
+ });
109
+ }
110
+ else if (typeof metadata.name !== 'string') {
111
+ errors.push({
112
+ field: 'name',
113
+ message: 'Field "name" must be a string',
114
+ severity: 'error',
115
+ });
116
+ }
117
+ else if (metadata.name.length > FIELD_LIMITS.name) {
118
+ errors.push({
119
+ field: 'name',
120
+ message: `Field "name" exceeds maximum length of ${FIELD_LIMITS.name} characters`,
121
+ severity: 'error',
122
+ });
123
+ }
124
+ // Description validation
125
+ if (!metadata.description) {
126
+ errors.push({
127
+ field: 'description',
128
+ message: 'Required field "description" is missing',
129
+ severity: strict ? 'error' : 'warning',
130
+ });
131
+ }
132
+ else if (typeof metadata.description !== 'string') {
133
+ errors.push({
134
+ field: 'description',
135
+ message: 'Field "description" must be a string',
136
+ severity: 'error',
137
+ });
138
+ }
139
+ else if (metadata.description.length > FIELD_LIMITS.description) {
140
+ errors.push({
141
+ field: 'description',
142
+ message: `Field "description" exceeds maximum length of ${FIELD_LIMITS.description} characters`,
143
+ severity: 'error',
144
+ });
145
+ }
146
+ // Author validation
147
+ if (metadata.author !== undefined) {
148
+ if (typeof metadata.author !== 'string') {
149
+ errors.push({
150
+ field: 'author',
151
+ message: 'Field "author" must be a string',
152
+ severity: 'error',
153
+ });
154
+ }
155
+ else if (metadata.author.length > FIELD_LIMITS.author) {
156
+ errors.push({
157
+ field: 'author',
158
+ message: `Field "author" exceeds maximum length of ${FIELD_LIMITS.author} characters`,
159
+ severity: 'error',
160
+ });
161
+ }
162
+ }
163
+ // Version validation
164
+ if (metadata.version !== undefined) {
165
+ if (typeof metadata.version !== 'string') {
166
+ errors.push({
167
+ field: 'version',
168
+ message: 'Field "version" must be a string',
169
+ severity: 'error',
170
+ });
171
+ }
172
+ else if (metadata.version.length > FIELD_LIMITS.version) {
173
+ errors.push({
174
+ field: 'version',
175
+ message: `Field "version" exceeds maximum length of ${FIELD_LIMITS.version} characters`,
176
+ severity: 'error',
177
+ });
178
+ }
179
+ }
180
+ else if (strict) {
181
+ errors.push({
182
+ field: 'version',
183
+ message: 'Field "version" is recommended',
184
+ severity: 'warning',
185
+ });
186
+ }
187
+ // Tags validation
188
+ if (metadata.tags !== undefined) {
189
+ if (!Array.isArray(metadata.tags)) {
190
+ errors.push({
191
+ field: 'tags',
192
+ message: 'Field "tags" must be an array',
193
+ severity: 'error',
194
+ });
195
+ }
196
+ else {
197
+ if (metadata.tags.length > FIELD_LIMITS.maxTags) {
198
+ errors.push({
199
+ field: 'tags',
200
+ message: `Field "tags" exceeds maximum count of ${FIELD_LIMITS.maxTags}`,
201
+ severity: 'error',
202
+ });
203
+ }
204
+ for (let i = 0; i < metadata.tags.length; i++) {
205
+ const tag = metadata.tags[i];
206
+ if (typeof tag !== 'string') {
207
+ errors.push({
208
+ field: `tags[${i}]`,
209
+ message: 'Tag must be a string',
210
+ severity: 'error',
211
+ });
212
+ }
213
+ else if (tag.length > FIELD_LIMITS.tagLength) {
214
+ errors.push({
215
+ field: `tags[${i}]`,
216
+ message: `Tag exceeds maximum length of ${FIELD_LIMITS.tagLength} characters`,
217
+ severity: 'error',
218
+ });
219
+ }
220
+ }
221
+ }
222
+ }
223
+ else if (strict) {
224
+ errors.push({
225
+ field: 'tags',
226
+ message: 'Field "tags" is recommended for discoverability',
227
+ severity: 'warning',
228
+ });
229
+ }
230
+ // Security: Check repository URL for SSRF
231
+ if (metadata.repository !== undefined) {
232
+ if (typeof metadata.repository !== 'string') {
233
+ errors.push({
234
+ field: 'repository',
235
+ message: 'Field "repository" must be a string',
236
+ severity: 'error',
237
+ });
238
+ }
239
+ else if (hasSsrfPattern(metadata.repository)) {
240
+ errors.push({
241
+ field: 'repository',
242
+ message: 'Field "repository" contains potentially dangerous URL pattern',
243
+ severity: 'error',
244
+ });
245
+ }
246
+ }
247
+ // Security: Check homepage URL for SSRF
248
+ if (metadata.homepage !== undefined) {
249
+ if (typeof metadata.homepage !== 'string') {
250
+ errors.push({
251
+ field: 'homepage',
252
+ message: 'Field "homepage" must be a string',
253
+ severity: 'error',
254
+ });
255
+ }
256
+ else if (hasSsrfPattern(metadata.homepage)) {
257
+ errors.push({
258
+ field: 'homepage',
259
+ message: 'Field "homepage" contains potentially dangerous URL pattern',
260
+ severity: 'error',
261
+ });
262
+ }
263
+ }
264
+ // Security: Check for path traversal in any string fields
265
+ for (const [key, value] of Object.entries(metadata)) {
266
+ if (typeof value === 'string' && hasPathTraversal(value)) {
267
+ errors.push({
268
+ field: key,
269
+ message: `Field "${key}" contains path traversal pattern`,
270
+ severity: 'error',
271
+ });
272
+ }
273
+ }
274
+ return errors;
275
+ }
276
+ //# sourceMappingURL=validate.helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.helpers.js","sourceRoot":"","sources":["../../../src/tools/validate.helpers.ts"],"names":[],"mappings":"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"}
@@ -22,344 +22,13 @@
22
22
  * strict: true
23
23
  * });
24
24
  */
25
- import { z } from 'zod';
26
25
  import { promises as fs } from 'fs';
27
26
  import { join } from 'path';
28
27
  import { SkillsmithError, ErrorCodes } from '@skillsmith/core';
29
- /**
30
- * Zod schema for validate tool input
31
- */
32
- export const validateInputSchema = z.object({
33
- /** Path to SKILL.md file or skill directory */
34
- skill_path: z.string().min(1, 'skill_path is required'),
35
- /** Enable strict validation (default false) */
36
- strict: z.boolean().default(false),
37
- });
38
- /**
39
- * MCP tool schema definition for skill_validate
40
- */
41
- export const validateToolSchema = {
42
- name: 'skill_validate',
43
- description: 'Validate a SKILL.md file or skill directory against Skillsmith specification. Checks structure, required fields, and security patterns.',
44
- inputSchema: {
45
- type: 'object',
46
- properties: {
47
- skill_path: {
48
- type: 'string',
49
- description: 'Path to SKILL.md file or skill directory containing SKILL.md',
50
- },
51
- strict: {
52
- type: 'boolean',
53
- description: 'Enable strict validation mode (default false). Strict mode treats warnings as errors.',
54
- default: false,
55
- },
56
- },
57
- required: ['skill_path'],
58
- },
59
- };
60
- /**
61
- * Maximum field lengths for validation
62
- */
63
- const FIELD_LIMITS = {
64
- name: 64,
65
- description: 1024,
66
- author: 128,
67
- version: 32,
68
- category: 64,
69
- license: 64,
70
- tagLength: 32,
71
- maxTags: 20,
72
- };
73
- /**
74
- * Dangerous URL patterns for SSRF prevention
75
- */
76
- const SSRF_PATTERNS = [
77
- /^file:\/\//i,
78
- /^gopher:\/\//i,
79
- /^dict:\/\//i,
80
- /^ldap:\/\//i,
81
- /localhost/i,
82
- /127\.0\.0\.\d+/,
83
- /0\.0\.0\.0/,
84
- /\[::1\]/,
85
- /10\.\d+\.\d+\.\d+/,
86
- /172\.(1[6-9]|2\d|3[01])\.\d+\.\d+/,
87
- /192\.168\.\d+\.\d+/,
88
- ];
89
- /**
90
- * Path traversal patterns
91
- */
92
- const PATH_TRAVERSAL_PATTERNS = [/\.\./, /\.\.%2[fF]/, /%2[eE]%2[eE]/, /\\\.\\./];
93
- /**
94
- * Parse YAML frontmatter from markdown content
95
- */
96
- function parseYamlFrontmatter(content) {
97
- const trimmed = content.trim();
98
- if (!trimmed.startsWith('---')) {
99
- return null;
100
- }
101
- const endIndex = trimmed.indexOf('---', 3);
102
- if (endIndex === -1) {
103
- return null;
104
- }
105
- const yamlContent = trimmed.slice(3, endIndex).trim();
106
- const result = {};
107
- const lines = yamlContent.split('\n');
108
- let currentKey = null;
109
- let arrayBuffer = [];
110
- let inArray = false;
111
- for (const line of lines) {
112
- const trimmedLine = line.trim();
113
- if (!trimmedLine || trimmedLine.startsWith('#')) {
114
- continue;
115
- }
116
- if (trimmedLine.startsWith('- ')) {
117
- if (currentKey && inArray) {
118
- const value = trimmedLine
119
- .slice(2)
120
- .trim()
121
- .replace(/^["']|["']$/g, '');
122
- arrayBuffer.push(value);
123
- }
124
- continue;
125
- }
126
- const colonIndex = trimmedLine.indexOf(':');
127
- if (colonIndex > 0) {
128
- if (currentKey && inArray && arrayBuffer.length > 0) {
129
- result[currentKey] = arrayBuffer;
130
- arrayBuffer = [];
131
- }
132
- const key = trimmedLine.slice(0, colonIndex).trim();
133
- const value = trimmedLine.slice(colonIndex + 1).trim();
134
- if (value === '' || value === '|' || value === '>') {
135
- currentKey = key;
136
- inArray = true;
137
- arrayBuffer = [];
138
- }
139
- else {
140
- currentKey = null;
141
- inArray = false;
142
- let parsedValue = value;
143
- if ((value.startsWith('"') && value.endsWith('"')) ||
144
- (value.startsWith("'") && value.endsWith("'"))) {
145
- parsedValue = value.slice(1, -1);
146
- }
147
- else if (value === 'true') {
148
- parsedValue = true;
149
- }
150
- else if (value === 'false') {
151
- parsedValue = false;
152
- }
153
- else if (/^-?\d+(\.\d+)?$/.test(value)) {
154
- parsedValue = parseFloat(value);
155
- }
156
- else if (value.startsWith('[') && value.endsWith(']')) {
157
- parsedValue = value
158
- .slice(1, -1)
159
- .split(',')
160
- .map((item) => item.trim().replace(/^["']|["']$/g, ''))
161
- .filter((item) => item.length > 0);
162
- }
163
- result[key] = parsedValue;
164
- }
165
- }
166
- }
167
- if (currentKey && inArray && arrayBuffer.length > 0) {
168
- result[currentKey] = arrayBuffer;
169
- }
170
- return result;
171
- }
172
- /**
173
- * Check for SSRF patterns in a URL
174
- */
175
- function hasSsrfPattern(url) {
176
- return SSRF_PATTERNS.some((pattern) => pattern.test(url));
177
- }
178
- /**
179
- * Check for path traversal patterns
180
- */
181
- function hasPathTraversal(path) {
182
- return PATH_TRAVERSAL_PATTERNS.some((pattern) => pattern.test(path));
183
- }
184
- /**
185
- * Validate skill metadata
186
- */
187
- function validateMetadata(metadata, strict) {
188
- const errors = [];
189
- // Required fields
190
- if (!metadata.name) {
191
- errors.push({
192
- field: 'name',
193
- message: 'Required field "name" is missing',
194
- severity: 'error',
195
- });
196
- }
197
- else if (typeof metadata.name !== 'string') {
198
- errors.push({
199
- field: 'name',
200
- message: 'Field "name" must be a string',
201
- severity: 'error',
202
- });
203
- }
204
- else if (metadata.name.length > FIELD_LIMITS.name) {
205
- errors.push({
206
- field: 'name',
207
- message: `Field "name" exceeds maximum length of ${FIELD_LIMITS.name} characters`,
208
- severity: 'error',
209
- });
210
- }
211
- // Description validation
212
- if (!metadata.description) {
213
- errors.push({
214
- field: 'description',
215
- message: 'Required field "description" is missing',
216
- severity: strict ? 'error' : 'warning',
217
- });
218
- }
219
- else if (typeof metadata.description !== 'string') {
220
- errors.push({
221
- field: 'description',
222
- message: 'Field "description" must be a string',
223
- severity: 'error',
224
- });
225
- }
226
- else if (metadata.description.length > FIELD_LIMITS.description) {
227
- errors.push({
228
- field: 'description',
229
- message: `Field "description" exceeds maximum length of ${FIELD_LIMITS.description} characters`,
230
- severity: 'error',
231
- });
232
- }
233
- // Author validation
234
- if (metadata.author !== undefined) {
235
- if (typeof metadata.author !== 'string') {
236
- errors.push({
237
- field: 'author',
238
- message: 'Field "author" must be a string',
239
- severity: 'error',
240
- });
241
- }
242
- else if (metadata.author.length > FIELD_LIMITS.author) {
243
- errors.push({
244
- field: 'author',
245
- message: `Field "author" exceeds maximum length of ${FIELD_LIMITS.author} characters`,
246
- severity: 'error',
247
- });
248
- }
249
- }
250
- // Version validation
251
- if (metadata.version !== undefined) {
252
- if (typeof metadata.version !== 'string') {
253
- errors.push({
254
- field: 'version',
255
- message: 'Field "version" must be a string',
256
- severity: 'error',
257
- });
258
- }
259
- else if (metadata.version.length > FIELD_LIMITS.version) {
260
- errors.push({
261
- field: 'version',
262
- message: `Field "version" exceeds maximum length of ${FIELD_LIMITS.version} characters`,
263
- severity: 'error',
264
- });
265
- }
266
- }
267
- else if (strict) {
268
- errors.push({
269
- field: 'version',
270
- message: 'Field "version" is recommended',
271
- severity: 'warning',
272
- });
273
- }
274
- // Tags validation
275
- if (metadata.tags !== undefined) {
276
- if (!Array.isArray(metadata.tags)) {
277
- errors.push({
278
- field: 'tags',
279
- message: 'Field "tags" must be an array',
280
- severity: 'error',
281
- });
282
- }
283
- else {
284
- if (metadata.tags.length > FIELD_LIMITS.maxTags) {
285
- errors.push({
286
- field: 'tags',
287
- message: `Field "tags" exceeds maximum count of ${FIELD_LIMITS.maxTags}`,
288
- severity: 'error',
289
- });
290
- }
291
- for (let i = 0; i < metadata.tags.length; i++) {
292
- const tag = metadata.tags[i];
293
- if (typeof tag !== 'string') {
294
- errors.push({
295
- field: `tags[${i}]`,
296
- message: 'Tag must be a string',
297
- severity: 'error',
298
- });
299
- }
300
- else if (tag.length > FIELD_LIMITS.tagLength) {
301
- errors.push({
302
- field: `tags[${i}]`,
303
- message: `Tag exceeds maximum length of ${FIELD_LIMITS.tagLength} characters`,
304
- severity: 'error',
305
- });
306
- }
307
- }
308
- }
309
- }
310
- else if (strict) {
311
- errors.push({
312
- field: 'tags',
313
- message: 'Field "tags" is recommended for discoverability',
314
- severity: 'warning',
315
- });
316
- }
317
- // Security: Check repository URL for SSRF
318
- if (metadata.repository !== undefined) {
319
- if (typeof metadata.repository !== 'string') {
320
- errors.push({
321
- field: 'repository',
322
- message: 'Field "repository" must be a string',
323
- severity: 'error',
324
- });
325
- }
326
- else if (hasSsrfPattern(metadata.repository)) {
327
- errors.push({
328
- field: 'repository',
329
- message: 'Field "repository" contains potentially dangerous URL pattern',
330
- severity: 'error',
331
- });
332
- }
333
- }
334
- // Security: Check homepage URL for SSRF
335
- if (metadata.homepage !== undefined) {
336
- if (typeof metadata.homepage !== 'string') {
337
- errors.push({
338
- field: 'homepage',
339
- message: 'Field "homepage" must be a string',
340
- severity: 'error',
341
- });
342
- }
343
- else if (hasSsrfPattern(metadata.homepage)) {
344
- errors.push({
345
- field: 'homepage',
346
- message: 'Field "homepage" contains potentially dangerous URL pattern',
347
- severity: 'error',
348
- });
349
- }
350
- }
351
- // Security: Check for path traversal in any string fields
352
- for (const [key, value] of Object.entries(metadata)) {
353
- if (typeof value === 'string' && hasPathTraversal(value)) {
354
- errors.push({
355
- field: key,
356
- message: `Field "${key}" contains path traversal pattern`,
357
- severity: 'error',
358
- });
359
- }
360
- }
361
- return errors;
362
- }
28
+ import { validateInputSchema } from './validate.types.js';
29
+ // Import helpers
30
+ import { parseYamlFrontmatter, hasPathTraversal, validateMetadata } from './validate.helpers.js';
31
+ export { validateInputSchema, validateToolSchema } from './validate.types.js';
363
32
  /**
364
33
  * Execute skill validation.
365
34
  *
@@ -401,7 +70,6 @@ export async function executeValidate(input, _context) {
401
70
  }
402
71
  }
403
72
  catch {
404
- // Path doesn't exist or is inaccessible
405
73
  throw new SkillsmithError(ErrorCodes.SKILL_NOT_FOUND, `Path not found: ${skill_path}`, {
406
74
  details: { path: skill_path },
407
75
  });
@@ -427,7 +95,6 @@ export async function executeValidate(input, _context) {
427
95
  });
428
96
  }
429
97
  else {
430
- // Validate metadata
431
98
  errors.push(...validateMetadata(metadata, strict));
432
99
  }
433
100
  // Determine validity