@skillsmith/mcp-server 0.3.1 → 0.3.2

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 (314) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/src/__tests__/get-skill.test.d.ts +6 -0
  3. package/dist/src/__tests__/get-skill.test.d.ts.map +1 -0
  4. package/dist/src/__tests__/get-skill.test.js +88 -0
  5. package/dist/src/__tests__/get-skill.test.js.map +1 -0
  6. package/dist/src/__tests__/middleware/errorFormatter.test.d.ts +7 -0
  7. package/dist/src/__tests__/middleware/errorFormatter.test.d.ts.map +1 -0
  8. package/dist/src/__tests__/middleware/errorFormatter.test.js +304 -0
  9. package/dist/src/__tests__/middleware/errorFormatter.test.js.map +1 -0
  10. package/dist/src/__tests__/middleware/license.test.d.ts +7 -0
  11. package/dist/src/__tests__/middleware/license.test.d.ts.map +1 -0
  12. package/dist/src/__tests__/middleware/license.test.js +500 -0
  13. package/dist/src/__tests__/middleware/license.test.js.map +1 -0
  14. package/dist/src/__tests__/search.test.d.ts +6 -0
  15. package/dist/src/__tests__/search.test.d.ts.map +1 -0
  16. package/dist/src/__tests__/search.test.js +86 -0
  17. package/dist/src/__tests__/search.test.js.map +1 -0
  18. package/dist/src/__tests__/test-utils.d.ts +20 -0
  19. package/dist/src/__tests__/test-utils.d.ts.map +1 -0
  20. package/dist/src/__tests__/test-utils.js +91 -0
  21. package/dist/src/__tests__/test-utils.js.map +1 -0
  22. package/dist/src/context/index.d.ts +19 -0
  23. package/dist/src/context/index.d.ts.map +1 -0
  24. package/dist/src/context/index.js +25 -0
  25. package/dist/src/context/index.js.map +1 -0
  26. package/dist/src/context/project-detector.d.ts +145 -0
  27. package/dist/src/context/project-detector.d.ts.map +1 -0
  28. package/dist/src/context/project-detector.js +321 -0
  29. package/dist/src/context/project-detector.js.map +1 -0
  30. package/dist/src/context.d.ts +157 -0
  31. package/dist/src/context.d.ts.map +1 -0
  32. package/dist/src/context.js +231 -0
  33. package/dist/src/context.js.map +1 -0
  34. package/dist/src/core-shim.d.ts +7 -0
  35. package/dist/src/core-shim.d.ts.map +1 -0
  36. package/dist/src/core-shim.js +9 -0
  37. package/dist/src/core-shim.js.map +1 -0
  38. package/dist/src/health/healthCheck.d.ts +88 -0
  39. package/dist/src/health/healthCheck.d.ts.map +1 -0
  40. package/dist/src/health/healthCheck.js +117 -0
  41. package/dist/src/health/healthCheck.js.map +1 -0
  42. package/dist/src/health/index.d.ts +21 -0
  43. package/dist/src/health/index.d.ts.map +1 -0
  44. package/dist/src/health/index.js +21 -0
  45. package/dist/src/health/index.js.map +1 -0
  46. package/dist/src/health/readinessCheck.d.ts +139 -0
  47. package/dist/src/health/readinessCheck.d.ts.map +1 -0
  48. package/dist/src/health/readinessCheck.js +266 -0
  49. package/dist/src/health/readinessCheck.js.map +1 -0
  50. package/dist/src/index.d.ts +10 -0
  51. package/dist/src/index.d.ts.map +1 -0
  52. package/dist/src/index.js +237 -0
  53. package/dist/src/index.js.map +1 -0
  54. package/dist/src/index.test.d.ts +2 -0
  55. package/dist/src/index.test.d.ts.map +1 -0
  56. package/dist/src/index.test.js +43 -0
  57. package/dist/src/index.test.js.map +1 -0
  58. package/dist/src/logger.d.ts +26 -0
  59. package/dist/src/logger.d.ts.map +1 -0
  60. package/dist/src/logger.js +179 -0
  61. package/dist/src/logger.js.map +1 -0
  62. package/dist/src/middleware/__tests__/csp.test.d.ts +2 -0
  63. package/dist/src/middleware/__tests__/csp.test.d.ts.map +1 -0
  64. package/dist/src/middleware/__tests__/csp.test.js +390 -0
  65. package/dist/src/middleware/__tests__/csp.test.js.map +1 -0
  66. package/dist/src/middleware/csp.d.ts +103 -0
  67. package/dist/src/middleware/csp.d.ts.map +1 -0
  68. package/dist/src/middleware/csp.js +273 -0
  69. package/dist/src/middleware/csp.js.map +1 -0
  70. package/dist/src/middleware/degradation.d.ts +105 -0
  71. package/dist/src/middleware/degradation.d.ts.map +1 -0
  72. package/dist/src/middleware/degradation.js +319 -0
  73. package/dist/src/middleware/degradation.js.map +1 -0
  74. package/dist/src/middleware/errorFormatter.d.ts +119 -0
  75. package/dist/src/middleware/errorFormatter.d.ts.map +1 -0
  76. package/dist/src/middleware/errorFormatter.js +294 -0
  77. package/dist/src/middleware/errorFormatter.js.map +1 -0
  78. package/dist/src/middleware/index.d.ts +11 -0
  79. package/dist/src/middleware/index.d.ts.map +1 -0
  80. package/dist/src/middleware/index.js +16 -0
  81. package/dist/src/middleware/index.js.map +1 -0
  82. package/dist/src/middleware/license.d.ts +169 -0
  83. package/dist/src/middleware/license.d.ts.map +1 -0
  84. package/dist/src/middleware/license.js +292 -0
  85. package/dist/src/middleware/license.js.map +1 -0
  86. package/dist/src/middleware/quota.d.ts +182 -0
  87. package/dist/src/middleware/quota.d.ts.map +1 -0
  88. package/dist/src/middleware/quota.js +309 -0
  89. package/dist/src/middleware/quota.js.map +1 -0
  90. package/dist/src/middleware/toolFeatureMapping.d.ts +36 -0
  91. package/dist/src/middleware/toolFeatureMapping.d.ts.map +1 -0
  92. package/dist/src/middleware/toolFeatureMapping.js +96 -0
  93. package/dist/src/middleware/toolFeatureMapping.js.map +1 -0
  94. package/dist/src/onboarding/first-run.d.ts +65 -0
  95. package/dist/src/onboarding/first-run.d.ts.map +1 -0
  96. package/dist/src/onboarding/first-run.js +79 -0
  97. package/dist/src/onboarding/first-run.js.map +1 -0
  98. package/dist/src/onboarding/index.d.ts +7 -0
  99. package/dist/src/onboarding/index.d.ts.map +1 -0
  100. package/dist/src/onboarding/index.js +7 -0
  101. package/dist/src/onboarding/index.js.map +1 -0
  102. package/dist/src/onboarding/install-assets.d.ts +27 -0
  103. package/dist/src/onboarding/install-assets.d.ts.map +1 -0
  104. package/dist/src/onboarding/install-assets.js +128 -0
  105. package/dist/src/onboarding/install-assets.js.map +1 -0
  106. package/dist/src/suggestions/index.d.ts +21 -0
  107. package/dist/src/suggestions/index.d.ts.map +1 -0
  108. package/dist/src/suggestions/index.js +20 -0
  109. package/dist/src/suggestions/index.js.map +1 -0
  110. package/dist/src/suggestions/suggestion-engine.d.ts +185 -0
  111. package/dist/src/suggestions/suggestion-engine.d.ts.map +1 -0
  112. package/dist/src/suggestions/suggestion-engine.js +352 -0
  113. package/dist/src/suggestions/suggestion-engine.js.map +1 -0
  114. package/dist/src/suggestions/types.d.ts +88 -0
  115. package/dist/src/suggestions/types.d.ts.map +1 -0
  116. package/dist/src/suggestions/types.js +21 -0
  117. package/dist/src/suggestions/types.js.map +1 -0
  118. package/dist/src/tools/analyze.d.ts +151 -0
  119. package/dist/src/tools/analyze.d.ts.map +1 -0
  120. package/dist/src/tools/analyze.js +205 -0
  121. package/dist/src/tools/analyze.js.map +1 -0
  122. package/dist/src/tools/compare.d.ts +149 -0
  123. package/dist/src/tools/compare.d.ts.map +1 -0
  124. package/dist/src/tools/compare.js +464 -0
  125. package/dist/src/tools/compare.js.map +1 -0
  126. package/dist/src/tools/get-skill.d.ts +107 -0
  127. package/dist/src/tools/get-skill.d.ts.map +1 -0
  128. package/dist/src/tools/get-skill.js +260 -0
  129. package/dist/src/tools/get-skill.js.map +1 -0
  130. package/dist/src/tools/index.d.ts +20 -0
  131. package/dist/src/tools/index.d.ts.map +1 -0
  132. package/dist/src/tools/index.js +20 -0
  133. package/dist/src/tools/index.js.map +1 -0
  134. package/dist/src/tools/install.d.ts +122 -0
  135. package/dist/src/tools/install.d.ts.map +1 -0
  136. package/dist/src/tools/install.js +326 -0
  137. package/dist/src/tools/install.js.map +1 -0
  138. package/dist/src/tools/recommend.d.ts +169 -0
  139. package/dist/src/tools/recommend.d.ts.map +1 -0
  140. package/dist/src/tools/recommend.js +357 -0
  141. package/dist/src/tools/recommend.js.map +1 -0
  142. package/dist/src/tools/search.d.ts +114 -0
  143. package/dist/src/tools/search.d.ts.map +1 -0
  144. package/dist/src/tools/search.js +247 -0
  145. package/dist/src/tools/search.js.map +1 -0
  146. package/dist/src/tools/suggest.d.ts +181 -0
  147. package/dist/src/tools/suggest.d.ts.map +1 -0
  148. package/dist/src/tools/suggest.js +310 -0
  149. package/dist/src/tools/suggest.js.map +1 -0
  150. package/dist/src/tools/uninstall.d.ts +123 -0
  151. package/dist/src/tools/uninstall.d.ts.map +1 -0
  152. package/dist/src/tools/uninstall.js +250 -0
  153. package/dist/src/tools/uninstall.js.map +1 -0
  154. package/dist/src/tools/validate.d.ts +122 -0
  155. package/dist/src/tools/validate.d.ts.map +1 -0
  156. package/dist/src/tools/validate.js +497 -0
  157. package/dist/src/tools/validate.js.map +1 -0
  158. package/dist/src/utils/installed-skills.d.ts +101 -0
  159. package/dist/src/utils/installed-skills.d.ts.map +1 -0
  160. package/dist/src/utils/installed-skills.js +220 -0
  161. package/dist/src/utils/installed-skills.js.map +1 -0
  162. package/dist/src/utils/validation.d.ts +95 -0
  163. package/dist/src/utils/validation.d.ts.map +1 -0
  164. package/dist/src/utils/validation.js +186 -0
  165. package/dist/src/utils/validation.js.map +1 -0
  166. package/dist/src/webhooks/index.d.ts +8 -0
  167. package/dist/src/webhooks/index.d.ts.map +1 -0
  168. package/dist/src/webhooks/index.js +9 -0
  169. package/dist/src/webhooks/index.js.map +1 -0
  170. package/dist/src/webhooks/webhook-endpoint.d.ts +149 -0
  171. package/dist/src/webhooks/webhook-endpoint.d.ts.map +1 -0
  172. package/dist/src/webhooks/webhook-endpoint.js +339 -0
  173. package/dist/src/webhooks/webhook-endpoint.js.map +1 -0
  174. package/dist/tests/compare.test.d.ts +6 -0
  175. package/dist/tests/compare.test.d.ts.map +1 -0
  176. package/dist/tests/compare.test.js +225 -0
  177. package/dist/tests/compare.test.js.map +1 -0
  178. package/dist/tests/context/project-detector.test.d.ts +6 -0
  179. package/dist/tests/context/project-detector.test.d.ts.map +1 -0
  180. package/dist/tests/context/project-detector.test.js +719 -0
  181. package/dist/tests/context/project-detector.test.js.map +1 -0
  182. package/dist/tests/e2e/compare.e2e.test.d.ts +10 -0
  183. package/dist/tests/e2e/compare.e2e.test.d.ts.map +1 -0
  184. package/dist/tests/e2e/compare.e2e.test.js +296 -0
  185. package/dist/tests/e2e/compare.e2e.test.js.map +1 -0
  186. package/dist/tests/e2e/install-flow.e2e.test.d.ts +10 -0
  187. package/dist/tests/e2e/install-flow.e2e.test.d.ts.map +1 -0
  188. package/dist/tests/e2e/install-flow.e2e.test.js +229 -0
  189. package/dist/tests/e2e/install-flow.e2e.test.js.map +1 -0
  190. package/dist/tests/e2e/recommend.e2e.test.d.ts +12 -0
  191. package/dist/tests/e2e/recommend.e2e.test.d.ts.map +1 -0
  192. package/dist/tests/e2e/recommend.e2e.test.js +357 -0
  193. package/dist/tests/e2e/recommend.e2e.test.js.map +1 -0
  194. package/dist/tests/e2e/skill-flow.e2e.test.d.ts +10 -0
  195. package/dist/tests/e2e/skill-flow.e2e.test.d.ts.map +1 -0
  196. package/dist/tests/e2e/skill-flow.e2e.test.js +311 -0
  197. package/dist/tests/e2e/skill-flow.e2e.test.js.map +1 -0
  198. package/dist/tests/e2e/suggest.e2e.test.d.ts +13 -0
  199. package/dist/tests/e2e/suggest.e2e.test.d.ts.map +1 -0
  200. package/dist/tests/e2e/suggest.e2e.test.js +367 -0
  201. package/dist/tests/e2e/suggest.e2e.test.js.map +1 -0
  202. package/dist/tests/e2e/utils/baseline-collector.d.ts +107 -0
  203. package/dist/tests/e2e/utils/baseline-collector.d.ts.map +1 -0
  204. package/dist/tests/e2e/utils/baseline-collector.js +211 -0
  205. package/dist/tests/e2e/utils/baseline-collector.js.map +1 -0
  206. package/dist/tests/e2e/utils/hardcoded-detector.d.ts +46 -0
  207. package/dist/tests/e2e/utils/hardcoded-detector.d.ts.map +1 -0
  208. package/dist/tests/e2e/utils/hardcoded-detector.js +255 -0
  209. package/dist/tests/e2e/utils/hardcoded-detector.js.map +1 -0
  210. package/dist/tests/e2e/utils/index.d.ts +7 -0
  211. package/dist/tests/e2e/utils/index.d.ts.map +1 -0
  212. package/dist/tests/e2e/utils/index.js +7 -0
  213. package/dist/tests/e2e/utils/index.js.map +1 -0
  214. package/dist/tests/e2e/utils/linear-reporter.d.ts +60 -0
  215. package/dist/tests/e2e/utils/linear-reporter.d.ts.map +1 -0
  216. package/dist/tests/e2e/utils/linear-reporter.js +232 -0
  217. package/dist/tests/e2e/utils/linear-reporter.js.map +1 -0
  218. package/dist/tests/health.test.d.ts +9 -0
  219. package/dist/tests/health.test.d.ts.map +1 -0
  220. package/dist/tests/health.test.js +308 -0
  221. package/dist/tests/health.test.js.map +1 -0
  222. package/dist/tests/integration/analyze.integration.test.d.ts +2 -0
  223. package/dist/tests/integration/analyze.integration.test.d.ts.map +1 -0
  224. package/dist/tests/integration/analyze.integration.test.js +244 -0
  225. package/dist/tests/integration/analyze.integration.test.js.map +1 -0
  226. package/dist/tests/integration/compare.integration.test.d.ts +2 -0
  227. package/dist/tests/integration/compare.integration.test.d.ts.map +1 -0
  228. package/dist/tests/integration/compare.integration.test.js +120 -0
  229. package/dist/tests/integration/compare.integration.test.js.map +1 -0
  230. package/dist/tests/integration/fixtures/test-skills.d.ts +62 -0
  231. package/dist/tests/integration/fixtures/test-skills.d.ts.map +1 -0
  232. package/dist/tests/integration/fixtures/test-skills.js +644 -0
  233. package/dist/tests/integration/fixtures/test-skills.js.map +1 -0
  234. package/dist/tests/integration/get-skill.integration.test.d.ts +6 -0
  235. package/dist/tests/integration/get-skill.integration.test.d.ts.map +1 -0
  236. package/dist/tests/integration/get-skill.integration.test.js +203 -0
  237. package/dist/tests/integration/get-skill.integration.test.js.map +1 -0
  238. package/dist/tests/integration/github-api.integration.test.d.ts +14 -0
  239. package/dist/tests/integration/github-api.integration.test.d.ts.map +1 -0
  240. package/dist/tests/integration/github-api.integration.test.js +190 -0
  241. package/dist/tests/integration/github-api.integration.test.js.map +1 -0
  242. package/dist/tests/integration/install.integration.test.d.ts +6 -0
  243. package/dist/tests/integration/install.integration.test.d.ts.map +1 -0
  244. package/dist/tests/integration/install.integration.test.js +282 -0
  245. package/dist/tests/integration/install.integration.test.js.map +1 -0
  246. package/dist/tests/integration/recommend.integration.test.d.ts +2 -0
  247. package/dist/tests/integration/recommend.integration.test.d.ts.map +1 -0
  248. package/dist/tests/integration/recommend.integration.test.js +217 -0
  249. package/dist/tests/integration/recommend.integration.test.js.map +1 -0
  250. package/dist/tests/integration/search.integration.test.d.ts +6 -0
  251. package/dist/tests/integration/search.integration.test.d.ts.map +1 -0
  252. package/dist/tests/integration/search.integration.test.js +229 -0
  253. package/dist/tests/integration/search.integration.test.js.map +1 -0
  254. package/dist/tests/integration/setup.d.ts +74 -0
  255. package/dist/tests/integration/setup.d.ts.map +1 -0
  256. package/dist/tests/integration/setup.js +131 -0
  257. package/dist/tests/integration/setup.js.map +1 -0
  258. package/dist/tests/integration/uninstall.integration.test.d.ts +6 -0
  259. package/dist/tests/integration/uninstall.integration.test.d.ts.map +1 -0
  260. package/dist/tests/integration/uninstall.integration.test.js +296 -0
  261. package/dist/tests/integration/uninstall.integration.test.js.map +1 -0
  262. package/dist/tests/integration/validate.integration.test.d.ts +2 -0
  263. package/dist/tests/integration/validate.integration.test.d.ts.map +1 -0
  264. package/dist/tests/integration/validate.integration.test.js +181 -0
  265. package/dist/tests/integration/validate.integration.test.js.map +1 -0
  266. package/dist/tests/onboarding/first-run.test.d.ts +7 -0
  267. package/dist/tests/onboarding/first-run.test.d.ts.map +1 -0
  268. package/dist/tests/onboarding/first-run.test.js +264 -0
  269. package/dist/tests/onboarding/first-run.test.js.map +1 -0
  270. package/dist/tests/performance/search-performance.test.d.ts +10 -0
  271. package/dist/tests/performance/search-performance.test.d.ts.map +1 -0
  272. package/dist/tests/performance/search-performance.test.js +222 -0
  273. package/dist/tests/performance/search-performance.test.js.map +1 -0
  274. package/dist/tests/recommend.test.d.ts +6 -0
  275. package/dist/tests/recommend.test.d.ts.map +1 -0
  276. package/dist/tests/recommend.test.js +210 -0
  277. package/dist/tests/recommend.test.js.map +1 -0
  278. package/dist/tests/suggestions/suggestion-engine.test.d.ts +6 -0
  279. package/dist/tests/suggestions/suggestion-engine.test.d.ts.map +1 -0
  280. package/dist/tests/suggestions/suggestion-engine.test.js +448 -0
  281. package/dist/tests/suggestions/suggestion-engine.test.js.map +1 -0
  282. package/dist/tests/test-utils.d.ts +74 -0
  283. package/dist/tests/test-utils.d.ts.map +1 -0
  284. package/dist/tests/test-utils.js +98 -0
  285. package/dist/tests/test-utils.js.map +1 -0
  286. package/dist/tests/tools.test.d.ts +5 -0
  287. package/dist/tests/tools.test.d.ts.map +1 -0
  288. package/dist/tests/tools.test.js +138 -0
  289. package/dist/tests/tools.test.js.map +1 -0
  290. package/dist/tests/unit/installed-skills.test.d.ts +6 -0
  291. package/dist/tests/unit/installed-skills.test.d.ts.map +1 -0
  292. package/dist/tests/unit/installed-skills.test.js +285 -0
  293. package/dist/tests/unit/installed-skills.test.js.map +1 -0
  294. package/dist/tests/unit/logger.test.d.ts +6 -0
  295. package/dist/tests/unit/logger.test.d.ts.map +1 -0
  296. package/dist/tests/unit/logger.test.js +281 -0
  297. package/dist/tests/unit/logger.test.js.map +1 -0
  298. package/dist/tests/validate.test.d.ts +5 -0
  299. package/dist/tests/validate.test.d.ts.map +1 -0
  300. package/dist/tests/validate.test.js +303 -0
  301. package/dist/tests/validate.test.js.map +1 -0
  302. package/dist/tests/webhooks/proxy-trust.security.test.d.ts +8 -0
  303. package/dist/tests/webhooks/proxy-trust.security.test.d.ts.map +1 -0
  304. package/dist/tests/webhooks/proxy-trust.security.test.js +145 -0
  305. package/dist/tests/webhooks/proxy-trust.security.test.js.map +1 -0
  306. package/dist/tests/webhooks/rate-limiter.security.test.d.ts +8 -0
  307. package/dist/tests/webhooks/rate-limiter.security.test.d.ts.map +1 -0
  308. package/dist/tests/webhooks/rate-limiter.security.test.js +122 -0
  309. package/dist/tests/webhooks/rate-limiter.security.test.js.map +1 -0
  310. package/dist/vitest.config.d.ts +6 -0
  311. package/dist/vitest.config.d.ts.map +1 -0
  312. package/dist/vitest.config.js +13 -0
  313. package/dist/vitest.config.js.map +1 -0
  314. package/package.json +1 -1
@@ -0,0 +1,357 @@
1
+ /**
2
+ * @fileoverview MCP Skill Recommend Tool for suggesting relevant skills
3
+ * @module @skillsmith/mcp-server/tools/recommend
4
+ * @see SMI-741: Add MCP Tool skill_recommend
5
+ * @see SMI-602: Integrate semantic matching with EmbeddingService
6
+ * @see SMI-604: Add trigger phrase overlap detection
7
+ *
8
+ * Provides skill recommendations based on:
9
+ * - Currently installed skills (semantic similarity)
10
+ * - Optional project context (semantic matching)
11
+ * - Codebase analysis (framework detection)
12
+ * - Overlap detection (avoid similar skills)
13
+ *
14
+ * @example
15
+ * // Basic recommendation
16
+ * const results = await executeRecommend({
17
+ * installed_skills: ['anthropic/commit'],
18
+ * limit: 5
19
+ * }, toolContext);
20
+ *
21
+ * @example
22
+ * // Recommendation with project context
23
+ * const results = await executeRecommend({
24
+ * installed_skills: ['anthropic/commit'],
25
+ * project_context: 'React frontend with Jest testing',
26
+ * limit: 10
27
+ * }, toolContext);
28
+ */
29
+ import { z } from 'zod';
30
+ import { SkillMatcher, OverlapDetector, trackEvent, } from '@skillsmith/core';
31
+ import { getInstalledSkills } from '../utils/installed-skills.js';
32
+ import { mapTrustTierFromDb, getTrustBadge } from '../utils/validation.js';
33
+ /**
34
+ * Zod schema for recommend tool input validation
35
+ */
36
+ export const recommendInputSchema = z.object({
37
+ /** Currently installed skill IDs */
38
+ installed_skills: z.array(z.string()).min(0).default([]),
39
+ /** Optional project description for context-aware recommendations */
40
+ project_context: z.string().optional(),
41
+ /** Maximum recommendations to return (default 5) */
42
+ limit: z.number().min(1).max(50).default(5),
43
+ /** Enable overlap detection (default true) */
44
+ detect_overlap: z.boolean().default(true),
45
+ /** Minimum similarity threshold (0-1, default 0.3) */
46
+ min_similarity: z.number().min(0).max(1).default(0.3),
47
+ });
48
+ /**
49
+ * MCP tool schema definition for skill_recommend
50
+ */
51
+ export const recommendToolSchema = {
52
+ name: 'skill_recommend',
53
+ description: 'Recommend skills based on currently installed skills and optional project context. Uses semantic similarity to find relevant skills. Auto-detects installed skills from ~/.claude/skills/ if not provided.',
54
+ inputSchema: {
55
+ type: 'object',
56
+ properties: {
57
+ installed_skills: {
58
+ type: 'array',
59
+ items: { type: 'string' },
60
+ description: 'Currently installed skill IDs (e.g., ["anthropic/commit", "community/jest-helper"]). If empty, auto-detects from ~/.claude/skills/',
61
+ },
62
+ project_context: {
63
+ type: 'string',
64
+ description: 'Optional project description for context-aware recommendations (e.g., "React frontend with Jest testing")',
65
+ },
66
+ limit: {
67
+ type: 'number',
68
+ description: 'Maximum recommendations to return (default 5, max 50)',
69
+ minimum: 1,
70
+ maximum: 50,
71
+ default: 5,
72
+ },
73
+ detect_overlap: {
74
+ type: 'boolean',
75
+ description: 'Enable overlap detection to filter similar skills (default true)',
76
+ default: true,
77
+ },
78
+ min_similarity: {
79
+ type: 'number',
80
+ description: 'Minimum similarity threshold (0-1, default 0.3)',
81
+ minimum: 0,
82
+ maximum: 1,
83
+ default: 0.3,
84
+ },
85
+ },
86
+ required: [],
87
+ },
88
+ };
89
+ /**
90
+ * Transform a database skill to SkillData format for matching
91
+ */
92
+ function transformSkillToMatchData(skill) {
93
+ // Generate trigger phrases from name and first few tags
94
+ const triggerPhrases = [
95
+ skill.name,
96
+ `use ${skill.name}`,
97
+ `${skill.name} help`,
98
+ ...skill.tags.slice(0, 3).map((tag) => `${tag} ${skill.name}`),
99
+ ];
100
+ return {
101
+ id: skill.id,
102
+ name: skill.name,
103
+ description: skill.description || '',
104
+ triggerPhrases,
105
+ keywords: skill.tags,
106
+ qualityScore: Math.round((skill.qualityScore ?? 0.5) * 100),
107
+ trustTier: mapTrustTierFromDb(skill.trustTier),
108
+ };
109
+ }
110
+ /**
111
+ * Load skills from database via ToolContext
112
+ * Returns skills transformed to SkillData format for matching
113
+ */
114
+ async function loadSkillsFromDatabase(context, limit = 500) {
115
+ const result = context.skillRepository.findAll(limit, 0);
116
+ return result.items.map(transformSkillToMatchData);
117
+ }
118
+ /**
119
+ * Execute skill recommendation based on installed skills and context.
120
+ *
121
+ * SMI-1183: Uses API as primary source with local fallback.
122
+ * - Tries live API first (api.skillsmith.app)
123
+ * - Falls back to local semantic matching if API is offline or fails
124
+ *
125
+ * @param input - Recommendation parameters
126
+ * @returns Promise resolving to recommendation response
127
+ * @throws {SkillsmithError} When validation fails
128
+ *
129
+ * @example
130
+ * const response = await executeRecommend({
131
+ * installed_skills: ['anthropic/commit'],
132
+ * project_context: 'React TypeScript frontend',
133
+ * limit: 5
134
+ * }, toolContext);
135
+ */
136
+ export async function executeRecommend(input, context) {
137
+ const startTime = performance.now();
138
+ // Validate input with Zod
139
+ const validated = recommendInputSchema.parse(input);
140
+ let { installed_skills } = validated;
141
+ const { project_context, limit, detect_overlap, min_similarity } = validated;
142
+ // SMI-906: Auto-detect installed skills from ~/.claude/skills/ if not provided
143
+ const autoDetected = installed_skills.length === 0;
144
+ if (autoDetected) {
145
+ installed_skills = await getInstalledSkills();
146
+ }
147
+ // SMI-1183: Try API first, fall back to local semantic matching
148
+ if (!context.apiClient.isOffline()) {
149
+ try {
150
+ // Build stack from installed skill names and project context keywords
151
+ const stack = [...installed_skills.map((id) => id.split('/').pop() || id)];
152
+ if (project_context) {
153
+ // Extract key terms from project context (simple word split)
154
+ const contextWords = project_context
155
+ .toLowerCase()
156
+ .split(/\s+/)
157
+ .filter((w) => w.length > 3)
158
+ .slice(0, 5);
159
+ stack.push(...contextWords);
160
+ }
161
+ const apiResponse = await context.apiClient.getRecommendations({
162
+ stack: stack.slice(0, 10), // API limits to 10 stack items
163
+ limit,
164
+ });
165
+ const endTime = performance.now();
166
+ // Convert API results to response format
167
+ const recommendations = apiResponse.data.map((skill) => ({
168
+ skill_id: skill.id,
169
+ name: skill.name,
170
+ reason: `Matches your stack: ${stack.slice(0, 3).join(', ')}`,
171
+ similarity_score: 0.8, // API doesn't return similarity score, use default
172
+ trust_tier: mapTrustTierFromDb(skill.trust_tier),
173
+ quality_score: Math.round((skill.quality_score ?? 0.5) * 100),
174
+ }));
175
+ const response = {
176
+ recommendations,
177
+ candidates_considered: recommendations.length,
178
+ overlap_filtered: 0,
179
+ context: {
180
+ installed_count: installed_skills.length,
181
+ has_project_context: !!project_context,
182
+ using_semantic_matching: true,
183
+ auto_detected: autoDetected,
184
+ },
185
+ timing: {
186
+ totalMs: Math.round(endTime - startTime),
187
+ },
188
+ };
189
+ // SMI-1184: Track recommend event (silent on failure)
190
+ if (context.distinctId) {
191
+ trackEvent(context.distinctId, 'skill_recommend', {
192
+ result_count: response.recommendations.length,
193
+ duration_ms: response.timing.totalMs,
194
+ source: 'mcp',
195
+ });
196
+ }
197
+ return response;
198
+ }
199
+ catch (error) {
200
+ // Log and fall through to local semantic matching
201
+ console.warn('[skillsmith] API recommend failed, using local matching:', error.message);
202
+ }
203
+ }
204
+ // Fallback: Load skills from database and use local semantic matching
205
+ // Use 500 as default to balance coverage vs performance
206
+ const skillDatabase = await loadSkillsFromDatabase(context, 500);
207
+ // Initialize matcher with fallback mode for now (real embeddings in production)
208
+ const matcher = new SkillMatcher({
209
+ useFallback: true, // Use mock embeddings for consistent behavior
210
+ minSimilarity: min_similarity,
211
+ qualityWeight: 0.3,
212
+ });
213
+ // Get installed skill data
214
+ const installedSkillData = skillDatabase.filter((s) => installed_skills.some((id) => id.toLowerCase() === s.id.toLowerCase()));
215
+ // SMI-907: Extract installed skill names for name-based overlap detection
216
+ // This filters skills with semantically similar names (e.g., "docker" filters "docker-compose")
217
+ const installedNames = installed_skills.map((id) => {
218
+ // Extract the skill name from the ID (e.g., "community/docker" -> "docker")
219
+ const idName = id.split('/').pop()?.toLowerCase() || '';
220
+ // Also check if any installed skill data has a matching name
221
+ const skillData = installedSkillData.find((s) => s.id.toLowerCase() === id.toLowerCase());
222
+ return {
223
+ idName,
224
+ skillName: skillData?.name.toLowerCase() || idName,
225
+ };
226
+ });
227
+ // Filter out already installed skills AND semantically similar names from candidates
228
+ const candidates = skillDatabase.filter((s) => {
229
+ const skillName = s.name.toLowerCase();
230
+ const skillIdName = s.id.split('/').pop()?.toLowerCase() || '';
231
+ // Exclude if exact ID match (case-insensitive)
232
+ if (installed_skills.some((id) => id.toLowerCase() === s.id.toLowerCase())) {
233
+ return false;
234
+ }
235
+ // SMI-907: Exclude if name is contained in or contains installed skill name
236
+ // This prevents recommending "docker-compose" when "docker" is installed
237
+ for (const installed of installedNames) {
238
+ const { idName, skillName: installedSkillName } = installed;
239
+ if (!idName && !installedSkillName)
240
+ continue;
241
+ // Check name containment both ways
242
+ if ((installedSkillName && skillName.includes(installedSkillName)) ||
243
+ (installedSkillName && installedSkillName.includes(skillName)) ||
244
+ (idName && skillIdName.includes(idName)) ||
245
+ (idName && idName.includes(skillIdName))) {
246
+ return false;
247
+ }
248
+ }
249
+ return true;
250
+ });
251
+ let overlapFiltered = 0;
252
+ // Apply overlap detection if enabled and there are installed skills
253
+ let filteredCandidates = candidates;
254
+ if (detect_overlap && installedSkillData.length > 0) {
255
+ const overlapDetector = new OverlapDetector({
256
+ useFallback: true,
257
+ overlapThreshold: 0.6,
258
+ phraseThreshold: 0.75,
259
+ });
260
+ const filterResult = await overlapDetector.filterByOverlap(candidates, installedSkillData);
261
+ filteredCandidates = filterResult.accepted;
262
+ overlapFiltered = filterResult.rejected.length;
263
+ overlapDetector.close();
264
+ }
265
+ // Build query from installed skills and project context
266
+ let query = '';
267
+ if (installedSkillData.length > 0) {
268
+ query = installedSkillData
269
+ .map((s) => `${s.name} ${s.description} ${s.keywords?.join(' ') || ''}`)
270
+ .join(' ');
271
+ }
272
+ if (project_context) {
273
+ query = query ? `${query} ${project_context}` : project_context;
274
+ }
275
+ if (!query) {
276
+ query = 'general development productivity tools';
277
+ }
278
+ // Find similar skills using semantic matching
279
+ const matchResults = await matcher.findSimilarSkills(query, filteredCandidates, limit);
280
+ // Transform to response format
281
+ const recommendations = matchResults.map((result) => {
282
+ const skill = result.skill;
283
+ return {
284
+ skill_id: skill.id,
285
+ name: skill.name,
286
+ reason: result.matchReason,
287
+ similarity_score: result.similarityScore,
288
+ trust_tier: skill.trustTier,
289
+ quality_score: skill.qualityScore ?? 50,
290
+ };
291
+ });
292
+ const endTime = performance.now();
293
+ matcher.close();
294
+ const response = {
295
+ recommendations,
296
+ candidates_considered: candidates.length,
297
+ overlap_filtered: overlapFiltered,
298
+ context: {
299
+ installed_count: installed_skills.length,
300
+ has_project_context: !!project_context,
301
+ using_semantic_matching: true,
302
+ auto_detected: autoDetected,
303
+ },
304
+ timing: {
305
+ totalMs: Math.round(endTime - startTime),
306
+ },
307
+ };
308
+ // SMI-1184: Track recommend event (silent on failure)
309
+ if (context.distinctId) {
310
+ trackEvent(context.distinctId, 'skill_recommend', {
311
+ result_count: response.recommendations.length,
312
+ duration_ms: response.timing.totalMs,
313
+ source: 'mcp',
314
+ });
315
+ }
316
+ return response;
317
+ }
318
+ /**
319
+ * Format recommendations for terminal display
320
+ */
321
+ export function formatRecommendations(response) {
322
+ const lines = [];
323
+ lines.push('\n=== Skill Recommendations ===\n');
324
+ if (response.recommendations.length === 0) {
325
+ lines.push('No recommendations found.');
326
+ lines.push('');
327
+ lines.push('Suggestions:');
328
+ lines.push(' - Try adding more installed skills for better matching');
329
+ lines.push(' - Provide a project context for more relevant results');
330
+ }
331
+ else {
332
+ lines.push(`Found ${response.recommendations.length} recommendation(s):\n`);
333
+ response.recommendations.forEach((rec, index) => {
334
+ const trustBadge = getTrustBadge(rec.trust_tier);
335
+ lines.push(`${index + 1}. ${rec.name} ${trustBadge}`);
336
+ lines.push(` Score: ${rec.quality_score}/100 | Relevance: ${Math.round(rec.similarity_score * 100)}%`);
337
+ lines.push(` ${rec.reason}`);
338
+ lines.push(` ID: ${rec.skill_id}`);
339
+ lines.push('');
340
+ });
341
+ }
342
+ lines.push('---');
343
+ lines.push(`Candidates considered: ${response.candidates_considered}`);
344
+ if (response.overlap_filtered > 0) {
345
+ lines.push(`Filtered for overlap: ${response.overlap_filtered}`);
346
+ }
347
+ if (response.context.auto_detected) {
348
+ lines.push(`Installed skills: ${response.context.installed_count} (auto-detected from ~/.claude/skills/)`);
349
+ }
350
+ else {
351
+ lines.push(`Installed skills: ${response.context.installed_count}`);
352
+ }
353
+ lines.push(`Semantic matching: ${response.context.using_semantic_matching ? 'enabled' : 'disabled'}`);
354
+ lines.push(`Completed in ${response.timing.totalMs}ms`);
355
+ return lines.join('\n');
356
+ }
357
+ //# sourceMappingURL=recommend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommend.js","sourceRoot":"","sources":["../../../src/tools/recommend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @fileoverview MCP Search Tool for Claude Code skill discovery
3
+ * @module @skillsmith/mcp-server/tools/search
4
+ * @see {@link https://github.com/wrsmith108/skillsmith|Skillsmith Repository}
5
+ * @see SMI-789: Wire search tool to SearchService
6
+ *
7
+ * Provides skill search functionality with support for:
8
+ * - Full-text search across skill names, descriptions, and authors
9
+ * - Category filtering (development, testing, devops, etc.)
10
+ * - Trust tier filtering (verified, community, experimental, unknown)
11
+ * - Minimum quality score filtering
12
+ *
13
+ * @example
14
+ * // Basic search with context
15
+ * const results = await executeSearch({ query: 'commit' }, context);
16
+ *
17
+ * @example
18
+ * // Search with filters
19
+ * const results = await executeSearch({
20
+ * query: 'test',
21
+ * category: 'testing',
22
+ * trust_tier: 'verified',
23
+ * min_score: 80
24
+ * }, context);
25
+ */
26
+ import { type MCPSearchResponse as SearchResponse } from '@skillsmith/core';
27
+ import type { ToolContext } from '../context.js';
28
+ /**
29
+ * Search tool schema for MCP
30
+ */
31
+ export declare const searchToolSchema: {
32
+ name: string;
33
+ description: string;
34
+ inputSchema: {
35
+ type: "object";
36
+ properties: {
37
+ query: {
38
+ type: string;
39
+ description: string;
40
+ };
41
+ category: {
42
+ type: string;
43
+ description: string;
44
+ enum: string[];
45
+ };
46
+ trust_tier: {
47
+ type: string;
48
+ description: string;
49
+ enum: string[];
50
+ };
51
+ min_score: {
52
+ type: string;
53
+ description: string;
54
+ minimum: number;
55
+ maximum: number;
56
+ };
57
+ };
58
+ required: string[];
59
+ };
60
+ };
61
+ /**
62
+ * Input parameters for the search operation
63
+ * @interface SearchInput
64
+ */
65
+ export interface SearchInput {
66
+ /** Search query string (minimum 2 characters) */
67
+ query: string;
68
+ /** Filter by skill category */
69
+ category?: string;
70
+ /** Filter by trust tier level */
71
+ trust_tier?: string;
72
+ /** Minimum quality score (0-100) */
73
+ min_score?: number;
74
+ }
75
+ /**
76
+ * Execute a search for Claude Code skills with optional filters.
77
+ *
78
+ * SMI-1183: Uses API as primary source with local DB fallback.
79
+ * - Tries live API first (api.skillsmith.app)
80
+ * - Falls back to local SearchService if API is offline or fails
81
+ *
82
+ * @param input - Search parameters including query and optional filters
83
+ * @param context - Tool context with API client and local services
84
+ * @returns Promise resolving to search response with results and timing
85
+ * @throws {SkillsmithError} When query is empty or less than 2 characters
86
+ * @throws {SkillsmithError} When min_score is outside 0-100 range
87
+ *
88
+ * @example
89
+ * // Search for commit-related skills
90
+ * const response = await executeSearch({ query: 'commit' }, context);
91
+ * console.log(`Found ${response.total} skills in ${response.timing.totalMs}ms`);
92
+ */
93
+ export declare function executeSearch(input: SearchInput, context: ToolContext): Promise<SearchResponse>;
94
+ /**
95
+ * Format search results for terminal/CLI display.
96
+ *
97
+ * Produces a human-readable string with skill listings including
98
+ * trust badges, scores, and timing information.
99
+ *
100
+ * @param response - Search response from executeSearch
101
+ * @returns Formatted string suitable for terminal output
102
+ *
103
+ * @example
104
+ * const response = await executeSearch({ query: 'test' });
105
+ * console.log(formatSearchResults(response));
106
+ * // Output:
107
+ * // === Search Results for "test" ===
108
+ * // Found 3 skill(s):
109
+ * // 1. jest-helper [COMMUNITY]
110
+ * // Author: community | Score: 87/100
111
+ * // Generate Jest test cases...
112
+ */
113
+ export declare function formatSearchResults(response: SearchResponse): string;
114
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/tools/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAGL,KAAK,iBAAiB,IAAI,cAAc,EAMzC,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAQhD;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwC5B,CAAA;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CA4IzB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAgCpE"}