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,88 @@
1
+ /**
2
+ * Disconnect API Route
3
+ *
4
+ * POST /api/playground/v2/disconnect
5
+ * Closes an MCP server connection and cleans up the session.
6
+ */
7
+
8
+ import { NextRequest, NextResponse } from 'next/server';
9
+ import {
10
+ handleApiError,
11
+ createSuccessResponse,
12
+ SessionExpiredError,
13
+ validateSessionId,
14
+ applyMiddleware,
15
+ corsPreflightResponse,
16
+ createLogContext,
17
+ SessionManager,
18
+ withRequestId,
19
+ } from '@/lib/api';
20
+ import type { DisconnectResponseData } from '@/lib/api';
21
+
22
+ const PATH = '/api/playground/v2/disconnect';
23
+
24
+ /**
25
+ * Handle CORS preflight request
26
+ */
27
+ export async function OPTIONS(): Promise<NextResponse> {
28
+ return corsPreflightResponse({ methods: ['POST', 'OPTIONS'] });
29
+ }
30
+
31
+ /**
32
+ * POST /api/playground/v2/disconnect
33
+ *
34
+ * Closes an MCP server connection.
35
+ *
36
+ * Request body:
37
+ * - sessionId: string (required) - The session ID to disconnect
38
+ *
39
+ * Response:
40
+ * - success: boolean
41
+ * - data: { disconnected: boolean }
42
+ */
43
+ export async function POST(request: NextRequest): Promise<NextResponse> {
44
+ const middleware = await applyMiddleware(request);
45
+ if ('response' in middleware) {
46
+ return middleware.response;
47
+ }
48
+
49
+ const { context } = middleware;
50
+ const log = createLogContext('POST', PATH, context.requestId, context.clientId);
51
+
52
+ try {
53
+ const body = await request.json();
54
+ const sessionId = validateSessionId(body);
55
+
56
+ const sessionManager = SessionManager.getInstance();
57
+
58
+ // Check if session exists
59
+ const session = await sessionManager.getSession(sessionId);
60
+ if (!session) {
61
+ throw new SessionExpiredError(
62
+ `Session not found or already expired: ${sessionId}`,
63
+ sessionId
64
+ );
65
+ }
66
+
67
+ // Delete the session
68
+ const deleted = await sessionManager.deleteSession(sessionId);
69
+
70
+ const responseData: DisconnectResponseData = {
71
+ disconnected: deleted,
72
+ };
73
+
74
+ log.log(200, sessionId);
75
+
76
+ return createSuccessResponse(responseData, 200, {
77
+ ...context.rateLimitHeaders,
78
+ ...withRequestId({}, context.requestId),
79
+ 'Access-Control-Allow-Origin': '*',
80
+ });
81
+ } catch (error) {
82
+ const status = error instanceof Error && 'statusCode' in error
83
+ ? (error as { statusCode: number }).statusCode
84
+ : 500;
85
+ log.log(status, undefined, error instanceof Error ? error.message : 'Unknown error');
86
+ return handleApiError(error);
87
+ }
88
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Health API Route
3
+ *
4
+ * GET /api/playground/v2/health - Get API health status
5
+ */
6
+
7
+ import { NextRequest, NextResponse } from 'next/server';
8
+ import {
9
+ handleApiError,
10
+ createSuccessResponse,
11
+ corsPreflightResponse,
12
+ createLogContext,
13
+ SessionManager,
14
+ withRequestId,
15
+ NO_RATE_LIMIT,
16
+ applyMiddleware,
17
+ } from '@/lib/api';
18
+ import type { HealthResponseData } from '@/lib/api';
19
+
20
+ const PATH = '/api/playground/v2/health';
21
+ const startTime = Date.now();
22
+
23
+ /**
24
+ * Handle CORS preflight request
25
+ */
26
+ export async function OPTIONS(): Promise<NextResponse> {
27
+ return corsPreflightResponse({ methods: ['GET', 'OPTIONS'] });
28
+ }
29
+
30
+ /**
31
+ * GET /api/playground/v2/health
32
+ *
33
+ * Returns health status of the playground API.
34
+ *
35
+ * Response:
36
+ * - success: boolean
37
+ * - data: { status: string, version: string, uptime: number, sessions: { active: number } }
38
+ */
39
+ export async function GET(request: NextRequest): Promise<NextResponse> {
40
+ const middleware = await applyMiddleware(request, { rateLimit: NO_RATE_LIMIT });
41
+ if ('response' in middleware) {
42
+ return middleware.response;
43
+ }
44
+
45
+ const { context } = middleware;
46
+ const log = createLogContext('GET', PATH, context.requestId, context.clientId);
47
+
48
+ try {
49
+ const sessionManager = SessionManager.getInstance();
50
+ const sessions = await sessionManager.listSessions();
51
+ const activeSessions = sessions.length;
52
+
53
+ const uptime = Math.floor((Date.now() - startTime) / 1000);
54
+
55
+ const responseData: HealthResponseData = {
56
+ status: 'healthy',
57
+ version: '2.0.0',
58
+ uptime,
59
+ sessions: {
60
+ active: activeSessions,
61
+ },
62
+ timestamp: new Date().toISOString(),
63
+ };
64
+
65
+ log.log(200, undefined, undefined, { activeSessions, uptime });
66
+
67
+ return createSuccessResponse(responseData, 200, {
68
+ ...withRequestId({}, context.requestId),
69
+ 'Access-Control-Allow-Origin': '*',
70
+ // Don't cache health checks
71
+ 'Cache-Control': 'no-store, no-cache, must-revalidate',
72
+ });
73
+ } catch (error) {
74
+ const status = error instanceof Error && 'statusCode' in error
75
+ ? (error as { statusCode: number }).statusCode
76
+ : 500;
77
+ log.log(status, undefined, error instanceof Error ? error.message : 'Unknown error');
78
+ return handleApiError(error);
79
+ }
80
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Prompts API Route
3
+ *
4
+ * GET /api/playground/v2/prompts - List all prompts
5
+ * POST /api/playground/v2/prompts - Get a prompt
6
+ */
7
+
8
+ import { NextRequest, NextResponse } from 'next/server';
9
+ import {
10
+ handleApiError,
11
+ createSuccessResponse,
12
+ SessionExpiredError,
13
+ McpExecutionError,
14
+ NotFoundError,
15
+ validatePromptGet,
16
+ validateSessionIdFromQuery,
17
+ applyMiddleware,
18
+ corsPreflightResponse,
19
+ createLogContext,
20
+ SessionManager,
21
+ withRequestId,
22
+ RELAXED_RATE_LIMIT,
23
+ } from '@/lib/api';
24
+ import type { PromptsListResponseData, PromptGetResponseData } from '@/lib/api';
25
+
26
+ const PATH = '/api/playground/v2/prompts';
27
+
28
+ /**
29
+ * Handle CORS preflight request
30
+ */
31
+ export async function OPTIONS(): Promise<NextResponse> {
32
+ return corsPreflightResponse({ methods: ['GET', 'POST', 'OPTIONS'] });
33
+ }
34
+
35
+ /**
36
+ * GET /api/playground/v2/prompts
37
+ *
38
+ * Lists all available prompts for a session.
39
+ *
40
+ * Query parameters:
41
+ * - sessionId: string (required) - The session ID
42
+ *
43
+ * Response:
44
+ * - success: boolean
45
+ * - data: { prompts: McpPrompt[] }
46
+ */
47
+ export async function GET(request: NextRequest): Promise<NextResponse> {
48
+ const middleware = await applyMiddleware(request, { rateLimit: RELAXED_RATE_LIMIT });
49
+ if ('response' in middleware) {
50
+ return middleware.response;
51
+ }
52
+
53
+ const { context } = middleware;
54
+ const log = createLogContext('GET', PATH, context.requestId, context.clientId);
55
+
56
+ try {
57
+ const { searchParams } = new URL(request.url);
58
+ const sessionId = validateSessionIdFromQuery(searchParams);
59
+
60
+ const sessionManager = SessionManager.getInstance();
61
+
62
+ const session = await sessionManager.getSession(sessionId);
63
+ if (!session) {
64
+ throw new SessionExpiredError(
65
+ `Session not found or expired: ${sessionId}`,
66
+ sessionId
67
+ );
68
+ }
69
+
70
+ const prompts = await sessionManager.listPrompts(sessionId);
71
+
72
+ const responseData: PromptsListResponseData = { prompts };
73
+
74
+ log.log(200, sessionId);
75
+
76
+ return createSuccessResponse(responseData, 200, {
77
+ ...context.rateLimitHeaders,
78
+ ...withRequestId({}, context.requestId),
79
+ 'Access-Control-Allow-Origin': '*',
80
+ });
81
+ } catch (error) {
82
+ const status = error instanceof Error && 'statusCode' in error
83
+ ? (error as { statusCode: number }).statusCode
84
+ : 500;
85
+ log.log(status, undefined, error instanceof Error ? error.message : 'Unknown error');
86
+ return handleApiError(error);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * POST /api/playground/v2/prompts
92
+ *
93
+ * Gets a prompt with optional arguments.
94
+ *
95
+ * Request body:
96
+ * - sessionId: string (required) - The session ID
97
+ * - name: string (required) - Name of the prompt to get
98
+ * - args: object (optional) - Prompt arguments
99
+ *
100
+ * Response:
101
+ * - success: boolean
102
+ * - data: { description?: string, messages: McpPromptMessage[] }
103
+ */
104
+ export async function POST(request: NextRequest): Promise<NextResponse> {
105
+ const middleware = await applyMiddleware(request, { rateLimit: RELAXED_RATE_LIMIT });
106
+ if ('response' in middleware) {
107
+ return middleware.response;
108
+ }
109
+
110
+ const { context } = middleware;
111
+ const log = createLogContext('POST', PATH, context.requestId, context.clientId);
112
+
113
+ try {
114
+ const body = await request.json();
115
+ const { sessionId, name, args } = validatePromptGet(body);
116
+
117
+ const sessionManager = SessionManager.getInstance();
118
+
119
+ const session = await sessionManager.getSession(sessionId);
120
+ if (!session) {
121
+ throw new SessionExpiredError(
122
+ `Session not found or expired: ${sessionId}`,
123
+ sessionId
124
+ );
125
+ }
126
+
127
+ let result;
128
+
129
+ try {
130
+ result = await sessionManager.getPrompt(sessionId, name, args);
131
+ } catch (error) {
132
+ if (error instanceof Error && error.message.includes('not found')) {
133
+ throw new NotFoundError(`Prompt not found: ${name}`, 'prompt');
134
+ }
135
+ throw new McpExecutionError(
136
+ `Prompt retrieval failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
137
+ { executionType: 'prompt', name }
138
+ );
139
+ }
140
+
141
+ const responseData: PromptGetResponseData = {
142
+ description: result.description,
143
+ messages: result.messages,
144
+ };
145
+
146
+ log.log(200, sessionId, undefined, { promptName: name });
147
+
148
+ return createSuccessResponse(responseData, 200, {
149
+ ...context.rateLimitHeaders,
150
+ ...withRequestId({}, context.requestId),
151
+ 'Access-Control-Allow-Origin': '*',
152
+ });
153
+ } catch (error) {
154
+ const status = error instanceof Error && 'statusCode' in error
155
+ ? (error as { statusCode: number }).statusCode
156
+ : 500;
157
+ log.log(status, undefined, error instanceof Error ? error.message : 'Unknown error');
158
+ return handleApiError(error);
159
+ }
160
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Resources API Route
3
+ *
4
+ * GET /api/playground/v2/resources - List all resources
5
+ * POST /api/playground/v2/resources - Read a resource
6
+ */
7
+
8
+ import { NextRequest, NextResponse } from 'next/server';
9
+ import {
10
+ handleApiError,
11
+ createSuccessResponse,
12
+ SessionExpiredError,
13
+ McpExecutionError,
14
+ NotFoundError,
15
+ validateResourceRead,
16
+ validateSessionIdFromQuery,
17
+ applyMiddleware,
18
+ corsPreflightResponse,
19
+ createLogContext,
20
+ SessionManager,
21
+ withRequestId,
22
+ RELAXED_RATE_LIMIT,
23
+ } from '@/lib/api';
24
+ import type { ResourcesListResponseData, ResourceReadResponseData } from '@/lib/api';
25
+
26
+ const PATH = '/api/playground/v2/resources';
27
+
28
+ /**
29
+ * Handle CORS preflight request
30
+ */
31
+ export async function OPTIONS(): Promise<NextResponse> {
32
+ return corsPreflightResponse({ methods: ['GET', 'POST', 'OPTIONS'] });
33
+ }
34
+
35
+ /**
36
+ * GET /api/playground/v2/resources
37
+ *
38
+ * Lists all available resources for a session.
39
+ *
40
+ * Query parameters:
41
+ * - sessionId: string (required) - The session ID
42
+ *
43
+ * Response:
44
+ * - success: boolean
45
+ * - data: { resources: McpResource[], resourceTemplates?: McpResourceTemplate[] }
46
+ */
47
+ export async function GET(request: NextRequest): Promise<NextResponse> {
48
+ const middleware = await applyMiddleware(request, { rateLimit: RELAXED_RATE_LIMIT });
49
+ if ('response' in middleware) {
50
+ return middleware.response;
51
+ }
52
+
53
+ const { context } = middleware;
54
+ const log = createLogContext('GET', PATH, context.requestId, context.clientId);
55
+
56
+ try {
57
+ const { searchParams } = new URL(request.url);
58
+ const sessionId = validateSessionIdFromQuery(searchParams);
59
+
60
+ const sessionManager = SessionManager.getInstance();
61
+
62
+ const session = await sessionManager.getSession(sessionId);
63
+ if (!session) {
64
+ throw new SessionExpiredError(
65
+ `Session not found or expired: ${sessionId}`,
66
+ sessionId
67
+ );
68
+ }
69
+
70
+ const { resources, resourceTemplates } = await sessionManager.listResources(sessionId);
71
+
72
+ const responseData: ResourcesListResponseData = {
73
+ resources,
74
+ resourceTemplates,
75
+ };
76
+
77
+ log.log(200, sessionId);
78
+
79
+ return createSuccessResponse(responseData, 200, {
80
+ ...context.rateLimitHeaders,
81
+ ...withRequestId({}, context.requestId),
82
+ 'Access-Control-Allow-Origin': '*',
83
+ });
84
+ } catch (error) {
85
+ const status = error instanceof Error && 'statusCode' in error
86
+ ? (error as { statusCode: number }).statusCode
87
+ : 500;
88
+ log.log(status, undefined, error instanceof Error ? error.message : 'Unknown error');
89
+ return handleApiError(error);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * POST /api/playground/v2/resources
95
+ *
96
+ * Reads a resource.
97
+ *
98
+ * Request body:
99
+ * - sessionId: string (required) - The session ID
100
+ * - uri: string (required) - URI of the resource to read
101
+ *
102
+ * Response:
103
+ * - success: boolean
104
+ * - data: { contents: McpResourceContents[] }
105
+ */
106
+ export async function POST(request: NextRequest): Promise<NextResponse> {
107
+ const middleware = await applyMiddleware(request, { rateLimit: RELAXED_RATE_LIMIT });
108
+ if ('response' in middleware) {
109
+ return middleware.response;
110
+ }
111
+
112
+ const { context } = middleware;
113
+ const log = createLogContext('POST', PATH, context.requestId, context.clientId);
114
+
115
+ try {
116
+ const body = await request.json();
117
+ const { sessionId, uri } = validateResourceRead(body);
118
+
119
+ const sessionManager = SessionManager.getInstance();
120
+
121
+ const session = await sessionManager.getSession(sessionId);
122
+ if (!session) {
123
+ throw new SessionExpiredError(
124
+ `Session not found or expired: ${sessionId}`,
125
+ sessionId
126
+ );
127
+ }
128
+
129
+ let contents;
130
+
131
+ try {
132
+ contents = await sessionManager.readResource(sessionId, uri);
133
+ } catch (error) {
134
+ if (error instanceof Error && error.message.includes('not found')) {
135
+ throw new NotFoundError(`Resource not found: ${uri}`, 'resource');
136
+ }
137
+ throw new McpExecutionError(
138
+ `Resource read failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
139
+ { executionType: 'resource', name: uri }
140
+ );
141
+ }
142
+
143
+ const responseData: ResourceReadResponseData = { contents };
144
+
145
+ log.log(200, sessionId, undefined, { uri });
146
+
147
+ return createSuccessResponse(responseData, 200, {
148
+ ...context.rateLimitHeaders,
149
+ ...withRequestId({}, context.requestId),
150
+ 'Access-Control-Allow-Origin': '*',
151
+ });
152
+ } catch (error) {
153
+ const status = error instanceof Error && 'statusCode' in error
154
+ ? (error as { statusCode: number }).statusCode
155
+ : 500;
156
+ log.log(status, undefined, error instanceof Error ? error.message : 'Unknown error');
157
+ return handleApiError(error);
158
+ }
159
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Sessions API Route
3
+ *
4
+ * GET /api/playground/v2/sessions - List active sessions
5
+ * DELETE /api/playground/v2/sessions - Remove a session
6
+ */
7
+
8
+ import { NextRequest, NextResponse } from 'next/server';
9
+ import {
10
+ handleApiError,
11
+ createSuccessResponse,
12
+ ValidationError,
13
+ SessionExpiredError,
14
+ applyMiddleware,
15
+ corsPreflightResponse,
16
+ createLogContext,
17
+ SessionManager,
18
+ withRequestId,
19
+ DEFAULT_RATE_LIMIT,
20
+ RELAXED_RATE_LIMIT,
21
+ } from '@/lib/api';
22
+ import type { SessionsListResponseData, DisconnectResponseData } from '@/lib/api';
23
+
24
+ const PATH = '/api/playground/v2/sessions';
25
+
26
+ /**
27
+ * Handle CORS preflight request
28
+ */
29
+ export async function OPTIONS(): Promise<NextResponse> {
30
+ return corsPreflightResponse({ methods: ['GET', 'DELETE', 'OPTIONS'] });
31
+ }
32
+
33
+ /**
34
+ * GET /api/playground/v2/sessions
35
+ *
36
+ * Lists all active sessions.
37
+ *
38
+ * Query parameters:
39
+ * - clientId: string (optional) - Filter by client ID
40
+ *
41
+ * Response:
42
+ * - success: boolean
43
+ * - data: { sessions: SessionInfo[], count: number }
44
+ */
45
+ export async function GET(request: NextRequest): Promise<NextResponse> {
46
+ const middleware = await applyMiddleware(request, { rateLimit: RELAXED_RATE_LIMIT });
47
+ if ('response' in middleware) {
48
+ return middleware.response;
49
+ }
50
+
51
+ const { context } = middleware;
52
+ const log = createLogContext('GET', PATH, context.requestId, context.clientId);
53
+
54
+ try {
55
+ const { searchParams } = new URL(request.url);
56
+ const clientId = searchParams.get('clientId');
57
+
58
+ const sessionManager = SessionManager.getInstance();
59
+ let sessions = await sessionManager.listSessions();
60
+
61
+ // Filter by clientId if provided
62
+ if (clientId) {
63
+ sessions = sessions.filter((s) => s.clientId === clientId);
64
+ }
65
+
66
+ const responseData: SessionsListResponseData = {
67
+ sessions,
68
+ count: sessions.length,
69
+ };
70
+
71
+ log.log(200, undefined, undefined, { sessionCount: sessions.length });
72
+
73
+ return createSuccessResponse(responseData, 200, {
74
+ ...context.rateLimitHeaders,
75
+ ...withRequestId({}, context.requestId),
76
+ 'Access-Control-Allow-Origin': '*',
77
+ });
78
+ } catch (error) {
79
+ const status = error instanceof Error && 'statusCode' in error
80
+ ? (error as { statusCode: number }).statusCode
81
+ : 500;
82
+ log.log(status, undefined, error instanceof Error ? error.message : 'Unknown error');
83
+ return handleApiError(error);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * DELETE /api/playground/v2/sessions
89
+ *
90
+ * Removes a session by ID or all sessions for a client.
91
+ *
92
+ * Query parameters:
93
+ * - sessionId: string (optional) - Specific session to remove
94
+ * - clientId: string (optional) - Remove all sessions for this client
95
+ *
96
+ * At least one of sessionId or clientId must be provided.
97
+ *
98
+ * Response:
99
+ * - success: boolean
100
+ * - data: { disconnected: boolean, sessionId?: string, removedCount?: number }
101
+ */
102
+ export async function DELETE(request: NextRequest): Promise<NextResponse> {
103
+ const middleware = await applyMiddleware(request, { rateLimit: DEFAULT_RATE_LIMIT });
104
+ if ('response' in middleware) {
105
+ return middleware.response;
106
+ }
107
+
108
+ const { context } = middleware;
109
+ const log = createLogContext('DELETE', PATH, context.requestId, context.clientId);
110
+
111
+ try {
112
+ const { searchParams } = new URL(request.url);
113
+ const sessionId = searchParams.get('sessionId');
114
+ const clientId = searchParams.get('clientId');
115
+
116
+ if (!sessionId && !clientId) {
117
+ throw new ValidationError(
118
+ 'Either sessionId or clientId is required',
119
+ 'query'
120
+ );
121
+ }
122
+
123
+ const sessionManager = SessionManager.getInstance();
124
+
125
+ if (sessionId) {
126
+ // Remove specific session
127
+ const session = await sessionManager.getSession(sessionId);
128
+ if (!session) {
129
+ throw new SessionExpiredError(
130
+ `Session not found or expired: ${sessionId}`,
131
+ sessionId
132
+ );
133
+ }
134
+
135
+ const success = await sessionManager.deleteSession(sessionId);
136
+
137
+ const responseData: DisconnectResponseData = {
138
+ disconnected: success,
139
+ sessionId,
140
+ };
141
+
142
+ log.log(200, sessionId);
143
+
144
+ return createSuccessResponse(responseData, 200, {
145
+ ...context.rateLimitHeaders,
146
+ ...withRequestId({}, context.requestId),
147
+ 'Access-Control-Allow-Origin': '*',
148
+ });
149
+ } else if (clientId) {
150
+ // Remove all sessions for this client
151
+ const allSessions = await sessionManager.listSessions();
152
+ const sessions = allSessions.filter((s) => s.clientId === clientId);
153
+ let removedCount = 0;
154
+
155
+ for (const session of sessions) {
156
+ const success = await sessionManager.deleteSession(session.id);
157
+ if (success) removedCount++;
158
+ }
159
+
160
+ const responseData = {
161
+ disconnected: removedCount > 0,
162
+ removedCount,
163
+ clientId,
164
+ };
165
+
166
+ log.log(200, undefined, undefined, { removedCount });
167
+
168
+ return createSuccessResponse(responseData, 200, {
169
+ ...context.rateLimitHeaders,
170
+ ...withRequestId({}, context.requestId),
171
+ 'Access-Control-Allow-Origin': '*',
172
+ });
173
+ }
174
+
175
+ // Should never reach here due to validation above
176
+ throw new ValidationError('Invalid request parameters', 'query');
177
+ } catch (error) {
178
+ const status = error instanceof Error && 'statusCode' in error
179
+ ? (error as { statusCode: number }).statusCode
180
+ : 500;
181
+ log.log(status, undefined, error instanceof Error ? error.message : 'Unknown error');
182
+ return handleApiError(error);
183
+ }
184
+ }