webmcp-cli 1.2.2 → 1.2.3

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 (330) hide show
  1. package/dist/analysis/form-to-tool-mapper.d.ts +61 -0
  2. package/dist/analysis/form-to-tool-mapper.js +360 -0
  3. package/dist/analysis/form-to-tool-mapper.js.map +1 -0
  4. package/dist/analysis/index.d.ts +84 -0
  5. package/dist/analysis/index.js +81 -0
  6. package/dist/analysis/index.js.map +1 -0
  7. package/dist/analysis/missing-tool-analyzer.d.ts +35 -0
  8. package/dist/analysis/missing-tool-analyzer.js +617 -0
  9. package/dist/analysis/missing-tool-analyzer.js.map +1 -0
  10. package/dist/audit/run-multi-page-audit.d.ts +34 -0
  11. package/dist/audit/run-multi-page-audit.js +233 -0
  12. package/dist/audit/run-multi-page-audit.js.map +1 -0
  13. package/dist/cli/commands/potential.d.ts +8 -0
  14. package/dist/cli/commands/potential.js +323 -0
  15. package/dist/cli/commands/potential.js.map +1 -0
  16. package/dist/cli/commands/report.d.ts +12 -0
  17. package/dist/cli/commands/report.js +89 -0
  18. package/dist/cli/commands/report.js.map +1 -0
  19. package/dist/cli/index.js +35 -0
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/config/defaults.d.ts +36 -0
  22. package/dist/config/defaults.js +33 -0
  23. package/dist/config/defaults.js.map +1 -0
  24. package/dist/config/index.d.ts +7 -0
  25. package/dist/config/index.js +7 -0
  26. package/dist/config/index.js.map +1 -0
  27. package/dist/config/loader.d.ts +22 -0
  28. package/dist/config/loader.js +91 -0
  29. package/dist/config/loader.js.map +1 -0
  30. package/dist/config/schema.d.ts +280 -0
  31. package/dist/config/schema.js +42 -0
  32. package/dist/config/schema.js.map +1 -0
  33. package/dist/core/types/audit.d.ts +1 -1
  34. package/dist/core/types/index.d.ts +1 -0
  35. package/dist/core/types/index.js +1 -0
  36. package/dist/core/types/index.js.map +1 -1
  37. package/dist/core/types/recon.d.ts +265 -0
  38. package/dist/core/types/recon.js +5 -0
  39. package/dist/core/types/recon.js.map +1 -0
  40. package/dist/core/types/rule.d.ts +1 -1
  41. package/dist/core/types/rule.js +7 -5
  42. package/dist/core/types/rule.js.map +1 -1
  43. package/dist/crawler/depth-crawler.d.ts +29 -0
  44. package/dist/crawler/depth-crawler.js +212 -0
  45. package/dist/crawler/depth-crawler.js.map +1 -0
  46. package/dist/crawler/index.d.ts +2 -0
  47. package/dist/crawler/index.js +3 -0
  48. package/dist/crawler/index.js.map +1 -0
  49. package/dist/crawler/link-extractor.d.ts +1 -0
  50. package/dist/crawler/link-extractor.js +49 -0
  51. package/dist/crawler/link-extractor.js.map +1 -0
  52. package/dist/generators/index.d.ts +10 -0
  53. package/dist/generators/index.js +8 -0
  54. package/dist/generators/index.js.map +1 -0
  55. package/dist/generators/report-html.d.ts +12 -0
  56. package/dist/generators/report-html.js +470 -0
  57. package/dist/generators/report-html.js.map +1 -0
  58. package/dist/generators/report-json.d.ts +95 -0
  59. package/dist/generators/report-json.js +144 -0
  60. package/dist/generators/report-json.js.map +1 -0
  61. package/dist/generators/report-manager.d.ts +31 -0
  62. package/dist/generators/report-manager.js +208 -0
  63. package/dist/generators/report-manager.js.map +1 -0
  64. package/dist/generators/tool-code-generator.d.ts +31 -0
  65. package/dist/generators/tool-code-generator.js +201 -0
  66. package/dist/generators/tool-code-generator.js.map +1 -0
  67. package/dist/potential/ai-recommender.d.ts +33 -0
  68. package/dist/potential/ai-recommender.js +414 -0
  69. package/dist/potential/ai-recommender.js.map +1 -0
  70. package/dist/potential/analyzer.d.ts +32 -0
  71. package/dist/potential/analyzer.js +383 -0
  72. package/dist/potential/analyzer.js.map +1 -0
  73. package/dist/potential/index.d.ts +3 -0
  74. package/dist/potential/index.js +4 -0
  75. package/dist/potential/index.js.map +1 -0
  76. package/dist/potential/prompts.d.ts +20 -0
  77. package/dist/potential/prompts.js +42 -0
  78. package/dist/potential/prompts.js.map +1 -0
  79. package/dist/potential/types.d.ts +40 -0
  80. package/dist/potential/types.js +2 -0
  81. package/dist/potential/types.js.map +1 -0
  82. package/dist/recon/index.d.ts +20 -0
  83. package/dist/recon/index.js +143 -0
  84. package/dist/recon/index.js.map +1 -0
  85. package/dist/recon/manifest.d.ts +16 -0
  86. package/dist/recon/manifest.js +108 -0
  87. package/dist/recon/manifest.js.map +1 -0
  88. package/dist/recon/meta-extractor.d.ts +11 -0
  89. package/dist/recon/meta-extractor.js +276 -0
  90. package/dist/recon/meta-extractor.js.map +1 -0
  91. package/dist/recon/robots.d.ts +16 -0
  92. package/dist/recon/robots.js +158 -0
  93. package/dist/recon/robots.js.map +1 -0
  94. package/dist/recon/route-discovery.d.ts +25 -0
  95. package/dist/recon/route-discovery.js +303 -0
  96. package/dist/recon/route-discovery.js.map +1 -0
  97. package/dist/recon/sitemap.d.ts +12 -0
  98. package/dist/recon/sitemap.js +177 -0
  99. package/dist/recon/sitemap.js.map +1 -0
  100. package/dist/rules/accessibility/AXE-001.d.ts +9 -0
  101. package/dist/rules/accessibility/AXE-001.js +109 -0
  102. package/dist/rules/accessibility/AXE-001.js.map +1 -0
  103. package/dist/rules/accessibility/AXE-002.d.ts +8 -0
  104. package/dist/rules/accessibility/AXE-002.js +85 -0
  105. package/dist/rules/accessibility/AXE-002.js.map +1 -0
  106. package/dist/rules/accessibility/AXE-003.d.ts +8 -0
  107. package/dist/rules/accessibility/AXE-003.js +94 -0
  108. package/dist/rules/accessibility/AXE-003.js.map +1 -0
  109. package/dist/rules/accessibility/AXE-004.d.ts +8 -0
  110. package/dist/rules/accessibility/AXE-004.js +101 -0
  111. package/dist/rules/accessibility/AXE-004.js.map +1 -0
  112. package/dist/rules/accessibility/AXE-005.d.ts +9 -0
  113. package/dist/rules/accessibility/AXE-005.js +89 -0
  114. package/dist/rules/accessibility/AXE-005.js.map +1 -0
  115. package/dist/rules/best-practices/BP-004.d.ts +9 -0
  116. package/dist/rules/best-practices/BP-004.js +96 -0
  117. package/dist/rules/best-practices/BP-004.js.map +1 -0
  118. package/dist/rules/best-practices/BP-005.d.ts +8 -0
  119. package/dist/rules/best-practices/BP-005.js +94 -0
  120. package/dist/rules/best-practices/BP-005.js.map +1 -0
  121. package/dist/rules/best-practices/BP-006.d.ts +8 -0
  122. package/dist/rules/best-practices/BP-006.js +80 -0
  123. package/dist/rules/best-practices/BP-006.js.map +1 -0
  124. package/dist/rules/best-practices/BP-007.d.ts +8 -0
  125. package/dist/rules/best-practices/BP-007.js +92 -0
  126. package/dist/rules/best-practices/BP-007.js.map +1 -0
  127. package/dist/rules/best-practices/BP-008.d.ts +12 -0
  128. package/dist/rules/best-practices/BP-008.js +86 -0
  129. package/dist/rules/best-practices/BP-008.js.map +1 -0
  130. package/dist/rules/best-practices/BP-009.d.ts +9 -0
  131. package/dist/rules/best-practices/BP-009.js +77 -0
  132. package/dist/rules/best-practices/BP-009.js.map +1 -0
  133. package/dist/rules/best-practices/BP-010.d.ts +8 -0
  134. package/dist/rules/best-practices/BP-010.js +85 -0
  135. package/dist/rules/best-practices/BP-010.js.map +1 -0
  136. package/dist/rules/coverage/COV-002.d.ts +8 -0
  137. package/dist/rules/coverage/COV-002.js +68 -0
  138. package/dist/rules/coverage/COV-002.js.map +1 -0
  139. package/dist/rules/coverage/COV-003.d.ts +8 -0
  140. package/dist/rules/coverage/COV-003.js +68 -0
  141. package/dist/rules/coverage/COV-003.js.map +1 -0
  142. package/dist/rules/coverage/COV-004.d.ts +8 -0
  143. package/dist/rules/coverage/COV-004.js +89 -0
  144. package/dist/rules/coverage/COV-004.js.map +1 -0
  145. package/dist/rules/coverage/COV-005.d.ts +8 -0
  146. package/dist/rules/coverage/COV-005.js +67 -0
  147. package/dist/rules/coverage/COV-005.js.map +1 -0
  148. package/dist/rules/coverage/COV-006.d.ts +9 -0
  149. package/dist/rules/coverage/COV-006.js +76 -0
  150. package/dist/rules/coverage/COV-006.js.map +1 -0
  151. package/dist/rules/coverage/COV-007.d.ts +8 -0
  152. package/dist/rules/coverage/COV-007.js +67 -0
  153. package/dist/rules/coverage/COV-007.js.map +1 -0
  154. package/dist/rules/coverage/COV-008.d.ts +9 -0
  155. package/dist/rules/coverage/COV-008.js +87 -0
  156. package/dist/rules/coverage/COV-008.js.map +1 -0
  157. package/dist/rules/coverage/COV-009.d.ts +8 -0
  158. package/dist/rules/coverage/COV-009.js +73 -0
  159. package/dist/rules/coverage/COV-009.js.map +1 -0
  160. package/dist/rules/coverage/COV-010.d.ts +9 -0
  161. package/dist/rules/coverage/COV-010.js +82 -0
  162. package/dist/rules/coverage/COV-010.js.map +1 -0
  163. package/dist/rules/description/DESC-001.d.ts +9 -0
  164. package/dist/rules/description/DESC-001.js +88 -0
  165. package/dist/rules/description/DESC-001.js.map +1 -0
  166. package/dist/rules/description/DESC-002.d.ts +10 -0
  167. package/dist/rules/description/DESC-002.js +99 -0
  168. package/dist/rules/description/DESC-002.js.map +1 -0
  169. package/dist/rules/description/DESC-006.d.ts +9 -0
  170. package/dist/rules/description/DESC-006.js +78 -0
  171. package/dist/rules/description/DESC-006.js.map +1 -0
  172. package/dist/rules/description/DESC-007.d.ts +9 -0
  173. package/dist/rules/description/DESC-007.js +70 -0
  174. package/dist/rules/description/DESC-007.js.map +1 -0
  175. package/dist/rules/description/DESC-008.d.ts +9 -0
  176. package/dist/rules/description/DESC-008.js +70 -0
  177. package/dist/rules/description/DESC-008.js.map +1 -0
  178. package/dist/rules/description/DESC-009.d.ts +8 -0
  179. package/dist/rules/description/DESC-009.js +55 -0
  180. package/dist/rules/description/DESC-009.js.map +1 -0
  181. package/dist/rules/description/DESC-010.d.ts +9 -0
  182. package/dist/rules/description/DESC-010.js +92 -0
  183. package/dist/rules/description/DESC-010.js.map +1 -0
  184. package/dist/rules/description/DESC-011.d.ts +9 -0
  185. package/dist/rules/description/DESC-011.js +81 -0
  186. package/dist/rules/description/DESC-011.js.map +1 -0
  187. package/dist/rules/description/DESC-012.d.ts +9 -0
  188. package/dist/rules/description/DESC-012.js +98 -0
  189. package/dist/rules/description/DESC-012.js.map +1 -0
  190. package/dist/rules/implementation/IMP-002.d.ts +9 -0
  191. package/dist/rules/implementation/IMP-002.js +59 -0
  192. package/dist/rules/implementation/IMP-002.js.map +1 -0
  193. package/dist/rules/implementation/IMP-006.d.ts +9 -0
  194. package/dist/rules/implementation/IMP-006.js +48 -0
  195. package/dist/rules/implementation/IMP-006.js.map +1 -0
  196. package/dist/rules/implementation/IMP-008.d.ts +9 -0
  197. package/dist/rules/implementation/IMP-008.js +46 -0
  198. package/dist/rules/implementation/IMP-008.js.map +1 -0
  199. package/dist/rules/implementation/IMP-009.d.ts +9 -0
  200. package/dist/rules/implementation/IMP-009.js +48 -0
  201. package/dist/rules/implementation/IMP-009.js.map +1 -0
  202. package/dist/rules/implementation/IMP-010.d.ts +9 -0
  203. package/dist/rules/implementation/IMP-010.js +66 -0
  204. package/dist/rules/implementation/IMP-010.js.map +1 -0
  205. package/dist/rules/implementation/IMP-011.d.ts +9 -0
  206. package/dist/rules/implementation/IMP-011.js +82 -0
  207. package/dist/rules/implementation/IMP-011.js.map +1 -0
  208. package/dist/rules/implementation/IMP-012.d.ts +9 -0
  209. package/dist/rules/implementation/IMP-012.js +88 -0
  210. package/dist/rules/implementation/IMP-012.js.map +1 -0
  211. package/dist/rules/implementation/IMP-014.d.ts +9 -0
  212. package/dist/rules/implementation/IMP-014.js +58 -0
  213. package/dist/rules/implementation/IMP-014.js.map +1 -0
  214. package/dist/rules/implementation/IMP-015.d.ts +9 -0
  215. package/dist/rules/implementation/IMP-015.js +64 -0
  216. package/dist/rules/implementation/IMP-015.js.map +1 -0
  217. package/dist/rules/implementation/IMP-016.d.ts +9 -0
  218. package/dist/rules/implementation/IMP-016.js +52 -0
  219. package/dist/rules/implementation/IMP-016.js.map +1 -0
  220. package/dist/rules/implementation/IMP-017.d.ts +8 -0
  221. package/dist/rules/implementation/IMP-017.js +51 -0
  222. package/dist/rules/implementation/IMP-017.js.map +1 -0
  223. package/dist/rules/implementation/IMP-018.d.ts +8 -0
  224. package/dist/rules/implementation/IMP-018.js +52 -0
  225. package/dist/rules/implementation/IMP-018.js.map +1 -0
  226. package/dist/rules/implementation/IMP-019.d.ts +8 -0
  227. package/dist/rules/implementation/IMP-019.js +53 -0
  228. package/dist/rules/implementation/IMP-019.js.map +1 -0
  229. package/dist/rules/implementation/IMP-020.d.ts +9 -0
  230. package/dist/rules/implementation/IMP-020.js +62 -0
  231. package/dist/rules/implementation/IMP-020.js.map +1 -0
  232. package/dist/rules/implementation/IMP-021.d.ts +8 -0
  233. package/dist/rules/implementation/IMP-021.js +64 -0
  234. package/dist/rules/implementation/IMP-021.js.map +1 -0
  235. package/dist/rules/implementation/IMP-022.d.ts +8 -0
  236. package/dist/rules/implementation/IMP-022.js +70 -0
  237. package/dist/rules/implementation/IMP-022.js.map +1 -0
  238. package/dist/rules/index.d.ts +73 -6
  239. package/dist/rules/index.js +141 -6
  240. package/dist/rules/index.js.map +1 -1
  241. package/dist/rules/schema/SCHEMA-004.d.ts +9 -0
  242. package/dist/rules/schema/SCHEMA-004.js +57 -0
  243. package/dist/rules/schema/SCHEMA-004.js.map +1 -0
  244. package/dist/rules/schema/SCHEMA-005.d.ts +9 -0
  245. package/dist/rules/schema/SCHEMA-005.js +61 -0
  246. package/dist/rules/schema/SCHEMA-005.js.map +1 -0
  247. package/dist/rules/schema/SCHEMA-006.d.ts +10 -0
  248. package/dist/rules/schema/SCHEMA-006.js +85 -0
  249. package/dist/rules/schema/SCHEMA-006.js.map +1 -0
  250. package/dist/rules/schema/SCHEMA-007.d.ts +9 -0
  251. package/dist/rules/schema/SCHEMA-007.js +73 -0
  252. package/dist/rules/schema/SCHEMA-007.js.map +1 -0
  253. package/dist/rules/schema/SCHEMA-008.d.ts +9 -0
  254. package/dist/rules/schema/SCHEMA-008.js +70 -0
  255. package/dist/rules/schema/SCHEMA-008.js.map +1 -0
  256. package/dist/rules/schema/SCHEMA-009.d.ts +10 -0
  257. package/dist/rules/schema/SCHEMA-009.js +80 -0
  258. package/dist/rules/schema/SCHEMA-009.js.map +1 -0
  259. package/dist/rules/schema/SCHEMA-010.d.ts +9 -0
  260. package/dist/rules/schema/SCHEMA-010.js +96 -0
  261. package/dist/rules/schema/SCHEMA-010.js.map +1 -0
  262. package/dist/rules/schema/SCHEMA-012.d.ts +9 -0
  263. package/dist/rules/schema/SCHEMA-012.js +65 -0
  264. package/dist/rules/schema/SCHEMA-012.js.map +1 -0
  265. package/dist/rules/security/SEC-002.d.ts +8 -0
  266. package/dist/rules/security/SEC-002.js +81 -0
  267. package/dist/rules/security/SEC-002.js.map +1 -0
  268. package/dist/rules/security/SEC-003.d.ts +8 -0
  269. package/dist/rules/security/SEC-003.js +85 -0
  270. package/dist/rules/security/SEC-003.js.map +1 -0
  271. package/dist/rules/security/SEC-004.d.ts +9 -0
  272. package/dist/rules/security/SEC-004.js +87 -0
  273. package/dist/rules/security/SEC-004.js.map +1 -0
  274. package/dist/rules/security/SEC-005.d.ts +8 -0
  275. package/dist/rules/security/SEC-005.js +87 -0
  276. package/dist/rules/security/SEC-005.js.map +1 -0
  277. package/dist/rules/security/SEC-006.d.ts +10 -0
  278. package/dist/rules/security/SEC-006.js +108 -0
  279. package/dist/rules/security/SEC-006.js.map +1 -0
  280. package/dist/rules/security/SEC-007.d.ts +9 -0
  281. package/dist/rules/security/SEC-007.js +108 -0
  282. package/dist/rules/security/SEC-007.js.map +1 -0
  283. package/dist/rules/security/SEC-008.d.ts +8 -0
  284. package/dist/rules/security/SEC-008.js +109 -0
  285. package/dist/rules/security/SEC-008.js.map +1 -0
  286. package/dist/rules/security/SEC-009.d.ts +9 -0
  287. package/dist/rules/security/SEC-009.js +93 -0
  288. package/dist/rules/security/SEC-009.js.map +1 -0
  289. package/dist/rules/security/SEC-010.d.ts +8 -0
  290. package/dist/rules/security/SEC-010.js +78 -0
  291. package/dist/rules/security/SEC-010.js.map +1 -0
  292. package/dist/rules/security/SEC-011.d.ts +8 -0
  293. package/dist/rules/security/SEC-011.js +93 -0
  294. package/dist/rules/security/SEC-011.js.map +1 -0
  295. package/dist/rules/security/SEC-012.d.ts +8 -0
  296. package/dist/rules/security/SEC-012.js +79 -0
  297. package/dist/rules/security/SEC-012.js.map +1 -0
  298. package/dist/rules/security/SEC-013.d.ts +9 -0
  299. package/dist/rules/security/SEC-013.js +107 -0
  300. package/dist/rules/security/SEC-013.js.map +1 -0
  301. package/dist/scoring/calculator.js +1 -0
  302. package/dist/scoring/calculator.js.map +1 -1
  303. package/dist/ui/ink/components/AIRecommendationCard.d.ts +11 -0
  304. package/dist/ui/ink/components/AIRecommendationCard.js +23 -0
  305. package/dist/ui/ink/components/AIRecommendationCard.js.map +1 -0
  306. package/dist/ui/ink/components/OpportunityList.d.ts +10 -0
  307. package/dist/ui/ink/components/OpportunityList.js +48 -0
  308. package/dist/ui/ink/components/OpportunityList.js.map +1 -0
  309. package/dist/ui/ink/components/PotentialPageCard.d.ts +13 -0
  310. package/dist/ui/ink/components/PotentialPageCard.js +43 -0
  311. package/dist/ui/ink/components/PotentialPageCard.js.map +1 -0
  312. package/dist/ui/ink/components/PotentialProgress.d.ts +16 -0
  313. package/dist/ui/ink/components/PotentialProgress.js +44 -0
  314. package/dist/ui/ink/components/PotentialProgress.js.map +1 -0
  315. package/dist/ui/ink/components/PotentialSummary.d.ts +10 -0
  316. package/dist/ui/ink/components/PotentialSummary.js +86 -0
  317. package/dist/ui/ink/components/PotentialSummary.js.map +1 -0
  318. package/dist/ui/ink/components/SuggestionCard.d.ts +34 -0
  319. package/dist/ui/ink/components/SuggestionCard.js +36 -0
  320. package/dist/ui/ink/components/SuggestionCard.js.map +1 -0
  321. package/dist/ui/ink/components/views/MultiPageCrawlView.d.ts +21 -0
  322. package/dist/ui/ink/components/views/MultiPageCrawlView.js +55 -0
  323. package/dist/ui/ink/components/views/MultiPageCrawlView.js.map +1 -0
  324. package/dist/ui/ink/components/views/PotentialView.d.ts +18 -0
  325. package/dist/ui/ink/components/views/PotentialView.js +74 -0
  326. package/dist/ui/ink/components/views/PotentialView.js.map +1 -0
  327. package/dist/ui/ink/components/views/ReconView.d.ts +22 -0
  328. package/dist/ui/ink/components/views/ReconView.js +30 -0
  329. package/dist/ui/ink/components/views/ReconView.js.map +1 -0
  330. package/package.json +2 -1
@@ -0,0 +1,77 @@
1
+ /**
2
+ * BP-009: No Orphaned Tools
3
+ *
4
+ * Checks that all registered tools are reachable through some UI path.
5
+ * An orphaned tool is one that exists in the MCP registry but has no
6
+ * corresponding UI element on the page.
7
+ */
8
+ import { createRuleResult } from '../runner.js';
9
+ export const BP_009 = {
10
+ id: 'BP-009',
11
+ category: 'best-practices',
12
+ name: 'No Orphaned Tools',
13
+ description: 'All registered tools should be reachable through some UI path',
14
+ severity: 'info',
15
+ maxScore: 5,
16
+ async check(context) {
17
+ const tools = context.tools;
18
+ if (tools.length === 0) {
19
+ return createRuleResult('BP-009', 5, {
20
+ passed: true,
21
+ score: 5,
22
+ message: 'No tools detected (rule not applicable)',
23
+ });
24
+ }
25
+ const html = context.html ?? '';
26
+ if (html.length === 0) {
27
+ return createRuleResult('BP-009', 5, {
28
+ passed: true,
29
+ score: 5,
30
+ message: 'No HTML available for analysis',
31
+ });
32
+ }
33
+ const orphaned = [];
34
+ const htmlLower = html.toLowerCase();
35
+ for (const tool of tools) {
36
+ // Declarative tools are always tied to a form
37
+ if (tool.source === 'declarative')
38
+ continue;
39
+ const toolName = tool.name.toLowerCase();
40
+ // Convert snake_case to words for search
41
+ const toolWords = toolName.replace(/[_-]/g, ' ').split(/\s+/);
42
+ // Search for any reference in the HTML to this tool's name/words
43
+ const hasUIReference = htmlLower.includes(toolName) ||
44
+ htmlLower.includes(toolName.replace(/_/g, '-')) ||
45
+ toolWords.some((word) => word.length > 3 &&
46
+ (htmlLower.includes(`id="${word}`) ||
47
+ htmlLower.includes(`class="${word}`) ||
48
+ htmlLower.includes(`data-action="${word}`) ||
49
+ htmlLower.includes(`aria-label="${word}`)));
50
+ if (!hasUIReference) {
51
+ orphaned.push(tool.name);
52
+ }
53
+ }
54
+ if (orphaned.length === 0) {
55
+ return createRuleResult('BP-009', 5, {
56
+ passed: true,
57
+ score: 5,
58
+ message: 'All imperative tools have corresponding UI elements',
59
+ });
60
+ }
61
+ const penalty = Math.min(orphaned.length, 5);
62
+ const score = Math.max(0, 5 - penalty);
63
+ return createRuleResult('BP-009', 5, {
64
+ passed: false,
65
+ score,
66
+ message: `${orphaned.length} tool(s) appear orphaned (no matching UI element)`,
67
+ details: orphaned.map((name) => `Tool "${name}" has no detectable corresponding UI element on the page`),
68
+ suggestions: [
69
+ 'Ensure each tool corresponds to a visible UI element or action',
70
+ 'Remove tools that no longer have a UI counterpart',
71
+ 'Tools without UI elements confuse agents about available actions',
72
+ ],
73
+ affectedTools: orphaned,
74
+ });
75
+ },
76
+ };
77
+ //# sourceMappingURL=BP-009.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BP-009.js","sourceRoot":"","sources":["../../../src/rules/best-practices/BP-009.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,CAAC,MAAM,MAAM,GAAS;IAC1B,EAAE,EAAE,QAAQ;IACZ,QAAQ,EAAE,gBAAgB;IAC1B,IAAI,EAAE,mBAAmB;IACzB,WAAW,EACT,+DAA+D;IACjE,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,CAAC;IAEX,KAAK,CAAC,KAAK,CAAC,OAAoB;QAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAE5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,gBAAgB,CAAC,QAAQ,EAAE,CAAC,EAAE;gBACnC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,yCAAyC;aACnD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,gBAAgB,CAAC,QAAQ,EAAE,CAAC,EAAE;gBACnC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,gCAAgC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAErC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,8CAA8C;YAC9C,IAAI,IAAI,CAAC,MAAM,KAAK,aAAa;gBAAE,SAAS;YAE5C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACzC,yCAAyC;YACzC,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAE9D,iEAAiE;YACjE,MAAM,cAAc,GAClB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAC5B,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBAC/C,SAAS,CAAC,IAAI,CACZ,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,MAAM,GAAG,CAAC;oBACf,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC;wBAChC,SAAS,CAAC,QAAQ,CAAC,UAAU,IAAI,EAAE,CAAC;wBACpC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,IAAI,EAAE,CAAC;wBAC1C,SAAS,CAAC,QAAQ,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,CAC/C,CAAC;YAEJ,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,gBAAgB,CAAC,QAAQ,EAAE,CAAC,EAAE;gBACnC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,qDAAqD;aAC/D,CAAC,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;QAEvC,OAAO,gBAAgB,CAAC,QAAQ,EAAE,CAAC,EAAE;YACnC,MAAM,EAAE,KAAK;YACb,KAAK;YACL,OAAO,EAAE,GAAG,QAAQ,CAAC,MAAM,mDAAmD;YAC9E,OAAO,EAAE,QAAQ,CAAC,GAAG,CACnB,CAAC,IAAI,EAAE,EAAE,CACP,SAAS,IAAI,0DAA0D,CAC1E;YACD,WAAW,EAAE;gBACX,gEAAgE;gBAChE,mDAAmD;gBACnD,kEAAkE;aACnE;YACD,aAAa,EAAE,QAAQ;SACxB,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * BP-010: Documentation Quality
3
+ *
4
+ * Checks that tool definitions are self-documenting with complete
5
+ * descriptions, parameter documentation, and examples where appropriate.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const BP_010: Rule;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * BP-010: Documentation Quality
3
+ *
4
+ * Checks that tool definitions are self-documenting with complete
5
+ * descriptions, parameter documentation, and examples where appropriate.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ const MIN_DESCRIPTION_LENGTH = 15;
9
+ const MIN_PARAM_DESC_LENGTH = 5;
10
+ export const BP_010 = {
11
+ id: 'BP-010',
12
+ category: 'best-practices',
13
+ name: 'Documentation Quality',
14
+ description: 'Tool definitions should be self-documenting and complete',
15
+ severity: 'info',
16
+ maxScore: 5,
17
+ async check(context) {
18
+ const tools = context.tools;
19
+ if (tools.length === 0) {
20
+ return createRuleResult('BP-010', 5, {
21
+ passed: true,
22
+ score: 5,
23
+ message: 'No tools detected (rule not applicable)',
24
+ });
25
+ }
26
+ const issues = [];
27
+ for (const tool of tools) {
28
+ const problems = [];
29
+ // Check tool description quality
30
+ if (!tool.description || tool.description.length < MIN_DESCRIPTION_LENGTH) {
31
+ problems.push(`Description too short (${tool.description?.length ?? 0} chars, need ${MIN_DESCRIPTION_LENGTH}+)`);
32
+ }
33
+ // Check that description is not just the tool name repeated
34
+ if (tool.description &&
35
+ tool.description.toLowerCase().replace(/[_\s-]/g, '') ===
36
+ tool.name.toLowerCase().replace(/[_\s-]/g, '')) {
37
+ problems.push('Description is just the tool name restated');
38
+ }
39
+ // Check parameter descriptions
40
+ const schema = tool.inputSchema;
41
+ if (schema?.properties) {
42
+ const params = Object.entries(schema.properties);
43
+ const undocumentedParams = [];
44
+ for (const [paramName, prop] of params) {
45
+ if (!prop.description ||
46
+ prop.description.length < MIN_PARAM_DESC_LENGTH) {
47
+ undocumentedParams.push(paramName);
48
+ }
49
+ }
50
+ if (undocumentedParams.length > 0) {
51
+ problems.push(`${undocumentedParams.length} param(s) lack descriptions: [${undocumentedParams.join(', ')}]`);
52
+ }
53
+ }
54
+ // Check if annotations title is present for better readability
55
+ if (!tool.annotations?.title && tool.name.includes('_')) {
56
+ problems.push('No annotations.title — snake_case names benefit from human-readable titles');
57
+ }
58
+ if (problems.length > 0) {
59
+ issues.push({ tool: tool.name, problems });
60
+ }
61
+ }
62
+ if (issues.length === 0) {
63
+ return createRuleResult('BP-010', 5, {
64
+ passed: true,
65
+ score: 5,
66
+ message: 'All tools are well-documented',
67
+ });
68
+ }
69
+ const penalty = Math.min(issues.length, 5);
70
+ const score = Math.max(0, 5 - penalty);
71
+ return createRuleResult('BP-010', 5, {
72
+ passed: false,
73
+ score,
74
+ message: `${issues.length} tool(s) have documentation quality issues`,
75
+ details: issues.flatMap((i) => i.problems.map((p) => `Tool "${i.tool}": ${p}`)),
76
+ suggestions: [
77
+ 'Write descriptive tool descriptions (15+ characters) explaining what the tool does',
78
+ 'Add descriptions to all parameters explaining expected values',
79
+ 'Use annotations.title for a human-readable display name',
80
+ ],
81
+ affectedTools: issues.map((i) => i.tool),
82
+ });
83
+ },
84
+ };
85
+ //# sourceMappingURL=BP-010.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BP-010.js","sourceRoot":"","sources":["../../../src/rules/best-practices/BP-010.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * COV-002: Missing Search Tool
3
+ *
4
+ * Checks if the site has search functionality (search inputs, search forms)
5
+ * but no corresponding WebMCP search tool.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const COV_002: Rule;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * COV-002: Missing Search Tool
3
+ *
4
+ * Checks if the site has search functionality (search inputs, search forms)
5
+ * but no corresponding WebMCP search tool.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ const SEARCH_HTML_PATTERNS = [
9
+ /type\s*=\s*["']search["']/i,
10
+ /role\s*=\s*["']search["']/i,
11
+ /name\s*=\s*["'](?:q|query|search|keyword|s)["']/i,
12
+ /id\s*=\s*["'](?:search|searchbox|search-input|searchbar)["']/i,
13
+ /class\s*=\s*["'][^"']*search[^"']*["']/i,
14
+ /placeholder\s*=\s*["'][^"']*search[^"']*["']/i,
15
+ /aria-label\s*=\s*["'][^"']*search[^"']*["']/i,
16
+ ];
17
+ export const COV_002 = {
18
+ id: 'COV-002',
19
+ category: 'coverage',
20
+ name: 'Missing Search Tool',
21
+ description: 'Sites with search functionality should expose a WebMCP search tool',
22
+ severity: 'warning',
23
+ maxScore: 5,
24
+ async check(context) {
25
+ const html = context.html ?? '';
26
+ if (html.length === 0) {
27
+ return createRuleResult('COV-002', 5, {
28
+ passed: true,
29
+ score: 5,
30
+ message: 'No HTML available for analysis',
31
+ });
32
+ }
33
+ const hasSearchUI = SEARCH_HTML_PATTERNS.some((p) => p.test(html));
34
+ if (!hasSearchUI) {
35
+ return createRuleResult('COV-002', 5, {
36
+ passed: true,
37
+ score: 5,
38
+ message: 'No search UI detected on the page',
39
+ });
40
+ }
41
+ // Check if any tool covers search
42
+ const hasSearchTool = context.tools.some((t) => {
43
+ const text = `${t.name} ${t.description}`.toLowerCase();
44
+ return /\bsearch\b|\bfind\b|\bquery\b|\blookup\b/.test(text);
45
+ });
46
+ if (hasSearchTool) {
47
+ return createRuleResult('COV-002', 5, {
48
+ passed: true,
49
+ score: 5,
50
+ message: 'Search functionality is covered by a WebMCP tool',
51
+ });
52
+ }
53
+ return createRuleResult('COV-002', 5, {
54
+ passed: false,
55
+ score: 0,
56
+ message: 'Site has search UI but no WebMCP search tool',
57
+ details: [
58
+ 'Search input or form detected on the page without a corresponding tool',
59
+ ],
60
+ suggestions: [
61
+ 'Register a search tool using navigator.modelContext.registerTool()',
62
+ 'Or add toolname="search" to the search form element',
63
+ 'A search tool is one of the most valuable tools for AI agents',
64
+ ],
65
+ });
66
+ },
67
+ };
68
+ //# sourceMappingURL=COV-002.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"COV-002.js","sourceRoot":"","sources":["../../../src/rules/coverage/COV-002.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,oBAAoB,GAAG;IAC3B,4BAA4B;IAC5B,4BAA4B;IAC5B,kDAAkD;IAClD,+DAA+D;IAC/D,yCAAyC;IACzC,+CAA+C;IAC/C,8CAA8C;CAC/C,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAS;IAC3B,EAAE,EAAE,SAAS;IACb,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EACT,oEAAoE;IACtE,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,CAAC;IAEX,KAAK,CAAC,KAAK,CAAC,OAAoB;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;QAEhC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,gCAAgC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,MAAM,WAAW,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAEnE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,mCAAmC;aAC7C,CAAC,CAAC;QACL,CAAC;QAED,kCAAkC;QAClC,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,CAAC;YACxD,OAAO,0CAA0C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QAEH,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,kDAAkD;aAC5D,CAAC,CAAC;QACL,CAAC;QAED,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;YACpC,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,CAAC;YACR,OAAO,EAAE,8CAA8C;YACvD,OAAO,EAAE;gBACP,wEAAwE;aACzE;YACD,WAAW,EAAE;gBACX,oEAAoE;gBACpE,qDAAqD;gBACrD,+DAA+D;aAChE;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * COV-003: Missing Filter Tool
3
+ *
4
+ * Checks if the site has filter/sort controls but no corresponding
5
+ * WebMCP filter tool.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const COV_003: Rule;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * COV-003: Missing Filter Tool
3
+ *
4
+ * Checks if the site has filter/sort controls but no corresponding
5
+ * WebMCP filter tool.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ const FILTER_HTML_PATTERNS = [
9
+ /class\s*=\s*["'][^"']*filter[^"']*["']/i,
10
+ /id\s*=\s*["'][^"']*filter[^"']*["']/i,
11
+ /data-filter/i,
12
+ /aria-label\s*=\s*["'][^"']*filter[^"']*["']/i,
13
+ /aria-label\s*=\s*["'][^"']*sort[^"']*["']/i,
14
+ /class\s*=\s*["'][^"']*sort[^"']*["']/i,
15
+ /name\s*=\s*["'](?:sort|sort_by|sortBy|order|orderBy|filter)["']/i,
16
+ /<select[^>]*(?:sort|filter|order)/i,
17
+ ];
18
+ export const COV_003 = {
19
+ id: 'COV-003',
20
+ category: 'coverage',
21
+ name: 'Missing Filter Tool',
22
+ description: 'Sites with filter/sort controls should expose WebMCP filter tools',
23
+ severity: 'info',
24
+ maxScore: 3,
25
+ async check(context) {
26
+ const html = context.html ?? '';
27
+ if (html.length === 0) {
28
+ return createRuleResult('COV-003', 3, {
29
+ passed: true,
30
+ score: 3,
31
+ message: 'No HTML available for analysis',
32
+ });
33
+ }
34
+ const hasFilterUI = FILTER_HTML_PATTERNS.some((p) => p.test(html));
35
+ if (!hasFilterUI) {
36
+ return createRuleResult('COV-003', 3, {
37
+ passed: true,
38
+ score: 3,
39
+ message: 'No filter/sort UI detected on the page',
40
+ });
41
+ }
42
+ const hasFilterTool = context.tools.some((t) => {
43
+ const text = `${t.name} ${t.description}`.toLowerCase();
44
+ return /\bfilter\b|\bsort\b|\border\b|\brefine\b/.test(text);
45
+ });
46
+ if (hasFilterTool) {
47
+ return createRuleResult('COV-003', 3, {
48
+ passed: true,
49
+ score: 3,
50
+ message: 'Filter/sort functionality is covered by a WebMCP tool',
51
+ });
52
+ }
53
+ return createRuleResult('COV-003', 3, {
54
+ passed: false,
55
+ score: 0,
56
+ message: 'Site has filter/sort UI but no WebMCP filter tool',
57
+ details: [
58
+ 'Filter or sort controls detected on the page without a corresponding tool',
59
+ ],
60
+ suggestions: [
61
+ 'Register a filter/sort tool to let agents refine results',
62
+ 'Add toolname attribute to filter forms',
63
+ 'Filter tools help agents navigate large datasets efficiently',
64
+ ],
65
+ });
66
+ },
67
+ };
68
+ //# sourceMappingURL=COV-003.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"COV-003.js","sourceRoot":"","sources":["../../../src/rules/coverage/COV-003.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,oBAAoB,GAAG;IAC3B,yCAAyC;IACzC,sCAAsC;IACtC,cAAc;IACd,8CAA8C;IAC9C,4CAA4C;IAC5C,uCAAuC;IACvC,kEAAkE;IAClE,oCAAoC;CACrC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAS;IAC3B,EAAE,EAAE,SAAS;IACb,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EACT,mEAAmE;IACrE,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,CAAC;IAEX,KAAK,CAAC,KAAK,CAAC,OAAoB;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;QAEhC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,gCAAgC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,MAAM,WAAW,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAEnE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,wCAAwC;aAClD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,CAAC;YACxD,OAAO,0CAA0C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QAEH,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,uDAAuD;aACjE,CAAC,CAAC;QACL,CAAC;QAED,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;YACpC,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,CAAC;YACR,OAAO,EAAE,mDAAmD;YAC5D,OAAO,EAAE;gBACP,2EAA2E;aAC5E;YACD,WAAW,EAAE;gBACX,0DAA0D;gBAC1D,wCAAwC;gBACxC,8DAA8D;aAC/D;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * COV-004: Missing Action Tool
3
+ *
4
+ * Checks if the site has action buttons (add to cart, like, follow, etc.)
5
+ * without corresponding WebMCP tools.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const COV_004: Rule;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * COV-004: Missing Action Tool
3
+ *
4
+ * Checks if the site has action buttons (add to cart, like, follow, etc.)
5
+ * without corresponding WebMCP tools.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ const ACTION_BUTTON_PATTERNS = [
9
+ /(?:add.to.cart|buy.now|purchase|order.now)/i,
10
+ /(?:subscribe|follow|like|favorite|bookmark|save)/i,
11
+ /(?:share|download|export|print)/i,
12
+ /(?:apply|register|sign.up|join)/i,
13
+ /(?:submit|send|post|publish)/i,
14
+ /(?:edit|update|modify|change)/i,
15
+ ];
16
+ const ACTION_HTML_SELECTORS = [
17
+ /<button[^>]*>([^<]*)<\/button>/gi,
18
+ /<input[^>]*type\s*=\s*["'](?:submit|button)["'][^>]*value\s*=\s*["']([^"']+)["']/gi,
19
+ /<a[^>]*class\s*=\s*["'][^"']*(?:btn|button|cta)[^"']*["'][^>]*>([^<]*)<\/a>/gi,
20
+ ];
21
+ export const COV_004 = {
22
+ id: 'COV-004',
23
+ category: 'coverage',
24
+ name: 'Missing Action Tool',
25
+ description: 'Sites with action buttons should have corresponding WebMCP tools',
26
+ severity: 'warning',
27
+ maxScore: 5,
28
+ async check(context) {
29
+ const html = context.html ?? '';
30
+ if (html.length === 0) {
31
+ return createRuleResult('COV-004', 5, {
32
+ passed: true,
33
+ score: 5,
34
+ message: 'No HTML available for analysis',
35
+ });
36
+ }
37
+ // Extract button/action text from HTML
38
+ const actionTexts = [];
39
+ for (const regex of ACTION_HTML_SELECTORS) {
40
+ const r = new RegExp(regex.source, regex.flags);
41
+ let match;
42
+ while ((match = r.exec(html)) !== null) {
43
+ const text = (match[1] ?? '').trim();
44
+ if (text.length > 2 && text.length < 50) {
45
+ actionTexts.push(text);
46
+ }
47
+ }
48
+ }
49
+ // Find actions that match known action patterns but have no tool
50
+ const uncoveredActions = [];
51
+ for (const text of actionTexts) {
52
+ const isAction = ACTION_BUTTON_PATTERNS.some((p) => p.test(text));
53
+ if (!isAction)
54
+ continue;
55
+ // Check if any tool covers this action
56
+ const textWords = text.toLowerCase().split(/\s+/);
57
+ const isCovered = context.tools.some((t) => {
58
+ const toolText = `${t.name} ${t.description}`.toLowerCase();
59
+ return textWords.some((word) => word.length > 3 && toolText.includes(word));
60
+ });
61
+ if (!isCovered) {
62
+ uncoveredActions.push(text);
63
+ }
64
+ }
65
+ if (uncoveredActions.length === 0) {
66
+ return createRuleResult('COV-004', 5, {
67
+ passed: true,
68
+ score: 5,
69
+ message: 'Action buttons are covered by WebMCP tools',
70
+ });
71
+ }
72
+ const coverageRatio = actionTexts.length > 0
73
+ ? (actionTexts.length - uncoveredActions.length) / actionTexts.length
74
+ : 1;
75
+ const score = Math.round(coverageRatio * 5);
76
+ return createRuleResult('COV-004', 5, {
77
+ passed: false,
78
+ score,
79
+ message: `${uncoveredActions.length} action button(s) without WebMCP tools`,
80
+ details: uncoveredActions.map((text) => `Action button "${text}" has no corresponding tool`),
81
+ suggestions: [
82
+ 'Register tools for interactive action buttons',
83
+ 'Action buttons like "Add to Cart" are high-value tool opportunities',
84
+ 'Even simple click actions benefit from WebMCP tool registration',
85
+ ],
86
+ });
87
+ },
88
+ };
89
+ //# sourceMappingURL=COV-004.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"COV-004.js","sourceRoot":"","sources":["../../../src/rules/coverage/COV-004.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * COV-005: Missing Navigation Tool
3
+ *
4
+ * Checks if complex navigation (dropdowns, mega menus, tab navigation)
5
+ * is exposed as WebMCP tools for agent navigation.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const COV_005: Rule;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * COV-005: Missing Navigation Tool
3
+ *
4
+ * Checks if complex navigation (dropdowns, mega menus, tab navigation)
5
+ * is exposed as WebMCP tools for agent navigation.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ const COMPLEX_NAV_PATTERNS = [
9
+ /<nav[^>]*class\s*=\s*["'][^"']*(?:mega|dropdown|sidebar|subnav)[^"']*["']/i,
10
+ /class\s*=\s*["'][^"']*(?:tab-nav|tabs|tab-list|tab-bar)[^"']*["']/i,
11
+ /role\s*=\s*["']tablist["']/i,
12
+ /class\s*=\s*["'][^"']*(?:breadcrumb)[^"']*["']/i,
13
+ /class\s*=\s*["'][^"']*(?:pagination)[^"']*["']/i,
14
+ /class\s*=\s*["'][^"']*(?:accordion|collapsible)[^"']*["']/i,
15
+ /class\s*=\s*["'][^"']*(?:stepper|wizard|multi-step)[^"']*["']/i,
16
+ ];
17
+ export const COV_005 = {
18
+ id: 'COV-005',
19
+ category: 'coverage',
20
+ name: 'Missing Navigation Tool',
21
+ description: 'Complex navigation should be exposed as WebMCP tools',
22
+ severity: 'info',
23
+ maxScore: 3,
24
+ async check(context) {
25
+ const html = context.html ?? '';
26
+ if (html.length === 0) {
27
+ return createRuleResult('COV-005', 3, {
28
+ passed: true,
29
+ score: 3,
30
+ message: 'No HTML available for analysis',
31
+ });
32
+ }
33
+ const hasComplexNav = COMPLEX_NAV_PATTERNS.some((p) => p.test(html));
34
+ if (!hasComplexNav) {
35
+ return createRuleResult('COV-005', 3, {
36
+ passed: true,
37
+ score: 3,
38
+ message: 'No complex navigation detected',
39
+ });
40
+ }
41
+ const hasNavTool = context.tools.some((t) => {
42
+ const text = `${t.name} ${t.description}`.toLowerCase();
43
+ return /\bnavigate\b|\bgoto\b|\bgo_to\b|\bswitch\b|\btab\b|\bpage\b|\bnext\b|\bprev/.test(text);
44
+ });
45
+ if (hasNavTool) {
46
+ return createRuleResult('COV-005', 3, {
47
+ passed: true,
48
+ score: 3,
49
+ message: 'Complex navigation is covered by WebMCP tools',
50
+ });
51
+ }
52
+ return createRuleResult('COV-005', 3, {
53
+ passed: false,
54
+ score: 0,
55
+ message: 'Complex navigation exists without WebMCP navigation tools',
56
+ details: [
57
+ 'Tabs, mega menus, pagination, or wizard steps detected without navigation tools',
58
+ ],
59
+ suggestions: [
60
+ 'Register navigation tools for tabs, pagination, and multi-step flows',
61
+ 'Navigation tools help agents traverse complex page structures',
62
+ 'Example: registerTool({ name: "navigate_tab", ... })',
63
+ ],
64
+ });
65
+ },
66
+ };
67
+ //# sourceMappingURL=COV-005.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"COV-005.js","sourceRoot":"","sources":["../../../src/rules/coverage/COV-005.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,oBAAoB,GAAG;IAC3B,4EAA4E;IAC5E,oEAAoE;IACpE,6BAA6B;IAC7B,iDAAiD;IACjD,iDAAiD;IACjD,4DAA4D;IAC5D,gEAAgE;CACjE,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAS;IAC3B,EAAE,EAAE,SAAS;IACb,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,yBAAyB;IAC/B,WAAW,EACT,sDAAsD;IACxD,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,CAAC;IAEX,KAAK,CAAC,KAAK,CAAC,OAAoB;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;QAEhC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,gCAAgC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,MAAM,aAAa,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAErE,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,gCAAgC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,CAAC;YACxD,OAAO,6EAA6E,CAAC,IAAI,CACvF,IAAI,CACL,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,+CAA+C;aACzD,CAAC,CAAC;QACL,CAAC;QAED,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;YACpC,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,CAAC;YACR,OAAO,EAAE,2DAA2D;YACpE,OAAO,EAAE;gBACP,iFAAiF;aAClF;YACD,WAAW,EAAE;gBACX,sEAAsE;gBACtE,+DAA+D;gBAC/D,sDAAsD;aACvD;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * COV-006: JavaScript-Only Functionality
3
+ *
4
+ * Checks for functionality that exists only in JavaScript without
5
+ * corresponding WebMCP tools, such as keyboard shortcuts,
6
+ * event-driven interactions, or JS-only features.
7
+ */
8
+ import type { Rule } from '../../core/types/rule.js';
9
+ export declare const COV_006: Rule;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * COV-006: JavaScript-Only Functionality
3
+ *
4
+ * Checks for functionality that exists only in JavaScript without
5
+ * corresponding WebMCP tools, such as keyboard shortcuts,
6
+ * event-driven interactions, or JS-only features.
7
+ */
8
+ import { createRuleResult } from '../runner.js';
9
+ const JS_ONLY_PATTERNS = [
10
+ /addEventListener\s*\(\s*['"](?:click|dblclick|contextmenu)["']/,
11
+ /\.onclick\s*=/,
12
+ /\.ondblclick\s*=/,
13
+ /\$\([^)]*\)\.(?:click|on|bind)\s*\(/,
14
+ /document\.querySelector[^.]*\.addEventListener/,
15
+ ];
16
+ export const COV_006 = {
17
+ id: 'COV-006',
18
+ category: 'coverage',
19
+ name: 'JavaScript-Only Functionality',
20
+ description: 'Functionality existing only in JS without WebMCP should be flagged',
21
+ severity: 'info',
22
+ maxScore: 3,
23
+ async check(context) {
24
+ const scripts = context.scripts ?? [];
25
+ if (scripts.length === 0) {
26
+ return createRuleResult('COV-006', 3, {
27
+ passed: true,
28
+ score: 3,
29
+ message: 'No scripts available for analysis',
30
+ });
31
+ }
32
+ if (context.tools.length === 0 && !context.hasWebMCP) {
33
+ return createRuleResult('COV-006', 3, {
34
+ passed: true,
35
+ score: 3,
36
+ message: 'No WebMCP implementation detected (rule not applicable)',
37
+ });
38
+ }
39
+ let jsOnlyInteractions = 0;
40
+ for (const script of scripts) {
41
+ for (const pattern of JS_ONLY_PATTERNS) {
42
+ const matches = script.content.match(new RegExp(pattern.source, 'g'));
43
+ if (matches) {
44
+ jsOnlyInteractions += matches.length;
45
+ }
46
+ }
47
+ }
48
+ // Compare JS interactions to tool count
49
+ const toolCount = context.tools.length;
50
+ const gap = jsOnlyInteractions - toolCount;
51
+ if (gap <= 2 || jsOnlyInteractions <= 3) {
52
+ return createRuleResult('COV-006', 3, {
53
+ passed: true,
54
+ score: 3,
55
+ message: `JS interactions (${jsOnlyInteractions}) adequately covered by tools (${toolCount})`,
56
+ });
57
+ }
58
+ const coverageRatio = toolCount / Math.max(jsOnlyInteractions, 1);
59
+ const score = Math.min(3, Math.round(coverageRatio * 3));
60
+ return createRuleResult('COV-006', 3, {
61
+ passed: false,
62
+ score,
63
+ message: `${jsOnlyInteractions} JS interactions but only ${toolCount} WebMCP tools`,
64
+ details: [
65
+ `Detected ${jsOnlyInteractions} JavaScript-based interactions with ${toolCount} tool(s)`,
66
+ `Coverage gap of ~${gap} interactions not exposed as tools`,
67
+ ],
68
+ suggestions: [
69
+ 'Register tools for JavaScript-driven interactions that agents should access',
70
+ 'Focus on high-value interactions (actions, data mutations) over decorative ones',
71
+ 'Not every click handler needs a tool — prioritize user-facing functionality',
72
+ ],
73
+ });
74
+ },
75
+ };
76
+ //# sourceMappingURL=COV-006.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"COV-006.js","sourceRoot":"","sources":["../../../src/rules/coverage/COV-006.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,gBAAgB,GAAG;IACvB,gEAAgE;IAChE,eAAe;IACf,kBAAkB;IAClB,qCAAqC;IACrC,gDAAgD;CACjD,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAS;IAC3B,EAAE,EAAE,SAAS;IACb,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,+BAA+B;IACrC,WAAW,EACT,oEAAoE;IACtE,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,CAAC;IAEX,KAAK,CAAC,KAAK,CAAC,OAAoB;QAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QAEtC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,mCAAmC;aAC7C,CAAC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACrD,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,yDAAyD;aACnE,CAAC,CAAC;QACL,CAAC;QAED,IAAI,kBAAkB,GAAG,CAAC,CAAC;QAE3B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;gBACvC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;gBACtE,IAAI,OAAO,EAAE,CAAC;oBACZ,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC;gBACvC,CAAC;YACH,CAAC;QACH,CAAC;QAED,wCAAwC;QACxC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;QACvC,MAAM,GAAG,GAAG,kBAAkB,GAAG,SAAS,CAAC;QAE3C,IAAI,GAAG,IAAI,CAAC,IAAI,kBAAkB,IAAI,CAAC,EAAE,CAAC;YACxC,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,oBAAoB,kBAAkB,kCAAkC,SAAS,GAAG;aAC9F,CAAC,CAAC;QACL,CAAC;QAED,MAAM,aAAa,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC;QAEzD,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;YACpC,MAAM,EAAE,KAAK;YACb,KAAK;YACL,OAAO,EAAE,GAAG,kBAAkB,6BAA6B,SAAS,eAAe;YACnF,OAAO,EAAE;gBACP,YAAY,kBAAkB,uCAAuC,SAAS,UAAU;gBACxF,oBAAoB,GAAG,oCAAoC;aAC5D;YACD,WAAW,EAAE;gBACX,6EAA6E;gBAC7E,iFAAiF;gBACjF,6EAA6E;aAC9E;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * COV-007: Keyboard Shortcuts Not Exposed
3
+ *
4
+ * Checks if the page has keyboard shortcuts that aren't documented
5
+ * as WebMCP tools, leaving agents unaware of these capabilities.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const COV_007: Rule;