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,924 @@
1
+ /**
2
+ * GitHub to MCP Converter - Main Page
3
+ * @copyright 2024-2026 nirholas
4
+ * @license MIT
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import { useState, useCallback, useEffect, useRef } from 'react';
10
+ import {
11
+ Github,
12
+ Zap,
13
+ Download,
14
+ Copy,
15
+ Check,
16
+ Loader2,
17
+ Server,
18
+ Box,
19
+ ExternalLink,
20
+ Star,
21
+ Clock,
22
+ FileCode,
23
+ ChevronDown,
24
+ ChevronRight,
25
+ Trash2,
26
+ History,
27
+ Settings,
28
+ AlertCircle,
29
+ CheckCircle,
30
+ Search,
31
+ X,
32
+ ArrowRight,
33
+ ArrowLeft,
34
+ Code2,
35
+ FileJson,
36
+ Terminal,
37
+ Cpu,
38
+ Package,
39
+ FolderOpen,
40
+ } from 'lucide-react';
41
+ import { Button } from '@/components/ui/button';
42
+ import { Input } from '@/components/ui/input';
43
+ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
44
+ import { Badge } from '@/components/ui/badge';
45
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
46
+ import { CodeBlock } from '@/components/ui/code-block';
47
+ import { cn } from '@/lib/utils';
48
+ import { copyToClipboard, downloadAsFile, formatRelativeTime, isValidGitHubUrl, parseGitHubUrl, truncate } from '@/lib/utils';
49
+ import { EXAMPLE_REPOS, CLASSIFICATION_LABELS, SOURCE_TYPE_LABELS, PLATFORMS, APP_NAME, GITHUB_URL } from '@/lib/constants';
50
+ import { useLocalStorage } from '@/hooks/use-local-storage';
51
+ import type { ConversionResult, Tool, ConversionHistory, ApiError, ConversionStatus } from '@/types';
52
+
53
+ // Loading steps
54
+ const LOADING_STEPS = [
55
+ { id: 'fetch', label: 'Fetching repository', icon: Github },
56
+ { id: 'analyze', label: 'Analyzing codebase', icon: Code2 },
57
+ { id: 'extract', label: 'Extracting tools', icon: Package },
58
+ { id: 'generate', label: 'Generating server', icon: Terminal },
59
+ ];
60
+
61
+ // Features for landing
62
+ const FEATURES = [
63
+ {
64
+ icon: Code2,
65
+ title: 'Smart Extraction',
66
+ description: 'Extracts tools from README, source code, OpenAPI specs, and GraphQL schemas',
67
+ },
68
+ {
69
+ icon: FileJson,
70
+ title: 'Ready-to-Use Config',
71
+ description: 'Generate configs for Claude Desktop, Cursor, and other MCP clients',
72
+ },
73
+ {
74
+ icon: Terminal,
75
+ title: 'TypeScript & Python',
76
+ description: 'Get production-ready MCP server code in both languages',
77
+ },
78
+ {
79
+ icon: Cpu,
80
+ title: 'Auto Classification',
81
+ description: 'Detects repo type: API SDK, CLI tool, library, MCP server',
82
+ },
83
+ ];
84
+
85
+ export default function HomePage() {
86
+ // State
87
+ const [url, setUrl] = useState('');
88
+ const [status, setStatus] = useState<ConversionStatus>('idle');
89
+ const [result, setResult] = useState<ConversionResult | null>(null);
90
+ const [error, setError] = useState<ApiError | null>(null);
91
+ const [copiedItem, setCopiedItem] = useState<string | null>(null);
92
+ const [expandedTools, setExpandedTools] = useState<Set<string>>(new Set());
93
+ const [showHistory, setShowHistory] = useState(false);
94
+ const [activeConfigTab, setActiveConfigTab] = useState('claude');
95
+ const [history, setHistory] = useLocalStorage<ConversionHistory[]>('conversion-history', []);
96
+ const [searchFilter, setSearchFilter] = useState('');
97
+ const [loadingStep, setLoadingStep] = useState(0);
98
+ const inputRef = useRef<HTMLInputElement>(null);
99
+
100
+ // Loading step animation
101
+ useEffect(() => {
102
+ if (status === 'loading') {
103
+ setLoadingStep(0);
104
+ const interval = setInterval(() => {
105
+ setLoadingStep(prev => (prev < LOADING_STEPS.length - 1 ? prev + 1 : prev));
106
+ }, 1500);
107
+ return () => clearInterval(interval);
108
+ }
109
+ }, [status]);
110
+
111
+ // Convert handler
112
+ const handleConvert = useCallback(async () => {
113
+ if (!url.trim()) return;
114
+
115
+ if (!isValidGitHubUrl(url.trim())) {
116
+ setError({
117
+ error: 'Invalid GitHub URL',
118
+ code: 'INVALID_URL',
119
+ details: 'Please enter a valid GitHub repository URL (e.g., https://github.com/owner/repo)',
120
+ });
121
+ setStatus('error');
122
+ return;
123
+ }
124
+
125
+ setStatus('loading');
126
+ setError(null);
127
+ setResult(null);
128
+
129
+ try {
130
+ const response = await fetch('/api/convert', {
131
+ method: 'POST',
132
+ headers: { 'Content-Type': 'application/json' },
133
+ body: JSON.stringify({ url: url.trim() }),
134
+ });
135
+
136
+ const data = await response.json();
137
+
138
+ if (!response.ok) {
139
+ setError({
140
+ error: data.error || 'Conversion failed',
141
+ code: data.code || 'UNKNOWN_ERROR',
142
+ details: data.details,
143
+ retryAfter: response.status === 429 ? parseInt(response.headers.get('Retry-After') || '60') : undefined,
144
+ });
145
+ setStatus('error');
146
+ return;
147
+ }
148
+
149
+ setResult(data);
150
+ setStatus('success');
151
+
152
+ // Add to history
153
+ const parsed = parseGitHubUrl(url.trim());
154
+ if (parsed) {
155
+ const entry: ConversionHistory = {
156
+ id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
157
+ url: url.trim(),
158
+ name: data.name,
159
+ toolCount: data.tools?.length || 0,
160
+ classification: data.classification?.type || 'unknown',
161
+ convertedAt: new Date().toISOString(),
162
+ };
163
+ setHistory(prev => [entry, ...prev.filter(h => h.url !== url.trim())].slice(0, 50));
164
+ }
165
+ } catch (err) {
166
+ setError({
167
+ error: err instanceof Error ? err.message : 'Network error',
168
+ code: 'NETWORK_ERROR',
169
+ details: 'Failed to connect to the server. Please check your internet connection.',
170
+ });
171
+ setStatus('error');
172
+ }
173
+ }, [url, setHistory]);
174
+
175
+ // Copy handler
176
+ const handleCopy = useCallback(async (text: string, id: string) => {
177
+ const success = await copyToClipboard(text);
178
+ if (success) {
179
+ setCopiedItem(id);
180
+ setTimeout(() => setCopiedItem(null), 2000);
181
+ }
182
+ }, []);
183
+
184
+ // Download handler
185
+ const handleDownload = useCallback(() => {
186
+ if (!result) return;
187
+ downloadAsFile(result.code, `${result.name}-mcp-server.ts`, 'text/typescript');
188
+ }, [result]);
189
+
190
+ // Toggle tool expansion
191
+ const toggleToolExpansion = useCallback((toolName: string) => {
192
+ setExpandedTools(prev => {
193
+ const next = new Set(prev);
194
+ if (next.has(toolName)) {
195
+ next.delete(toolName);
196
+ } else {
197
+ next.add(toolName);
198
+ }
199
+ return next;
200
+ });
201
+ }, []);
202
+
203
+ // Filter tools
204
+ const filteredTools = result?.tools.filter(tool =>
205
+ !searchFilter ||
206
+ tool.name.toLowerCase().includes(searchFilter.toLowerCase()) ||
207
+ tool.description.toLowerCase().includes(searchFilter.toLowerCase())
208
+ ) || [];
209
+
210
+ // Classification info
211
+ const classificationInfo = result?.classification
212
+ ? CLASSIFICATION_LABELS[result.classification.type] || CLASSIFICATION_LABELS.unknown
213
+ : null;
214
+
215
+ return (
216
+ <div className="min-h-screen bg-background text-foreground dark">
217
+ {/* Background */}
218
+ <div className="fixed inset-0 -z-10 bg-dots opacity-30" />
219
+
220
+ {/* Header */}
221
+ <header className="sticky top-0 z-50 border-b border-border bg-background/80 backdrop-blur-xl">
222
+ <div className="mx-auto max-w-5xl px-4 sm:px-6">
223
+ <div className="flex h-16 items-center justify-between">
224
+ {/* Logo */}
225
+ <a href="/" className="flex items-center gap-3">
226
+ <div className="flex h-9 w-9 items-center justify-center rounded-lg bg-foreground">
227
+ <Zap className="h-5 w-5 text-background" />
228
+ </div>
229
+ <span className="text-lg font-bold">{APP_NAME}</span>
230
+ </a>
231
+
232
+ {/* Actions */}
233
+ <div className="flex items-center gap-2">
234
+ <Button
235
+ variant="ghost"
236
+ size="icon-sm"
237
+ onClick={() => setShowHistory(!showHistory)}
238
+ className="relative"
239
+ >
240
+ <History className="h-4 w-4" />
241
+ {history.length > 0 && (
242
+ <span className="absolute -right-0.5 -top-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-foreground text-[10px] font-medium text-background">
243
+ {history.length > 9 ? '9+' : history.length}
244
+ </span>
245
+ )}
246
+ </Button>
247
+ <Button variant="ghost" size="sm" asChild>
248
+ <a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" className="gap-1.5">
249
+ <Github className="h-4 w-4" />
250
+ <span className="hidden sm:inline">GitHub</span>
251
+ </a>
252
+ </Button>
253
+ </div>
254
+ </div>
255
+ </div>
256
+ </header>
257
+
258
+ <main className="mx-auto max-w-5xl px-4 sm:px-6 py-12">
259
+ {/* Hero Section */}
260
+ <section className="text-center mb-10">
261
+ <div className="inline-flex items-center gap-2 rounded-full border border-border bg-muted px-3 py-1 text-xs font-medium text-muted-foreground mb-6">
262
+ <span className="flex h-1.5 w-1.5 rounded-full bg-emerald-500" />
263
+ Free & Open Source
264
+ </div>
265
+ <h2 className="text-4xl sm:text-5xl font-bold tracking-tight mb-4">
266
+ Convert GitHub repos to
267
+ <span className="block text-muted-foreground">MCP servers</span>
268
+ </h2>
269
+ <p className="text-lg text-muted-foreground max-w-2xl mx-auto">
270
+ Extract tools from any GitHub repository and generate ready-to-use MCP servers for Claude, Cursor, and other AI assistants.
271
+ </p>
272
+ </section>
273
+
274
+ {/* Input Section */}
275
+ <div className="rounded-2xl border border-border bg-card p-6 mb-8">
276
+ <div className="flex flex-col sm:flex-row gap-3">
277
+ <div className="flex-1 relative">
278
+ <Input
279
+ ref={inputRef}
280
+ type="url"
281
+ value={url}
282
+ onChange={(e) => setUrl(e.target.value)}
283
+ placeholder="https://github.com/owner/repo"
284
+ leftIcon={<Github className="h-4 w-4" />}
285
+ rightIcon={
286
+ url ? (
287
+ <button onClick={() => setUrl('')} className="hover:text-foreground transition-colors">
288
+ <X className="h-4 w-4" />
289
+ </button>
290
+ ) : null
291
+ }
292
+ onKeyDown={(e) => e.key === 'Enter' && handleConvert()}
293
+ disabled={status === 'loading'}
294
+ className="h-12 text-base"
295
+ />
296
+ </div>
297
+ <Button
298
+ onClick={handleConvert}
299
+ disabled={status === 'loading' || !url.trim()}
300
+ size="lg"
301
+ className="h-12 min-w-[140px]"
302
+ >
303
+ {status === 'loading' ? (
304
+ <>
305
+ <Loader2 className="h-4 w-4 animate-spin" />
306
+ Converting...
307
+ </>
308
+ ) : (
309
+ <>
310
+ Extract
311
+ <ArrowRight className="h-4 w-4" />
312
+ </>
313
+ )}
314
+ </Button>
315
+ </div>
316
+
317
+ {/* Example Repos */}
318
+ <div className="mt-5 pt-5 border-t border-border">
319
+ <p className="text-xs text-muted-foreground mb-3">Try:</p>
320
+ <div className="flex flex-wrap gap-2">
321
+ {EXAMPLE_REPOS.map((repo) => (
322
+ <button
323
+ key={repo.url}
324
+ onClick={() => setUrl(repo.url)}
325
+ className="px-3 py-1.5 text-sm bg-muted border border-border rounded-lg hover:border-muted-foreground/50 hover:bg-muted/80 transition-all text-muted-foreground hover:text-foreground"
326
+ disabled={status === 'loading'}
327
+ >
328
+ {repo.name}
329
+ </button>
330
+ ))}
331
+ </div>
332
+ </div>
333
+ </div>
334
+
335
+ {/* Error Display */}
336
+ {status === 'error' && error && (
337
+ <div className="mb-8 rounded-xl border border-red-500/20 bg-red-500/5 p-5">
338
+ <div className="flex items-start gap-3">
339
+ <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-red-500/10">
340
+ <AlertCircle className="h-4 w-4 text-red-500" />
341
+ </div>
342
+ <div className="flex-1">
343
+ <h3 className="font-medium text-red-500">{error.error}</h3>
344
+ {error.details && (
345
+ <p className="mt-1 text-sm text-muted-foreground">{error.details}</p>
346
+ )}
347
+ {error.retryAfter && (
348
+ <p className="mt-2 text-sm text-muted-foreground">
349
+ Rate limited. Try again in {error.retryAfter}s.
350
+ </p>
351
+ )}
352
+ </div>
353
+ <Button
354
+ variant="ghost"
355
+ size="icon-sm"
356
+ onClick={() => { setStatus('idle'); setError(null); }}
357
+ >
358
+ <X className="h-4 w-4" />
359
+ </Button>
360
+ </div>
361
+ </div>
362
+ )}
363
+
364
+ {/* Loading State */}
365
+ {status === 'loading' && (
366
+ <div className="mb-8 rounded-2xl border border-border bg-card p-8">
367
+ <div className="flex flex-col items-center text-center mb-8">
368
+ <div className="relative mb-4">
369
+ <div className="h-16 w-16 rounded-2xl bg-muted flex items-center justify-center">
370
+ <Loader2 className="h-8 w-8 animate-spin" />
371
+ </div>
372
+ </div>
373
+ <h3 className="text-lg font-semibold mb-1">Converting Repository</h3>
374
+ <p className="text-sm text-muted-foreground">This usually takes 10-30 seconds</p>
375
+ </div>
376
+
377
+ <div className="max-w-md mx-auto space-y-3">
378
+ {LOADING_STEPS.map((step, index) => {
379
+ const StepIcon = step.icon;
380
+ const isActive = index === loadingStep;
381
+ const isComplete = index < loadingStep;
382
+
383
+ return (
384
+ <div
385
+ key={step.id}
386
+ className={cn(
387
+ 'flex items-center gap-3 p-3 rounded-lg transition-all duration-300',
388
+ isActive && 'bg-muted',
389
+ isComplete && 'opacity-60'
390
+ )}
391
+ >
392
+ <div className={cn(
393
+ 'h-8 w-8 rounded-lg flex items-center justify-center transition-colors',
394
+ isActive && 'bg-foreground text-background',
395
+ isComplete && 'bg-emerald-500/20 text-emerald-600',
396
+ !isActive && !isComplete && 'bg-muted text-muted-foreground'
397
+ )}>
398
+ {isComplete ? (
399
+ <Check className="h-4 w-4" />
400
+ ) : isActive ? (
401
+ <Loader2 className="h-4 w-4 animate-spin" />
402
+ ) : (
403
+ <StepIcon className="h-4 w-4" />
404
+ )}
405
+ </div>
406
+ <span className={cn(
407
+ 'text-sm font-medium',
408
+ isActive && 'text-foreground',
409
+ !isActive && 'text-muted-foreground'
410
+ )}>
411
+ {step.label}
412
+ </span>
413
+ {isActive && (
414
+ <div className="ml-auto flex gap-1">
415
+ <span className="h-1.5 w-1.5 rounded-full bg-foreground animate-bounce" style={{ animationDelay: '0ms' }} />
416
+ <span className="h-1.5 w-1.5 rounded-full bg-foreground animate-bounce" style={{ animationDelay: '150ms' }} />
417
+ <span className="h-1.5 w-1.5 rounded-full bg-foreground animate-bounce" style={{ animationDelay: '300ms' }} />
418
+ </div>
419
+ )}
420
+ </div>
421
+ );
422
+ })}
423
+ </div>
424
+ </div>
425
+ )}
426
+
427
+ {/* Features - Show when idle */}
428
+ {status === 'idle' && !result && (
429
+ <section className="mb-12">
430
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
431
+ {FEATURES.map((feature) => {
432
+ const FeatureIcon = feature.icon;
433
+ return (
434
+ <div key={feature.title} className="group rounded-xl border border-border p-5 bg-card hover:border-muted-foreground/30 transition-colors card-lift">
435
+ <div className="h-10 w-10 rounded-lg bg-muted flex items-center justify-center mb-3 group-hover:bg-muted/80 transition-colors">
436
+ <FeatureIcon className="h-5 w-5" />
437
+ </div>
438
+ <h3 className="font-semibold mb-1">{feature.title}</h3>
439
+ <p className="text-sm text-muted-foreground">{feature.description}</p>
440
+ </div>
441
+ );
442
+ })}
443
+ </div>
444
+
445
+ {/* How it works */}
446
+ <div className="mt-12 text-center">
447
+ <h3 className="text-lg font-semibold mb-6">How it works</h3>
448
+ <div className="flex flex-col sm:flex-row items-center justify-center gap-4 sm:gap-8">
449
+ <div className="flex items-center gap-3">
450
+ <div className="h-10 w-10 rounded-lg bg-foreground text-background flex items-center justify-center text-sm font-bold">1</div>
451
+ <span className="text-sm text-muted-foreground">Paste a GitHub URL</span>
452
+ </div>
453
+ <ArrowRight className="h-4 w-4 text-muted-foreground hidden sm:block" />
454
+ <div className="flex items-center gap-3">
455
+ <div className="h-10 w-10 rounded-lg bg-foreground text-background flex items-center justify-center text-sm font-bold">2</div>
456
+ <span className="text-sm text-muted-foreground">We analyze the repo</span>
457
+ </div>
458
+ <ArrowRight className="h-4 w-4 text-muted-foreground hidden sm:block" />
459
+ <div className="flex items-center gap-3">
460
+ <div className="h-10 w-10 rounded-lg bg-foreground text-background flex items-center justify-center text-sm font-bold">3</div>
461
+ <span className="text-sm text-muted-foreground">Get your MCP server</span>
462
+ </div>
463
+ </div>
464
+ </div>
465
+ </section>
466
+ )}
467
+
468
+ {/* Results */}
469
+ {status === 'success' && result && (
470
+ <div className="space-y-6">
471
+ {/* Success Banner */}
472
+ <div className="flex items-center gap-3 p-4 rounded-xl bg-emerald-500/10 border border-emerald-500/20">
473
+ <div className="h-10 w-10 rounded-lg bg-emerald-500/20 flex items-center justify-center">
474
+ <CheckCircle className="h-5 w-5 text-emerald-500" />
475
+ </div>
476
+ <div className="flex-1">
477
+ <h3 className="font-medium text-emerald-600 dark:text-emerald-400">Conversion Complete!</h3>
478
+ <p className="text-sm text-emerald-600/70 dark:text-emerald-400/70">
479
+ Successfully extracted {result.tools.length} tools from {result.name}
480
+ </p>
481
+ </div>
482
+ <Button variant="outline" size="sm" onClick={() => { setStatus('idle'); setResult(null); }}>
483
+ Convert Another
484
+ </Button>
485
+ </div>
486
+
487
+ {/* Summary Card */}
488
+ <div className="rounded-2xl border border-border bg-card p-6">
489
+ <div className="flex flex-col sm:flex-row sm:items-start justify-between gap-4">
490
+ <div className="flex-1">
491
+ <div className="flex items-center gap-3 mb-3">
492
+ <h3 className="text-xl font-semibold">{result.name}</h3>
493
+ {result.repository?.url && (
494
+ <a
495
+ href={result.repository.url}
496
+ target="_blank"
497
+ rel="noopener noreferrer"
498
+ className="text-muted-foreground hover:text-foreground transition-colors"
499
+ >
500
+ <ExternalLink className="h-4 w-4" />
501
+ </a>
502
+ )}
503
+ </div>
504
+ <div className="flex flex-wrap items-center gap-2">
505
+ {classificationInfo && (
506
+ <Badge>
507
+ {classificationInfo.icon} {classificationInfo.label}
508
+ </Badge>
509
+ )}
510
+ <Badge variant="secondary">
511
+ {Math.round((result.classification?.confidence || 0) * 100)}% confidence
512
+ </Badge>
513
+ {result.repository?.stars && (
514
+ <Badge variant="secondary">
515
+ <Star className="mr-1 h-3 w-3" />
516
+ {result.repository.stars.toLocaleString()}
517
+ </Badge>
518
+ )}
519
+ {result.repository?.language && (
520
+ <Badge variant="secondary">{result.repository.language}</Badge>
521
+ )}
522
+ </div>
523
+ </div>
524
+ <div className="flex items-center gap-3">
525
+ <div className="flex h-14 w-14 items-center justify-center rounded-xl bg-muted">
526
+ <span className="text-2xl font-bold">{result.tools.length}</span>
527
+ </div>
528
+ <div className="text-left">
529
+ <div className="text-sm font-medium">Tools</div>
530
+ <div className="text-xs text-muted-foreground">extracted</div>
531
+ </div>
532
+ </div>
533
+ </div>
534
+
535
+ {/* Source Breakdown */}
536
+ {result.sources && result.sources.length > 0 && (
537
+ <div className="flex flex-wrap gap-2 mt-4 pt-4 border-t border-border">
538
+ {result.sources.map((source) => {
539
+ const sourceInfo = SOURCE_TYPE_LABELS[source.type] || { label: source.type, color: 'gray' };
540
+ return (
541
+ <Badge key={source.type} variant="secondary">
542
+ {sourceInfo.label}: {source.count}
543
+ </Badge>
544
+ );
545
+ })}
546
+ </div>
547
+ )}
548
+
549
+ {result.description && (
550
+ <p className="text-muted-foreground mt-4 pt-4 border-t border-border">{result.description}</p>
551
+ )}
552
+ </div>
553
+
554
+ {/* Tools List */}
555
+ <div className="rounded-2xl border border-border bg-card">
556
+ <div className="p-6 border-b border-border">
557
+ <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
558
+ <div className="flex items-center gap-2">
559
+ <Box className="h-4 w-4 text-muted-foreground" />
560
+ <h3 className="font-semibold">Extracted Tools</h3>
561
+ <Badge variant="secondary">{filteredTools.length}</Badge>
562
+ </div>
563
+ {result.tools.length > 5 && (
564
+ <div className="relative w-full sm:w-56">
565
+ <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
566
+ <input
567
+ type="text"
568
+ placeholder="Filter tools..."
569
+ value={searchFilter}
570
+ onChange={(e) => setSearchFilter(e.target.value)}
571
+ className="w-full rounded-lg border border-border bg-background py-2 pl-9 pr-4 text-sm transition-colors focus:border-foreground focus:outline-none"
572
+ />
573
+ </div>
574
+ )}
575
+ </div>
576
+ </div>
577
+ <div className="p-6">
578
+ <div className="max-h-[500px] space-y-2 overflow-y-auto pr-2">
579
+ {filteredTools.map((tool, i) => (
580
+ <ToolCard
581
+ key={`${tool.name}-${i}`}
582
+ tool={tool}
583
+ expanded={expandedTools.has(tool.name)}
584
+ onToggle={() => toggleToolExpansion(tool.name)}
585
+ />
586
+ ))}
587
+ {filteredTools.length === 0 && searchFilter && (
588
+ <div className="py-8 text-center text-muted-foreground">
589
+ No tools match "{searchFilter}"
590
+ </div>
591
+ )}
592
+ </div>
593
+ </div>
594
+ </div>
595
+
596
+ {/* Configuration Tabs */}
597
+ <div className="rounded-2xl border border-border bg-card">
598
+ <div className="p-6 border-b border-border">
599
+ <h3 className="font-semibold flex items-center gap-2">
600
+ <Settings className="h-4 w-4 text-muted-foreground" />
601
+ Configuration
602
+ </h3>
603
+ <p className="text-sm text-muted-foreground mt-1">
604
+ Copy the configuration for your AI assistant
605
+ </p>
606
+ </div>
607
+ <div className="p-6">
608
+ <Tabs value={activeConfigTab} onValueChange={setActiveConfigTab}>
609
+ <TabsList className="mb-4 w-full sm:w-auto">
610
+ {Object.entries(PLATFORMS).map(([key, platform]) => (
611
+ <TabsTrigger key={key} value={key}>
612
+ {platform.icon} {platform.name}
613
+ </TabsTrigger>
614
+ ))}
615
+ </TabsList>
616
+
617
+ <TabsContent value="claude">
618
+ <div className="space-y-3">
619
+ <p className="text-sm text-muted-foreground">
620
+ Add to: <code className="rounded bg-muted px-1.5 py-0.5 text-xs font-mono">
621
+ {PLATFORMS.claude.configPath}
622
+ </code>
623
+ </p>
624
+ <CodeBlock
625
+ code={result.claudeConfig || '{}'}
626
+ language="json"
627
+ filename="claude_desktop_config.json"
628
+ showDownload
629
+ />
630
+ </div>
631
+ </TabsContent>
632
+
633
+ <TabsContent value="cursor">
634
+ <div className="space-y-3">
635
+ <p className="text-sm text-muted-foreground">
636
+ Add to: <code className="rounded bg-muted px-1.5 py-0.5 text-xs font-mono">
637
+ {PLATFORMS.cursor.configPath}
638
+ </code>
639
+ </p>
640
+ <CodeBlock
641
+ code={result.cursorConfig || '{}'}
642
+ language="json"
643
+ filename="mcp.json"
644
+ showDownload
645
+ />
646
+ </div>
647
+ </TabsContent>
648
+
649
+ <TabsContent value="openai">
650
+ <div className="space-y-3">
651
+ <p className="text-sm text-muted-foreground">
652
+ Configuration for OpenAI-compatible clients
653
+ </p>
654
+ <CodeBlock
655
+ code={result.openaiConfig || result.claudeConfig || '{}'}
656
+ language="json"
657
+ filename="openai-mcp-config.json"
658
+ showDownload
659
+ />
660
+ </div>
661
+ </TabsContent>
662
+ </Tabs>
663
+ </div>
664
+ </div>
665
+
666
+ {/* Generated Code */}
667
+ <div className="rounded-2xl border border-border bg-card">
668
+ <div className="p-6 border-b border-border">
669
+ <div className="flex items-center justify-between">
670
+ <div>
671
+ <h3 className="font-semibold flex items-center gap-2">
672
+ <FileCode className="h-4 w-4 text-muted-foreground" />
673
+ Generated Server
674
+ </h3>
675
+ <p className="text-sm text-muted-foreground mt-1">
676
+ Ready-to-run MCP server implementation
677
+ </p>
678
+ </div>
679
+ <div className="flex gap-2">
680
+ <Button
681
+ variant="outline"
682
+ size="sm"
683
+ onClick={() => handleCopy(result.code, 'code')}
684
+ >
685
+ {copiedItem === 'code' ? (
686
+ <>
687
+ <Check className="h-3.5 w-3.5" />
688
+ Copied
689
+ </>
690
+ ) : (
691
+ <>
692
+ <Copy className="h-3.5 w-3.5" />
693
+ Copy
694
+ </>
695
+ )}
696
+ </Button>
697
+ <Button size="sm" onClick={handleDownload}>
698
+ <Download className="h-3.5 w-3.5" />
699
+ Download
700
+ </Button>
701
+ </div>
702
+ </div>
703
+ </div>
704
+ <div className="p-6">
705
+ <Tabs defaultValue="typescript" className="w-full">
706
+ <TabsList className="mb-4">
707
+ <TabsTrigger value="typescript">TypeScript</TabsTrigger>
708
+ <TabsTrigger value="python">Python</TabsTrigger>
709
+ </TabsList>
710
+ <TabsContent value="typescript">
711
+ <CodeBlock
712
+ code={result.code}
713
+ language="typescript"
714
+ filename={`${result.name}-mcp-server.ts`}
715
+ maxHeight="400px"
716
+ showDownload
717
+ />
718
+ </TabsContent>
719
+ <TabsContent value="python">
720
+ <CodeBlock
721
+ code={result.pythonCode || '# Python code generation not available'}
722
+ language="python"
723
+ filename={`${result.name}_mcp_server.py`}
724
+ maxHeight="400px"
725
+ showDownload
726
+ />
727
+ </TabsContent>
728
+ </Tabs>
729
+ </div>
730
+ </div>
731
+ </div>
732
+ )}
733
+
734
+ {/* History Panel */}
735
+ {showHistory && (
736
+ <div className="fixed inset-y-0 right-0 z-50 w-full overflow-hidden bg-background border-l border-border shadow-2xl sm:w-80">
737
+ <div className="flex h-14 items-center justify-between border-b border-border px-4">
738
+ <h3 className="flex items-center gap-2 text-sm font-semibold">
739
+ <History className="h-4 w-4" />
740
+ History
741
+ </h3>
742
+ <Button variant="ghost" size="icon-sm" onClick={() => setShowHistory(false)}>
743
+ <X className="h-4 w-4" />
744
+ </Button>
745
+ </div>
746
+ <div className="space-y-2 overflow-y-auto p-4" style={{ maxHeight: 'calc(100vh - 120px)' }}>
747
+ {history.length === 0 ? (
748
+ <p className="py-8 text-center text-sm text-muted-foreground">No conversion history yet</p>
749
+ ) : (
750
+ <>
751
+ <Button
752
+ variant="ghost"
753
+ size="sm"
754
+ className="w-full text-red-500 hover:bg-red-500/10 hover:text-red-500"
755
+ onClick={() => setHistory([])}
756
+ >
757
+ <Trash2 className="mr-2 h-3.5 w-3.5" />
758
+ Clear History
759
+ </Button>
760
+ {history.map((item) => (
761
+ <button
762
+ key={item.id}
763
+ onClick={() => {
764
+ setUrl(item.url);
765
+ setShowHistory(false);
766
+ }}
767
+ className="w-full rounded-lg border border-border bg-card p-3 text-left transition-colors hover:border-muted-foreground/50"
768
+ >
769
+ <div className="mb-1.5 flex items-center justify-between">
770
+ <span className="truncate text-sm font-medium">{item.name}</span>
771
+ <Badge variant="secondary" className="ml-2 shrink-0 text-[10px]">
772
+ {item.toolCount} tools
773
+ </Badge>
774
+ </div>
775
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
776
+ <Clock className="h-3 w-3" />
777
+ {formatRelativeTime(item.convertedAt)}
778
+ </div>
779
+ </button>
780
+ ))}
781
+ </>
782
+ )}
783
+ </div>
784
+ </div>
785
+ )}
786
+
787
+ {/* History Overlay */}
788
+ {showHistory && (
789
+ <div
790
+ className="fixed inset-0 z-40 bg-background/80 backdrop-blur-sm"
791
+ onClick={() => setShowHistory(false)}
792
+ />
793
+ )}
794
+ </main>
795
+
796
+ {/* Footer */}
797
+ <footer className="mt-auto border-t border-border py-8">
798
+ <div className="mx-auto max-w-5xl px-4 sm:px-6">
799
+ <div className="flex flex-col items-center justify-between gap-4 sm:flex-row">
800
+ <div className="flex items-center gap-4">
801
+ <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-foreground">
802
+ <Zap className="h-4 w-4 text-background" />
803
+ </div>
804
+ <span className="text-sm text-muted-foreground">
805
+ Built by{' '}
806
+ <a
807
+ href="https://github.com/nirholas"
808
+ target="_blank"
809
+ rel="noopener noreferrer"
810
+ className="text-foreground hover:underline"
811
+ >
812
+ nirholas
813
+ </a>
814
+ </span>
815
+ </div>
816
+
817
+ <div className="flex items-center gap-6 text-sm text-muted-foreground">
818
+ <a
819
+ href={GITHUB_URL}
820
+ target="_blank"
821
+ rel="noopener noreferrer"
822
+ className="hover:text-foreground transition-colors"
823
+ >
824
+ GitHub
825
+ </a>
826
+ <a
827
+ href={`${GITHUB_URL}#readme`}
828
+ target="_blank"
829
+ rel="noopener noreferrer"
830
+ className="hover:text-foreground transition-colors"
831
+ >
832
+ Docs
833
+ </a>
834
+ <a
835
+ href={`${GITHUB_URL}/issues`}
836
+ target="_blank"
837
+ rel="noopener noreferrer"
838
+ className="hover:text-foreground transition-colors"
839
+ >
840
+ Issues
841
+ </a>
842
+ </div>
843
+ </div>
844
+ </div>
845
+ </footer>
846
+ </div>
847
+ );
848
+ }
849
+
850
+ // Tool Card Component
851
+ function ToolCard({
852
+ tool,
853
+ expanded,
854
+ onToggle,
855
+ }: {
856
+ tool: Tool;
857
+ expanded: boolean;
858
+ onToggle: () => void;
859
+ }) {
860
+ const sourceInfo = SOURCE_TYPE_LABELS[tool.source.type] || { label: tool.source.type, color: 'gray' };
861
+ const hasSchema = tool.inputSchema?.properties && Object.keys(tool.inputSchema.properties).length > 0;
862
+
863
+ return (
864
+ <div className="overflow-hidden rounded-xl border border-border bg-card transition-colors hover:border-muted-foreground/30">
865
+ <button
866
+ onClick={onToggle}
867
+ className="flex w-full items-start justify-between p-4 text-left transition-colors hover:bg-muted/50"
868
+ >
869
+ <div className="min-w-0 flex-1">
870
+ <div className="mb-1.5 flex items-center gap-2">
871
+ <code className="font-mono text-sm font-medium">
872
+ {tool.name}
873
+ </code>
874
+ <Badge variant="secondary" className="text-[10px]">
875
+ {sourceInfo.label}
876
+ </Badge>
877
+ </div>
878
+ <p className="line-clamp-2 text-sm text-muted-foreground">
879
+ {tool.description}
880
+ </p>
881
+ </div>
882
+ <div className="ml-4 shrink-0">
883
+ {expanded ? (
884
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
885
+ ) : (
886
+ <ChevronRight className="h-4 w-4 text-muted-foreground" />
887
+ )}
888
+ </div>
889
+ </button>
890
+
891
+ {expanded && hasSchema && (
892
+ <div className="border-t border-border bg-muted/30 p-4">
893
+ <h4 className="mb-3 text-xs font-medium uppercase tracking-wide text-muted-foreground">Parameters</h4>
894
+ <div className="space-y-2">
895
+ {Object.entries(tool.inputSchema.properties || {}).map(([name, prop]: [string, any]) => (
896
+ <div key={name} className="flex flex-wrap items-center gap-2 text-sm">
897
+ <code className="font-mono text-xs">
898
+ {name}
899
+ </code>
900
+ <Badge variant="outline" className="text-[10px]">
901
+ {prop.type}
902
+ </Badge>
903
+ {tool.inputSchema.required?.includes(name) && (
904
+ <Badge variant="secondary" className="text-[10px] bg-red-500/10 text-red-500">
905
+ required
906
+ </Badge>
907
+ )}
908
+ {prop.description && (
909
+ <span className="text-xs text-muted-foreground">{prop.description}</span>
910
+ )}
911
+ </div>
912
+ ))}
913
+ </div>
914
+ {tool.source.file && (
915
+ <p className="mt-4 text-xs text-muted-foreground">
916
+ Source: {tool.source.file}
917
+ {tool.source.line && `:${tool.source.line}`}
918
+ </p>
919
+ )}
920
+ </div>
921
+ )}
922
+ </div>
923
+ );
924
+ }