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,279 @@
1
+ /**
2
+ * @fileoverview MCP server introspection - extract tools from existing MCP repos
3
+ * @copyright Copyright (c) 2024-2026 nirholas
4
+ * @license MIT
5
+ */
6
+
7
+ import type { ExtractedTool, McpToolDefinition } from './types';
8
+
9
+ /**
10
+ * Extract tool definitions from existing MCP server code
11
+ */
12
+ export class McpIntrospector {
13
+ /**
14
+ * Extract tools from TypeScript/JavaScript MCP server code
15
+ */
16
+ extractFromTypeScript(code: string, filePath: string): ExtractedTool[] {
17
+ const tools: ExtractedTool[] = [];
18
+
19
+ // Pattern 1: Tool definitions in array format
20
+ // tools: [{ name: "...", description: "...", inputSchema: {...} }]
21
+ const toolArrayMatch = code.match(/tools\s*:\s*\[([\s\S]*?)\]/);
22
+ if (toolArrayMatch) {
23
+ const toolsFromArray = this.parseToolArray(toolArrayMatch[1], filePath);
24
+ tools.push(...toolsFromArray);
25
+ }
26
+
27
+ // Pattern 2: ListToolsRequestSchema handler
28
+ // server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [...] }))
29
+ const listToolsMatch = code.match(
30
+ /setRequestHandler\s*\(\s*ListToolsRequestSchema[\s\S]*?tools\s*:\s*\[([\s\S]*?)\]\s*\}/
31
+ );
32
+ if (listToolsMatch) {
33
+ const toolsFromHandler = this.parseToolArray(listToolsMatch[1], filePath);
34
+ tools.push(...toolsFromHandler);
35
+ }
36
+
37
+ // Pattern 3: Individual tool definitions
38
+ // { name: "tool_name", description: "...", inputSchema: {...} }
39
+ const individualToolMatches = code.matchAll(
40
+ /\{\s*name\s*:\s*['"`](\w+)['"`]\s*,\s*description\s*:\s*['"`]([^'"`]+)['"`]\s*,\s*inputSchema\s*:\s*(\{[\s\S]*?\})\s*\}/g
41
+ );
42
+ for (const match of individualToolMatches) {
43
+ const [, name, description, schemaStr] = match;
44
+ try {
45
+ // Try to parse the schema (may fail for complex cases)
46
+ const inputSchema = this.parseJsonLike(schemaStr);
47
+ tools.push({
48
+ name,
49
+ description,
50
+ inputSchema,
51
+ source: { type: 'mcp-introspect', file: filePath }
52
+ });
53
+ } catch {
54
+ // Fallback with basic schema
55
+ tools.push({
56
+ name,
57
+ description,
58
+ inputSchema: { type: 'object', properties: {}, required: [] },
59
+ source: { type: 'mcp-introspect', file: filePath }
60
+ });
61
+ }
62
+ }
63
+
64
+ // Pattern 4: server.tool() calls (MCP SDK pattern)
65
+ // server.tool("name", "description", { schema }, handler)
66
+ const serverToolMatches = code.matchAll(
67
+ /server\.tool\s*\(\s*['"`](\w+)['"`]\s*,\s*['"`]([^'"`]+)['"`]\s*,?\s*(\{[\s\S]*?\})?\s*,/g
68
+ );
69
+ for (const match of serverToolMatches) {
70
+ const [, name, description, schemaStr] = match;
71
+ let inputSchema = { type: 'object', properties: {}, required: [] as string[] };
72
+
73
+ if (schemaStr) {
74
+ try {
75
+ inputSchema = this.parseJsonLike(schemaStr);
76
+ } catch { /* use default */ }
77
+ }
78
+
79
+ tools.push({
80
+ name,
81
+ description,
82
+ inputSchema,
83
+ source: { type: 'mcp-introspect', file: filePath }
84
+ });
85
+ }
86
+
87
+ return this.deduplicateTools(tools);
88
+ }
89
+
90
+ /**
91
+ * Extract tools from Python MCP server code
92
+ */
93
+ extractFromPython(code: string, filePath: string): ExtractedTool[] {
94
+ const tools: ExtractedTool[] = [];
95
+
96
+ // Pattern 1: @server.tool() decorator
97
+ // @server.tool()
98
+ // async def tool_name(arg1: str, arg2: int) -> str:
99
+ // """Description"""
100
+ const decoratorMatches = code.matchAll(
101
+ /@(?:server|mcp)\.(?:tool|call_tool)\s*\([^)]*\)\s*\n\s*(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)[^:]*:\s*\n\s*(?:"""([^"]+)"""|'''([^']+)''')?/g
102
+ );
103
+
104
+ for (const match of decoratorMatches) {
105
+ const [, name, argsStr, docstring1, docstring2] = match;
106
+ const description = (docstring1 || docstring2 || `Python tool: ${name}`).trim();
107
+ const inputSchema = this.pythonArgsToSchema(argsStr);
108
+
109
+ tools.push({
110
+ name,
111
+ description,
112
+ inputSchema,
113
+ source: { type: 'mcp-introspect', file: filePath }
114
+ });
115
+ }
116
+
117
+ // Pattern 2: Tool class definitions
118
+ // class MyTool(Tool):
119
+ // name = "my_tool"
120
+ // description = "..."
121
+ const classMatches = code.matchAll(
122
+ /class\s+(\w+)\s*\([^)]*Tool[^)]*\)[\s\S]*?name\s*=\s*['"`](\w+)['"`][\s\S]*?description\s*=\s*['"`]([^'"`]+)['"`]/g
123
+ );
124
+
125
+ for (const match of classMatches) {
126
+ const [, , name, description] = match;
127
+ tools.push({
128
+ name,
129
+ description,
130
+ inputSchema: { type: 'object', properties: {}, required: [] },
131
+ source: { type: 'mcp-introspect', file: filePath }
132
+ });
133
+ }
134
+
135
+ // Pattern 3: tools list/dict
136
+ // tools = [{"name": "...", "description": "..."}]
137
+ const toolsListMatch = code.match(/tools\s*=\s*\[([\s\S]*?)\]/);
138
+ if (toolsListMatch) {
139
+ const dictMatches = toolsListMatch[1].matchAll(
140
+ /\{\s*["']name["']\s*:\s*["'](\w+)["']\s*,\s*["']description["']\s*:\s*["']([^"']+)["']/g
141
+ );
142
+ for (const m of dictMatches) {
143
+ tools.push({
144
+ name: m[1],
145
+ description: m[2],
146
+ inputSchema: { type: 'object', properties: {}, required: [] },
147
+ source: { type: 'mcp-introspect', file: filePath }
148
+ });
149
+ }
150
+ }
151
+
152
+ return this.deduplicateTools(tools);
153
+ }
154
+
155
+ /**
156
+ * Parse Python function arguments to JSON schema
157
+ */
158
+ private pythonArgsToSchema(argsStr: string): { type: string; properties: Record<string, any>; required: string[] } {
159
+ const properties: Record<string, any> = {};
160
+ const required: string[] = [];
161
+
162
+ // Parse args like: arg1: str, arg2: int = 5, arg3: Optional[str] = None
163
+ const argMatches = argsStr.matchAll(
164
+ /(\w+)\s*:\s*([^,=]+)(?:\s*=\s*([^,]+))?/g
165
+ );
166
+
167
+ for (const match of argMatches) {
168
+ const [, name, typeHint, defaultValue] = match;
169
+ if (name === 'self' || name === 'ctx' || name === 'context') continue;
170
+
171
+ const jsonType = this.pythonTypeToJsonType(typeHint.trim());
172
+ properties[name] = {
173
+ type: jsonType,
174
+ description: `${name} parameter`
175
+ };
176
+
177
+ // If no default value and not Optional, it's required
178
+ if (!defaultValue && !typeHint.includes('Optional') && !typeHint.includes('None')) {
179
+ required.push(name);
180
+ }
181
+ }
182
+
183
+ return { type: 'object', properties, required };
184
+ }
185
+
186
+ /**
187
+ * Map Python type hints to JSON Schema types
188
+ */
189
+ private pythonTypeToJsonType(pyType: string): string {
190
+ const typeMap: Record<string, string> = {
191
+ 'str': 'string',
192
+ 'int': 'integer',
193
+ 'float': 'number',
194
+ 'bool': 'boolean',
195
+ 'list': 'array',
196
+ 'dict': 'object',
197
+ 'List': 'array',
198
+ 'Dict': 'object',
199
+ 'Any': 'string'
200
+ };
201
+
202
+ // Handle Optional[X], List[X], etc
203
+ const baseMatch = pyType.match(/^(?:Optional|List|Dict|Union)?\[?(\w+)/);
204
+ if (baseMatch) {
205
+ return typeMap[baseMatch[1]] || 'string';
206
+ }
207
+
208
+ return typeMap[pyType] || 'string';
209
+ }
210
+
211
+ /**
212
+ * Parse a tool array string into tool definitions
213
+ */
214
+ private parseToolArray(arrayContent: string, filePath: string): ExtractedTool[] {
215
+ const tools: ExtractedTool[] = [];
216
+
217
+ // Find each tool object
218
+ const toolMatches = arrayContent.matchAll(
219
+ /\{\s*(?:["'])?name(?:["'])?\s*:\s*["'](\w+)["']\s*,\s*(?:["'])?description(?:["'])?\s*:\s*["']([^"']+)["']/g
220
+ );
221
+
222
+ for (const match of toolMatches) {
223
+ tools.push({
224
+ name: match[1],
225
+ description: match[2],
226
+ inputSchema: { type: 'object', properties: {}, required: [] },
227
+ source: { type: 'mcp-introspect', file: filePath }
228
+ });
229
+ }
230
+
231
+ return tools;
232
+ }
233
+
234
+ /**
235
+ * Parse JSON-like object strings (handles some JS patterns)
236
+ */
237
+ private parseJsonLike(str: string): any {
238
+ // Convert JS object syntax to valid JSON
239
+ let jsonStr = str
240
+ .replace(/(\w+)\s*:/g, '"$1":') // unquoted keys
241
+ .replace(/'/g, '"') // single to double quotes
242
+ .replace(/,\s*}/g, '}') // trailing commas
243
+ .replace(/,\s*]/g, ']');
244
+
245
+ return JSON.parse(jsonStr);
246
+ }
247
+
248
+ /**
249
+ * Remove duplicate tools by name
250
+ */
251
+ private deduplicateTools(tools: ExtractedTool[]): ExtractedTool[] {
252
+ const seen = new Set<string>();
253
+ return tools.filter(tool => {
254
+ if (seen.has(tool.name)) return false;
255
+ seen.add(tool.name);
256
+ return true;
257
+ });
258
+ }
259
+
260
+ /**
261
+ * Detect if a file is likely an MCP server
262
+ */
263
+ isLikelyMcpServer(code: string): boolean {
264
+ const indicators = [
265
+ '@modelcontextprotocol',
266
+ 'mcp.server',
267
+ 'McpServer',
268
+ 'MCP server',
269
+ 'ListToolsRequestSchema',
270
+ 'CallToolRequestSchema',
271
+ 'StdioServerTransport',
272
+ '@server.tool',
273
+ 'from mcp',
274
+ 'import mcp'
275
+ ];
276
+
277
+ return indicators.some(indicator => code.includes(indicator));
278
+ }
279
+ }
@@ -0,0 +1,378 @@
1
+ /**
2
+ * @fileoverview Monorepo detection and processing
3
+ * @copyright Copyright (c) 2024-2026 nirholas
4
+ * @license MIT
5
+ */
6
+
7
+ import type { MonorepoInfo, MonorepoType, MonorepoPackage } from './types';
8
+ import type { GithubClient } from './github-client';
9
+
10
+ /**
11
+ * Detects and processes monorepo structures
12
+ */
13
+ export class MonorepoDetector {
14
+ private verbose: boolean;
15
+
16
+ constructor(verbose: boolean = false) {
17
+ this.verbose = verbose;
18
+ }
19
+
20
+ /**
21
+ * Detect if repository is a monorepo and identify its structure
22
+ */
23
+ async detect(
24
+ github: GithubClient,
25
+ owner: string,
26
+ repo: string,
27
+ branch?: string
28
+ ): Promise<MonorepoInfo | null> {
29
+ // Try each detection method in order of specificity
30
+ const detectors: Array<() => Promise<MonorepoInfo | null>> = [
31
+ () => this.detectLerna(github, owner, repo, branch),
32
+ () => this.detectNx(github, owner, repo, branch),
33
+ () => this.detectTurborepo(github, owner, repo, branch),
34
+ () => this.detectPnpmWorkspace(github, owner, repo, branch),
35
+ () => this.detectNpmYarnWorkspace(github, owner, repo, branch),
36
+ () => this.detectCustomLayout(github, owner, repo, branch)
37
+ ];
38
+
39
+ for (const detector of detectors) {
40
+ const result = await detector();
41
+ if (result && result.packages.length > 0) {
42
+ if (this.verbose) {
43
+ console.log(`[Monorepo] Detected ${result.type} monorepo with ${result.packages.length} packages`);
44
+ }
45
+ return result;
46
+ }
47
+ }
48
+
49
+ return null;
50
+ }
51
+
52
+ /**
53
+ * Detect Lerna monorepo (lerna.json)
54
+ */
55
+ private async detectLerna(
56
+ github: GithubClient,
57
+ owner: string,
58
+ repo: string,
59
+ branch?: string
60
+ ): Promise<MonorepoInfo | null> {
61
+ const lernaConfig = await github.getFileContent(owner, repo, 'lerna.json', branch);
62
+ if (!lernaConfig) return null;
63
+
64
+ try {
65
+ const config = JSON.parse(lernaConfig.content);
66
+ const packagePatterns = config.packages || ['packages/*'];
67
+ const packages = await this.resolvePackagePatterns(github, owner, repo, packagePatterns, branch);
68
+
69
+ return {
70
+ type: 'lerna',
71
+ packages,
72
+ rootPath: ''
73
+ };
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Detect Nx monorepo (nx.json)
81
+ */
82
+ private async detectNx(
83
+ github: GithubClient,
84
+ owner: string,
85
+ repo: string,
86
+ branch?: string
87
+ ): Promise<MonorepoInfo | null> {
88
+ const nxConfig = await github.getFileContent(owner, repo, 'nx.json', branch);
89
+ if (!nxConfig) return null;
90
+
91
+ try {
92
+ // Nx typically uses apps/ and libs/ directories
93
+ const patterns = ['apps/*', 'libs/*', 'packages/*'];
94
+ const packages = await this.resolvePackagePatterns(github, owner, repo, patterns, branch);
95
+
96
+ return {
97
+ type: 'nx',
98
+ packages,
99
+ rootPath: ''
100
+ };
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Detect Turborepo monorepo (turbo.json)
108
+ */
109
+ private async detectTurborepo(
110
+ github: GithubClient,
111
+ owner: string,
112
+ repo: string,
113
+ branch?: string
114
+ ): Promise<MonorepoInfo | null> {
115
+ const turboConfig = await github.getFileContent(owner, repo, 'turbo.json', branch);
116
+ if (!turboConfig) return null;
117
+
118
+ try {
119
+ // Turborepo typically uses packages/ and apps/ directories
120
+ const patterns = ['packages/*', 'apps/*'];
121
+ const packages = await this.resolvePackagePatterns(github, owner, repo, patterns, branch);
122
+
123
+ return {
124
+ type: 'turborepo',
125
+ packages,
126
+ rootPath: ''
127
+ };
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Detect pnpm workspace (pnpm-workspace.yaml)
135
+ */
136
+ private async detectPnpmWorkspace(
137
+ github: GithubClient,
138
+ owner: string,
139
+ repo: string,
140
+ branch?: string
141
+ ): Promise<MonorepoInfo | null> {
142
+ const pnpmConfig = await github.getFileContent(owner, repo, 'pnpm-workspace.yaml', branch);
143
+ if (!pnpmConfig) return null;
144
+
145
+ try {
146
+ // Simple YAML parsing for packages array
147
+ const patterns = this.parseYamlPackages(pnpmConfig.content);
148
+ if (patterns.length === 0) return null;
149
+
150
+ const packages = await this.resolvePackagePatterns(github, owner, repo, patterns, branch);
151
+
152
+ return {
153
+ type: 'pnpm',
154
+ packages,
155
+ rootPath: ''
156
+ };
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Detect npm/yarn workspaces from package.json
164
+ */
165
+ private async detectNpmYarnWorkspace(
166
+ github: GithubClient,
167
+ owner: string,
168
+ repo: string,
169
+ branch?: string
170
+ ): Promise<MonorepoInfo | null> {
171
+ const packageJson = await github.getFileContent(owner, repo, 'package.json', branch);
172
+ if (!packageJson) return null;
173
+
174
+ try {
175
+ const pkg = JSON.parse(packageJson.content);
176
+ const workspaces = pkg.workspaces;
177
+
178
+ if (!workspaces) return null;
179
+
180
+ // Handle both array format and object format (yarn)
181
+ const patterns = Array.isArray(workspaces)
182
+ ? workspaces
183
+ : (workspaces.packages || []);
184
+
185
+ if (patterns.length === 0) return null;
186
+
187
+ const packages = await this.resolvePackagePatterns(github, owner, repo, patterns, branch);
188
+
189
+ // Detect yarn.lock vs package-lock.json
190
+ const yarnLock = await github.getFileContent(owner, repo, 'yarn.lock', branch);
191
+ const type = yarnLock ? 'yarn' : 'npm';
192
+
193
+ return {
194
+ type,
195
+ packages,
196
+ rootPath: ''
197
+ };
198
+ } catch {
199
+ return null;
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Detect custom directory layout (packages/*, apps/*, libs/*)
205
+ */
206
+ private async detectCustomLayout(
207
+ github: GithubClient,
208
+ owner: string,
209
+ repo: string,
210
+ branch?: string
211
+ ): Promise<MonorepoInfo | null> {
212
+ const patterns = ['packages/*', 'apps/*', 'libs/*', 'modules/*'];
213
+ const packages = await this.resolvePackagePatterns(github, owner, repo, patterns, branch);
214
+
215
+ if (packages.length < 2) return null; // Need at least 2 packages to be a monorepo
216
+
217
+ return {
218
+ type: 'custom',
219
+ packages,
220
+ rootPath: ''
221
+ };
222
+ }
223
+
224
+ /**
225
+ * Parse packages from pnpm-workspace.yaml
226
+ */
227
+ private parseYamlPackages(yamlContent: string): string[] {
228
+ const patterns: string[] = [];
229
+ const lines = yamlContent.split('\n');
230
+ let inPackages = false;
231
+
232
+ for (const line of lines) {
233
+ if (line.trim() === 'packages:') {
234
+ inPackages = true;
235
+ continue;
236
+ }
237
+ if (inPackages) {
238
+ if (line.match(/^\s+-\s+["']?(.+?)["']?\s*$/)) {
239
+ const match = line.match(/^\s+-\s+["']?(.+?)["']?\s*$/);
240
+ if (match) {
241
+ patterns.push(match[1]);
242
+ }
243
+ } else if (!line.startsWith(' ') && !line.startsWith('-') && line.trim()) {
244
+ break; // New section started
245
+ }
246
+ }
247
+ }
248
+
249
+ return patterns;
250
+ }
251
+
252
+ /**
253
+ * Resolve package patterns to actual packages
254
+ */
255
+ private async resolvePackagePatterns(
256
+ github: GithubClient,
257
+ owner: string,
258
+ repo: string,
259
+ patterns: string[],
260
+ branch?: string
261
+ ): Promise<MonorepoPackage[]> {
262
+ const packages: MonorepoPackage[] = [];
263
+ const seen = new Set<string>();
264
+
265
+ for (const pattern of patterns) {
266
+ // Handle simple patterns like "packages/*"
267
+ const basePath = pattern.replace(/\/\*.*$/, '');
268
+
269
+ try {
270
+ const dirs = await github.listDirectory(owner, repo, basePath, branch);
271
+
272
+ for (const dir of dirs) {
273
+ if (dir.type !== 'dir') continue;
274
+ if (seen.has(dir.path)) continue;
275
+ seen.add(dir.path);
276
+
277
+ // Try to get package name from package.json or Cargo.toml or similar
278
+ const packageInfo = await this.getPackageInfo(github, owner, repo, dir.path, branch);
279
+
280
+ if (packageInfo) {
281
+ packages.push({
282
+ name: packageInfo.name,
283
+ path: dir.path,
284
+ language: packageInfo.language
285
+ });
286
+ }
287
+ }
288
+ } catch {
289
+ // Directory doesn't exist or can't be listed
290
+ continue;
291
+ }
292
+ }
293
+
294
+ return packages;
295
+ }
296
+
297
+ /**
298
+ * Get package info from manifest file
299
+ */
300
+ private async getPackageInfo(
301
+ github: GithubClient,
302
+ owner: string,
303
+ repo: string,
304
+ packagePath: string,
305
+ branch?: string
306
+ ): Promise<{ name: string; language?: string } | null> {
307
+ // Try package.json (Node.js)
308
+ const packageJson = await github.getFileContent(owner, repo, `${packagePath}/package.json`, branch);
309
+ if (packageJson) {
310
+ try {
311
+ const pkg = JSON.parse(packageJson.content);
312
+ const name = pkg.name || packagePath.split('/').pop()!;
313
+ return { name, language: 'typescript' };
314
+ } catch {}
315
+ }
316
+
317
+ // Try Cargo.toml (Rust)
318
+ const cargoToml = await github.getFileContent(owner, repo, `${packagePath}/Cargo.toml`, branch);
319
+ if (cargoToml) {
320
+ const nameMatch = cargoToml.content.match(/name\s*=\s*"([^"]+)"/);
321
+ return {
322
+ name: nameMatch?.[1] || packagePath.split('/').pop()!,
323
+ language: 'rust'
324
+ };
325
+ }
326
+
327
+ // Try pyproject.toml (Python)
328
+ const pyprojectToml = await github.getFileContent(owner, repo, `${packagePath}/pyproject.toml`, branch);
329
+ if (pyprojectToml) {
330
+ const nameMatch = pyprojectToml.content.match(/name\s*=\s*"([^"]+)"/);
331
+ return {
332
+ name: nameMatch?.[1] || packagePath.split('/').pop()!,
333
+ language: 'python'
334
+ };
335
+ }
336
+
337
+ // Try setup.py (Python legacy)
338
+ const setupPy = await github.getFileContent(owner, repo, `${packagePath}/setup.py`, branch);
339
+ if (setupPy) {
340
+ const nameMatch = setupPy.content.match(/name\s*=\s*["']([^"']+)["']/);
341
+ return {
342
+ name: nameMatch?.[1] || packagePath.split('/').pop()!,
343
+ language: 'python'
344
+ };
345
+ }
346
+
347
+ // Try go.mod (Go)
348
+ const goMod = await github.getFileContent(owner, repo, `${packagePath}/go.mod`, branch);
349
+ if (goMod) {
350
+ const moduleMatch = goMod.content.match(/module\s+(\S+)/);
351
+ const name = moduleMatch?.[1].split('/').pop() || packagePath.split('/').pop()!;
352
+ return { name, language: 'go' };
353
+ }
354
+
355
+ // Try looking for main source file
356
+ const contents = await github.listDirectory(owner, repo, packagePath, branch);
357
+ for (const item of contents) {
358
+ if (item.path.endsWith('.gemspec')) {
359
+ return { name: packagePath.split('/').pop()!, language: 'ruby' };
360
+ }
361
+ }
362
+
363
+ // Fallback: use directory name
364
+ return { name: packagePath.split('/').pop()! };
365
+ }
366
+
367
+ /**
368
+ * Add namespace prefix to tool names for monorepo packages
369
+ */
370
+ addNamespacePrefix(toolName: string, packageName: string): string {
371
+ // Clean package name for use as prefix
372
+ const prefix = packageName
373
+ .replace(/^@[^/]+\//, '') // Remove scope like @org/
374
+ .replace(/[^a-zA-Z0-9]/g, '_'); // Replace special chars
375
+
376
+ return `${prefix}/${toolName}`;
377
+ }
378
+ }