mcpmake 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/README.md +691 -0
  2. package/bin/mcpmake.mjs +2 -0
  3. package/dist/analyzer/auth-detector.d.ts +12 -0
  4. package/dist/analyzer/auth-detector.js +142 -0
  5. package/dist/analyzer/dom-parser.d.ts +10 -0
  6. package/dist/analyzer/dom-parser.js +259 -0
  7. package/dist/analyzer/goal-crawler.d.ts +25 -0
  8. package/dist/analyzer/goal-crawler.js +177 -0
  9. package/dist/analyzer/hybrid-detector.d.ts +28 -0
  10. package/dist/analyzer/hybrid-detector.js +96 -0
  11. package/dist/analyzer/index.d.ts +12 -0
  12. package/dist/analyzer/index.js +8 -0
  13. package/dist/analyzer/screenshot-capture.d.ts +29 -0
  14. package/dist/analyzer/screenshot-capture.js +42 -0
  15. package/dist/analyzer/selector-builder.d.ts +19 -0
  16. package/dist/analyzer/selector-builder.js +199 -0
  17. package/dist/analyzer/semantic-analyzer.d.ts +13 -0
  18. package/dist/analyzer/semantic-analyzer.js +145 -0
  19. package/dist/analyzer/site-crawler.d.ts +38 -0
  20. package/dist/analyzer/site-crawler.js +235 -0
  21. package/dist/cloud/billing/billing-engine.d.ts +44 -0
  22. package/dist/cloud/billing/billing-engine.js +81 -0
  23. package/dist/cloud/billing/credit-store.d.ts +64 -0
  24. package/dist/cloud/billing/credit-store.js +168 -0
  25. package/dist/cloud/billing/index.d.ts +4 -0
  26. package/dist/cloud/billing/index.js +2 -0
  27. package/dist/cloud/billing/usage-store.d.ts +42 -0
  28. package/dist/cloud/billing/usage-store.js +85 -0
  29. package/dist/cloud/billing/usage-tracker.d.ts +38 -0
  30. package/dist/cloud/billing/usage-tracker.js +95 -0
  31. package/dist/cloud/build-pipeline.d.ts +39 -0
  32. package/dist/cloud/build-pipeline.js +310 -0
  33. package/dist/cloud/build-queue.d.ts +30 -0
  34. package/dist/cloud/build-queue.js +70 -0
  35. package/dist/cloud/caddy-manager.d.ts +18 -0
  36. package/dist/cloud/caddy-manager.js +97 -0
  37. package/dist/cloud/container-backend.d.ts +62 -0
  38. package/dist/cloud/container-backend.js +59 -0
  39. package/dist/cloud/container-manager.d.ts +64 -0
  40. package/dist/cloud/container-manager.js +301 -0
  41. package/dist/cloud/crypto.d.ts +27 -0
  42. package/dist/cloud/crypto.js +63 -0
  43. package/dist/cloud/db/index.d.ts +27 -0
  44. package/dist/cloud/db/index.js +53 -0
  45. package/dist/cloud/db/migrations.d.ts +12 -0
  46. package/dist/cloud/db/migrations.js +329 -0
  47. package/dist/cloud/db/pg-store.d.ts +45 -0
  48. package/dist/cloud/db/pg-store.js +336 -0
  49. package/dist/cloud/failure-tracker.d.ts +51 -0
  50. package/dist/cloud/failure-tracker.js +102 -0
  51. package/dist/cloud/idle-monitor.d.ts +30 -0
  52. package/dist/cloud/idle-monitor.js +70 -0
  53. package/dist/cloud/mailer.d.ts +21 -0
  54. package/dist/cloud/mailer.js +193 -0
  55. package/dist/cloud/mcp-proxy.d.ts +58 -0
  56. package/dist/cloud/mcp-proxy.js +203 -0
  57. package/dist/cloud/metric-samples.d.ts +43 -0
  58. package/dist/cloud/metric-samples.js +85 -0
  59. package/dist/cloud/metrics.d.ts +26 -0
  60. package/dist/cloud/metrics.js +59 -0
  61. package/dist/cloud/multipart.d.ts +26 -0
  62. package/dist/cloud/multipart.js +132 -0
  63. package/dist/cloud/observability.d.ts +27 -0
  64. package/dist/cloud/observability.js +98 -0
  65. package/dist/cloud/rate-limiter.d.ts +31 -0
  66. package/dist/cloud/rate-limiter.js +58 -0
  67. package/dist/cloud/request-security.d.ts +5 -0
  68. package/dist/cloud/request-security.js +74 -0
  69. package/dist/cloud/resource-monitor.d.ts +69 -0
  70. package/dist/cloud/resource-monitor.js +130 -0
  71. package/dist/cloud/secret-store.d.ts +38 -0
  72. package/dist/cloud/secret-store.js +103 -0
  73. package/dist/cloud/security.d.ts +26 -0
  74. package/dist/cloud/security.js +142 -0
  75. package/dist/cloud/server.d.ts +21 -0
  76. package/dist/cloud/server.js +1079 -0
  77. package/dist/cloud/shared-state.d.ts +72 -0
  78. package/dist/cloud/shared-state.js +159 -0
  79. package/dist/cloud/ssrf.d.ts +43 -0
  80. package/dist/cloud/ssrf.js +150 -0
  81. package/dist/cloud/store.d.ts +41 -0
  82. package/dist/cloud/store.js +75 -0
  83. package/dist/cloud/stripe.d.ts +78 -0
  84. package/dist/cloud/stripe.js +317 -0
  85. package/dist/cloud/telemetry-store.d.ts +53 -0
  86. package/dist/cloud/telemetry-store.js +108 -0
  87. package/dist/cloud/web/auth.d.ts +225 -0
  88. package/dist/cloud/web/auth.js +555 -0
  89. package/dist/cloud/web/charts.d.ts +70 -0
  90. package/dist/cloud/web/charts.js +178 -0
  91. package/dist/cloud/web/csrf.d.ts +14 -0
  92. package/dist/cloud/web/csrf.js +22 -0
  93. package/dist/cloud/web/docs.d.ts +40 -0
  94. package/dist/cloud/web/docs.js +174 -0
  95. package/dist/cloud/web/router.d.ts +25 -0
  96. package/dist/cloud/web/router.js +1921 -0
  97. package/dist/cloud/web/static/alpine.min.js +5 -0
  98. package/dist/cloud/web/static/favicon.svg +4 -0
  99. package/dist/cloud/web/static/htmx-sse.js +290 -0
  100. package/dist/cloud/web/static/htmx.min.js +1 -0
  101. package/dist/cloud/web/static/style.css +2683 -0
  102. package/dist/cloud/web/static-server.d.ts +13 -0
  103. package/dist/cloud/web/static-server.js +73 -0
  104. package/dist/cloud/web/template-engine.d.ts +27 -0
  105. package/dist/cloud/web/template-engine.js +146 -0
  106. package/dist/cloud/web/templates/layouts/admin.hbs +57 -0
  107. package/dist/cloud/web/templates/layouts/auth.hbs +138 -0
  108. package/dist/cloud/web/templates/layouts/base.hbs +16 -0
  109. package/dist/cloud/web/templates/layouts/dashboard.hbs +39 -0
  110. package/dist/cloud/web/templates/layouts/landing.hbs +82 -0
  111. package/dist/cloud/web/templates/pages/admin/overview.hbs +123 -0
  112. package/dist/cloud/web/templates/pages/admin/servers.hbs +129 -0
  113. package/dist/cloud/web/templates/pages/admin/telemetry.hbs +39 -0
  114. package/dist/cloud/web/templates/pages/admin/user-edit.hbs +91 -0
  115. package/dist/cloud/web/templates/pages/admin/users.hbs +179 -0
  116. package/dist/cloud/web/templates/pages/auth/forgot-password.hbs +25 -0
  117. package/dist/cloud/web/templates/pages/auth/login.hbs +33 -0
  118. package/dist/cloud/web/templates/pages/auth/register.hbs +32 -0
  119. package/dist/cloud/web/templates/pages/auth/reset-password.hbs +34 -0
  120. package/dist/cloud/web/templates/pages/dashboard/billing.hbs +140 -0
  121. package/dist/cloud/web/templates/pages/dashboard/create.hbs +173 -0
  122. package/dist/cloud/web/templates/pages/dashboard/index.hbs +8 -0
  123. package/dist/cloud/web/templates/pages/dashboard/server-detail.hbs +280 -0
  124. package/dist/cloud/web/templates/pages/dashboard/server-logs.hbs +35 -0
  125. package/dist/cloud/web/templates/pages/dashboard/server-metrics.hbs +63 -0
  126. package/dist/cloud/web/templates/pages/dashboard/servers-partial.hbs +21 -0
  127. package/dist/cloud/web/templates/pages/dashboard/servers.hbs +44 -0
  128. package/dist/cloud/web/templates/pages/docs/show.hbs +16 -0
  129. package/dist/cloud/web/templates/pages/errors/404.hbs +9 -0
  130. package/dist/cloud/web/templates/pages/errors/500.hbs +8 -0
  131. package/dist/cloud/web/templates/pages/landing/index.hbs +223 -0
  132. package/dist/cloud/web/templates/pages/legal/privacy.hbs +71 -0
  133. package/dist/cloud/web/templates/pages/legal/terms.hbs +73 -0
  134. package/dist/cloud/web/templates/partials/admin-stats.hbs +52 -0
  135. package/dist/cloud/web/templates/partials/flash-message.hbs +6 -0
  136. package/dist/cloud/web/templates/partials/pricing-table.hbs +103 -0
  137. package/dist/cloud/web/templates/partials/server-card.hbs +19 -0
  138. package/dist/cloud/web/templates/partials/status-badge.hbs +1 -0
  139. package/dist/commands/bundle.d.ts +18 -0
  140. package/dist/commands/bundle.js +82 -0
  141. package/dist/commands/ci.d.ts +25 -0
  142. package/dist/commands/ci.js +149 -0
  143. package/dist/commands/deploy.d.ts +24 -0
  144. package/dist/commands/deploy.js +145 -0
  145. package/dist/commands/diff.d.ts +18 -0
  146. package/dist/commands/diff.js +185 -0
  147. package/dist/commands/from/describe.d.ts +65 -0
  148. package/dist/commands/from/describe.js +173 -0
  149. package/dist/commands/from/har.d.ts +81 -0
  150. package/dist/commands/from/har.js +255 -0
  151. package/dist/commands/from/openapi.d.ts +105 -0
  152. package/dist/commands/from/openapi.js +302 -0
  153. package/dist/commands/from/postman.d.ts +51 -0
  154. package/dist/commands/from/postman.js +146 -0
  155. package/dist/commands/from/target-support.d.ts +11 -0
  156. package/dist/commands/from/target-support.js +33 -0
  157. package/dist/commands/from/url.d.ts +75 -0
  158. package/dist/commands/from/url.js +244 -0
  159. package/dist/commands/from/website.d.ts +75 -0
  160. package/dist/commands/from/website.js +284 -0
  161. package/dist/commands/lint.d.ts +24 -0
  162. package/dist/commands/lint.js +184 -0
  163. package/dist/commands/merge.d.ts +18 -0
  164. package/dist/commands/merge.js +161 -0
  165. package/dist/commands/publish.d.ts +27 -0
  166. package/dist/commands/publish.js +334 -0
  167. package/dist/commands/rescan.d.ts +40 -0
  168. package/dist/commands/rescan.js +255 -0
  169. package/dist/commands/update.d.ts +14 -0
  170. package/dist/commands/update.js +87 -0
  171. package/dist/commands/verify.d.ts +14 -0
  172. package/dist/commands/verify.js +71 -0
  173. package/dist/config/configurable-command.d.ts +13 -0
  174. package/dist/config/configurable-command.js +70 -0
  175. package/dist/config/mcpmake-config.d.ts +68 -0
  176. package/dist/config/mcpmake-config.js +207 -0
  177. package/dist/docs/cli.md +400 -0
  178. package/dist/docs/mcp-2026-07-28-migration.md +78 -0
  179. package/dist/docs/migrate-from-stainless.md +94 -0
  180. package/dist/docs/quickstart.md +166 -0
  181. package/dist/docs/show-hn.md +26 -0
  182. package/dist/docs/website-servers.md +169 -0
  183. package/dist/emitter/code-writer.d.ts +8 -0
  184. package/dist/emitter/code-writer.js +25 -0
  185. package/dist/emitter/index.d.ts +32 -0
  186. package/dist/emitter/index.js +280 -0
  187. package/dist/emitter/mcpb-bundler.d.ts +31 -0
  188. package/dist/emitter/mcpb-bundler.js +172 -0
  189. package/dist/emitter/project-scaffolder.d.ts +4 -0
  190. package/dist/emitter/project-scaffolder.js +89 -0
  191. package/dist/emitter/python-template-loader.d.ts +4 -0
  192. package/dist/emitter/python-template-loader.js +30 -0
  193. package/dist/emitter/python-templates/dockerfile.hbs +14 -0
  194. package/dist/emitter/python-templates/env.example.hbs +6 -0
  195. package/dist/emitter/python-templates/requirements.txt.hbs +4 -0
  196. package/dist/emitter/python-templates/server.py.hbs +77 -0
  197. package/dist/emitter/site-scaffolder.d.ts +13 -0
  198. package/dist/emitter/site-scaffolder.js +70 -0
  199. package/dist/emitter/site-template-loader.d.ts +5 -0
  200. package/dist/emitter/site-template-loader.js +47 -0
  201. package/dist/emitter/site-templates/browser-manager.ts.hbs +233 -0
  202. package/dist/emitter/site-templates/config.ts.hbs +28 -0
  203. package/dist/emitter/site-templates/dockerfile.hbs +31 -0
  204. package/dist/emitter/site-templates/env.example.hbs +19 -0
  205. package/dist/emitter/site-templates/package.json.hbs +26 -0
  206. package/dist/emitter/site-templates/server-main-http.ts.hbs +108 -0
  207. package/dist/emitter/site-templates/server-main.ts.hbs +23 -0
  208. package/dist/emitter/site-templates/tool-handler-action.ts.hbs +86 -0
  209. package/dist/emitter/site-templates/tool-handler-form.ts.hbs +116 -0
  210. package/dist/emitter/site-templates/tool-handler-lifecycle.ts.hbs +146 -0
  211. package/dist/emitter/site-templates/tool-index.ts.hbs +11 -0
  212. package/dist/emitter/template-loader.d.ts +1 -0
  213. package/dist/emitter/template-loader.js +27 -0
  214. package/dist/emitter/templates/auth-provider.ts.hbs +57 -0
  215. package/dist/emitter/templates/config.ts.hbs +63 -0
  216. package/dist/emitter/templates/discovery.ts.hbs +301 -0
  217. package/dist/emitter/templates/dockerfile.hbs +34 -0
  218. package/dist/emitter/templates/env.example.hbs +28 -0
  219. package/dist/emitter/templates/gitignore.hbs +5 -0
  220. package/dist/emitter/templates/http-executor.ts.hbs +117 -0
  221. package/dist/emitter/templates/oauth.ts.hbs +188 -0
  222. package/dist/emitter/templates/package.json.hbs +25 -0
  223. package/dist/emitter/templates/prompts.ts.hbs +22 -0
  224. package/dist/emitter/templates/readme.md.hbs +123 -0
  225. package/dist/emitter/templates/resources.ts.hbs +63 -0
  226. package/dist/emitter/templates/server-main-http.ts.hbs +407 -0
  227. package/dist/emitter/templates/server-main.ts.hbs +40 -0
  228. package/dist/emitter/templates/task-handlers.ts.hbs +189 -0
  229. package/dist/emitter/templates/task-manager.ts.hbs +139 -0
  230. package/dist/emitter/templates/task-sse.ts.hbs +105 -0
  231. package/dist/emitter/templates/tool-handler.ts.hbs +124 -0
  232. package/dist/emitter/templates/tool-index.ts.hbs +11 -0
  233. package/dist/emitter/templates/tool-test.ts.hbs +57 -0
  234. package/dist/emitter/templates/trace.ts.hbs +79 -0
  235. package/dist/emitter/templates/tsconfig.json.hbs +16 -0
  236. package/dist/emitter/templates/types.ts.hbs +5 -0
  237. package/dist/emitter/worker-template-loader.d.ts +5 -0
  238. package/dist/emitter/worker-template-loader.js +33 -0
  239. package/dist/emitter/worker-templates/config.ts.hbs +54 -0
  240. package/dist/emitter/worker-templates/dev-vars.example.hbs +10 -0
  241. package/dist/emitter/worker-templates/gitignore.hbs +6 -0
  242. package/dist/emitter/worker-templates/package.json.hbs +24 -0
  243. package/dist/emitter/worker-templates/readme.md.hbs +53 -0
  244. package/dist/emitter/worker-templates/server.test.ts.hbs +20 -0
  245. package/dist/emitter/worker-templates/tool-handler.ts.hbs +85 -0
  246. package/dist/emitter/worker-templates/tool-index.ts.hbs +28 -0
  247. package/dist/emitter/worker-templates/tsconfig.json.hbs +17 -0
  248. package/dist/emitter/worker-templates/worker.ts.hbs +242 -0
  249. package/dist/emitter/worker-templates/wrangler.toml.hbs +19 -0
  250. package/dist/generator/spec-generator.d.ts +6 -0
  251. package/dist/generator/spec-generator.js +50 -0
  252. package/dist/index.d.ts +1 -0
  253. package/dist/index.js +64 -0
  254. package/dist/parser/har-filter.d.ts +8 -0
  255. package/dist/parser/har-filter.js +71 -0
  256. package/dist/parser/har-loader.d.ts +2 -0
  257. package/dist/parser/har-loader.js +14 -0
  258. package/dist/parser/har-normalizer.d.ts +20 -0
  259. package/dist/parser/har-normalizer.js +78 -0
  260. package/dist/parser/index.d.ts +10 -0
  261. package/dist/parser/index.js +6 -0
  262. package/dist/parser/openapi-loader.d.ts +6 -0
  263. package/dist/parser/openapi-loader.js +308 -0
  264. package/dist/parser/operation-extractor.d.ts +13 -0
  265. package/dist/parser/operation-extractor.js +155 -0
  266. package/dist/parser/overlay-loader.d.ts +10 -0
  267. package/dist/parser/overlay-loader.js +184 -0
  268. package/dist/parser/postman-loader.d.ts +9 -0
  269. package/dist/parser/postman-loader.js +106 -0
  270. package/dist/parser/schema-converter.d.ts +12 -0
  271. package/dist/parser/schema-converter.js +117 -0
  272. package/dist/plugins/adapter.d.ts +40 -0
  273. package/dist/plugins/adapter.js +15 -0
  274. package/dist/plugins/loader.d.ts +25 -0
  275. package/dist/plugins/loader.js +58 -0
  276. package/dist/pricing.d.ts +55 -0
  277. package/dist/pricing.js +133 -0
  278. package/dist/providers/index.d.ts +15 -0
  279. package/dist/providers/index.js +56 -0
  280. package/dist/recorder/browser-recorder.d.ts +22 -0
  281. package/dist/recorder/browser-recorder.js +205 -0
  282. package/dist/registry/official-registry.d.ts +90 -0
  283. package/dist/registry/official-registry.js +129 -0
  284. package/dist/rescan/diff-engine.d.ts +5 -0
  285. package/dist/rescan/diff-engine.js +312 -0
  286. package/dist/rescan/index.d.ts +3 -0
  287. package/dist/rescan/index.js +2 -0
  288. package/dist/rescan/rescan-runner.d.ts +42 -0
  289. package/dist/rescan/rescan-runner.js +69 -0
  290. package/dist/rescan/rescan-scheduler.d.ts +41 -0
  291. package/dist/rescan/rescan-scheduler.js +179 -0
  292. package/dist/site-transformer/browser-tools.d.ts +10 -0
  293. package/dist/site-transformer/browser-tools.js +59 -0
  294. package/dist/site-transformer/index.d.ts +2 -0
  295. package/dist/site-transformer/index.js +2 -0
  296. package/dist/site-transformer/selector-healer.d.ts +8 -0
  297. package/dist/site-transformer/selector-healer.js +106 -0
  298. package/dist/site-transformer/tool-generator.d.ts +13 -0
  299. package/dist/site-transformer/tool-generator.js +245 -0
  300. package/dist/transformer/auth-detector.d.ts +13 -0
  301. package/dist/transformer/auth-detector.js +90 -0
  302. package/dist/transformer/catalog-builder.d.ts +18 -0
  303. package/dist/transformer/catalog-builder.js +56 -0
  304. package/dist/transformer/client-compat.d.ts +6 -0
  305. package/dist/transformer/client-compat.js +44 -0
  306. package/dist/transformer/har-clusterer.d.ts +9 -0
  307. package/dist/transformer/har-clusterer.js +27 -0
  308. package/dist/transformer/har-dedup.d.ts +10 -0
  309. package/dist/transformer/har-dedup.js +81 -0
  310. package/dist/transformer/har-schema-inferrer.d.ts +15 -0
  311. package/dist/transformer/har-schema-inferrer.js +90 -0
  312. package/dist/transformer/har-to-operations.d.ts +13 -0
  313. package/dist/transformer/har-to-operations.js +192 -0
  314. package/dist/transformer/index.d.ts +8 -0
  315. package/dist/transformer/index.js +6 -0
  316. package/dist/transformer/llm-namer.d.ts +6 -0
  317. package/dist/transformer/llm-namer.js +59 -0
  318. package/dist/transformer/naming.d.ts +4 -0
  319. package/dist/transformer/naming.js +30 -0
  320. package/dist/transformer/operation-filter.d.ts +13 -0
  321. package/dist/transformer/operation-filter.js +52 -0
  322. package/dist/transformer/resource-builder.d.ts +12 -0
  323. package/dist/transformer/resource-builder.js +80 -0
  324. package/dist/transformer/schema-merger.d.ts +14 -0
  325. package/dist/transformer/schema-merger.js +65 -0
  326. package/dist/transformer/tool-builder.d.ts +3 -0
  327. package/dist/transformer/tool-builder.js +114 -0
  328. package/dist/types/index.d.ts +131 -0
  329. package/dist/types/index.js +1 -0
  330. package/dist/types/site.d.ts +284 -0
  331. package/dist/types/site.js +8 -0
  332. package/dist/utils/fail.d.ts +48 -0
  333. package/dist/utils/fail.js +204 -0
  334. package/dist/utils/fs.d.ts +5 -0
  335. package/dist/utils/fs.js +28 -0
  336. package/dist/utils/interactive.d.ts +6 -0
  337. package/dist/utils/interactive.js +30 -0
  338. package/dist/utils/logger.d.ts +1 -0
  339. package/dist/utils/logger.js +2 -0
  340. package/dist/utils/sanitize.d.ts +28 -0
  341. package/dist/utils/sanitize.js +44 -0
  342. package/dist/utils/watcher.d.ts +11 -0
  343. package/dist/utils/watcher.js +36 -0
  344. package/package.json +65 -0
@@ -0,0 +1,284 @@
1
+ import { defineConfigurableCommand } from '../../config/configurable-command.js';
2
+ import { crawlSite } from '../../analyzer/site-crawler.js';
3
+ import { goalDirectedCrawl } from '../../analyzer/goal-crawler.js';
4
+ import { classifyForms } from '../../analyzer/hybrid-detector.js';
5
+ import { analyzeSemantics } from '../../analyzer/semantic-analyzer.js';
6
+ import { detectAuthFlow } from '../../analyzer/auth-detector.js';
7
+ import { generateSiteTools } from '../../site-transformer/tool-generator.js';
8
+ import { filterHarEntries } from '../../parser/har-filter.js';
9
+ import { normalizeEntry } from '../../parser/har-normalizer.js';
10
+ import { clusterEntries } from '../../transformer/har-clusterer.js';
11
+ import { clustersToOperations } from '../../transformer/har-to-operations.js';
12
+ import { deduplicateEntries } from '../../transformer/har-dedup.js';
13
+ import { buildAllTools } from '../../transformer/tool-builder.js';
14
+ import { emitSiteProject } from '../../emitter/index.js';
15
+ import { logger } from '../../utils/logger.js';
16
+ import { fail } from '../../utils/fail.js';
17
+ function toPackageName(name) {
18
+ return name
19
+ .toLowerCase()
20
+ .replace(/[^a-z0-9]+/g, '-')
21
+ .replace(/^-|-$/g, '');
22
+ }
23
+ export default defineConfigurableCommand('website', {
24
+ meta: {
25
+ name: 'website',
26
+ description: "Generate a Playwright-based MCP server by analyzing a website's DOM",
27
+ },
28
+ args: {
29
+ url: {
30
+ type: 'positional',
31
+ description: 'URL of the website to analyze',
32
+ required: true,
33
+ },
34
+ output: {
35
+ type: 'string',
36
+ alias: 'o',
37
+ description: 'Output directory for generated project',
38
+ required: true,
39
+ },
40
+ name: {
41
+ type: 'string',
42
+ alias: 'n',
43
+ description: 'Server name (defaults to hostname)',
44
+ },
45
+ depth: {
46
+ type: 'string',
47
+ description: 'Crawl depth (default: 2)',
48
+ default: '2',
49
+ },
50
+ 'max-pages': {
51
+ type: 'string',
52
+ description: 'Maximum pages to crawl (default: 20)',
53
+ default: '20',
54
+ },
55
+ timeout: {
56
+ type: 'string',
57
+ description: 'Idle timeout in seconds (default: 300)',
58
+ default: '300',
59
+ },
60
+ transport: {
61
+ type: 'string',
62
+ alias: 't',
63
+ description: 'Transport mode: "stdio" (default) or "http"',
64
+ default: 'stdio',
65
+ },
66
+ headless: {
67
+ type: 'boolean',
68
+ description: 'Run browser in headless mode during analysis',
69
+ default: false,
70
+ },
71
+ force: {
72
+ type: 'boolean',
73
+ alias: 'f',
74
+ description: 'Overwrite existing output directory',
75
+ default: false,
76
+ },
77
+ 'dry-run': {
78
+ type: 'boolean',
79
+ description: 'Preview generated files without writing',
80
+ default: false,
81
+ },
82
+ 'improve-names': {
83
+ type: 'boolean',
84
+ description: 'Use LLM to infer semantic names for forms, buttons, and links',
85
+ default: false,
86
+ },
87
+ 'max-sessions': {
88
+ type: 'string',
89
+ description: 'Maximum concurrent browser sessions for the generated server (default: 10)',
90
+ default: '10',
91
+ },
92
+ hybrid: {
93
+ type: 'boolean',
94
+ description: 'Hybrid mode: use HTTP fetch for API-backed forms and Playwright for browser-only forms',
95
+ default: false,
96
+ },
97
+ goal: {
98
+ type: 'string',
99
+ description: 'Goal-directed crawl: use an LLM to navigate toward a goal instead of BFS crawling (requires ANTHROPIC_API_KEY)',
100
+ },
101
+ },
102
+ async run({ args }) {
103
+ const url = args.url;
104
+ const depth = parseInt(args.depth ?? '2', 10);
105
+ const maxPages = parseInt(args['max-pages'] ?? '20', 10);
106
+ const timeoutMs = parseInt(args.timeout ?? '300', 10) * 1000;
107
+ const maxSessions = parseInt(args['max-sessions'] ?? '10', 10);
108
+ const hybridMode = args.hybrid ?? false;
109
+ const goal = args.goal;
110
+ // Validate URL
111
+ let parsedUrl;
112
+ try {
113
+ parsedUrl = new URL(url);
114
+ }
115
+ catch (err) {
116
+ return await fail(`Invalid URL: ${url}`, err);
117
+ }
118
+ logger.info(`Analyzing website: ${url}`);
119
+ let siteDescriptor;
120
+ let screenshots;
121
+ let harEntries;
122
+ if (goal) {
123
+ // Goal-directed crawl: use LLM to navigate toward the goal
124
+ logger.info(`Goal-directed crawl: "${goal}"`);
125
+ const result = await goalDirectedCrawl({
126
+ url,
127
+ goal,
128
+ maxSteps: maxPages,
129
+ headless: args.headless ?? false,
130
+ });
131
+ siteDescriptor = result.siteDescriptor;
132
+ screenshots = result.screenshots;
133
+ }
134
+ else {
135
+ // Standard BFS crawl (with optional HAR capture for hybrid mode)
136
+ logger.info(`Crawl depth: ${depth}, Max pages: ${maxPages}`);
137
+ const result = await crawlSite({
138
+ url,
139
+ depth,
140
+ maxPages,
141
+ timeout: timeoutMs,
142
+ headless: args.headless ?? false,
143
+ captureHar: hybridMode,
144
+ });
145
+ siteDescriptor = result.siteDescriptor;
146
+ screenshots = result.screenshots;
147
+ harEntries = result.harEntries;
148
+ }
149
+ const totalForms = siteDescriptor.pages.reduce((n, p) => n + p.forms.length, 0);
150
+ const totalButtons = siteDescriptor.pages.reduce((n, p) => n + p.buttons.length, 0);
151
+ const totalLinks = siteDescriptor.pages.reduce((n, p) => n + p.links.length, 0);
152
+ logger.info(`Discovered: ${siteDescriptor.pages.length} pages, ${totalForms} forms, ${totalButtons} buttons, ${totalLinks} links`);
153
+ logger.info(`Screenshots captured: ${screenshots.size}`);
154
+ if (siteDescriptor.pages.length === 0) {
155
+ await fail('No pages could be analyzed. Check the URL and try again.');
156
+ }
157
+ // Semantic analysis (optional, requires ANTHROPIC_API_KEY)
158
+ if (args['improve-names']) {
159
+ siteDescriptor.pages = await analyzeSemantics(siteDescriptor.pages, screenshots);
160
+ }
161
+ // Auth flow detection
162
+ const authFlow = detectAuthFlow(siteDescriptor.pages);
163
+ if (authFlow) {
164
+ siteDescriptor.authFlow = authFlow;
165
+ }
166
+ // Generate browser-based tools from site descriptor
167
+ const siteTools = generateSiteTools(siteDescriptor);
168
+ // Hybrid mode: classify forms and merge API tools with browser tools
169
+ let tools;
170
+ let hybridClassifications;
171
+ if (hybridMode && harEntries && harEntries.length > 0) {
172
+ logger.info(`Hybrid mode: analyzing ${harEntries.length} captured network requests`);
173
+ // Classify all forms
174
+ const allForms = siteDescriptor.pages.flatMap((p) => p.forms);
175
+ hybridClassifications = classifyForms(allForms, harEntries);
176
+ const apiFormIds = new Set(hybridClassifications.filter((c) => c.strategy === 'api').map((c) => c.formId));
177
+ const apiCount = apiFormIds.size;
178
+ const browserCount = hybridClassifications.length - apiCount;
179
+ logger.info(`Hybrid classification: ${apiCount} API-backed forms, ${browserCount} browser-only forms`);
180
+ // Build API tools from HAR entries
181
+ const targetHost = new URL(url).hostname;
182
+ const filteredHar = filterHarEntries(harEntries, {
183
+ allowedDomains: [targetHost],
184
+ includeErrors: false,
185
+ });
186
+ if (filteredHar.length > 0) {
187
+ const normalized = filteredHar.map(normalizeEntry);
188
+ const deduped = deduplicateEntries(normalized);
189
+ const clusters = clusterEntries(deduped);
190
+ const { operations } = clustersToOperations(clusters);
191
+ const apiTools = buildAllTools(operations);
192
+ // Remove browser-based form tools that have API equivalents
193
+ const browserTools = siteTools.filter((tool) => {
194
+ if (tool.form && apiFormIds.has(tool.form.formId)) {
195
+ return false; // Prefer the API tool
196
+ }
197
+ return true;
198
+ });
199
+ // Convert API ToolDefinitions to SiteToolDefinitions for uniform output
200
+ const apiSiteTools = apiTools.map((apiTool) => ({
201
+ name: apiTool.name,
202
+ title: apiTool.title,
203
+ description: `[API] ${apiTool.description}`,
204
+ inputSchemaCode: apiTool.inputSchemaCode,
205
+ fileName: apiTool.fileName,
206
+ functionName: apiTool.functionName,
207
+ toolType: 'page-action',
208
+ selectors: [],
209
+ returnsScreenshot: false,
210
+ annotations: apiTool.annotations,
211
+ }));
212
+ tools = [...browserTools, ...apiSiteTools];
213
+ // Deduplicate by name
214
+ const seenNames = new Set();
215
+ tools = tools.filter((tool) => {
216
+ if (seenNames.has(tool.name))
217
+ return false;
218
+ seenNames.add(tool.name);
219
+ return true;
220
+ });
221
+ }
222
+ else {
223
+ tools = siteTools;
224
+ }
225
+ }
226
+ else {
227
+ tools = siteTools;
228
+ }
229
+ logger.info(`Generated ${tools.length} MCP tools`);
230
+ if (tools.length === 0) {
231
+ await fail('No interactive elements found on the site.');
232
+ }
233
+ // Build manifest
234
+ const serverName = args.name ?? toPackageName(parsedUrl.hostname);
235
+ const transport = args.transport === 'http' ? 'http' : 'stdio';
236
+ const browserConfig = {
237
+ headless: true,
238
+ idleTimeoutMs: 5 * 60 * 1000,
239
+ viewport: { width: 1280, height: 720 },
240
+ maxSessions,
241
+ };
242
+ const manifest = {
243
+ serverName,
244
+ serverVersion: '1.0.0',
245
+ baseUrl: siteDescriptor.baseUrl,
246
+ transport: transport,
247
+ siteDescriptor,
248
+ tools,
249
+ envVars: [
250
+ {
251
+ name: 'BASE_URL',
252
+ description: 'Target website URL',
253
+ required: true,
254
+ example: siteDescriptor.baseUrl,
255
+ },
256
+ ],
257
+ browserConfig,
258
+ };
259
+ // Emit the project
260
+ logger.info(`Generating Playwright MCP server: ${serverName}`);
261
+ await emitSiteProject(manifest, {
262
+ outputDir: args.output,
263
+ force: args.force ?? false,
264
+ dryRun: args['dry-run'] ?? false,
265
+ });
266
+ logger.success(`Site MCP server generated at: ${args.output}`);
267
+ logger.info('');
268
+ logger.info(`Pages analyzed: ${siteDescriptor.pages.length}`);
269
+ logger.info(`Tools generated: ${tools.length}`);
270
+ logger.info('');
271
+ logger.info('Tools:');
272
+ for (const tool of tools) {
273
+ const typeTag = `[${tool.toolType}]`;
274
+ logger.info(` ${tool.name} ${typeTag} — ${tool.description.slice(0, 60)}`);
275
+ }
276
+ logger.info('');
277
+ logger.info('Next steps:');
278
+ logger.info(` cd ${args.output}`);
279
+ logger.info(' npm install');
280
+ logger.info(' npx playwright install chromium');
281
+ logger.info(' npm run build');
282
+ logger.info(' npm start');
283
+ },
284
+ });
@@ -0,0 +1,24 @@
1
+ export interface LintResult {
2
+ level: 'error' | 'warn' | 'info';
3
+ rule: string;
4
+ tool: string;
5
+ message: string;
6
+ }
7
+ declare const _default: import("citty").CommandDef<{
8
+ spec: {
9
+ type: "positional";
10
+ description: string;
11
+ required: true;
12
+ };
13
+ format: {
14
+ type: "string";
15
+ description: string;
16
+ default: string;
17
+ };
18
+ level: {
19
+ type: "string";
20
+ description: string;
21
+ default: string;
22
+ };
23
+ }>;
24
+ export default _default;
@@ -0,0 +1,184 @@
1
+ import { defineCommand } from 'citty';
2
+ import { loadOpenApiSpec } from '../parser/openapi-loader.js';
3
+ import { extractOperations } from '../parser/operation-extractor.js';
4
+ import { buildAllTools } from '../transformer/tool-builder.js';
5
+ import { logger } from '../utils/logger.js';
6
+ import { fail } from '../utils/fail.js';
7
+ const MCP_BUILT_INS = new Set([
8
+ 'ping',
9
+ 'initialize',
10
+ 'notifications/initialized',
11
+ 'notifications/cancelled',
12
+ 'notifications/progress',
13
+ 'tools/list',
14
+ 'tools/call',
15
+ 'resources/list',
16
+ 'resources/read',
17
+ 'resources/subscribe',
18
+ 'resources/unsubscribe',
19
+ 'prompts/list',
20
+ 'prompts/get',
21
+ 'logging/setLevel',
22
+ 'completion/complete',
23
+ 'sampling/createMessage',
24
+ 'roots/list',
25
+ ]);
26
+ function lintTools(tools) {
27
+ const results = [];
28
+ // duplicate-names: check for collisions
29
+ const nameCount = new Map();
30
+ for (const tool of tools) {
31
+ nameCount.set(tool.name, (nameCount.get(tool.name) ?? 0) + 1);
32
+ }
33
+ for (const [name, count] of nameCount) {
34
+ if (count > 1) {
35
+ results.push({
36
+ level: 'error',
37
+ rule: 'duplicate-names',
38
+ tool: name,
39
+ message: `Tool name "${name}" is used ${count} times — names must be unique`,
40
+ });
41
+ }
42
+ }
43
+ for (const tool of tools) {
44
+ // tool-name-length
45
+ if (tool.name.length > 128) {
46
+ results.push({
47
+ level: 'error',
48
+ rule: 'tool-name-length',
49
+ tool: tool.name,
50
+ message: `Name is ${tool.name.length} chars — exceeds MCP spec limit of 128`,
51
+ });
52
+ }
53
+ else if (tool.name.length > 60) {
54
+ results.push({
55
+ level: 'warn',
56
+ rule: 'tool-name-length',
57
+ tool: tool.name,
58
+ message: `Name is ${tool.name.length} chars — exceeds Cursor limit of 60`,
59
+ });
60
+ }
61
+ // description-quality
62
+ if (!tool.description || tool.description.trim().length === 0) {
63
+ results.push({
64
+ level: 'warn',
65
+ rule: 'description-quality',
66
+ tool: tool.name,
67
+ message: 'Description is empty — AI clients need good descriptions to pick the right tool',
68
+ });
69
+ }
70
+ else if (tool.description.trim().length < 10) {
71
+ results.push({
72
+ level: 'warn',
73
+ rule: 'description-quality',
74
+ tool: tool.name,
75
+ message: `Description is only ${tool.description.trim().length} chars — consider adding more detail`,
76
+ });
77
+ }
78
+ // description-length
79
+ if (tool.description && tool.description.length > 1024) {
80
+ results.push({
81
+ level: 'warn',
82
+ rule: 'description-length',
83
+ tool: tool.name,
84
+ message: `Description is ${tool.description.length} chars — over 1024 wastes context window`,
85
+ });
86
+ }
87
+ // parameter-count
88
+ const paramCount = tool.pathParams.length +
89
+ tool.queryParams.length +
90
+ tool.headerParams.length +
91
+ (tool.hasRequestBody ? 1 : 0);
92
+ if (paramCount > 10) {
93
+ results.push({
94
+ level: 'warn',
95
+ rule: 'parameter-count',
96
+ tool: tool.name,
97
+ message: `Has ${paramCount} parameters — more than 10 is complex for AI to fill correctly`,
98
+ });
99
+ }
100
+ // missing-annotations
101
+ if (!tool.annotations) {
102
+ results.push({
103
+ level: 'info',
104
+ rule: 'missing-annotations',
105
+ tool: tool.name,
106
+ message: 'No readOnlyHint/destructiveHint annotations set — consider adding for safety',
107
+ });
108
+ }
109
+ // reserved-names
110
+ if (MCP_BUILT_INS.has(tool.name)) {
111
+ results.push({
112
+ level: 'error',
113
+ rule: 'reserved-names',
114
+ tool: tool.name,
115
+ message: `"${tool.name}" conflicts with MCP built-in method name`,
116
+ });
117
+ }
118
+ }
119
+ return results;
120
+ }
121
+ export default defineCommand({
122
+ meta: {
123
+ name: 'lint',
124
+ description: 'Lint an OpenAPI spec for MCP compatibility issues',
125
+ },
126
+ args: {
127
+ spec: {
128
+ type: 'positional',
129
+ description: 'Path or URL to an OpenAPI spec',
130
+ required: true,
131
+ },
132
+ format: {
133
+ type: 'string',
134
+ description: 'Output format: "text" (default) or "json"',
135
+ default: 'text',
136
+ },
137
+ level: {
138
+ type: 'string',
139
+ description: 'Minimum level to show: "info", "warn", or "error"',
140
+ default: 'info',
141
+ },
142
+ },
143
+ async run({ args }) {
144
+ logger.info(`Linting spec: ${args.spec}`);
145
+ const { api } = await loadOpenApiSpec(args.spec);
146
+ const { operations } = extractOperations(api);
147
+ if (operations.length === 0) {
148
+ await fail('No operations found in the spec.');
149
+ }
150
+ const tools = buildAllTools(operations);
151
+ logger.info(`Analyzing ${tools.length} tools...\n`);
152
+ const allResults = lintTools(tools);
153
+ // Filter by level
154
+ const levelOrder = { info: 0, warn: 1, error: 2 };
155
+ const minLevel = levelOrder[args.level ?? 'info'] ?? 0;
156
+ const results = allResults.filter((r) => levelOrder[r.level] >= minLevel);
157
+ if (args.format === 'json') {
158
+ console.log(JSON.stringify(results, null, 2));
159
+ }
160
+ else {
161
+ const icons = { error: 'ERROR', warn: 'WARN ', info: 'INFO ' };
162
+ for (const r of results) {
163
+ const icon = icons[r.level];
164
+ console.log(` ${icon} [${r.rule}] ${r.tool}: ${r.message}`);
165
+ }
166
+ // Summary
167
+ const errors = allResults.filter((r) => r.level === 'error').length;
168
+ const warns = allResults.filter((r) => r.level === 'warn').length;
169
+ const infos = allResults.filter((r) => r.level === 'info').length;
170
+ console.log('');
171
+ console.log(` ${tools.length} tools, ${allResults.length} issues: ${errors} errors, ${warns} warnings, ${infos} info`);
172
+ if (errors > 0) {
173
+ console.log('');
174
+ }
175
+ else if (allResults.length === 0) {
176
+ logger.success('No issues found');
177
+ }
178
+ }
179
+ // Exit code 1 if any errors
180
+ if (allResults.some((r) => r.level === 'error')) {
181
+ await fail('Lint failed with errors');
182
+ }
183
+ },
184
+ });
@@ -0,0 +1,18 @@
1
+ declare const _default: import("citty").CommandDef<{
2
+ spec1: {
3
+ type: "positional";
4
+ description: string;
5
+ required: true;
6
+ };
7
+ spec2: {
8
+ type: "positional";
9
+ description: string;
10
+ required: true;
11
+ };
12
+ output: {
13
+ type: "string";
14
+ alias: string;
15
+ description: string;
16
+ };
17
+ }>;
18
+ export default _default;
@@ -0,0 +1,161 @@
1
+ import { defineCommand } from 'citty';
2
+ import { writeFile } from 'node:fs/promises';
3
+ import { stringify as yamlStringify } from 'yaml';
4
+ import { loadOpenApiSpec } from '../parser/openapi-loader.js';
5
+ import { logger } from '../utils/logger.js';
6
+ export default defineCommand({
7
+ meta: {
8
+ name: 'merge',
9
+ description: 'Merge two OpenAPI specs into one',
10
+ },
11
+ args: {
12
+ spec1: {
13
+ type: 'positional',
14
+ description: 'Path to the first OpenAPI spec (used as base for info/servers)',
15
+ required: true,
16
+ },
17
+ spec2: {
18
+ type: 'positional',
19
+ description: 'Path to the second OpenAPI spec to merge in',
20
+ required: true,
21
+ },
22
+ output: {
23
+ type: 'string',
24
+ alias: 'o',
25
+ description: 'Output file path (default: stdout)',
26
+ },
27
+ },
28
+ async run({ args }) {
29
+ logger.info(`Merging specs: ${args.spec1} + ${args.spec2}`);
30
+ const { api: api1 } = await loadOpenApiSpec(args.spec1);
31
+ const { api: api2 } = await loadOpenApiSpec(args.spec2);
32
+ const spec1 = api1;
33
+ const spec2 = api2;
34
+ const merged = mergeSpecs(spec1, spec2);
35
+ const output = yamlStringify(merged, { lineWidth: 120 });
36
+ if (args.output) {
37
+ await writeFile(args.output, output, 'utf-8');
38
+ logger.success(`Merged spec written to: ${args.output}`);
39
+ }
40
+ else {
41
+ process.stdout.write(output);
42
+ }
43
+ },
44
+ });
45
+ function mergeSpecs(base, other) {
46
+ const merged = {
47
+ openapi: base.openapi ?? '3.0.0',
48
+ info: base.info,
49
+ servers: base.servers,
50
+ paths: { ...base.paths },
51
+ components: {
52
+ schemas: {},
53
+ securitySchemes: {},
54
+ ...(base.components ?? {}),
55
+ },
56
+ };
57
+ // Merge paths
58
+ const basePaths = base.paths ?? {};
59
+ const otherPaths = other.paths ?? {};
60
+ for (const [path, pathItem] of Object.entries(otherPaths)) {
61
+ if (basePaths[path]) {
62
+ // Check for method-level conflicts
63
+ const baseItem = basePaths[path];
64
+ const otherItem = pathItem;
65
+ const methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'];
66
+ for (const method of methods) {
67
+ if (baseItem[method] && otherItem[method]) {
68
+ throw new Error(`Path conflict: ${method.toUpperCase()} ${path} exists in both specs`);
69
+ }
70
+ }
71
+ // No method conflicts — merge path items
72
+ merged.paths[path] = { ...baseItem, ...otherItem };
73
+ }
74
+ else {
75
+ merged.paths[path] = pathItem;
76
+ }
77
+ }
78
+ // Merge components.schemas
79
+ const baseSchemas = base.components?.schemas ?? {};
80
+ const otherSchemas = other.components?.schemas ?? {};
81
+ for (const [name, schema] of Object.entries(otherSchemas)) {
82
+ if (baseSchemas[name]) {
83
+ throw new Error(`Schema conflict: "${name}" exists in both specs`);
84
+ }
85
+ merged.components.schemas[name] = schema;
86
+ }
87
+ // Merge components.securitySchemes
88
+ const baseSecSchemes = base.components?.securitySchemes ?? {};
89
+ const otherSecSchemes = other.components?.securitySchemes ?? {};
90
+ for (const [name, scheme] of Object.entries(otherSecSchemes)) {
91
+ if (baseSecSchemes[name]) {
92
+ // If both have the same security scheme name, check if they're identical
93
+ const baseScheme = JSON.stringify(baseSecSchemes[name]);
94
+ const otherScheme = JSON.stringify(scheme);
95
+ if (baseScheme !== otherScheme) {
96
+ throw new Error(`Security scheme conflict: "${name}" differs between specs`);
97
+ }
98
+ // Same scheme — skip silently
99
+ }
100
+ else {
101
+ merged.components.securitySchemes[name] = scheme;
102
+ }
103
+ }
104
+ // Merge other component types if present
105
+ const componentTypes = [
106
+ 'parameters',
107
+ 'requestBodies',
108
+ 'responses',
109
+ 'headers',
110
+ 'examples',
111
+ 'links',
112
+ 'callbacks',
113
+ ];
114
+ for (const compType of componentTypes) {
115
+ const baseComp = base.components?.[compType];
116
+ const otherComp = other.components?.[compType];
117
+ if (otherComp) {
118
+ if (!baseComp) {
119
+ merged.components[compType] = { ...otherComp };
120
+ }
121
+ else {
122
+ for (const [name, value] of Object.entries(otherComp)) {
123
+ if (baseComp[name]) {
124
+ throw new Error(`Component conflict in ${compType}: "${name}" exists in both specs`);
125
+ }
126
+ merged.components[compType][name] = value;
127
+ }
128
+ }
129
+ }
130
+ }
131
+ // Merge tags (deduplicate by name)
132
+ if (base.tags || other.tags) {
133
+ const tagMap = new Map();
134
+ for (const tag of base.tags ?? []) {
135
+ tagMap.set(tag.name, tag);
136
+ }
137
+ for (const tag of other.tags ?? []) {
138
+ if (!tagMap.has(tag.name)) {
139
+ tagMap.set(tag.name, tag);
140
+ }
141
+ }
142
+ merged.tags = [...tagMap.values()];
143
+ }
144
+ // Merge top-level security (union)
145
+ if (base.security || other.security) {
146
+ const seen = new Set();
147
+ const mergedSecurity = [];
148
+ for (const req of [...(base.security ?? []), ...(other.security ?? [])]) {
149
+ const key = JSON.stringify(req);
150
+ if (!seen.has(key)) {
151
+ seen.add(key);
152
+ mergedSecurity.push(req);
153
+ }
154
+ }
155
+ merged.security = mergedSecurity;
156
+ }
157
+ const pathCount = Object.keys(merged.paths).length;
158
+ const schemaCount = Object.keys(merged.components?.schemas ?? {}).length;
159
+ logger.info(`Merged result: ${pathCount} paths, ${schemaCount} schemas`);
160
+ return merged;
161
+ }