github-to-mcp-monorepo 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (388) hide show
  1. package/.env.example +8 -0
  2. package/.github/CODEOWNERS +6 -0
  3. package/.husky/pre-commit +1 -0
  4. package/.nvmrc +1 -0
  5. package/.prettierignore +5 -0
  6. package/.prettierrc +7 -0
  7. package/.vscode/settings.json +4 -0
  8. package/ARCHITECTURE.md +1429 -0
  9. package/CHANGELOG.md +167 -0
  10. package/CONTRIBUTING.md +327 -0
  11. package/LICENSE +201 -0
  12. package/README.md +1028 -0
  13. package/SECURITY.md +248 -0
  14. package/VISUAL_GUIDE.md +437 -0
  15. package/apps/vscode/IMPLEMENTATION.md +480 -0
  16. package/apps/vscode/README.md +248 -0
  17. package/apps/vscode/package.json +381 -0
  18. package/apps/vscode/resources/icon.png +0 -0
  19. package/apps/vscode/resources/icon.svg +5 -0
  20. package/apps/vscode/src/commands/browseRegistry.ts +211 -0
  21. package/apps/vscode/src/commands/configureClaudeDesktop.ts +332 -0
  22. package/apps/vscode/src/commands/convert.ts +82 -0
  23. package/apps/vscode/src/commands/convertCurrentRepo.ts +109 -0
  24. package/apps/vscode/src/commands/convertFromUrl.ts +138 -0
  25. package/apps/vscode/src/commands/index.ts +121 -0
  26. package/apps/vscode/src/commands/validate.ts +197 -0
  27. package/apps/vscode/src/extension.ts +464 -0
  28. package/apps/vscode/src/global.d.ts +36 -0
  29. package/apps/vscode/src/test/extension.test.ts +73 -0
  30. package/apps/vscode/src/utils/file-generator.ts +529 -0
  31. package/apps/vscode/src/utils/github-api.ts +335 -0
  32. package/apps/vscode/src/utils/index.ts +29 -0
  33. package/apps/vscode/src/utils/mcp-config.ts +334 -0
  34. package/apps/vscode/src/utils/storage.ts +87 -0
  35. package/apps/vscode/src/views/McpServersTreeView.ts +160 -0
  36. package/apps/vscode/src/views/OutputChannelView.ts +195 -0
  37. package/apps/vscode/src/views/StatusBarItem.ts +251 -0
  38. package/apps/vscode/src/views/ToolsExplorerView.ts +314 -0
  39. package/apps/vscode/src/views/historyProvider.ts +75 -0
  40. package/apps/vscode/src/views/index.ts +12 -0
  41. package/apps/vscode/src/views/resultsPanel.ts +330 -0
  42. package/apps/vscode/src/webviews/ConversionPanel.ts +350 -0
  43. package/apps/vscode/src/webviews/ToolDetailsPanel.ts +448 -0
  44. package/apps/vscode/src/webviews/index.ts +9 -0
  45. package/apps/vscode/src/webviews/webview-ui/styles.ts +492 -0
  46. package/apps/vscode/tsconfig.json +20 -0
  47. package/apps/web/PLAYGROUND_GUIDE.md +499 -0
  48. package/apps/web/README.md +505 -0
  49. package/apps/web/app/api/convert/route.ts +100 -0
  50. package/apps/web/app/api/convert/stream/route.ts +198 -0
  51. package/apps/web/app/api/deploy/route.ts +157 -0
  52. package/apps/web/app/api/edge/route.ts +308 -0
  53. package/apps/web/app/api/export-docker/route.ts +284 -0
  54. package/apps/web/app/api/generate-openapi/route.ts +119 -0
  55. package/apps/web/app/api/mcp/[serverId]/route.ts +263 -0
  56. package/apps/web/app/api/playground/connect/route.ts +143 -0
  57. package/apps/web/app/api/playground/disconnect/route.ts +78 -0
  58. package/apps/web/app/api/playground/execute/route.ts +135 -0
  59. package/apps/web/app/api/playground/sessions/route.ts +103 -0
  60. package/apps/web/app/api/playground/tools/route.ts +117 -0
  61. package/apps/web/app/api/playground/v2/connect/route.ts +96 -0
  62. package/apps/web/app/api/playground/v2/disconnect/route.ts +88 -0
  63. package/apps/web/app/api/playground/v2/health/route.ts +80 -0
  64. package/apps/web/app/api/playground/v2/prompts/route.ts +160 -0
  65. package/apps/web/app/api/playground/v2/resources/route.ts +159 -0
  66. package/apps/web/app/api/playground/v2/sessions/route.ts +184 -0
  67. package/apps/web/app/api/playground/v2/tools/route.ts +167 -0
  68. package/apps/web/app/api/stream/route.ts +232 -0
  69. package/apps/web/app/batch/BatchConvertClient.tsx +190 -0
  70. package/apps/web/app/batch/page.tsx +37 -0
  71. package/apps/web/app/convert/page.tsx +269 -0
  72. package/apps/web/app/dashboard/page.tsx +380 -0
  73. package/apps/web/app/globals.css +622 -0
  74. package/apps/web/app/layout.tsx +120 -0
  75. package/apps/web/app/manifest.ts +31 -0
  76. package/apps/web/app/opengraph-image.tsx +112 -0
  77. package/apps/web/app/page.old.tsx +924 -0
  78. package/apps/web/app/page.tsx +77 -0
  79. package/apps/web/app/playground/page.tsx +306 -0
  80. package/apps/web/app/playground/v2/error.tsx +163 -0
  81. package/apps/web/app/playground/v2/layout.tsx +58 -0
  82. package/apps/web/app/playground/v2/loading.tsx +152 -0
  83. package/apps/web/app/playground/v2/page.tsx +644 -0
  84. package/apps/web/app/playground/v2/providers.tsx +214 -0
  85. package/apps/web/app/playground/v2/use-shortcuts.ts +209 -0
  86. package/apps/web/app/playground/v2/use-url-state.ts +296 -0
  87. package/apps/web/app/providers.tsx +22 -0
  88. package/apps/web/app/sitemap.ts +32 -0
  89. package/apps/web/app/twitter-image.tsx +112 -0
  90. package/apps/web/components/BranchSelector.tsx +401 -0
  91. package/apps/web/components/ClaudeConfigExport.tsx +226 -0
  92. package/apps/web/components/Features.tsx +84 -0
  93. package/apps/web/components/Footer.tsx +119 -0
  94. package/apps/web/components/GenerationProgress.tsx +248 -0
  95. package/apps/web/components/GithubUrlInput.tsx +483 -0
  96. package/apps/web/components/Header.tsx +175 -0
  97. package/apps/web/components/Hero.tsx +117 -0
  98. package/apps/web/components/HowItWorks.tsx +119 -0
  99. package/apps/web/components/InstallBanner.tsx +158 -0
  100. package/apps/web/components/Logo.tsx +116 -0
  101. package/apps/web/components/ParticleBackground.tsx +105 -0
  102. package/apps/web/components/Playground.tsx +472 -0
  103. package/apps/web/components/PlaygroundToolTester.tsx +410 -0
  104. package/apps/web/components/ProductCards.tsx +179 -0
  105. package/apps/web/components/SplitView.tsx +194 -0
  106. package/apps/web/components/ToolFilter.tsx +260 -0
  107. package/apps/web/components/ToolList.tsx +325 -0
  108. package/apps/web/components/batch/BatchConvert.tsx +785 -0
  109. package/apps/web/components/batch/index.ts +7 -0
  110. package/apps/web/components/convert/ConfigTabs.tsx +230 -0
  111. package/apps/web/components/convert/ConversionResult.tsx +482 -0
  112. package/apps/web/components/convert/InlinePlayground.tsx +259 -0
  113. package/apps/web/components/convert/LoadingSteps.tsx +311 -0
  114. package/apps/web/components/convert/OneClickInstall.tsx +224 -0
  115. package/apps/web/components/convert/ToolCard.tsx +189 -0
  116. package/apps/web/components/convert/TryInPlayground.tsx +242 -0
  117. package/apps/web/components/convert/index.ts +12 -0
  118. package/apps/web/components/deploy/DeployButton.tsx +369 -0
  119. package/apps/web/components/deploy/index.ts +7 -0
  120. package/apps/web/components/docker/DockerExport.tsx +690 -0
  121. package/apps/web/components/docker/index.ts +7 -0
  122. package/apps/web/components/install/OneClickInstall.tsx +676 -0
  123. package/apps/web/components/install/index.ts +7 -0
  124. package/apps/web/components/playground/CapabilityTabs.tsx +150 -0
  125. package/apps/web/components/playground/ConnectionStatusV2.tsx +322 -0
  126. package/apps/web/components/playground/EmptyStates.tsx +305 -0
  127. package/apps/web/components/playground/ExecutionLog.tsx +260 -0
  128. package/apps/web/components/playground/ExecutionLogV2.tsx +378 -0
  129. package/apps/web/components/playground/JsonViewer.tsx +388 -0
  130. package/apps/web/components/playground/PlaygroundLayout.tsx +244 -0
  131. package/apps/web/components/playground/PromptsPanel.tsx +385 -0
  132. package/apps/web/components/playground/ResourcesPanel.tsx +378 -0
  133. package/apps/web/components/playground/SchemaForm.tsx +477 -0
  134. package/apps/web/components/playground/ServerStatus.tsx +151 -0
  135. package/apps/web/components/playground/ShareButton.tsx +239 -0
  136. package/apps/web/components/playground/ToolsPanel.tsx +309 -0
  137. package/apps/web/components/playground/TransportConfigurator.tsx +563 -0
  138. package/apps/web/components/playground/index.ts +74 -0
  139. package/apps/web/components/playground/types.ts +202 -0
  140. package/apps/web/components/streaming/StreamingProgress.tsx +441 -0
  141. package/apps/web/components/streaming/index.ts +7 -0
  142. package/apps/web/components/ui/badge.tsx +42 -0
  143. package/apps/web/components/ui/button.tsx +88 -0
  144. package/apps/web/components/ui/card.tsx +75 -0
  145. package/apps/web/components/ui/code-block.tsx +122 -0
  146. package/apps/web/components/ui/index.ts +12 -0
  147. package/apps/web/components/ui/input.tsx +55 -0
  148. package/apps/web/components/ui/tabs.tsx +61 -0
  149. package/apps/web/hooks/index.ts +85 -0
  150. package/apps/web/hooks/types.ts +1173 -0
  151. package/apps/web/hooks/use-conversion.ts +133 -0
  152. package/apps/web/hooks/use-execution-history.ts +376 -0
  153. package/apps/web/hooks/use-generation-progress.ts +147 -0
  154. package/apps/web/hooks/use-local-storage.ts +88 -0
  155. package/apps/web/hooks/use-mcp-client.ts +623 -0
  156. package/apps/web/hooks/use-mcp-connection.ts +500 -0
  157. package/apps/web/hooks/use-mcp-execution.ts +282 -0
  158. package/apps/web/hooks/use-mcp-prompts.ts +441 -0
  159. package/apps/web/hooks/use-mcp-resources.ts +430 -0
  160. package/apps/web/hooks/use-mcp-tools.ts +540 -0
  161. package/apps/web/hooks/use-playground-store.ts +299 -0
  162. package/apps/web/hooks/use-playground.ts +184 -0
  163. package/apps/web/hooks/use-streaming-conversion.ts +227 -0
  164. package/apps/web/hooks/useBatchConversion.ts +271 -0
  165. package/apps/web/hooks/useDockerConfig.ts +161 -0
  166. package/apps/web/hooks/usePlatformDetection.ts +80 -0
  167. package/apps/web/hooks/useStreaming.ts +199 -0
  168. package/apps/web/lib/api/errors.ts +386 -0
  169. package/apps/web/lib/api/index.ts +137 -0
  170. package/apps/web/lib/api/logger.ts +187 -0
  171. package/apps/web/lib/api/middleware.ts +364 -0
  172. package/apps/web/lib/api/openapi.ts +977 -0
  173. package/apps/web/lib/api/session-manager.ts +594 -0
  174. package/apps/web/lib/api/types.ts +433 -0
  175. package/apps/web/lib/api/validation.ts +523 -0
  176. package/apps/web/lib/constants.ts +114 -0
  177. package/apps/web/lib/mcp/client.ts +1137 -0
  178. package/apps/web/lib/mcp/events.ts +651 -0
  179. package/apps/web/lib/mcp/index.ts +347 -0
  180. package/apps/web/lib/mcp/logger.ts +428 -0
  181. package/apps/web/lib/mcp/metrics.ts +703 -0
  182. package/apps/web/lib/mcp/retry.ts +616 -0
  183. package/apps/web/lib/mcp/session-manager.ts +779 -0
  184. package/apps/web/lib/mcp/transports.ts +988 -0
  185. package/apps/web/lib/mcp/types.ts +594 -0
  186. package/apps/web/lib/mcp-client-enhanced.ts +871 -0
  187. package/apps/web/lib/mcp-client.ts +778 -0
  188. package/apps/web/lib/mcp-errors.ts +489 -0
  189. package/apps/web/lib/mcp-sandbox.ts +593 -0
  190. package/apps/web/lib/mcp-testing.ts +428 -0
  191. package/apps/web/lib/mcp-types.ts +448 -0
  192. package/apps/web/lib/playground-store.tsx +1147 -0
  193. package/apps/web/lib/utils.ts +439 -0
  194. package/apps/web/next-env.d.ts +5 -0
  195. package/apps/web/next.config.js +23 -0
  196. package/apps/web/package.json +55 -0
  197. package/apps/web/postcss.config.js +6 -0
  198. package/apps/web/public/.well-known/ai-plugin.json +17 -0
  199. package/apps/web/public/logo.svg +6 -0
  200. package/apps/web/public/robots.txt +22 -0
  201. package/apps/web/public/schema.json +27 -0
  202. package/apps/web/tailwind.config.js +26 -0
  203. package/apps/web/tailwind.config.ts +123 -0
  204. package/apps/web/tsconfig.json +20 -0
  205. package/apps/web/types/deploy.ts +139 -0
  206. package/apps/web/types/index.ts +247 -0
  207. package/apps/web/vercel.json +39 -0
  208. package/eslint.config.mjs +23 -0
  209. package/llms.txt +102 -0
  210. package/mkdocs/docs/api/core.md +318 -0
  211. package/mkdocs/docs/api/index.md +128 -0
  212. package/mkdocs/docs/api/mcp-server.md +301 -0
  213. package/mkdocs/docs/api/openapi-parser.md +254 -0
  214. package/mkdocs/docs/assets/logo.svg +7 -0
  215. package/mkdocs/docs/changelog.md +118 -0
  216. package/mkdocs/docs/cli/generate.md +148 -0
  217. package/mkdocs/docs/cli/index.md +52 -0
  218. package/mkdocs/docs/cli/inspect.md +164 -0
  219. package/mkdocs/docs/cli/serve.md +136 -0
  220. package/mkdocs/docs/concepts/classification.md +254 -0
  221. package/mkdocs/docs/concepts/how-it-works.md +299 -0
  222. package/mkdocs/docs/concepts/index.md +77 -0
  223. package/mkdocs/docs/concepts/mcp-protocol.md +362 -0
  224. package/mkdocs/docs/concepts/tool-types.md +382 -0
  225. package/mkdocs/docs/contributing/architecture.md +262 -0
  226. package/mkdocs/docs/contributing/development.md +245 -0
  227. package/mkdocs/docs/contributing/index.md +73 -0
  228. package/mkdocs/docs/contributing/testing.md +320 -0
  229. package/mkdocs/docs/getting-started/configuration.md +235 -0
  230. package/mkdocs/docs/getting-started/index.md +54 -0
  231. package/mkdocs/docs/getting-started/installation.md +145 -0
  232. package/mkdocs/docs/getting-started/quickstart.md +160 -0
  233. package/mkdocs/docs/guides/batch.md +375 -0
  234. package/mkdocs/docs/guides/claude-desktop.md +227 -0
  235. package/mkdocs/docs/guides/cursor.md +188 -0
  236. package/mkdocs/docs/guides/custom-tools.md +367 -0
  237. package/mkdocs/docs/guides/index.md +78 -0
  238. package/mkdocs/docs/guides/private-repos.md +221 -0
  239. package/mkdocs/docs/guides/vscode.md +247 -0
  240. package/mkdocs/docs/index.md +175 -0
  241. package/mkdocs/docs/reference/config.md +223 -0
  242. package/mkdocs/docs/reference/env.md +192 -0
  243. package/mkdocs/docs/reference/index.md +102 -0
  244. package/mkdocs/docs/reference/tools.md +309 -0
  245. package/mkdocs/docs/stylesheets/extra.css +231 -0
  246. package/mkdocs/mkdocs.yml +204 -0
  247. package/mkdocs/overrides/.gitkeep +1 -0
  248. package/mkdocs/overrides/main.html +7 -0
  249. package/mkdocs/python-deps.txt +7 -0
  250. package/mkdocs/vercel.json +11 -0
  251. package/package.json +63 -0
  252. package/packages/core/package.json +61 -0
  253. package/packages/core/src/__tests__/bitbucket-client.test.ts +366 -0
  254. package/packages/core/src/__tests__/cli.test.ts +235 -0
  255. package/packages/core/src/__tests__/code-extractor.test.ts +378 -0
  256. package/packages/core/src/__tests__/docker-generator.test.ts +255 -0
  257. package/packages/core/src/__tests__/github-client.test.ts +390 -0
  258. package/packages/core/src/__tests__/gitlab-client.test.ts +319 -0
  259. package/packages/core/src/__tests__/go-extractor.test.ts +351 -0
  260. package/packages/core/src/__tests__/graphql-extractor.test.ts +330 -0
  261. package/packages/core/src/__tests__/java-extractor.test.ts +497 -0
  262. package/packages/core/src/__tests__/plugins.test.ts +467 -0
  263. package/packages/core/src/__tests__/readme-extractor.test.ts +258 -0
  264. package/packages/core/src/__tests__/redis-cache.test.ts +307 -0
  265. package/packages/core/src/__tests__/rust-extractor.test.ts +252 -0
  266. package/packages/core/src/__tests__/streaming.test.ts +251 -0
  267. package/packages/core/src/additional-extractors.ts +333 -0
  268. package/packages/core/src/cache/cache-interface.ts +179 -0
  269. package/packages/core/src/cache/index.ts +210 -0
  270. package/packages/core/src/cache/redis-cache.ts +291 -0
  271. package/packages/core/src/cache/upstash-cache.ts +379 -0
  272. package/packages/core/src/cache.ts +251 -0
  273. package/packages/core/src/cli.ts +822 -0
  274. package/packages/core/src/code-extractor.ts +696 -0
  275. package/packages/core/src/docker-generator.ts +470 -0
  276. package/packages/core/src/edge-compatible.ts +491 -0
  277. package/packages/core/src/extractors/go-extractor.ts +791 -0
  278. package/packages/core/src/extractors/index.ts +9 -0
  279. package/packages/core/src/extractors/java-extractor.ts +937 -0
  280. package/packages/core/src/extractors/rust-extractor.ts +744 -0
  281. package/packages/core/src/github-client.ts +319 -0
  282. package/packages/core/src/go-generator.ts +356 -0
  283. package/packages/core/src/graphql-extractor.ts +358 -0
  284. package/packages/core/src/index.ts +797 -0
  285. package/packages/core/src/langchain-exporter.ts +617 -0
  286. package/packages/core/src/language-parsers.ts +1114 -0
  287. package/packages/core/src/mcp-introspector.ts +279 -0
  288. package/packages/core/src/monorepo-detector.ts +378 -0
  289. package/packages/core/src/plugins/index.ts +370 -0
  290. package/packages/core/src/plugins/registry.ts +404 -0
  291. package/packages/core/src/plugins/types.ts +215 -0
  292. package/packages/core/src/providers/base-provider.ts +246 -0
  293. package/packages/core/src/providers/bitbucket-client.ts +464 -0
  294. package/packages/core/src/providers/gitlab-client.ts +388 -0
  295. package/packages/core/src/providers/index.ts +176 -0
  296. package/packages/core/src/python-generator.ts +260 -0
  297. package/packages/core/src/queue/index.ts +100 -0
  298. package/packages/core/src/queue/memory-queue.ts +445 -0
  299. package/packages/core/src/queue/redis-queue.ts +578 -0
  300. package/packages/core/src/queue/types.ts +251 -0
  301. package/packages/core/src/readme-extractor.ts +409 -0
  302. package/packages/core/src/schema-generator.ts +638 -0
  303. package/packages/core/src/streaming.ts +999 -0
  304. package/packages/core/src/types.ts +289 -0
  305. package/packages/core/tsconfig.json +9 -0
  306. package/packages/core/tsup.config.ts +25 -0
  307. package/packages/mcp-server/README.md +297 -0
  308. package/packages/mcp-server/package.json +55 -0
  309. package/packages/mcp-server/src/__tests__/mcp-server.test.ts +177 -0
  310. package/packages/mcp-server/src/__tests__/tools.test.ts +217 -0
  311. package/packages/mcp-server/src/index.ts +1206 -0
  312. package/packages/mcp-server/src/prompts/index.ts +601 -0
  313. package/packages/mcp-server/src/tools/export-docker.ts +362 -0
  314. package/packages/mcp-server/src/tools/generate-openapi.ts +162 -0
  315. package/packages/mcp-server/src/tools/monitor-mcp-server.ts +448 -0
  316. package/packages/mcp-server/src/tools/stream-convert.ts +398 -0
  317. package/packages/mcp-server/src/tools/test-mcp-tool.ts +531 -0
  318. package/packages/mcp-server/tsconfig.json +12 -0
  319. package/packages/mcp-server/tsup.config.ts +14 -0
  320. package/packages/openapi-parser/package-lock.json +3028 -0
  321. package/packages/openapi-parser/package.json +41 -0
  322. package/packages/openapi-parser/src/analyzer.ts +700 -0
  323. package/packages/openapi-parser/src/asyncapi-parser.ts +475 -0
  324. package/packages/openapi-parser/src/cli.ts +302 -0
  325. package/packages/openapi-parser/src/generator.ts +570 -0
  326. package/packages/openapi-parser/src/generators/express-analyzer.ts +649 -0
  327. package/packages/openapi-parser/src/generators/fastapi-analyzer.ts +960 -0
  328. package/packages/openapi-parser/src/generators/index.ts +200 -0
  329. package/packages/openapi-parser/src/generators/nextjs-analyzer.ts +768 -0
  330. package/packages/openapi-parser/src/generators/openapi-builder.ts +527 -0
  331. package/packages/openapi-parser/src/generators/types.ts +298 -0
  332. package/packages/openapi-parser/src/graphql-parser.ts +462 -0
  333. package/packages/openapi-parser/src/grpc-parser.ts +649 -0
  334. package/packages/openapi-parser/src/har-parser.ts +723 -0
  335. package/packages/openapi-parser/src/index.ts +635 -0
  336. package/packages/openapi-parser/src/insomnia-parser.ts +614 -0
  337. package/packages/openapi-parser/src/parser.ts +231 -0
  338. package/packages/openapi-parser/src/postman-parser.ts +611 -0
  339. package/packages/openapi-parser/src/ref-resolver.ts +313 -0
  340. package/packages/openapi-parser/src/transformer.ts +459 -0
  341. package/packages/openapi-parser/tests/generators/express.test.ts +209 -0
  342. package/packages/openapi-parser/tests/generators/fastapi.test.ts +236 -0
  343. package/packages/openapi-parser/tests/generators/nextjs.test.ts +273 -0
  344. package/packages/openapi-parser/tests/parsers.test.ts +847 -0
  345. package/packages/openapi-parser/tsconfig.json +9 -0
  346. package/packages/openapi-parser/tsup.config.ts +11 -0
  347. package/packages/registry/package.json +59 -0
  348. package/packages/registry/src/cli.ts +456 -0
  349. package/packages/registry/src/index.ts +44 -0
  350. package/packages/registry/src/popular/github.json +47 -0
  351. package/packages/registry/src/popular/index.ts +55 -0
  352. package/packages/registry/src/popular/linear.json +42 -0
  353. package/packages/registry/src/popular/notion.json +42 -0
  354. package/packages/registry/src/popular/openai.json +40 -0
  355. package/packages/registry/src/popular/resend.json +38 -0
  356. package/packages/registry/src/popular/slack.json +42 -0
  357. package/packages/registry/src/popular/stripe.json +163 -0
  358. package/packages/registry/src/popular/supabase.json +42 -0
  359. package/packages/registry/src/popular/twilio.json +40 -0
  360. package/packages/registry/src/popular/vercel.json +40 -0
  361. package/packages/registry/src/registry.ts +492 -0
  362. package/packages/registry/src/storage.ts +334 -0
  363. package/packages/registry/src/types.ts +275 -0
  364. package/packages/registry/src/updater.ts +208 -0
  365. package/packages/registry/tsconfig.json +10 -0
  366. package/packages/registry/tsup.config.ts +11 -0
  367. package/pnpm-workspace.yaml +3 -0
  368. package/scripts/build-docs.sh +16 -0
  369. package/server.json +9 -0
  370. package/templates/Dockerfile.python.template +60 -0
  371. package/templates/Dockerfile.typescript.template +60 -0
  372. package/templates/docker-compose.template.yml +68 -0
  373. package/tests/fixtures/express-app/index.js +34 -0
  374. package/tests/fixtures/express-app/routes/posts.js +43 -0
  375. package/tests/fixtures/express-app/routes/users.js +58 -0
  376. package/tests/fixtures/fastapi-app/main.py +125 -0
  377. package/tests/fixtures/fastapi-app/routes/admin.py +42 -0
  378. package/tests/fixtures/graphql/simple-schema.graphql +65 -0
  379. package/tests/fixtures/mocks/github-api-responses.json +63 -0
  380. package/tests/fixtures/nextjs-app/app/api/posts/route.ts +55 -0
  381. package/tests/fixtures/nextjs-app/app/api/users/[id]/route.ts +63 -0
  382. package/tests/fixtures/nextjs-app/app/api/users/route.ts +44 -0
  383. package/tests/fixtures/nextjs-app/pages/api/health.ts +28 -0
  384. package/tests/fixtures/openapi/petstore.yaml +179 -0
  385. package/tests/integration/langchain-export.test.ts +405 -0
  386. package/tests/integration/openapi-conversion.test.ts +221 -0
  387. package/tsconfig.json +18 -0
  388. package/vitest.config.ts +32 -0
@@ -0,0 +1,822 @@
1
+ /**
2
+ * @fileoverview Command-line interface
3
+ * @copyright Copyright (c) 2024-2026 nirholas
4
+ * @license MIT
5
+ */
6
+
7
+ /**
8
+ * GitHub to MCP CLI
9
+ */
10
+
11
+ import { Command } from 'commander';
12
+ import { generateFromGithub, generateFromGithubBatch, GenerationResult, ExtractedTool } from './index';
13
+ import ora from 'ora';
14
+ import chalk from 'chalk';
15
+ import * as fs from 'fs/promises';
16
+ import * as path from 'path';
17
+
18
+ // Dynamic imports for optional dependencies
19
+ async function importPrompts() {
20
+ const prompts = await import('prompts');
21
+ return prompts.default;
22
+ }
23
+
24
+ async function importChokidar() {
25
+ const chokidar = await import('chokidar');
26
+ return chokidar.default;
27
+ }
28
+
29
+ async function importTable() {
30
+ const Table = await import('cli-table3');
31
+ return Table.default;
32
+ }
33
+
34
+ const program = new Command();
35
+
36
+ // Logging utilities
37
+ let isQuiet = false;
38
+ let isVerbose = false;
39
+
40
+ function log(message: string): void {
41
+ if (!isQuiet) {
42
+ console.log(message);
43
+ }
44
+ }
45
+
46
+ function debug(message: string): void {
47
+ if (isVerbose && !isQuiet) {
48
+ console.log(chalk.gray(`[debug] ${message}`));
49
+ }
50
+ }
51
+
52
+ function errorLog(message: string): void {
53
+ console.error(chalk.red(message));
54
+ }
55
+
56
+ /**
57
+ * Display a summary box for conversion results
58
+ */
59
+ async function displayResultBox(result: GenerationResult): Promise<void> {
60
+ const Table = await importTable();
61
+
62
+ const table = new Table({
63
+ chars: {
64
+ 'top': '─', 'top-mid': '┬', 'top-left': '┌', 'top-right': '┐',
65
+ 'bottom': '─', 'bottom-mid': '┴', 'bottom-left': '└', 'bottom-right': '┘',
66
+ 'left': '│', 'left-mid': '├', 'mid': '─', 'mid-mid': '┼',
67
+ 'right': '│', 'right-mid': '┤', 'middle': '│'
68
+ },
69
+ style: { head: ['cyan'], border: ['gray'] }
70
+ });
71
+
72
+ const confidence = result.classification
73
+ ? Math.round(result.classification.confidence * 100)
74
+ : 0;
75
+
76
+ table.push(
77
+ [{ colSpan: 2, content: chalk.bold.cyan(' GitHub to MCP Converter '), hAlign: 'center' }],
78
+ [chalk.gray('Repository'), chalk.white(result.name)],
79
+ [chalk.gray('Tools Found'), chalk.green(result.tools.length.toString())],
80
+ [chalk.gray('Confidence'), chalk.yellow(`${confidence}%`)],
81
+ [chalk.gray('Type'), chalk.blue(result.classification?.type || 'unknown')]
82
+ );
83
+
84
+ log(table.toString());
85
+ }
86
+
87
+ /**
88
+ * Interactive mode - prompts for all options
89
+ */
90
+ async function runInteractive(): Promise<void> {
91
+ const prompts = await importPrompts();
92
+
93
+ log(chalk.bold.cyan('\n GitHub to MCP - Interactive Mode\n'));
94
+
95
+ const response = await prompts([
96
+ {
97
+ type: 'text',
98
+ name: 'url',
99
+ message: 'GitHub repository URL:',
100
+ validate: (value: string) => {
101
+ if (!value) return 'URL is required';
102
+ if (!value.match(/^https?:\/\/github\.com\/[\w-]+\/[\w.-]+/)) {
103
+ return 'Invalid GitHub URL format';
104
+ }
105
+ return true;
106
+ }
107
+ },
108
+ {
109
+ type: 'select',
110
+ name: 'output',
111
+ message: 'Output format:',
112
+ choices: [
113
+ { title: 'TypeScript', value: 'typescript' },
114
+ { title: 'Python', value: 'python' },
115
+ { title: 'JSON', value: 'json' }
116
+ ],
117
+ initial: 0
118
+ },
119
+ {
120
+ type: 'select',
121
+ name: 'platform',
122
+ message: 'Target platform:',
123
+ choices: [
124
+ { title: 'Claude Desktop', value: 'claude' },
125
+ { title: 'Cursor', value: 'cursor' },
126
+ { title: 'None (just generate)', value: 'none' }
127
+ ],
128
+ initial: 0
129
+ },
130
+ {
131
+ type: 'multiselect',
132
+ name: 'sources',
133
+ message: 'Sources to extract from:',
134
+ choices: [
135
+ { title: 'README', value: 'readme', selected: true },
136
+ { title: 'OpenAPI specs', value: 'openapi', selected: true },
137
+ { title: 'Code analysis', value: 'code', selected: true }
138
+ ],
139
+ min: 1
140
+ },
141
+ {
142
+ type: 'text',
143
+ name: 'outputDir',
144
+ message: 'Output directory:',
145
+ initial: './mcp-tools'
146
+ },
147
+ {
148
+ type: 'confirm',
149
+ name: 'proceed',
150
+ message: 'Proceed with conversion?',
151
+ initial: true
152
+ }
153
+ ]);
154
+
155
+ if (!response.proceed || !response.url) {
156
+ log(chalk.yellow('Cancelled.'));
157
+ return;
158
+ }
159
+
160
+ const spinner = ora('Converting repository...').start();
161
+
162
+ try {
163
+ const result = await generateFromGithub(response.url, {
164
+ sources: response.sources,
165
+ outputLanguage: response.output === 'python' ? 'python' : 'typescript'
166
+ });
167
+
168
+ spinner.succeed(chalk.green('Conversion complete!'));
169
+
170
+ await displayResultBox(result);
171
+
172
+ // Save based on output format
173
+ const outputDir = `${response.outputDir}/${result.name}`;
174
+
175
+ if (response.output === 'json') {
176
+ await fs.mkdir(outputDir, { recursive: true });
177
+ const jsonOutput = {
178
+ name: result.name,
179
+ tools: result.tools.map((t: ExtractedTool) => ({
180
+ name: t.name,
181
+ description: t.description,
182
+ inputSchema: t.inputSchema
183
+ })),
184
+ classification: result.classification
185
+ };
186
+ await fs.writeFile(
187
+ path.join(outputDir, 'tools.json'),
188
+ JSON.stringify(jsonOutput, null, 2)
189
+ );
190
+ log(chalk.green(`\n✓ Saved JSON to ${outputDir}/tools.json`));
191
+ } else {
192
+ await result.save(outputDir);
193
+ log(chalk.green(`\n✓ Saved to ${outputDir}`));
194
+ }
195
+
196
+ // Show platform config if requested
197
+ if (response.platform !== 'none') {
198
+ log(chalk.cyan('\nMCP Configuration:'));
199
+ const config = generatePlatformConfig(result.name, response.platform);
200
+ log(chalk.gray(config));
201
+ }
202
+ } catch (error) {
203
+ spinner.fail(chalk.red('Conversion failed'));
204
+ errorLog(`Error: ${(error as Error).message}`);
205
+ process.exit(1);
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Watch mode - monitor local directory for changes
211
+ */
212
+ async function runWatchMode(localPath: string, options: Record<string, unknown>): Promise<void> {
213
+ const chokidar = await importChokidar();
214
+
215
+ log(chalk.cyan(`\n Watching ${localPath} for changes...\n`));
216
+ log(chalk.gray(' Press Ctrl+C to stop\n'));
217
+
218
+ let debounceTimer: ReturnType<typeof setTimeout> | null = null;
219
+ const debounceMs = 500;
220
+
221
+ async function processChange(): Promise<void> {
222
+ console.clear();
223
+ log(chalk.cyan(`\n File change detected - Re-analyzing...\n`));
224
+
225
+ try {
226
+ // For local paths, we'd need to implement local analysis
227
+ // For now, show a message about this feature
228
+ log(chalk.yellow(' Watch mode analyzes local directories for tool definitions.'));
229
+ log(chalk.gray(` Watching: ${path.resolve(localPath)}`));
230
+ log(chalk.gray(` Last update: ${new Date().toLocaleTimeString()}`));
231
+
232
+ // In a full implementation, this would:
233
+ // 1. Scan the local directory for OpenAPI specs, code files, etc.
234
+ // 2. Extract tool definitions
235
+ // 3. Regenerate the MCP server
236
+
237
+ const generateOptions = {
238
+ sources: (options.sources as string || 'readme,openapi,code').split(','),
239
+ outputLanguage: options.output === 'python' ? 'python' as const : 'typescript' as const
240
+ };
241
+
242
+ debug(`Options: ${JSON.stringify(generateOptions)}`);
243
+
244
+ } catch (error) {
245
+ errorLog(`Error processing changes: ${(error as Error).message}`);
246
+ }
247
+ }
248
+
249
+ const watcher = chokidar.watch(localPath, {
250
+ ignored: /(^|[/\\])\..|(node_modules|dist|\.git)/,
251
+ persistent: true,
252
+ ignoreInitial: false
253
+ });
254
+
255
+ watcher.on('all', (event, filePath) => {
256
+ debug(`${event}: ${filePath}`);
257
+
258
+ if (debounceTimer) {
259
+ clearTimeout(debounceTimer);
260
+ }
261
+
262
+ debounceTimer = setTimeout(() => {
263
+ processChange();
264
+ }, debounceMs);
265
+ });
266
+
267
+ watcher.on('error', (error) => {
268
+ errorLog(`Watcher error: ${error.message}`);
269
+ });
270
+
271
+ // Keep the process running
272
+ process.on('SIGINT', () => {
273
+ log(chalk.yellow('\n Stopping watch mode...'));
274
+ watcher.close();
275
+ process.exit(0);
276
+ });
277
+ }
278
+
279
+ /**
280
+ * Validate command - check generated MCP server
281
+ */
282
+ async function runValidate(filePath: string, options: Record<string, unknown>): Promise<void> {
283
+ const Table = await importTable();
284
+
285
+ log(chalk.cyan('\n Validating MCP Server\n'));
286
+
287
+ try {
288
+ let content: string;
289
+
290
+ if (options.url) {
291
+ // Validate from URL - not supported yet
292
+ errorLog('URL validation not yet supported. Please provide a local file path.');
293
+ process.exit(1);
294
+ }
295
+
296
+ content = await fs.readFile(filePath, 'utf-8');
297
+
298
+ const issues: Array<{ severity: 'error' | 'warning'; message: string; line?: number }> = [];
299
+ const lines = content.split('\n');
300
+
301
+ // Detect file type
302
+ const isPython = filePath.endsWith('.py');
303
+ const isTypeScript = filePath.endsWith('.ts') || filePath.endsWith('.js');
304
+
305
+ if (isTypeScript) {
306
+ // Check for MCP SDK import
307
+ if (!content.includes('@modelcontextprotocol/sdk')) {
308
+ issues.push({ severity: 'error', message: 'Missing @modelcontextprotocol/sdk import' });
309
+ }
310
+
311
+ // Check for Server instantiation
312
+ if (!content.includes('new Server(')) {
313
+ issues.push({ severity: 'error', message: 'Missing Server instantiation' });
314
+ }
315
+
316
+ // Check for ListToolsRequestSchema
317
+ if (!content.includes('ListToolsRequestSchema')) {
318
+ issues.push({ severity: 'warning', message: 'Missing ListToolsRequestSchema handler' });
319
+ }
320
+
321
+ // Check for CallToolRequestSchema
322
+ if (!content.includes('CallToolRequestSchema')) {
323
+ issues.push({ severity: 'warning', message: 'Missing CallToolRequestSchema handler' });
324
+ }
325
+
326
+ // Check for transport
327
+ if (!content.includes('StdioServerTransport') && !content.includes('transport')) {
328
+ issues.push({ severity: 'warning', message: 'Missing transport configuration' });
329
+ }
330
+
331
+ // Check for tool definitions
332
+ const toolMatches = content.match(/name:\s*['"`][\w_-]+['"`]/g);
333
+ if (toolMatches) {
334
+ debug(`Found ${toolMatches.length} tool definitions`);
335
+ }
336
+
337
+ // Check for syntax issues
338
+ lines.forEach((line, index) => {
339
+ if (line.includes('async function') && line.includes('{') && !line.includes('}')) {
340
+ // Check if next line has the closing brace or more code
341
+ const nextLine = lines[index + 1];
342
+ if (nextLine && nextLine.trim() === '') {
343
+ issues.push({ severity: 'warning', message: 'Possible incomplete function', line: index + 1 });
344
+ }
345
+ }
346
+ });
347
+ } else if (isPython) {
348
+ // Check for mcp import
349
+ if (!content.includes('from mcp') && !content.includes('import mcp')) {
350
+ issues.push({ severity: 'error', message: 'Missing mcp module import' });
351
+ }
352
+
353
+ // Check for Server
354
+ if (!content.includes('Server(')) {
355
+ issues.push({ severity: 'error', message: 'Missing Server instantiation' });
356
+ }
357
+
358
+ // Check for handlers
359
+ if (!content.includes('list_tools')) {
360
+ issues.push({ severity: 'warning', message: 'Missing list_tools handler' });
361
+ }
362
+
363
+ if (!content.includes('call_tool')) {
364
+ issues.push({ severity: 'warning', message: 'Missing call_tool handler' });
365
+ }
366
+ } else {
367
+ issues.push({ severity: 'warning', message: 'Unknown file type - limited validation' });
368
+ }
369
+
370
+ // Display results
371
+ if (issues.length === 0) {
372
+ log(chalk.green(' ✓ Validation passed! No issues found.\n'));
373
+ } else {
374
+ const table = new Table({
375
+ head: [chalk.white('Severity'), chalk.white('Line'), chalk.white('Message')],
376
+ style: { head: ['cyan'] }
377
+ });
378
+
379
+ for (const issue of issues) {
380
+ const icon = issue.severity === 'error' ? chalk.red('✗ ERROR') : chalk.yellow('⚠ WARN');
381
+ table.push([icon, issue.line?.toString() || '-', issue.message]);
382
+ }
383
+
384
+ log(table.toString());
385
+ log('');
386
+
387
+ const errorCount = issues.filter(i => i.severity === 'error').length;
388
+ const warnCount = issues.filter(i => i.severity === 'warning').length;
389
+
390
+ if (errorCount > 0) {
391
+ log(chalk.red(` ${errorCount} error(s), ${warnCount} warning(s)\n`));
392
+ process.exit(1);
393
+ } else {
394
+ log(chalk.yellow(` ${warnCount} warning(s)\n`));
395
+ }
396
+ }
397
+ } catch (error) {
398
+ errorLog(`Validation failed: ${(error as Error).message}`);
399
+ process.exit(1);
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Generate platform-specific config
405
+ */
406
+ function generatePlatformConfig(repoName: string, platform: string): string {
407
+ const serverName = repoName.includes('/') ? repoName.split('/')[1] : repoName;
408
+
409
+ switch (platform) {
410
+ case 'claude':
411
+ return JSON.stringify({
412
+ mcpServers: {
413
+ [serverName]: {
414
+ command: 'node',
415
+ args: [`${serverName}-mcp/index.js`]
416
+ }
417
+ }
418
+ }, null, 2);
419
+
420
+ case 'cursor':
421
+ return JSON.stringify({
422
+ mcp: {
423
+ servers: {
424
+ [serverName]: {
425
+ command: 'node',
426
+ args: [`${serverName}-mcp/index.js`]
427
+ }
428
+ }
429
+ }
430
+ }, null, 2);
431
+
432
+ default:
433
+ return JSON.stringify({
434
+ name: serverName,
435
+ command: 'node',
436
+ args: [`${serverName}-mcp/index.js`]
437
+ }, null, 2);
438
+ }
439
+ }
440
+
441
+ /**
442
+ * Get config file path for each platform
443
+ */
444
+ function getConfigPath(platform: string): string {
445
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '~';
446
+ const isWindows = process.platform === 'win32';
447
+ const isMac = process.platform === 'darwin';
448
+
449
+ switch (platform) {
450
+ case 'claude':
451
+ if (isMac) {
452
+ return path.join(homeDir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
453
+ } else if (isWindows) {
454
+ return path.join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');
455
+ } else {
456
+ return path.join(homeDir, '.config', 'claude', 'claude_desktop_config.json');
457
+ }
458
+
459
+ case 'cursor':
460
+ return path.join(homeDir, '.cursor', 'mcp.json');
461
+
462
+ case 'vscode':
463
+ if (isMac) {
464
+ return path.join(homeDir, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'mcp_settings.json');
465
+ } else if (isWindows) {
466
+ return path.join(process.env.APPDATA || '', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'mcp_settings.json');
467
+ } else {
468
+ return path.join(homeDir, '.config', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'mcp_settings.json');
469
+ }
470
+
471
+ default:
472
+ return '';
473
+ }
474
+ }
475
+
476
+ /**
477
+ * Install command - One-click install MCP server to platforms
478
+ */
479
+ async function runInstall(url: string, options: Record<string, unknown>): Promise<void> {
480
+ const Table = await importTable();
481
+
482
+ log(chalk.bold.cyan('\n GitHub to MCP - One-Click Install\n'));
483
+
484
+ // Determine which platforms to install to
485
+ const platforms: string[] = [];
486
+ if (options.all) {
487
+ platforms.push('claude', 'cursor', 'vscode');
488
+ } else {
489
+ if (options.claude) platforms.push('claude');
490
+ if (options.cursor) platforms.push('cursor');
491
+ if (options.vscode) platforms.push('vscode');
492
+ }
493
+
494
+ // Default to Claude if no platform specified
495
+ if (platforms.length === 0) {
496
+ platforms.push('claude');
497
+ }
498
+
499
+ const spinner = ora('Converting repository...').start();
500
+
501
+ try {
502
+ // Generate MCP server
503
+ const result = await generateFromGithub(url, {
504
+ sources: ['readme', 'openapi', 'code'],
505
+ outputLanguage: 'typescript'
506
+ });
507
+
508
+ spinner.succeed(chalk.green(`Generated ${result.tools.length} tools from ${result.name}`));
509
+
510
+ // Expand home directory
511
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '~';
512
+ const installDir = (options.dir as string).replace('~', homeDir);
513
+ const serverDir = path.join(installDir, `${result.name}-mcp`);
514
+
515
+ if (options.dryRun) {
516
+ log(chalk.yellow('\n Dry run - no changes will be made\n'));
517
+ }
518
+
519
+ // Save the server code
520
+ if (!options.dryRun) {
521
+ await fs.mkdir(serverDir, { recursive: true });
522
+ const code = result.generate();
523
+ await fs.writeFile(path.join(serverDir, 'index.ts'), code);
524
+
525
+ // Create package.json
526
+ const packageJson = {
527
+ name: `${result.name}-mcp`,
528
+ version: '1.0.0',
529
+ type: 'module',
530
+ scripts: {
531
+ start: 'tsx index.ts'
532
+ },
533
+ dependencies: {
534
+ '@modelcontextprotocol/sdk': '^1.0.0',
535
+ 'tsx': '^4.0.0'
536
+ }
537
+ };
538
+ await fs.writeFile(
539
+ path.join(serverDir, 'package.json'),
540
+ JSON.stringify(packageJson, null, 2)
541
+ );
542
+
543
+ log(chalk.green(` ✓ Saved server to ${serverDir}`));
544
+ } else {
545
+ log(chalk.gray(` Would save server to ${serverDir}`));
546
+ }
547
+
548
+ // Install to each platform
549
+ log(chalk.cyan('\n Installing to platforms:\n'));
550
+
551
+ for (const platform of platforms) {
552
+ const configPath = getConfigPath(platform);
553
+ const platformName = platform === 'claude' ? 'Claude Desktop' :
554
+ platform === 'cursor' ? 'Cursor' : 'VS Code';
555
+
556
+ try {
557
+ // Read existing config or create new
558
+ let config: Record<string, unknown> = {};
559
+ try {
560
+ const existingConfig = await fs.readFile(configPath, 'utf-8');
561
+ config = JSON.parse(existingConfig);
562
+ } catch {
563
+ // Config doesn't exist, will create new
564
+ }
565
+
566
+ // Add our server to the config
567
+ const serverName = result.name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
568
+
569
+ if (platform === 'cursor') {
570
+ if (!config.mcp) config.mcp = { servers: {} };
571
+ if (!(config.mcp as Record<string, unknown>).servers) {
572
+ (config.mcp as Record<string, unknown>).servers = {};
573
+ }
574
+ ((config.mcp as Record<string, unknown>).servers as Record<string, unknown>)[serverName] = {
575
+ command: 'npx',
576
+ args: ['tsx', path.join(serverDir, 'index.ts')]
577
+ };
578
+ } else {
579
+ if (!config.mcpServers) config.mcpServers = {};
580
+ (config.mcpServers as Record<string, unknown>)[serverName] = {
581
+ command: 'npx',
582
+ args: ['tsx', path.join(serverDir, 'index.ts')]
583
+ };
584
+ }
585
+
586
+ if (!options.dryRun) {
587
+ // Ensure config directory exists
588
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
589
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
590
+ log(chalk.green(` ✓ ${platformName}: Added to ${configPath}`));
591
+ } else {
592
+ log(chalk.gray(` Would add to ${platformName}: ${configPath}`));
593
+ }
594
+
595
+ } catch (error) {
596
+ log(chalk.yellow(` ⚠ ${platformName}: ${(error as Error).message}`));
597
+ }
598
+ }
599
+
600
+ // Summary
601
+ log(chalk.cyan('\n Installation Summary:\n'));
602
+
603
+ const table = new Table({
604
+ head: [chalk.white('Item'), chalk.white('Value')],
605
+ style: { head: ['cyan'] }
606
+ });
607
+
608
+ table.push(
609
+ ['Server Name', result.name],
610
+ ['Tools', result.tools.length.toString()],
611
+ ['Location', serverDir],
612
+ ['Platforms', platforms.join(', ')]
613
+ );
614
+
615
+ log(table.toString());
616
+
617
+ // Next steps
618
+ log(chalk.cyan('\n Next Steps:\n'));
619
+ log(chalk.white(' 1. Restart your MCP client (Claude Desktop, Cursor, etc.)'));
620
+ log(chalk.white(' 2. The new tools will be available automatically'));
621
+ log(chalk.white(` 3. Run ${chalk.yellow(`cd ${serverDir} && npm install`)} to install dependencies`));
622
+ log('');
623
+
624
+ if (!options.dryRun) {
625
+ log(chalk.green.bold(' ✓ Installation complete!\n'));
626
+ }
627
+
628
+ } catch (error) {
629
+ spinner.fail(chalk.red('Installation failed'));
630
+ errorLog(`Error: ${(error as Error).message}`);
631
+ process.exit(1);
632
+ }
633
+ }
634
+
635
+ // Main program setup
636
+ program
637
+ .name('github-to-mcp')
638
+ .description('Convert GitHub repositories to MCP tools')
639
+ .version('1.0.0');
640
+
641
+ // Global options
642
+ program
643
+ .option('-q, --quiet', 'Minimal output')
644
+ .option('-v, --verbose', 'Debug logging')
645
+ .option('--no-cache', 'Skip cache');
646
+
647
+ // Default convert command
648
+ program
649
+ .argument('[urls...]', 'GitHub repository URLs')
650
+ .option('-o, --output <format>', 'Output format (typescript, json, python)', 'typescript')
651
+ .option('-d, --dir <path>', 'Output directory', './mcp-tools')
652
+ .option('-f, --file <path>', 'File containing list of GitHub URLs')
653
+ .option('-i, --interactive', 'Interactive mode')
654
+ .option('-w, --watch', 'Watch mode for local directory')
655
+ .option('--follow-docs', 'Follow documentation links', false)
656
+ .option('--depth <number>', 'Documentation scraping depth', '2')
657
+ .option('--sources <types>', 'Sources to extract from (comma-separated)', 'readme,openapi,code')
658
+ .option('--format <format>', 'Output format (deprecated, use -o)', 'typescript')
659
+ .option('--token <token>', 'GitHub token for private repos')
660
+ .option('--config <file>', 'Use config file')
661
+ .action(async (urls, options) => {
662
+ // Set logging modes
663
+ isQuiet = options.quiet || false;
664
+ isVerbose = options.verbose || false;
665
+
666
+ debug(`Options: ${JSON.stringify(options)}`);
667
+
668
+ // Interactive mode
669
+ if (options.interactive) {
670
+ await runInteractive();
671
+ return;
672
+ }
673
+
674
+ // Watch mode
675
+ if (options.watch && urls.length > 0) {
676
+ await runWatchMode(urls[0], options);
677
+ return;
678
+ }
679
+
680
+ try {
681
+ let repoUrls = urls;
682
+
683
+ // Load URLs from file if specified
684
+ if (options.file) {
685
+ const content = await fs.readFile(options.file, 'utf-8');
686
+ const fileUrls = content.split('\n').filter((line: string) => line.trim());
687
+ repoUrls = [...repoUrls, ...fileUrls];
688
+ }
689
+
690
+ // Load config file if specified
691
+ if (options.config) {
692
+ try {
693
+ const configContent = await fs.readFile(options.config, 'utf-8');
694
+ const config = JSON.parse(configContent);
695
+ debug(`Loaded config: ${JSON.stringify(config)}`);
696
+ // Merge config with options (command line takes precedence)
697
+ Object.assign(options, { ...config, ...options });
698
+ } catch (e) {
699
+ errorLog(`Failed to load config file: ${(e as Error).message}`);
700
+ process.exit(1);
701
+ }
702
+ }
703
+
704
+ if (repoUrls.length === 0) {
705
+ errorLog('No GitHub URLs provided. Use -i for interactive mode or provide URLs.');
706
+ program.help();
707
+ return;
708
+ }
709
+
710
+ const spinner = ora('Generating MCP tools...').start();
711
+
712
+ const outputFormat = options.output || options.format || 'typescript';
713
+ const generateOptions = {
714
+ sources: options.sources.split(','),
715
+ followDocs: options.followDocs,
716
+ depth: parseInt(options.depth),
717
+ outputLanguage: outputFormat === 'python' ? 'python' as const : 'typescript' as const,
718
+ githubToken: options.token,
719
+ cache: options.cache !== false
720
+ };
721
+
722
+ debug(`Generate options: ${JSON.stringify(generateOptions)}`);
723
+
724
+ if (repoUrls.length === 1) {
725
+ // Single repo
726
+ const result = await generateFromGithub(repoUrls[0], generateOptions);
727
+
728
+ spinner.succeed(chalk.green(`Generated ${result.tools.length} tools from ${result.name}`));
729
+
730
+ if (!isQuiet) {
731
+ await displayResultBox(result);
732
+
733
+ // Show breakdown
734
+ log('\nSources:');
735
+ result.sources.forEach(source => {
736
+ log(chalk.blue(` ${source.type}: ${source.count} tools`));
737
+ });
738
+ }
739
+
740
+ // Save based on output format
741
+ const outputDir = `${options.dir}/${result.name}`;
742
+
743
+ if (outputFormat === 'json') {
744
+ await fs.mkdir(outputDir, { recursive: true });
745
+ const jsonOutput = {
746
+ name: result.name,
747
+ tools: result.tools.map((t: ExtractedTool) => ({
748
+ name: t.name,
749
+ description: t.description,
750
+ inputSchema: t.inputSchema
751
+ })),
752
+ classification: result.classification,
753
+ sources: result.sources
754
+ };
755
+ await fs.writeFile(
756
+ path.join(outputDir, 'tools.json'),
757
+ JSON.stringify(jsonOutput, null, 2)
758
+ );
759
+ log(chalk.green(`\n✓ Saved JSON to ${outputDir}/tools.json`));
760
+ } else if (outputFormat === 'python') {
761
+ await fs.mkdir(outputDir, { recursive: true });
762
+ const pythonCode = result.generatePython?.() ?? result.generate();
763
+ await fs.writeFile(path.join(outputDir, 'server.py'), pythonCode);
764
+ log(chalk.green(`\n✓ Saved Python to ${outputDir}/server.py`));
765
+ } else {
766
+ await result.save(outputDir);
767
+ log(chalk.green(`\n✓ Saved to ${outputDir}`));
768
+ }
769
+ } else {
770
+ // Batch
771
+ const results = await generateFromGithubBatch(repoUrls, generateOptions);
772
+
773
+ spinner.succeed(chalk.green(`Generated tools from ${results.length} repositories`));
774
+
775
+ if (!isQuiet) {
776
+ log('\nResults:');
777
+ for (const result of results) {
778
+ const typeLabel = result.classification ? ` [${result.classification.type}]` : '';
779
+ log(chalk.blue(` ${result.name}${typeLabel}: ${result.tools.length} tools`));
780
+
781
+ const outputDir = `${options.dir}/${result.name}`;
782
+ await result.save(outputDir);
783
+ }
784
+ }
785
+
786
+ log(chalk.green(`\n✓ Saved all to ${options.dir}/`));
787
+ }
788
+ } catch (error) {
789
+ errorLog(`Error: ${(error as Error).message}`);
790
+ process.exit(1);
791
+ }
792
+ });
793
+
794
+ // Validate subcommand
795
+ program
796
+ .command('validate <file>')
797
+ .description('Validate a generated MCP server file')
798
+ .option('--url <url>', 'Validate from URL instead of file')
799
+ .action(async (file, options) => {
800
+ isQuiet = program.opts().quiet || false;
801
+ isVerbose = program.opts().verbose || false;
802
+ await runValidate(file, options);
803
+ });
804
+
805
+ // Install subcommand - One-click install to MCP clients
806
+ program
807
+ .command('install <url>')
808
+ .description('Install MCP server directly to Claude Desktop, Cursor, or other MCP clients')
809
+ .option('--claude', 'Install to Claude Desktop')
810
+ .option('--cursor', 'Install to Cursor')
811
+ .option('--vscode', 'Install to VS Code (Cline/Roo)')
812
+ .option('--all', 'Install to all supported platforms')
813
+ .option('-d, --dir <path>', 'Installation directory', '~/.mcp-servers')
814
+ .option('--dry-run', 'Show what would be installed without making changes')
815
+ .action(async (url, options) => {
816
+ isQuiet = program.opts().quiet || false;
817
+ isVerbose = program.opts().verbose || false;
818
+ await runInstall(url, options);
819
+ });
820
+
821
+ program.parse();
822
+