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,988 @@
1
+ /**
2
+ * MCP Transport Abstraction - Factory for creating MCP transports
3
+ * @copyright 2024-2026 nirholas
4
+ * @license MIT
5
+ */
6
+
7
+ import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
8
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
9
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
10
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
11
+
12
+ import type {
13
+ TransportConfig,
14
+ StdioTransportConfig,
15
+ SseTransportConfig,
16
+ StreamableHttpTransportConfig,
17
+ } from './types.js';
18
+ import {
19
+ McpTransportError,
20
+ McpErrorCode,
21
+ McpError,
22
+ isStdioTransportConfig,
23
+ isSseTransportConfig,
24
+ isStreamableHttpTransportConfig,
25
+ } from './types.js';
26
+ import type { Logger } from './logger.js';
27
+ import { createNoopLogger } from './logger.js';
28
+ import type { McpEventEmitter } from './events.js';
29
+ import type { RetryConfig } from './retry.js';
30
+ import { retry, CircuitBreaker } from './retry.js';
31
+
32
+ // ============================================================================
33
+ // Enhanced Transport Types
34
+ // ============================================================================
35
+
36
+ /**
37
+ * Transport health status
38
+ */
39
+ export interface TransportHealthStatus {
40
+ readonly healthy: boolean;
41
+ readonly lastCheck: Date;
42
+ readonly consecutiveFailures: number;
43
+ readonly latencyMs: number | null;
44
+ readonly error?: string;
45
+ }
46
+
47
+ /**
48
+ * Transport wrapper with enhanced capabilities
49
+ */
50
+ export interface EnhancedTransport {
51
+ readonly transport: Transport;
52
+ readonly config: TransportConfig;
53
+ readonly createdAt: Date;
54
+ readonly healthStatus: TransportHealthStatus;
55
+
56
+ /** Check if transport is healthy */
57
+ checkHealth(): Promise<TransportHealthStatus>;
58
+
59
+ /** Close and cleanup the transport */
60
+ close(): Promise<void>;
61
+ }
62
+
63
+ /**
64
+ * Configuration for transport with reconnection support
65
+ */
66
+ export interface ReconnectingTransportConfig {
67
+ readonly transportConfig: TransportConfig;
68
+ readonly retryConfig?: Partial<RetryConfig>;
69
+ readonly healthCheckIntervalMs?: number;
70
+ readonly autoReconnect?: boolean;
71
+ readonly logger?: Logger;
72
+ readonly events?: McpEventEmitter;
73
+ }
74
+
75
+ /**
76
+ * Connection pool configuration
77
+ */
78
+ export interface ConnectionPoolConfig {
79
+ readonly maxConnections: number;
80
+ readonly minConnections: number;
81
+ readonly acquireTimeoutMs: number;
82
+ readonly idleTimeoutMs: number;
83
+ readonly healthCheckIntervalMs: number;
84
+ readonly logger?: Logger;
85
+ readonly events?: McpEventEmitter;
86
+ }
87
+
88
+ /**
89
+ * Connection pool entry
90
+ */
91
+ interface PoolEntry {
92
+ transport: EnhancedTransport;
93
+ inUse: boolean;
94
+ lastUsed: Date;
95
+ acquiredAt: Date | null;
96
+ }
97
+
98
+ // ============================================================================
99
+ // Transport Factory
100
+ // ============================================================================
101
+
102
+ /**
103
+ * Creates an MCP transport based on the provided configuration.
104
+ *
105
+ * @param config - Transport configuration specifying the type and parameters
106
+ * @returns A Transport instance ready to be connected
107
+ * @throws {McpTransportError} If the transport type is unsupported or configuration is invalid
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * // Create stdio transport
112
+ * const stdioTransport = createTransport({
113
+ * type: 'stdio',
114
+ * command: 'npx',
115
+ * args: ['tsx', 'server.ts'],
116
+ * });
117
+ *
118
+ * // Create streamable HTTP transport
119
+ * const httpTransport = createTransport({
120
+ * type: 'streamable-http',
121
+ * url: 'http://localhost:3000/mcp',
122
+ * });
123
+ * ```
124
+ */
125
+ export function createTransport(config: TransportConfig): Transport {
126
+ if (isStdioTransportConfig(config)) {
127
+ return createStdioTransport(config);
128
+ }
129
+
130
+ if (isSseTransportConfig(config)) {
131
+ return createSseTransport(config);
132
+ }
133
+
134
+ if (isStreamableHttpTransportConfig(config)) {
135
+ return createStreamableHttpTransport(config);
136
+ }
137
+
138
+ // TypeScript should prevent this, but handle it for runtime safety
139
+ throw new McpError(
140
+ McpErrorCode.UnsupportedTransport,
141
+ `Unsupported transport type: ${(config as TransportConfig).type}`
142
+ );
143
+ }
144
+
145
+ // ============================================================================
146
+ // Stdio Transport
147
+ // ============================================================================
148
+
149
+ /**
150
+ * Creates a stdio transport for spawning local MCP server processes.
151
+ *
152
+ * @param config - Stdio transport configuration
153
+ * @returns StdioClientTransport instance
154
+ * @throws {McpTransportError} If the configuration is invalid
155
+ */
156
+ function createStdioTransport(config: StdioTransportConfig): StdioClientTransport {
157
+ if (!config.command || typeof config.command !== 'string') {
158
+ throw new McpTransportError('Stdio transport requires a valid command string');
159
+ }
160
+
161
+ try {
162
+ return new StdioClientTransport({
163
+ command: config.command,
164
+ args: config.args ? [...config.args] : undefined,
165
+ env: config.env ? { ...config.env } : undefined,
166
+ cwd: config.cwd,
167
+ });
168
+ } catch (error) {
169
+ throw new McpTransportError(
170
+ `Failed to create stdio transport: ${error instanceof Error ? error.message : String(error)}`,
171
+ error
172
+ );
173
+ }
174
+ }
175
+
176
+ // ============================================================================
177
+ // SSE Transport
178
+ // ============================================================================
179
+
180
+ /**
181
+ * Creates an SSE transport for connecting to legacy HTTP+SSE MCP servers.
182
+ *
183
+ * @param config - SSE transport configuration
184
+ * @returns SSEClientTransport instance
185
+ * @throws {McpTransportError} If the configuration is invalid
186
+ * @deprecated SSE transport is deprecated in favor of streamable-http
187
+ */
188
+ function createSseTransport(config: SseTransportConfig): SSEClientTransport {
189
+ if (!config.url || typeof config.url !== 'string') {
190
+ throw new McpTransportError('SSE transport requires a valid URL string');
191
+ }
192
+
193
+ let url: URL;
194
+ try {
195
+ url = new URL(config.url);
196
+ } catch (error) {
197
+ throw new McpTransportError(
198
+ `Invalid SSE URL: ${config.url}`,
199
+ error
200
+ );
201
+ }
202
+
203
+ // Validate URL protocol
204
+ if (!['http:', 'https:'].includes(url.protocol)) {
205
+ throw new McpTransportError(
206
+ `SSE transport requires http or https URL, got: ${url.protocol}`
207
+ );
208
+ }
209
+
210
+ try {
211
+ const options = config.headers
212
+ ? {
213
+ requestInit: {
214
+ headers: { ...config.headers },
215
+ },
216
+ }
217
+ : undefined;
218
+
219
+ return new SSEClientTransport(url, options);
220
+ } catch (error) {
221
+ throw new McpTransportError(
222
+ `Failed to create SSE transport: ${error instanceof Error ? error.message : String(error)}`,
223
+ error
224
+ );
225
+ }
226
+ }
227
+
228
+ // ============================================================================
229
+ // Streamable HTTP Transport
230
+ // ============================================================================
231
+
232
+ /**
233
+ * Creates a Streamable HTTP transport for connecting to modern MCP servers.
234
+ *
235
+ * @param config - Streamable HTTP transport configuration
236
+ * @returns StreamableHTTPClientTransport instance
237
+ * @throws {McpTransportError} If the configuration is invalid
238
+ */
239
+ function createStreamableHttpTransport(
240
+ config: StreamableHttpTransportConfig
241
+ ): StreamableHTTPClientTransport {
242
+ if (!config.url || typeof config.url !== 'string') {
243
+ throw new McpTransportError('Streamable HTTP transport requires a valid URL string');
244
+ }
245
+
246
+ let url: URL;
247
+ try {
248
+ url = new URL(config.url);
249
+ } catch (error) {
250
+ throw new McpTransportError(
251
+ `Invalid Streamable HTTP URL: ${config.url}`,
252
+ error
253
+ );
254
+ }
255
+
256
+ // Validate URL protocol
257
+ if (!['http:', 'https:'].includes(url.protocol)) {
258
+ throw new McpTransportError(
259
+ `Streamable HTTP transport requires http or https URL, got: ${url.protocol}`
260
+ );
261
+ }
262
+
263
+ try {
264
+ const options: {
265
+ requestInit?: RequestInit;
266
+ sessionId?: string;
267
+ } = {};
268
+
269
+ if (config.headers) {
270
+ options.requestInit = {
271
+ headers: { ...config.headers },
272
+ };
273
+ }
274
+
275
+ if (config.sessionId) {
276
+ options.sessionId = config.sessionId;
277
+ }
278
+
279
+ return new StreamableHTTPClientTransport(
280
+ url,
281
+ Object.keys(options).length > 0 ? options : undefined
282
+ );
283
+ } catch (error) {
284
+ throw new McpTransportError(
285
+ `Failed to create Streamable HTTP transport: ${error instanceof Error ? error.message : String(error)}`,
286
+ error
287
+ );
288
+ }
289
+ }
290
+
291
+ // ============================================================================
292
+ // Transport Utilities
293
+ // ============================================================================
294
+
295
+ /**
296
+ * Validates a transport configuration without creating the transport.
297
+ *
298
+ * @param config - Transport configuration to validate
299
+ * @returns True if the configuration is valid
300
+ * @throws {McpTransportError} If the configuration is invalid
301
+ */
302
+ export function validateTransportConfig(config: TransportConfig): boolean {
303
+ if (!config || typeof config !== 'object') {
304
+ throw new McpTransportError('Transport configuration must be an object');
305
+ }
306
+
307
+ if (!config.type || typeof config.type !== 'string') {
308
+ throw new McpTransportError('Transport configuration must have a type');
309
+ }
310
+
311
+ switch (config.type) {
312
+ case 'stdio':
313
+ if (!config.command || typeof config.command !== 'string') {
314
+ throw new McpTransportError('Stdio transport requires a valid command string');
315
+ }
316
+ break;
317
+
318
+ case 'sse':
319
+ case 'streamable-http':
320
+ if (!config.url || typeof config.url !== 'string') {
321
+ throw new McpTransportError(`${config.type} transport requires a valid URL string`);
322
+ }
323
+ try {
324
+ const url = new URL(config.url);
325
+ if (!['http:', 'https:'].includes(url.protocol)) {
326
+ throw new McpTransportError(
327
+ `${config.type} transport requires http or https URL, got: ${url.protocol}`
328
+ );
329
+ }
330
+ } catch (error) {
331
+ if (error instanceof McpTransportError) {
332
+ throw error;
333
+ }
334
+ throw new McpTransportError(`Invalid URL: ${config.url}`, error);
335
+ }
336
+ break;
337
+
338
+ default:
339
+ throw new McpError(
340
+ McpErrorCode.UnsupportedTransport,
341
+ `Unsupported transport type: ${(config as TransportConfig).type}`
342
+ );
343
+ }
344
+
345
+ return true;
346
+ }
347
+
348
+ /**
349
+ * Gets the display name for a transport type.
350
+ *
351
+ * @param type - Transport type
352
+ * @returns Human-readable transport name
353
+ */
354
+ export function getTransportDisplayName(type: TransportConfig['type']): string {
355
+ switch (type) {
356
+ case 'stdio':
357
+ return 'Standard I/O (Local Process)';
358
+ case 'sse':
359
+ return 'Server-Sent Events (Legacy HTTP)';
360
+ case 'streamable-http':
361
+ return 'Streamable HTTP';
362
+ default:
363
+ return 'Unknown Transport';
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Checks if a transport type is supported in the current environment.
369
+ *
370
+ * @param type - Transport type to check
371
+ * @returns True if the transport type is supported
372
+ */
373
+ export function isTransportSupported(type: TransportConfig['type']): boolean {
374
+ switch (type) {
375
+ case 'stdio':
376
+ // Stdio transport requires Node.js process spawning
377
+ return typeof globalThis.process !== 'undefined';
378
+
379
+ case 'sse':
380
+ case 'streamable-http':
381
+ // HTTP transports are supported in all environments with fetch
382
+ return typeof globalThis.fetch !== 'undefined';
383
+
384
+ default:
385
+ return false;
386
+ }
387
+ }
388
+
389
+ // ============================================================================
390
+ // Enhanced Transport Wrapper
391
+ // ============================================================================
392
+
393
+ /**
394
+ * Creates an enhanced transport with health checking capabilities
395
+ */
396
+ export function createEnhancedTransport(
397
+ config: TransportConfig,
398
+ logger: Logger = createNoopLogger()
399
+ ): EnhancedTransport {
400
+ const transport = createTransport(config);
401
+ const createdAt = new Date();
402
+
403
+ let healthStatus: TransportHealthStatus = {
404
+ healthy: true,
405
+ lastCheck: createdAt,
406
+ consecutiveFailures: 0,
407
+ latencyMs: null,
408
+ };
409
+
410
+ const checkHealth = async (): Promise<TransportHealthStatus> => {
411
+ const startTime = performance.now();
412
+
413
+ try {
414
+ // For HTTP transports, we can do a lightweight check
415
+ if (isSseTransportConfig(config) || isStreamableHttpTransportConfig(config)) {
416
+ const controller = new AbortController();
417
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
418
+
419
+ try {
420
+ const response = await fetch(config.url, {
421
+ method: 'HEAD',
422
+ signal: controller.signal,
423
+ });
424
+ clearTimeout(timeoutId);
425
+
426
+ const latencyMs = performance.now() - startTime;
427
+ healthStatus = {
428
+ healthy: response.ok,
429
+ lastCheck: new Date(),
430
+ consecutiveFailures: response.ok ? 0 : healthStatus.consecutiveFailures + 1,
431
+ latencyMs,
432
+ error: response.ok ? undefined : `HTTP ${response.status}`,
433
+ };
434
+ } catch (fetchError) {
435
+ clearTimeout(timeoutId);
436
+ throw fetchError;
437
+ }
438
+ } else {
439
+ // For stdio, we assume healthy if transport exists
440
+ // Actual health would be checked via ping through the client
441
+ healthStatus = {
442
+ healthy: true,
443
+ lastCheck: new Date(),
444
+ consecutiveFailures: 0,
445
+ latencyMs: performance.now() - startTime,
446
+ };
447
+ }
448
+
449
+ logger.debug('Transport health check passed', { data: { latencyMs: healthStatus.latencyMs } });
450
+ } catch (error) {
451
+ const latencyMs = performance.now() - startTime;
452
+ healthStatus = {
453
+ healthy: false,
454
+ lastCheck: new Date(),
455
+ consecutiveFailures: healthStatus.consecutiveFailures + 1,
456
+ latencyMs,
457
+ error: error instanceof Error ? error.message : String(error),
458
+ };
459
+
460
+ logger.warn('Transport health check failed', {
461
+ data: {
462
+ errorMessage: healthStatus.error,
463
+ consecutiveFailures: healthStatus.consecutiveFailures,
464
+ },
465
+ });
466
+ }
467
+
468
+ return healthStatus;
469
+ };
470
+
471
+ const close = async (): Promise<void> => {
472
+ try {
473
+ await transport.close?.();
474
+ logger.debug('Transport closed');
475
+ } catch (error) {
476
+ logger.error('Error closing transport', {
477
+ data: { errorMessage: error instanceof Error ? error.message : String(error) },
478
+ });
479
+ }
480
+ };
481
+
482
+ return {
483
+ transport,
484
+ config,
485
+ createdAt,
486
+ get healthStatus() {
487
+ return healthStatus;
488
+ },
489
+ checkHealth,
490
+ close,
491
+ };
492
+ }
493
+
494
+ // ============================================================================
495
+ // Reconnecting Transport
496
+ // ============================================================================
497
+
498
+ /**
499
+ * Transport wrapper with automatic reconnection support
500
+ */
501
+ export class ReconnectingTransport {
502
+ private _transport: EnhancedTransport | null = null;
503
+ private _config: ReconnectingTransportConfig;
504
+ private _logger: Logger;
505
+ private _events?: McpEventEmitter;
506
+ private _circuitBreaker: CircuitBreaker;
507
+ private _healthCheckInterval: ReturnType<typeof setInterval> | null = null;
508
+ private _closed = false;
509
+ private _reconnecting = false;
510
+ private _connectionState: 'disconnected' | 'connecting' | 'connected' | 'error' = 'disconnected';
511
+
512
+ constructor(config: ReconnectingTransportConfig) {
513
+ this._config = config;
514
+ this._logger = config.logger ?? createNoopLogger();
515
+ this._events = config.events;
516
+ this._circuitBreaker = new CircuitBreaker({
517
+ failureThreshold: 5,
518
+ resetTimeoutMs: 30000,
519
+ });
520
+ }
521
+
522
+ /**
523
+ * Get the underlying transport
524
+ */
525
+ get transport(): Transport | null {
526
+ return this._transport?.transport ?? null;
527
+ }
528
+
529
+ /**
530
+ * Check if currently connected
531
+ */
532
+ get isConnected(): boolean {
533
+ return this._transport !== null && this._transport.healthStatus.healthy;
534
+ }
535
+
536
+ /**
537
+ * Check if closed
538
+ */
539
+ get isClosed(): boolean {
540
+ return this._closed;
541
+ }
542
+
543
+ /**
544
+ * Connect the transport with retry support
545
+ */
546
+ async connect(): Promise<Transport> {
547
+ if (this._closed) {
548
+ throw new McpTransportError('Transport is closed');
549
+ }
550
+
551
+ const previousState = this._connectionState;
552
+ this._connectionState = 'connecting';
553
+
554
+ this._events?.emit({
555
+ type: 'connection:stateChange',
556
+ previousState: previousState,
557
+ currentState: 'connecting',
558
+ timestamp: new Date(),
559
+ });
560
+
561
+ const retryOptions: Partial<RetryConfig> = {
562
+ maxAttempts: this._config.retryConfig?.maxAttempts ?? 3,
563
+ initialDelayMs: this._config.retryConfig?.initialDelayMs ?? 1000,
564
+ maxDelayMs: this._config.retryConfig?.maxDelayMs ?? 30000,
565
+ backoffMultiplier: this._config.retryConfig?.backoffMultiplier ?? 2,
566
+ jitter: this._config.retryConfig?.jitter ?? true,
567
+ };
568
+
569
+ const result = await retry(
570
+ async () => {
571
+ // Use circuit breaker to guard connection attempts
572
+ return await this._circuitBreaker.execute(async () => {
573
+ const enhancedTransport = createEnhancedTransport(
574
+ this._config.transportConfig,
575
+ this._logger
576
+ );
577
+
578
+ // Verify transport is healthy
579
+ const health = await enhancedTransport.checkHealth();
580
+ if (!health.healthy) {
581
+ throw new McpTransportError(`Transport health check failed: ${health.error}`);
582
+ }
583
+
584
+ return enhancedTransport;
585
+ });
586
+ },
587
+ retryOptions
588
+ );
589
+
590
+ if (!result.success || !result.value) {
591
+ this._connectionState = 'error';
592
+ this._events?.emit({
593
+ type: 'connection:stateChange',
594
+ previousState: 'connecting',
595
+ currentState: 'error',
596
+ timestamp: new Date(),
597
+ });
598
+ throw result.error ?? new McpTransportError('Failed to connect');
599
+ }
600
+
601
+ this._transport = result.value;
602
+ this._connectionState = 'connected';
603
+
604
+ this._events?.emit({
605
+ type: 'connection:stateChange',
606
+ previousState: 'connecting',
607
+ currentState: 'connected',
608
+ timestamp: new Date(),
609
+ });
610
+
611
+ // Start health check interval
612
+ if (this._config.healthCheckIntervalMs && this._config.healthCheckIntervalMs > 0) {
613
+ this._startHealthCheck();
614
+ }
615
+
616
+ return this._transport.transport;
617
+ }
618
+
619
+ /**
620
+ * Reconnect the transport
621
+ */
622
+ async reconnect(): Promise<Transport> {
623
+ if (this._closed) {
624
+ throw new McpTransportError('Transport is closed');
625
+ }
626
+
627
+ if (this._reconnecting) {
628
+ throw new McpTransportError('Reconnection already in progress');
629
+ }
630
+
631
+ this._reconnecting = true;
632
+ const previousState = this._connectionState;
633
+
634
+ this._events?.emit({
635
+ type: 'connection:reconnecting',
636
+ attempt: 1,
637
+ maxAttempts: this._config.retryConfig?.maxAttempts ?? 3,
638
+ delayMs: this._config.retryConfig?.initialDelayMs ?? 1000,
639
+ timestamp: new Date(),
640
+ });
641
+
642
+ try {
643
+ // Close existing transport
644
+ if (this._transport) {
645
+ await this._transport.close();
646
+ this._transport = null;
647
+ }
648
+
649
+ // Connect again
650
+ return await this.connect();
651
+ } catch (error) {
652
+ this._connectionState = 'error';
653
+ this._events?.emit({
654
+ type: 'connection:stateChange',
655
+ previousState,
656
+ currentState: 'error',
657
+ timestamp: new Date(),
658
+ });
659
+ throw error;
660
+ } finally {
661
+ this._reconnecting = false;
662
+ }
663
+ }
664
+
665
+ /**
666
+ * Close the transport
667
+ */
668
+ async close(): Promise<void> {
669
+ this._closed = true;
670
+ this._stopHealthCheck();
671
+
672
+ const previousState = this._connectionState;
673
+
674
+ if (this._transport) {
675
+ await this._transport.close();
676
+ this._transport = null;
677
+ }
678
+
679
+ this._connectionState = 'disconnected';
680
+ this._events?.emit({
681
+ type: 'connection:stateChange',
682
+ previousState,
683
+ currentState: 'disconnected',
684
+ timestamp: new Date(),
685
+ });
686
+
687
+ this._events?.emit({
688
+ type: 'connection:closed',
689
+ reason: 'manual',
690
+ wasClean: true,
691
+ timestamp: new Date(),
692
+ });
693
+ }
694
+
695
+ /**
696
+ * Get current health status
697
+ */
698
+ async checkHealth(): Promise<TransportHealthStatus | null> {
699
+ return this._transport?.checkHealth() ?? null;
700
+ }
701
+
702
+ private _startHealthCheck(): void {
703
+ this._stopHealthCheck();
704
+
705
+ this._healthCheckInterval = setInterval(async () => {
706
+ if (this._closed || !this._transport) return;
707
+
708
+ const health = await this._transport.checkHealth();
709
+
710
+ if (!health.healthy && this._config.autoReconnect !== false) {
711
+ this._logger.warn('Health check failed, attempting reconnect', {
712
+ data: { consecutiveFailures: health.consecutiveFailures },
713
+ });
714
+
715
+ try {
716
+ await this.reconnect();
717
+ } catch (error) {
718
+ this._logger.error('Reconnect failed', {
719
+ data: { errorMessage: error instanceof Error ? error.message : String(error) },
720
+ });
721
+
722
+ this._events?.emit({
723
+ type: 'connection:error',
724
+ error: error instanceof Error ? error : new Error(String(error)),
725
+ recoverable: true,
726
+ timestamp: new Date(),
727
+ });
728
+ }
729
+ }
730
+ }, this._config.healthCheckIntervalMs);
731
+ }
732
+
733
+ private _stopHealthCheck(): void {
734
+ if (this._healthCheckInterval) {
735
+ clearInterval(this._healthCheckInterval);
736
+ this._healthCheckInterval = null;
737
+ }
738
+ }
739
+ }
740
+
741
+ // ============================================================================
742
+ // Connection Pool
743
+ // ============================================================================
744
+
745
+ /**
746
+ * Connection pool for managing multiple transports
747
+ */
748
+ export class TransportPool {
749
+ private _config: ConnectionPoolConfig;
750
+ private _transportConfig: TransportConfig;
751
+ private _pool: PoolEntry[] = [];
752
+ private _waitQueue: Array<{
753
+ resolve: (transport: EnhancedTransport) => void;
754
+ reject: (error: Error) => void;
755
+ timeoutId: ReturnType<typeof setTimeout>;
756
+ }> = [];
757
+ private _logger: Logger;
758
+ private _events?: McpEventEmitter;
759
+ private _healthCheckInterval: ReturnType<typeof setInterval> | null = null;
760
+ private _closed = false;
761
+
762
+ constructor(transportConfig: TransportConfig, config: Partial<ConnectionPoolConfig> = {}) {
763
+ this._transportConfig = transportConfig;
764
+ this._config = {
765
+ maxConnections: config.maxConnections ?? 10,
766
+ minConnections: config.minConnections ?? 1,
767
+ acquireTimeoutMs: config.acquireTimeoutMs ?? 30000,
768
+ idleTimeoutMs: config.idleTimeoutMs ?? 60000,
769
+ healthCheckIntervalMs: config.healthCheckIntervalMs ?? 30000,
770
+ logger: config.logger,
771
+ events: config.events,
772
+ };
773
+ this._logger = this._config.logger ?? createNoopLogger();
774
+ this._events = this._config.events;
775
+ }
776
+
777
+ /**
778
+ * Initialize the pool with minimum connections
779
+ */
780
+ async initialize(): Promise<void> {
781
+ if (this._closed) {
782
+ throw new McpTransportError('Pool is closed');
783
+ }
784
+
785
+ const promises: Promise<void>[] = [];
786
+
787
+ for (let i = 0; i < this._config.minConnections; i++) {
788
+ promises.push(this._createConnection());
789
+ }
790
+
791
+ await Promise.all(promises);
792
+ this._startHealthCheck();
793
+
794
+ this._logger.info('Connection pool initialized', {
795
+ data: {
796
+ minConnections: this._config.minConnections,
797
+ maxConnections: this._config.maxConnections,
798
+ },
799
+ });
800
+ }
801
+
802
+ /**
803
+ * Get the current pool size
804
+ */
805
+ get size(): number {
806
+ return this._pool.length;
807
+ }
808
+
809
+ /**
810
+ * Get the number of available connections
811
+ */
812
+ get available(): number {
813
+ return this._pool.filter(entry => !entry.inUse).length;
814
+ }
815
+
816
+ /**
817
+ * Get the number of in-use connections
818
+ */
819
+ get inUse(): number {
820
+ return this._pool.filter(entry => entry.inUse).length;
821
+ }
822
+
823
+ /**
824
+ * Acquire a transport from the pool
825
+ */
826
+ async acquire(): Promise<EnhancedTransport> {
827
+ if (this._closed) {
828
+ throw new McpTransportError('Pool is closed');
829
+ }
830
+
831
+ // Try to find an available healthy connection
832
+ const available = this._pool.find(entry => !entry.inUse && entry.transport.healthStatus.healthy);
833
+
834
+ if (available) {
835
+ available.inUse = true;
836
+ available.acquiredAt = new Date();
837
+ this._logger.debug('Acquired connection from pool', { data: { poolSize: this.size } });
838
+ return available.transport;
839
+ }
840
+
841
+ // Create a new connection if under max
842
+ if (this._pool.length < this._config.maxConnections) {
843
+ await this._createConnection();
844
+
845
+ const newEntry = this._pool.find(entry => !entry.inUse);
846
+ if (newEntry) {
847
+ newEntry.inUse = true;
848
+ newEntry.acquiredAt = new Date();
849
+ this._logger.debug('Created new connection', { data: { poolSize: this.size } });
850
+ return newEntry.transport;
851
+ }
852
+ }
853
+
854
+ // Wait for a connection to become available
855
+ return new Promise<EnhancedTransport>((resolve, reject) => {
856
+ const timeoutId = setTimeout(() => {
857
+ const index = this._waitQueue.findIndex(item => item.resolve === resolve);
858
+ if (index !== -1) {
859
+ this._waitQueue.splice(index, 1);
860
+ }
861
+ reject(new McpTransportError('Acquire timeout exceeded'));
862
+ }, this._config.acquireTimeoutMs);
863
+
864
+ this._waitQueue.push({ resolve, reject, timeoutId });
865
+ });
866
+ }
867
+
868
+ /**
869
+ * Release a transport back to the pool
870
+ */
871
+ release(transport: EnhancedTransport): void {
872
+ const entry = this._pool.find(e => e.transport === transport);
873
+
874
+ if (!entry) {
875
+ this._logger.warn('Attempted to release unknown transport');
876
+ return;
877
+ }
878
+
879
+ entry.inUse = false;
880
+ entry.lastUsed = new Date();
881
+ entry.acquiredAt = null;
882
+
883
+ this._logger.debug('Released connection to pool', { data: { poolSize: this.size, available: this.available } });
884
+
885
+ // Process wait queue
886
+ if (this._waitQueue.length > 0) {
887
+ const waiter = this._waitQueue.shift();
888
+ if (waiter) {
889
+ clearTimeout(waiter.timeoutId);
890
+ entry.inUse = true;
891
+ entry.acquiredAt = new Date();
892
+ waiter.resolve(entry.transport);
893
+ }
894
+ }
895
+ }
896
+
897
+ /**
898
+ * Close all connections and shutdown the pool
899
+ */
900
+ async close(): Promise<void> {
901
+ this._closed = true;
902
+ this._stopHealthCheck();
903
+
904
+ // Reject all waiters
905
+ for (const waiter of this._waitQueue) {
906
+ clearTimeout(waiter.timeoutId);
907
+ waiter.reject(new McpTransportError('Pool is closing'));
908
+ }
909
+ this._waitQueue = [];
910
+
911
+ // Close all connections
912
+ const closePromises = this._pool.map(entry => entry.transport.close());
913
+ await Promise.allSettled(closePromises);
914
+
915
+ this._pool = [];
916
+ this._logger.info('Connection pool closed');
917
+ }
918
+
919
+ private async _createConnection(): Promise<void> {
920
+ const transport = createEnhancedTransport(this._transportConfig, this._logger);
921
+ await transport.checkHealth();
922
+
923
+ this._pool.push({
924
+ transport,
925
+ inUse: false,
926
+ lastUsed: new Date(),
927
+ acquiredAt: null,
928
+ });
929
+ }
930
+
931
+ private _startHealthCheck(): void {
932
+ this._stopHealthCheck();
933
+
934
+ this._healthCheckInterval = setInterval(async () => {
935
+ if (this._closed) return;
936
+
937
+ const now = Date.now();
938
+ const toRemove: PoolEntry[] = [];
939
+
940
+ for (const entry of this._pool) {
941
+ // Skip in-use connections
942
+ if (entry.inUse) continue;
943
+
944
+ // Check for idle timeout
945
+ const idleTime = now - entry.lastUsed.getTime();
946
+ if (idleTime > this._config.idleTimeoutMs && this._pool.length > this._config.minConnections) {
947
+ toRemove.push(entry);
948
+ continue;
949
+ }
950
+
951
+ // Check health
952
+ const health = await entry.transport.checkHealth();
953
+ if (!health.healthy && health.consecutiveFailures >= 3) {
954
+ toRemove.push(entry);
955
+ }
956
+ }
957
+
958
+ // Remove unhealthy/idle connections
959
+ for (const entry of toRemove) {
960
+ const index = this._pool.indexOf(entry);
961
+ if (index !== -1) {
962
+ this._pool.splice(index, 1);
963
+ await entry.transport.close();
964
+ this._logger.debug('Removed connection from pool', { data: { reason: 'unhealthy or idle' } });
965
+ }
966
+ }
967
+
968
+ // Ensure minimum connections
969
+ while (this._pool.length < this._config.minConnections && !this._closed) {
970
+ try {
971
+ await this._createConnection();
972
+ } catch (error) {
973
+ this._logger.warn('Failed to create connection for pool', {
974
+ data: { errorMessage: error instanceof Error ? error.message : String(error) },
975
+ });
976
+ break;
977
+ }
978
+ }
979
+ }, this._config.healthCheckIntervalMs);
980
+ }
981
+
982
+ private _stopHealthCheck(): void {
983
+ if (this._healthCheckInterval) {
984
+ clearInterval(this._healthCheckInterval);
985
+ this._healthCheckInterval = null;
986
+ }
987
+ }
988
+ }