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.
- package/.env.example +8 -0
- package/.github/CODEOWNERS +6 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/.prettierignore +5 -0
- package/.prettierrc +7 -0
- package/.vscode/settings.json +4 -0
- package/ARCHITECTURE.md +1429 -0
- package/CHANGELOG.md +167 -0
- package/CONTRIBUTING.md +327 -0
- package/LICENSE +201 -0
- package/README.md +1028 -0
- package/SECURITY.md +248 -0
- package/VISUAL_GUIDE.md +437 -0
- package/apps/vscode/IMPLEMENTATION.md +480 -0
- package/apps/vscode/README.md +248 -0
- package/apps/vscode/package.json +381 -0
- package/apps/vscode/resources/icon.png +0 -0
- package/apps/vscode/resources/icon.svg +5 -0
- package/apps/vscode/src/commands/browseRegistry.ts +211 -0
- package/apps/vscode/src/commands/configureClaudeDesktop.ts +332 -0
- package/apps/vscode/src/commands/convert.ts +82 -0
- package/apps/vscode/src/commands/convertCurrentRepo.ts +109 -0
- package/apps/vscode/src/commands/convertFromUrl.ts +138 -0
- package/apps/vscode/src/commands/index.ts +121 -0
- package/apps/vscode/src/commands/validate.ts +197 -0
- package/apps/vscode/src/extension.ts +464 -0
- package/apps/vscode/src/global.d.ts +36 -0
- package/apps/vscode/src/test/extension.test.ts +73 -0
- package/apps/vscode/src/utils/file-generator.ts +529 -0
- package/apps/vscode/src/utils/github-api.ts +335 -0
- package/apps/vscode/src/utils/index.ts +29 -0
- package/apps/vscode/src/utils/mcp-config.ts +334 -0
- package/apps/vscode/src/utils/storage.ts +87 -0
- package/apps/vscode/src/views/McpServersTreeView.ts +160 -0
- package/apps/vscode/src/views/OutputChannelView.ts +195 -0
- package/apps/vscode/src/views/StatusBarItem.ts +251 -0
- package/apps/vscode/src/views/ToolsExplorerView.ts +314 -0
- package/apps/vscode/src/views/historyProvider.ts +75 -0
- package/apps/vscode/src/views/index.ts +12 -0
- package/apps/vscode/src/views/resultsPanel.ts +330 -0
- package/apps/vscode/src/webviews/ConversionPanel.ts +350 -0
- package/apps/vscode/src/webviews/ToolDetailsPanel.ts +448 -0
- package/apps/vscode/src/webviews/index.ts +9 -0
- package/apps/vscode/src/webviews/webview-ui/styles.ts +492 -0
- package/apps/vscode/tsconfig.json +20 -0
- package/apps/web/PLAYGROUND_GUIDE.md +499 -0
- package/apps/web/README.md +505 -0
- package/apps/web/app/api/convert/route.ts +100 -0
- package/apps/web/app/api/convert/stream/route.ts +198 -0
- package/apps/web/app/api/deploy/route.ts +157 -0
- package/apps/web/app/api/edge/route.ts +308 -0
- package/apps/web/app/api/export-docker/route.ts +284 -0
- package/apps/web/app/api/generate-openapi/route.ts +119 -0
- package/apps/web/app/api/mcp/[serverId]/route.ts +263 -0
- package/apps/web/app/api/playground/connect/route.ts +143 -0
- package/apps/web/app/api/playground/disconnect/route.ts +78 -0
- package/apps/web/app/api/playground/execute/route.ts +135 -0
- package/apps/web/app/api/playground/sessions/route.ts +103 -0
- package/apps/web/app/api/playground/tools/route.ts +117 -0
- package/apps/web/app/api/playground/v2/connect/route.ts +96 -0
- package/apps/web/app/api/playground/v2/disconnect/route.ts +88 -0
- package/apps/web/app/api/playground/v2/health/route.ts +80 -0
- package/apps/web/app/api/playground/v2/prompts/route.ts +160 -0
- package/apps/web/app/api/playground/v2/resources/route.ts +159 -0
- package/apps/web/app/api/playground/v2/sessions/route.ts +184 -0
- package/apps/web/app/api/playground/v2/tools/route.ts +167 -0
- package/apps/web/app/api/stream/route.ts +232 -0
- package/apps/web/app/batch/BatchConvertClient.tsx +190 -0
- package/apps/web/app/batch/page.tsx +37 -0
- package/apps/web/app/convert/page.tsx +269 -0
- package/apps/web/app/dashboard/page.tsx +380 -0
- package/apps/web/app/globals.css +622 -0
- package/apps/web/app/layout.tsx +120 -0
- package/apps/web/app/manifest.ts +31 -0
- package/apps/web/app/opengraph-image.tsx +112 -0
- package/apps/web/app/page.old.tsx +924 -0
- package/apps/web/app/page.tsx +77 -0
- package/apps/web/app/playground/page.tsx +306 -0
- package/apps/web/app/playground/v2/error.tsx +163 -0
- package/apps/web/app/playground/v2/layout.tsx +58 -0
- package/apps/web/app/playground/v2/loading.tsx +152 -0
- package/apps/web/app/playground/v2/page.tsx +644 -0
- package/apps/web/app/playground/v2/providers.tsx +214 -0
- package/apps/web/app/playground/v2/use-shortcuts.ts +209 -0
- package/apps/web/app/playground/v2/use-url-state.ts +296 -0
- package/apps/web/app/providers.tsx +22 -0
- package/apps/web/app/sitemap.ts +32 -0
- package/apps/web/app/twitter-image.tsx +112 -0
- package/apps/web/components/BranchSelector.tsx +401 -0
- package/apps/web/components/ClaudeConfigExport.tsx +226 -0
- package/apps/web/components/Features.tsx +84 -0
- package/apps/web/components/Footer.tsx +119 -0
- package/apps/web/components/GenerationProgress.tsx +248 -0
- package/apps/web/components/GithubUrlInput.tsx +483 -0
- package/apps/web/components/Header.tsx +175 -0
- package/apps/web/components/Hero.tsx +117 -0
- package/apps/web/components/HowItWorks.tsx +119 -0
- package/apps/web/components/InstallBanner.tsx +158 -0
- package/apps/web/components/Logo.tsx +116 -0
- package/apps/web/components/ParticleBackground.tsx +105 -0
- package/apps/web/components/Playground.tsx +472 -0
- package/apps/web/components/PlaygroundToolTester.tsx +410 -0
- package/apps/web/components/ProductCards.tsx +179 -0
- package/apps/web/components/SplitView.tsx +194 -0
- package/apps/web/components/ToolFilter.tsx +260 -0
- package/apps/web/components/ToolList.tsx +325 -0
- package/apps/web/components/batch/BatchConvert.tsx +785 -0
- package/apps/web/components/batch/index.ts +7 -0
- package/apps/web/components/convert/ConfigTabs.tsx +230 -0
- package/apps/web/components/convert/ConversionResult.tsx +482 -0
- package/apps/web/components/convert/InlinePlayground.tsx +259 -0
- package/apps/web/components/convert/LoadingSteps.tsx +311 -0
- package/apps/web/components/convert/OneClickInstall.tsx +224 -0
- package/apps/web/components/convert/ToolCard.tsx +189 -0
- package/apps/web/components/convert/TryInPlayground.tsx +242 -0
- package/apps/web/components/convert/index.ts +12 -0
- package/apps/web/components/deploy/DeployButton.tsx +369 -0
- package/apps/web/components/deploy/index.ts +7 -0
- package/apps/web/components/docker/DockerExport.tsx +690 -0
- package/apps/web/components/docker/index.ts +7 -0
- package/apps/web/components/install/OneClickInstall.tsx +676 -0
- package/apps/web/components/install/index.ts +7 -0
- package/apps/web/components/playground/CapabilityTabs.tsx +150 -0
- package/apps/web/components/playground/ConnectionStatusV2.tsx +322 -0
- package/apps/web/components/playground/EmptyStates.tsx +305 -0
- package/apps/web/components/playground/ExecutionLog.tsx +260 -0
- package/apps/web/components/playground/ExecutionLogV2.tsx +378 -0
- package/apps/web/components/playground/JsonViewer.tsx +388 -0
- package/apps/web/components/playground/PlaygroundLayout.tsx +244 -0
- package/apps/web/components/playground/PromptsPanel.tsx +385 -0
- package/apps/web/components/playground/ResourcesPanel.tsx +378 -0
- package/apps/web/components/playground/SchemaForm.tsx +477 -0
- package/apps/web/components/playground/ServerStatus.tsx +151 -0
- package/apps/web/components/playground/ShareButton.tsx +239 -0
- package/apps/web/components/playground/ToolsPanel.tsx +309 -0
- package/apps/web/components/playground/TransportConfigurator.tsx +563 -0
- package/apps/web/components/playground/index.ts +74 -0
- package/apps/web/components/playground/types.ts +202 -0
- package/apps/web/components/streaming/StreamingProgress.tsx +441 -0
- package/apps/web/components/streaming/index.ts +7 -0
- package/apps/web/components/ui/badge.tsx +42 -0
- package/apps/web/components/ui/button.tsx +88 -0
- package/apps/web/components/ui/card.tsx +75 -0
- package/apps/web/components/ui/code-block.tsx +122 -0
- package/apps/web/components/ui/index.ts +12 -0
- package/apps/web/components/ui/input.tsx +55 -0
- package/apps/web/components/ui/tabs.tsx +61 -0
- package/apps/web/hooks/index.ts +85 -0
- package/apps/web/hooks/types.ts +1173 -0
- package/apps/web/hooks/use-conversion.ts +133 -0
- package/apps/web/hooks/use-execution-history.ts +376 -0
- package/apps/web/hooks/use-generation-progress.ts +147 -0
- package/apps/web/hooks/use-local-storage.ts +88 -0
- package/apps/web/hooks/use-mcp-client.ts +623 -0
- package/apps/web/hooks/use-mcp-connection.ts +500 -0
- package/apps/web/hooks/use-mcp-execution.ts +282 -0
- package/apps/web/hooks/use-mcp-prompts.ts +441 -0
- package/apps/web/hooks/use-mcp-resources.ts +430 -0
- package/apps/web/hooks/use-mcp-tools.ts +540 -0
- package/apps/web/hooks/use-playground-store.ts +299 -0
- package/apps/web/hooks/use-playground.ts +184 -0
- package/apps/web/hooks/use-streaming-conversion.ts +227 -0
- package/apps/web/hooks/useBatchConversion.ts +271 -0
- package/apps/web/hooks/useDockerConfig.ts +161 -0
- package/apps/web/hooks/usePlatformDetection.ts +80 -0
- package/apps/web/hooks/useStreaming.ts +199 -0
- package/apps/web/lib/api/errors.ts +386 -0
- package/apps/web/lib/api/index.ts +137 -0
- package/apps/web/lib/api/logger.ts +187 -0
- package/apps/web/lib/api/middleware.ts +364 -0
- package/apps/web/lib/api/openapi.ts +977 -0
- package/apps/web/lib/api/session-manager.ts +594 -0
- package/apps/web/lib/api/types.ts +433 -0
- package/apps/web/lib/api/validation.ts +523 -0
- package/apps/web/lib/constants.ts +114 -0
- package/apps/web/lib/mcp/client.ts +1137 -0
- package/apps/web/lib/mcp/events.ts +651 -0
- package/apps/web/lib/mcp/index.ts +347 -0
- package/apps/web/lib/mcp/logger.ts +428 -0
- package/apps/web/lib/mcp/metrics.ts +703 -0
- package/apps/web/lib/mcp/retry.ts +616 -0
- package/apps/web/lib/mcp/session-manager.ts +779 -0
- package/apps/web/lib/mcp/transports.ts +988 -0
- package/apps/web/lib/mcp/types.ts +594 -0
- package/apps/web/lib/mcp-client-enhanced.ts +871 -0
- package/apps/web/lib/mcp-client.ts +778 -0
- package/apps/web/lib/mcp-errors.ts +489 -0
- package/apps/web/lib/mcp-sandbox.ts +593 -0
- package/apps/web/lib/mcp-testing.ts +428 -0
- package/apps/web/lib/mcp-types.ts +448 -0
- package/apps/web/lib/playground-store.tsx +1147 -0
- package/apps/web/lib/utils.ts +439 -0
- package/apps/web/next-env.d.ts +5 -0
- package/apps/web/next.config.js +23 -0
- package/apps/web/package.json +55 -0
- package/apps/web/postcss.config.js +6 -0
- package/apps/web/public/.well-known/ai-plugin.json +17 -0
- package/apps/web/public/logo.svg +6 -0
- package/apps/web/public/robots.txt +22 -0
- package/apps/web/public/schema.json +27 -0
- package/apps/web/tailwind.config.js +26 -0
- package/apps/web/tailwind.config.ts +123 -0
- package/apps/web/tsconfig.json +20 -0
- package/apps/web/types/deploy.ts +139 -0
- package/apps/web/types/index.ts +247 -0
- package/apps/web/vercel.json +39 -0
- package/eslint.config.mjs +23 -0
- package/llms.txt +102 -0
- package/mkdocs/docs/api/core.md +318 -0
- package/mkdocs/docs/api/index.md +128 -0
- package/mkdocs/docs/api/mcp-server.md +301 -0
- package/mkdocs/docs/api/openapi-parser.md +254 -0
- package/mkdocs/docs/assets/logo.svg +7 -0
- package/mkdocs/docs/changelog.md +118 -0
- package/mkdocs/docs/cli/generate.md +148 -0
- package/mkdocs/docs/cli/index.md +52 -0
- package/mkdocs/docs/cli/inspect.md +164 -0
- package/mkdocs/docs/cli/serve.md +136 -0
- package/mkdocs/docs/concepts/classification.md +254 -0
- package/mkdocs/docs/concepts/how-it-works.md +299 -0
- package/mkdocs/docs/concepts/index.md +77 -0
- package/mkdocs/docs/concepts/mcp-protocol.md +362 -0
- package/mkdocs/docs/concepts/tool-types.md +382 -0
- package/mkdocs/docs/contributing/architecture.md +262 -0
- package/mkdocs/docs/contributing/development.md +245 -0
- package/mkdocs/docs/contributing/index.md +73 -0
- package/mkdocs/docs/contributing/testing.md +320 -0
- package/mkdocs/docs/getting-started/configuration.md +235 -0
- package/mkdocs/docs/getting-started/index.md +54 -0
- package/mkdocs/docs/getting-started/installation.md +145 -0
- package/mkdocs/docs/getting-started/quickstart.md +160 -0
- package/mkdocs/docs/guides/batch.md +375 -0
- package/mkdocs/docs/guides/claude-desktop.md +227 -0
- package/mkdocs/docs/guides/cursor.md +188 -0
- package/mkdocs/docs/guides/custom-tools.md +367 -0
- package/mkdocs/docs/guides/index.md +78 -0
- package/mkdocs/docs/guides/private-repos.md +221 -0
- package/mkdocs/docs/guides/vscode.md +247 -0
- package/mkdocs/docs/index.md +175 -0
- package/mkdocs/docs/reference/config.md +223 -0
- package/mkdocs/docs/reference/env.md +192 -0
- package/mkdocs/docs/reference/index.md +102 -0
- package/mkdocs/docs/reference/tools.md +309 -0
- package/mkdocs/docs/stylesheets/extra.css +231 -0
- package/mkdocs/mkdocs.yml +204 -0
- package/mkdocs/overrides/.gitkeep +1 -0
- package/mkdocs/overrides/main.html +7 -0
- package/mkdocs/python-deps.txt +7 -0
- package/mkdocs/vercel.json +11 -0
- package/package.json +63 -0
- package/packages/core/package.json +61 -0
- package/packages/core/src/__tests__/bitbucket-client.test.ts +366 -0
- package/packages/core/src/__tests__/cli.test.ts +235 -0
- package/packages/core/src/__tests__/code-extractor.test.ts +378 -0
- package/packages/core/src/__tests__/docker-generator.test.ts +255 -0
- package/packages/core/src/__tests__/github-client.test.ts +390 -0
- package/packages/core/src/__tests__/gitlab-client.test.ts +319 -0
- package/packages/core/src/__tests__/go-extractor.test.ts +351 -0
- package/packages/core/src/__tests__/graphql-extractor.test.ts +330 -0
- package/packages/core/src/__tests__/java-extractor.test.ts +497 -0
- package/packages/core/src/__tests__/plugins.test.ts +467 -0
- package/packages/core/src/__tests__/readme-extractor.test.ts +258 -0
- package/packages/core/src/__tests__/redis-cache.test.ts +307 -0
- package/packages/core/src/__tests__/rust-extractor.test.ts +252 -0
- package/packages/core/src/__tests__/streaming.test.ts +251 -0
- package/packages/core/src/additional-extractors.ts +333 -0
- package/packages/core/src/cache/cache-interface.ts +179 -0
- package/packages/core/src/cache/index.ts +210 -0
- package/packages/core/src/cache/redis-cache.ts +291 -0
- package/packages/core/src/cache/upstash-cache.ts +379 -0
- package/packages/core/src/cache.ts +251 -0
- package/packages/core/src/cli.ts +822 -0
- package/packages/core/src/code-extractor.ts +696 -0
- package/packages/core/src/docker-generator.ts +470 -0
- package/packages/core/src/edge-compatible.ts +491 -0
- package/packages/core/src/extractors/go-extractor.ts +791 -0
- package/packages/core/src/extractors/index.ts +9 -0
- package/packages/core/src/extractors/java-extractor.ts +937 -0
- package/packages/core/src/extractors/rust-extractor.ts +744 -0
- package/packages/core/src/github-client.ts +319 -0
- package/packages/core/src/go-generator.ts +356 -0
- package/packages/core/src/graphql-extractor.ts +358 -0
- package/packages/core/src/index.ts +797 -0
- package/packages/core/src/langchain-exporter.ts +617 -0
- package/packages/core/src/language-parsers.ts +1114 -0
- package/packages/core/src/mcp-introspector.ts +279 -0
- package/packages/core/src/monorepo-detector.ts +378 -0
- package/packages/core/src/plugins/index.ts +370 -0
- package/packages/core/src/plugins/registry.ts +404 -0
- package/packages/core/src/plugins/types.ts +215 -0
- package/packages/core/src/providers/base-provider.ts +246 -0
- package/packages/core/src/providers/bitbucket-client.ts +464 -0
- package/packages/core/src/providers/gitlab-client.ts +388 -0
- package/packages/core/src/providers/index.ts +176 -0
- package/packages/core/src/python-generator.ts +260 -0
- package/packages/core/src/queue/index.ts +100 -0
- package/packages/core/src/queue/memory-queue.ts +445 -0
- package/packages/core/src/queue/redis-queue.ts +578 -0
- package/packages/core/src/queue/types.ts +251 -0
- package/packages/core/src/readme-extractor.ts +409 -0
- package/packages/core/src/schema-generator.ts +638 -0
- package/packages/core/src/streaming.ts +999 -0
- package/packages/core/src/types.ts +289 -0
- package/packages/core/tsconfig.json +9 -0
- package/packages/core/tsup.config.ts +25 -0
- package/packages/mcp-server/README.md +297 -0
- package/packages/mcp-server/package.json +55 -0
- package/packages/mcp-server/src/__tests__/mcp-server.test.ts +177 -0
- package/packages/mcp-server/src/__tests__/tools.test.ts +217 -0
- package/packages/mcp-server/src/index.ts +1206 -0
- package/packages/mcp-server/src/prompts/index.ts +601 -0
- package/packages/mcp-server/src/tools/export-docker.ts +362 -0
- package/packages/mcp-server/src/tools/generate-openapi.ts +162 -0
- package/packages/mcp-server/src/tools/monitor-mcp-server.ts +448 -0
- package/packages/mcp-server/src/tools/stream-convert.ts +398 -0
- package/packages/mcp-server/src/tools/test-mcp-tool.ts +531 -0
- package/packages/mcp-server/tsconfig.json +12 -0
- package/packages/mcp-server/tsup.config.ts +14 -0
- package/packages/openapi-parser/package-lock.json +3028 -0
- package/packages/openapi-parser/package.json +41 -0
- package/packages/openapi-parser/src/analyzer.ts +700 -0
- package/packages/openapi-parser/src/asyncapi-parser.ts +475 -0
- package/packages/openapi-parser/src/cli.ts +302 -0
- package/packages/openapi-parser/src/generator.ts +570 -0
- package/packages/openapi-parser/src/generators/express-analyzer.ts +649 -0
- package/packages/openapi-parser/src/generators/fastapi-analyzer.ts +960 -0
- package/packages/openapi-parser/src/generators/index.ts +200 -0
- package/packages/openapi-parser/src/generators/nextjs-analyzer.ts +768 -0
- package/packages/openapi-parser/src/generators/openapi-builder.ts +527 -0
- package/packages/openapi-parser/src/generators/types.ts +298 -0
- package/packages/openapi-parser/src/graphql-parser.ts +462 -0
- package/packages/openapi-parser/src/grpc-parser.ts +649 -0
- package/packages/openapi-parser/src/har-parser.ts +723 -0
- package/packages/openapi-parser/src/index.ts +635 -0
- package/packages/openapi-parser/src/insomnia-parser.ts +614 -0
- package/packages/openapi-parser/src/parser.ts +231 -0
- package/packages/openapi-parser/src/postman-parser.ts +611 -0
- package/packages/openapi-parser/src/ref-resolver.ts +313 -0
- package/packages/openapi-parser/src/transformer.ts +459 -0
- package/packages/openapi-parser/tests/generators/express.test.ts +209 -0
- package/packages/openapi-parser/tests/generators/fastapi.test.ts +236 -0
- package/packages/openapi-parser/tests/generators/nextjs.test.ts +273 -0
- package/packages/openapi-parser/tests/parsers.test.ts +847 -0
- package/packages/openapi-parser/tsconfig.json +9 -0
- package/packages/openapi-parser/tsup.config.ts +11 -0
- package/packages/registry/package.json +59 -0
- package/packages/registry/src/cli.ts +456 -0
- package/packages/registry/src/index.ts +44 -0
- package/packages/registry/src/popular/github.json +47 -0
- package/packages/registry/src/popular/index.ts +55 -0
- package/packages/registry/src/popular/linear.json +42 -0
- package/packages/registry/src/popular/notion.json +42 -0
- package/packages/registry/src/popular/openai.json +40 -0
- package/packages/registry/src/popular/resend.json +38 -0
- package/packages/registry/src/popular/slack.json +42 -0
- package/packages/registry/src/popular/stripe.json +163 -0
- package/packages/registry/src/popular/supabase.json +42 -0
- package/packages/registry/src/popular/twilio.json +40 -0
- package/packages/registry/src/popular/vercel.json +40 -0
- package/packages/registry/src/registry.ts +492 -0
- package/packages/registry/src/storage.ts +334 -0
- package/packages/registry/src/types.ts +275 -0
- package/packages/registry/src/updater.ts +208 -0
- package/packages/registry/tsconfig.json +10 -0
- package/packages/registry/tsup.config.ts +11 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/build-docs.sh +16 -0
- package/server.json +9 -0
- package/templates/Dockerfile.python.template +60 -0
- package/templates/Dockerfile.typescript.template +60 -0
- package/templates/docker-compose.template.yml +68 -0
- package/tests/fixtures/express-app/index.js +34 -0
- package/tests/fixtures/express-app/routes/posts.js +43 -0
- package/tests/fixtures/express-app/routes/users.js +58 -0
- package/tests/fixtures/fastapi-app/main.py +125 -0
- package/tests/fixtures/fastapi-app/routes/admin.py +42 -0
- package/tests/fixtures/graphql/simple-schema.graphql +65 -0
- package/tests/fixtures/mocks/github-api-responses.json +63 -0
- package/tests/fixtures/nextjs-app/app/api/posts/route.ts +55 -0
- package/tests/fixtures/nextjs-app/app/api/users/[id]/route.ts +63 -0
- package/tests/fixtures/nextjs-app/app/api/users/route.ts +44 -0
- package/tests/fixtures/nextjs-app/pages/api/health.ts +28 -0
- package/tests/fixtures/openapi/petstore.yaml +179 -0
- package/tests/integration/langchain-export.test.ts +405 -0
- package/tests/integration/openapi-conversion.test.ts +221 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +32 -0
|
@@ -0,0 +1,999 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Streaming generation for GitHub to MCP conversion
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { GithubToMcpGenerator } from './index';
|
|
8
|
+
import {
|
|
9
|
+
GithubToMcpOptions,
|
|
10
|
+
ExtractedTool,
|
|
11
|
+
SourceBreakdown,
|
|
12
|
+
RepoClassification,
|
|
13
|
+
GenerationResult,
|
|
14
|
+
SourceType
|
|
15
|
+
} from './types';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Event types emitted during streaming generation
|
|
19
|
+
*/
|
|
20
|
+
export type StreamEventType =
|
|
21
|
+
| 'start'
|
|
22
|
+
| 'metadata'
|
|
23
|
+
| 'classifying'
|
|
24
|
+
| 'classified'
|
|
25
|
+
| 'extracting'
|
|
26
|
+
| 'tool-found'
|
|
27
|
+
| 'source-complete'
|
|
28
|
+
| 'generating'
|
|
29
|
+
| 'complete'
|
|
30
|
+
| 'error'
|
|
31
|
+
| 'progress';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Base interface for all stream events
|
|
35
|
+
*/
|
|
36
|
+
export interface BaseStreamEvent {
|
|
37
|
+
type: StreamEventType;
|
|
38
|
+
timestamp: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Emitted when generation starts
|
|
43
|
+
*/
|
|
44
|
+
export interface StartEvent extends BaseStreamEvent {
|
|
45
|
+
type: 'start';
|
|
46
|
+
url: string;
|
|
47
|
+
owner: string;
|
|
48
|
+
repo: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Emitted when repository metadata is fetched
|
|
53
|
+
*/
|
|
54
|
+
export interface MetadataEvent extends BaseStreamEvent {
|
|
55
|
+
type: 'metadata';
|
|
56
|
+
metadata: {
|
|
57
|
+
stars: number;
|
|
58
|
+
language: string;
|
|
59
|
+
license?: string;
|
|
60
|
+
description?: string;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Emitted when classification begins
|
|
66
|
+
*/
|
|
67
|
+
export interface ClassifyingEvent extends BaseStreamEvent {
|
|
68
|
+
type: 'classifying';
|
|
69
|
+
message: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Emitted when classification completes
|
|
74
|
+
*/
|
|
75
|
+
export interface ClassifiedEvent extends BaseStreamEvent {
|
|
76
|
+
type: 'classified';
|
|
77
|
+
classification: RepoClassification;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Emitted when extraction from a source begins
|
|
82
|
+
*/
|
|
83
|
+
export interface ExtractingEvent extends BaseStreamEvent {
|
|
84
|
+
type: 'extracting';
|
|
85
|
+
source: SourceType;
|
|
86
|
+
message: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Emitted when a tool is found
|
|
91
|
+
*/
|
|
92
|
+
export interface ToolFoundEvent extends BaseStreamEvent {
|
|
93
|
+
type: 'tool-found';
|
|
94
|
+
tool: ExtractedTool;
|
|
95
|
+
source: SourceType;
|
|
96
|
+
totalFound: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Emitted when extraction from a source completes
|
|
101
|
+
*/
|
|
102
|
+
export interface SourceCompleteEvent extends BaseStreamEvent {
|
|
103
|
+
type: 'source-complete';
|
|
104
|
+
source: SourceType;
|
|
105
|
+
toolCount: number;
|
|
106
|
+
files: string[];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Emitted when code generation begins
|
|
111
|
+
*/
|
|
112
|
+
export interface GeneratingEvent extends BaseStreamEvent {
|
|
113
|
+
type: 'generating';
|
|
114
|
+
language: 'typescript' | 'python';
|
|
115
|
+
toolCount: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Emitted when generation completes successfully
|
|
120
|
+
*/
|
|
121
|
+
export interface CompleteEvent extends BaseStreamEvent {
|
|
122
|
+
type: 'complete';
|
|
123
|
+
result: GenerationResult;
|
|
124
|
+
totalTools: number;
|
|
125
|
+
sources: SourceBreakdown[];
|
|
126
|
+
duration: number;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Emitted when an error occurs
|
|
131
|
+
*/
|
|
132
|
+
export interface ErrorEvent extends BaseStreamEvent {
|
|
133
|
+
type: 'error';
|
|
134
|
+
error: Error;
|
|
135
|
+
phase: string;
|
|
136
|
+
recoverable: boolean;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Progress update event
|
|
141
|
+
*/
|
|
142
|
+
export interface ProgressEvent extends BaseStreamEvent {
|
|
143
|
+
type: 'progress';
|
|
144
|
+
phase: string;
|
|
145
|
+
progress: number; // 0-100
|
|
146
|
+
message: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Union of all stream event types
|
|
151
|
+
*/
|
|
152
|
+
export type StreamEvent =
|
|
153
|
+
| StartEvent
|
|
154
|
+
| MetadataEvent
|
|
155
|
+
| ClassifyingEvent
|
|
156
|
+
| ClassifiedEvent
|
|
157
|
+
| ExtractingEvent
|
|
158
|
+
| ToolFoundEvent
|
|
159
|
+
| SourceCompleteEvent
|
|
160
|
+
| GeneratingEvent
|
|
161
|
+
| CompleteEvent
|
|
162
|
+
| ErrorEvent
|
|
163
|
+
| ProgressEvent;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Options for streaming generation
|
|
167
|
+
*/
|
|
168
|
+
export interface StreamOptions extends GithubToMcpOptions {
|
|
169
|
+
/** Emit progress events with percentage */
|
|
170
|
+
emitProgress?: boolean;
|
|
171
|
+
/** Minimum interval between tool-found events in ms (debouncing) */
|
|
172
|
+
toolEventDebounce?: number;
|
|
173
|
+
/** Abort signal for cancellation */
|
|
174
|
+
abortSignal?: AbortSignal;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Create a timestamp for events
|
|
179
|
+
*/
|
|
180
|
+
function timestamp(): number {
|
|
181
|
+
return Date.now();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Streaming generator class that wraps the main generator
|
|
186
|
+
* and yields events as extraction progresses
|
|
187
|
+
*/
|
|
188
|
+
export class StreamingGenerator {
|
|
189
|
+
private options: StreamOptions;
|
|
190
|
+
private toolCount: number = 0;
|
|
191
|
+
private startTime: number = 0;
|
|
192
|
+
|
|
193
|
+
constructor(options: StreamOptions = {}) {
|
|
194
|
+
this.options = options;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Stream generation events as an async generator
|
|
199
|
+
*/
|
|
200
|
+
async *generate(githubUrl: string): AsyncGenerator<StreamEvent, void, unknown> {
|
|
201
|
+
this.startTime = Date.now();
|
|
202
|
+
this.toolCount = 0;
|
|
203
|
+
|
|
204
|
+
// Import dynamically to avoid circular dependencies
|
|
205
|
+
const { GithubClient } = await import('./github-client');
|
|
206
|
+
const { ReadmeExtractor } = await import('./readme-extractor');
|
|
207
|
+
const { CodeExtractor } = await import('./code-extractor');
|
|
208
|
+
const { GraphQLExtractor } = await import('./graphql-extractor');
|
|
209
|
+
const { McpIntrospector } = await import('./mcp-introspector');
|
|
210
|
+
const { PythonGenerator } = await import('./python-generator');
|
|
211
|
+
const { convertOpenApiToMcp } = await import('@github-to-mcp/openapi-parser');
|
|
212
|
+
|
|
213
|
+
const github = new GithubClient(this.options.githubToken);
|
|
214
|
+
const readmeExtractor = new ReadmeExtractor();
|
|
215
|
+
const codeExtractor = new CodeExtractor();
|
|
216
|
+
const graphqlExtractor = new GraphQLExtractor();
|
|
217
|
+
const mcpIntrospector = new McpIntrospector();
|
|
218
|
+
const pythonGenerator = new PythonGenerator();
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
// Check for abort signal
|
|
222
|
+
if (this.options.abortSignal?.aborted) {
|
|
223
|
+
throw new Error('Generation aborted');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Parse GitHub URL
|
|
227
|
+
const repoMeta = github.parseGithubUrl(githubUrl);
|
|
228
|
+
|
|
229
|
+
// Emit start event
|
|
230
|
+
yield {
|
|
231
|
+
type: 'start',
|
|
232
|
+
timestamp: timestamp(),
|
|
233
|
+
url: githubUrl,
|
|
234
|
+
owner: repoMeta.owner,
|
|
235
|
+
repo: repoMeta.repo
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Emit progress
|
|
239
|
+
if (this.options.emitProgress) {
|
|
240
|
+
yield {
|
|
241
|
+
type: 'progress',
|
|
242
|
+
timestamp: timestamp(),
|
|
243
|
+
phase: 'fetching',
|
|
244
|
+
progress: 5,
|
|
245
|
+
message: 'Fetching repository metadata...'
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Get repository metadata
|
|
250
|
+
const metadata = await github.getRepoMetadata(repoMeta.owner, repoMeta.repo);
|
|
251
|
+
|
|
252
|
+
yield {
|
|
253
|
+
type: 'metadata',
|
|
254
|
+
timestamp: timestamp(),
|
|
255
|
+
metadata: {
|
|
256
|
+
stars: metadata.stars,
|
|
257
|
+
language: metadata.language,
|
|
258
|
+
license: metadata.license,
|
|
259
|
+
description: metadata.description
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// Check for abort
|
|
264
|
+
if (this.options.abortSignal?.aborted) {
|
|
265
|
+
throw new Error('Generation aborted');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Get README
|
|
269
|
+
const readme = await github.getReadme(repoMeta.owner, repoMeta.repo);
|
|
270
|
+
|
|
271
|
+
// Emit classifying event
|
|
272
|
+
yield {
|
|
273
|
+
type: 'classifying',
|
|
274
|
+
timestamp: timestamp(),
|
|
275
|
+
message: 'Analyzing repository type...'
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
if (this.options.emitProgress) {
|
|
279
|
+
yield {
|
|
280
|
+
type: 'progress',
|
|
281
|
+
timestamp: timestamp(),
|
|
282
|
+
phase: 'classifying',
|
|
283
|
+
progress: 15,
|
|
284
|
+
message: 'Classifying repository...'
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Classify repository
|
|
289
|
+
const classification = await this.classifyRepo(github, repoMeta.owner, repoMeta.repo, readme, metadata);
|
|
290
|
+
|
|
291
|
+
yield {
|
|
292
|
+
type: 'classified',
|
|
293
|
+
timestamp: timestamp(),
|
|
294
|
+
classification
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// Prepare for extraction
|
|
298
|
+
const tools: ExtractedTool[] = [];
|
|
299
|
+
const sources: SourceBreakdown[] = [];
|
|
300
|
+
const enabledSources = this.options.sources || ['readme', 'openapi', 'code'];
|
|
301
|
+
|
|
302
|
+
let progressBase = 20;
|
|
303
|
+
const progressPerSource = 60 / (enabledSources.length + 2); // +2 for graphql and universal
|
|
304
|
+
|
|
305
|
+
// 1. Extract from OpenAPI specs
|
|
306
|
+
if (enabledSources.includes('openapi')) {
|
|
307
|
+
if (this.options.abortSignal?.aborted) throw new Error('Generation aborted');
|
|
308
|
+
|
|
309
|
+
yield {
|
|
310
|
+
type: 'extracting',
|
|
311
|
+
timestamp: timestamp(),
|
|
312
|
+
source: 'openapi',
|
|
313
|
+
message: 'Searching for OpenAPI/Swagger specifications...'
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const openapiTools = await this.extractFromOpenApi(github, repoMeta.owner, repoMeta.repo, convertOpenApiToMcp);
|
|
317
|
+
|
|
318
|
+
for (const tool of openapiTools) {
|
|
319
|
+
this.toolCount++;
|
|
320
|
+
tools.push(tool);
|
|
321
|
+
yield {
|
|
322
|
+
type: 'tool-found',
|
|
323
|
+
timestamp: timestamp(),
|
|
324
|
+
tool,
|
|
325
|
+
source: 'openapi',
|
|
326
|
+
totalFound: this.toolCount
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (openapiTools.length > 0) {
|
|
331
|
+
sources.push({
|
|
332
|
+
type: 'openapi',
|
|
333
|
+
count: openapiTools.length,
|
|
334
|
+
files: [...new Set(openapiTools.map(t => t.source.file))]
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
yield {
|
|
339
|
+
type: 'source-complete',
|
|
340
|
+
timestamp: timestamp(),
|
|
341
|
+
source: 'openapi',
|
|
342
|
+
toolCount: openapiTools.length,
|
|
343
|
+
files: openapiTools.length > 0 ? [...new Set(openapiTools.map(t => t.source.file))] : []
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
progressBase += progressPerSource;
|
|
347
|
+
if (this.options.emitProgress) {
|
|
348
|
+
yield {
|
|
349
|
+
type: 'progress',
|
|
350
|
+
timestamp: timestamp(),
|
|
351
|
+
phase: 'extracting',
|
|
352
|
+
progress: Math.round(progressBase),
|
|
353
|
+
message: `Extracted ${openapiTools.length} tools from OpenAPI specs`
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 2. Extract from README
|
|
359
|
+
if (enabledSources.includes('readme')) {
|
|
360
|
+
if (this.options.abortSignal?.aborted) throw new Error('Generation aborted');
|
|
361
|
+
|
|
362
|
+
yield {
|
|
363
|
+
type: 'extracting',
|
|
364
|
+
timestamp: timestamp(),
|
|
365
|
+
source: 'readme',
|
|
366
|
+
message: 'Extracting tools from README documentation...'
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const readmeTools = await this.extractFromReadme(readmeExtractor, readme, repoMeta.repo);
|
|
370
|
+
|
|
371
|
+
for (const tool of readmeTools) {
|
|
372
|
+
this.toolCount++;
|
|
373
|
+
tools.push(tool);
|
|
374
|
+
yield {
|
|
375
|
+
type: 'tool-found',
|
|
376
|
+
timestamp: timestamp(),
|
|
377
|
+
tool,
|
|
378
|
+
source: 'readme',
|
|
379
|
+
totalFound: this.toolCount
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (readmeTools.length > 0) {
|
|
384
|
+
sources.push({
|
|
385
|
+
type: 'readme',
|
|
386
|
+
count: readmeTools.length,
|
|
387
|
+
files: ['README.md']
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
yield {
|
|
392
|
+
type: 'source-complete',
|
|
393
|
+
timestamp: timestamp(),
|
|
394
|
+
source: 'readme',
|
|
395
|
+
toolCount: readmeTools.length,
|
|
396
|
+
files: readmeTools.length > 0 ? ['README.md'] : []
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
progressBase += progressPerSource;
|
|
400
|
+
if (this.options.emitProgress) {
|
|
401
|
+
yield {
|
|
402
|
+
type: 'progress',
|
|
403
|
+
timestamp: timestamp(),
|
|
404
|
+
phase: 'extracting',
|
|
405
|
+
progress: Math.round(progressBase),
|
|
406
|
+
message: `Extracted ${readmeTools.length} tools from README`
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// 3. Extract from code
|
|
412
|
+
if (enabledSources.includes('code')) {
|
|
413
|
+
if (this.options.abortSignal?.aborted) throw new Error('Generation aborted');
|
|
414
|
+
|
|
415
|
+
yield {
|
|
416
|
+
type: 'extracting',
|
|
417
|
+
timestamp: timestamp(),
|
|
418
|
+
source: 'code',
|
|
419
|
+
message: 'Analyzing source code for extractable tools...'
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const codeTools = await this.extractFromCode(github, codeExtractor, repoMeta.owner, repoMeta.repo);
|
|
423
|
+
|
|
424
|
+
for (const tool of codeTools) {
|
|
425
|
+
this.toolCount++;
|
|
426
|
+
tools.push(tool);
|
|
427
|
+
yield {
|
|
428
|
+
type: 'tool-found',
|
|
429
|
+
timestamp: timestamp(),
|
|
430
|
+
tool,
|
|
431
|
+
source: 'code',
|
|
432
|
+
totalFound: this.toolCount
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (codeTools.length > 0) {
|
|
437
|
+
sources.push({
|
|
438
|
+
type: 'code',
|
|
439
|
+
count: codeTools.length,
|
|
440
|
+
files: [...new Set(codeTools.map(t => t.source.file))]
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
yield {
|
|
445
|
+
type: 'source-complete',
|
|
446
|
+
timestamp: timestamp(),
|
|
447
|
+
source: 'code',
|
|
448
|
+
toolCount: codeTools.length,
|
|
449
|
+
files: codeTools.length > 0 ? [...new Set(codeTools.map(t => t.source.file))] : []
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
progressBase += progressPerSource;
|
|
453
|
+
if (this.options.emitProgress) {
|
|
454
|
+
yield {
|
|
455
|
+
type: 'progress',
|
|
456
|
+
timestamp: timestamp(),
|
|
457
|
+
phase: 'extracting',
|
|
458
|
+
progress: Math.round(progressBase),
|
|
459
|
+
message: `Extracted ${codeTools.length} tools from source code`
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// 4. Extract from GraphQL schemas
|
|
465
|
+
if (this.options.abortSignal?.aborted) throw new Error('Generation aborted');
|
|
466
|
+
|
|
467
|
+
yield {
|
|
468
|
+
type: 'extracting',
|
|
469
|
+
timestamp: timestamp(),
|
|
470
|
+
source: 'graphql',
|
|
471
|
+
message: 'Searching for GraphQL schemas...'
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const graphqlTools = await this.extractFromGraphQL(github, graphqlExtractor, repoMeta.owner, repoMeta.repo);
|
|
475
|
+
|
|
476
|
+
for (const tool of graphqlTools) {
|
|
477
|
+
this.toolCount++;
|
|
478
|
+
tools.push(tool);
|
|
479
|
+
yield {
|
|
480
|
+
type: 'tool-found',
|
|
481
|
+
timestamp: timestamp(),
|
|
482
|
+
tool,
|
|
483
|
+
source: 'graphql',
|
|
484
|
+
totalFound: this.toolCount
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (graphqlTools.length > 0) {
|
|
489
|
+
sources.push({
|
|
490
|
+
type: 'graphql',
|
|
491
|
+
count: graphqlTools.length,
|
|
492
|
+
files: [...new Set(graphqlTools.map(t => t.source.file))]
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
yield {
|
|
497
|
+
type: 'source-complete',
|
|
498
|
+
timestamp: timestamp(),
|
|
499
|
+
source: 'graphql',
|
|
500
|
+
toolCount: graphqlTools.length,
|
|
501
|
+
files: graphqlTools.length > 0 ? [...new Set(graphqlTools.map(t => t.source.file))] : []
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
progressBase += progressPerSource;
|
|
505
|
+
|
|
506
|
+
// 5. Introspect existing MCP servers
|
|
507
|
+
if (classification.type === 'mcp-server') {
|
|
508
|
+
if (this.options.abortSignal?.aborted) throw new Error('Generation aborted');
|
|
509
|
+
|
|
510
|
+
yield {
|
|
511
|
+
type: 'extracting',
|
|
512
|
+
timestamp: timestamp(),
|
|
513
|
+
source: 'mcp-introspect',
|
|
514
|
+
message: 'Introspecting existing MCP server tools...'
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
const mcpTools = await this.introspectMcpServer(github, mcpIntrospector, repoMeta.owner, repoMeta.repo);
|
|
518
|
+
|
|
519
|
+
for (const tool of mcpTools) {
|
|
520
|
+
this.toolCount++;
|
|
521
|
+
tools.push(tool);
|
|
522
|
+
yield {
|
|
523
|
+
type: 'tool-found',
|
|
524
|
+
timestamp: timestamp(),
|
|
525
|
+
tool,
|
|
526
|
+
source: 'mcp-introspect',
|
|
527
|
+
totalFound: this.toolCount
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (mcpTools.length > 0) {
|
|
532
|
+
sources.push({
|
|
533
|
+
type: 'mcp-introspect',
|
|
534
|
+
count: mcpTools.length,
|
|
535
|
+
files: [...new Set(mcpTools.map(t => t.source.file))]
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
yield {
|
|
540
|
+
type: 'source-complete',
|
|
541
|
+
timestamp: timestamp(),
|
|
542
|
+
source: 'mcp-introspect',
|
|
543
|
+
toolCount: mcpTools.length,
|
|
544
|
+
files: mcpTools.length > 0 ? [...new Set(mcpTools.map(t => t.source.file))] : []
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// 6. Add universal fallback tools
|
|
549
|
+
yield {
|
|
550
|
+
type: 'extracting',
|
|
551
|
+
timestamp: timestamp(),
|
|
552
|
+
source: 'universal',
|
|
553
|
+
message: 'Adding universal repository tools...'
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
const universalTools = this.generateUniversalTools(repoMeta.owner, repoMeta.repo);
|
|
557
|
+
|
|
558
|
+
for (const tool of universalTools) {
|
|
559
|
+
this.toolCount++;
|
|
560
|
+
tools.push(tool);
|
|
561
|
+
yield {
|
|
562
|
+
type: 'tool-found',
|
|
563
|
+
timestamp: timestamp(),
|
|
564
|
+
tool,
|
|
565
|
+
source: 'universal',
|
|
566
|
+
totalFound: this.toolCount
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
sources.push({
|
|
571
|
+
type: 'universal',
|
|
572
|
+
count: universalTools.length,
|
|
573
|
+
files: []
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
yield {
|
|
577
|
+
type: 'source-complete',
|
|
578
|
+
timestamp: timestamp(),
|
|
579
|
+
source: 'universal',
|
|
580
|
+
toolCount: universalTools.length,
|
|
581
|
+
files: []
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
if (this.options.emitProgress) {
|
|
585
|
+
yield {
|
|
586
|
+
type: 'progress',
|
|
587
|
+
timestamp: timestamp(),
|
|
588
|
+
phase: 'extracting',
|
|
589
|
+
progress: 80,
|
|
590
|
+
message: `Extraction complete. Found ${this.toolCount} total tools.`
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Deduplicate tools
|
|
595
|
+
const uniqueTools = this.deduplicateTools(tools);
|
|
596
|
+
|
|
597
|
+
// Generate code
|
|
598
|
+
const outputLanguage = this.options.outputLanguage || 'typescript';
|
|
599
|
+
|
|
600
|
+
yield {
|
|
601
|
+
type: 'generating',
|
|
602
|
+
timestamp: timestamp(),
|
|
603
|
+
language: outputLanguage,
|
|
604
|
+
toolCount: uniqueTools.length
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
if (this.options.emitProgress) {
|
|
608
|
+
yield {
|
|
609
|
+
type: 'progress',
|
|
610
|
+
timestamp: timestamp(),
|
|
611
|
+
phase: 'generating',
|
|
612
|
+
progress: 90,
|
|
613
|
+
message: `Generating ${outputLanguage} MCP server...`
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Build result using the main generator for code generation
|
|
618
|
+
const generator = new GithubToMcpGenerator(this.options);
|
|
619
|
+
|
|
620
|
+
const result: GenerationResult = {
|
|
621
|
+
repo: githubUrl,
|
|
622
|
+
name: repoMeta.repo,
|
|
623
|
+
tools: uniqueTools,
|
|
624
|
+
sources,
|
|
625
|
+
classification,
|
|
626
|
+
metadata,
|
|
627
|
+
generate: () => generator['generateCode'](uniqueTools, repoMeta.repo, repoMeta.owner, outputLanguage),
|
|
628
|
+
generatePython: () => pythonGenerator.generateServer(uniqueTools, repoMeta.repo, repoMeta.owner),
|
|
629
|
+
save: async (outputDir: string) => generator['saveToFiles'](uniqueTools, repoMeta.repo, repoMeta.owner, outputDir),
|
|
630
|
+
download: () => generator['downloadZip'](uniqueTools, repoMeta.repo)
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
const duration = Date.now() - this.startTime;
|
|
634
|
+
|
|
635
|
+
if (this.options.emitProgress) {
|
|
636
|
+
yield {
|
|
637
|
+
type: 'progress',
|
|
638
|
+
timestamp: timestamp(),
|
|
639
|
+
phase: 'complete',
|
|
640
|
+
progress: 100,
|
|
641
|
+
message: `Generation complete in ${duration}ms`
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Emit complete event
|
|
646
|
+
yield {
|
|
647
|
+
type: 'complete',
|
|
648
|
+
timestamp: timestamp(),
|
|
649
|
+
result,
|
|
650
|
+
totalTools: uniqueTools.length,
|
|
651
|
+
sources,
|
|
652
|
+
duration
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
} catch (error) {
|
|
656
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
657
|
+
yield {
|
|
658
|
+
type: 'error',
|
|
659
|
+
timestamp: timestamp(),
|
|
660
|
+
error: err,
|
|
661
|
+
phase: 'generation',
|
|
662
|
+
recoverable: false
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Classify repository type
|
|
669
|
+
*/
|
|
670
|
+
private async classifyRepo(
|
|
671
|
+
github: any,
|
|
672
|
+
owner: string,
|
|
673
|
+
repo: string,
|
|
674
|
+
readme: string | null,
|
|
675
|
+
metadata: any
|
|
676
|
+
): Promise<RepoClassification> {
|
|
677
|
+
const indicators: string[] = [];
|
|
678
|
+
let type: RepoClassification['type'] = 'unknown';
|
|
679
|
+
let confidence = 0.3;
|
|
680
|
+
|
|
681
|
+
const readmeLower = (readme || '').toLowerCase();
|
|
682
|
+
|
|
683
|
+
if (readmeLower.includes('mcp') || readmeLower.includes('model context protocol')) {
|
|
684
|
+
type = 'mcp-server';
|
|
685
|
+
confidence = 0.9;
|
|
686
|
+
indicators.push('MCP keywords in README');
|
|
687
|
+
} else if (readmeLower.includes('api') || readmeLower.includes('sdk') || readmeLower.includes('openapi')) {
|
|
688
|
+
type = 'api-sdk';
|
|
689
|
+
confidence = 0.7;
|
|
690
|
+
indicators.push('API/SDK keywords in README');
|
|
691
|
+
} else if (readmeLower.includes('cli') || readmeLower.includes('command line')) {
|
|
692
|
+
type = 'cli-tool';
|
|
693
|
+
confidence = 0.7;
|
|
694
|
+
indicators.push('CLI keywords in README');
|
|
695
|
+
} else if (metadata.language) {
|
|
696
|
+
type = 'library';
|
|
697
|
+
confidence = 0.5;
|
|
698
|
+
indicators.push(`${metadata.language} repository`);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
return { type, confidence, indicators };
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Extract tools from OpenAPI specs
|
|
706
|
+
*/
|
|
707
|
+
private async extractFromOpenApi(
|
|
708
|
+
github: any,
|
|
709
|
+
owner: string,
|
|
710
|
+
repo: string,
|
|
711
|
+
convertOpenApiToMcp: any
|
|
712
|
+
): Promise<ExtractedTool[]> {
|
|
713
|
+
const tools: ExtractedTool[] = [];
|
|
714
|
+
|
|
715
|
+
try {
|
|
716
|
+
const specPatterns = [
|
|
717
|
+
'openapi.json', 'openapi.yaml', 'openapi.yml',
|
|
718
|
+
'swagger.json', 'swagger.yaml', 'swagger.yml',
|
|
719
|
+
'api-spec.json', 'api-spec.yaml',
|
|
720
|
+
'api/openapi.json', 'api/openapi.yaml',
|
|
721
|
+
'docs/openapi.json', 'docs/openapi.yaml',
|
|
722
|
+
'spec/openapi.json', 'spec/openapi.yaml'
|
|
723
|
+
];
|
|
724
|
+
|
|
725
|
+
for (const pattern of specPatterns) {
|
|
726
|
+
try {
|
|
727
|
+
const content = await github.getFile(owner, repo, pattern);
|
|
728
|
+
if (content) {
|
|
729
|
+
const spec = pattern.endsWith('.json')
|
|
730
|
+
? JSON.parse(content)
|
|
731
|
+
: (await import('yaml')).parse(content);
|
|
732
|
+
|
|
733
|
+
const mcpTools = await convertOpenApiToMcp(spec);
|
|
734
|
+
tools.push(...mcpTools.map((t: any) => ({
|
|
735
|
+
...t,
|
|
736
|
+
source: { type: 'openapi' as const, file: pattern }
|
|
737
|
+
})));
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
} catch {
|
|
741
|
+
// File not found, continue
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
} catch (error) {
|
|
745
|
+
// OpenAPI extraction failed, return empty
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
return tools;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Extract tools from README
|
|
753
|
+
*/
|
|
754
|
+
private async extractFromReadme(
|
|
755
|
+
readmeExtractor: any,
|
|
756
|
+
readme: string | null,
|
|
757
|
+
repoName: string
|
|
758
|
+
): Promise<ExtractedTool[]> {
|
|
759
|
+
if (!readme) return [];
|
|
760
|
+
|
|
761
|
+
try {
|
|
762
|
+
return await readmeExtractor.extract(readme, repoName);
|
|
763
|
+
} catch {
|
|
764
|
+
return [];
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Extract tools from code
|
|
770
|
+
*/
|
|
771
|
+
private async extractFromCode(
|
|
772
|
+
github: any,
|
|
773
|
+
codeExtractor: any,
|
|
774
|
+
owner: string,
|
|
775
|
+
repo: string
|
|
776
|
+
): Promise<ExtractedTool[]> {
|
|
777
|
+
const tools: ExtractedTool[] = [];
|
|
778
|
+
|
|
779
|
+
try {
|
|
780
|
+
const codeFiles = await github.searchCode(owner, repo, 'extension:ts extension:py extension:js');
|
|
781
|
+
|
|
782
|
+
for (const file of codeFiles.slice(0, 20)) {
|
|
783
|
+
try {
|
|
784
|
+
const content = await github.getFile(owner, repo, file.path);
|
|
785
|
+
if (content) {
|
|
786
|
+
const fileTools = await codeExtractor.extract(content, file.path);
|
|
787
|
+
tools.push(...fileTools);
|
|
788
|
+
}
|
|
789
|
+
} catch {
|
|
790
|
+
// File extraction failed, continue
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
} catch {
|
|
794
|
+
// Code extraction failed
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
return tools;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Extract tools from GraphQL schemas
|
|
802
|
+
*/
|
|
803
|
+
private async extractFromGraphQL(
|
|
804
|
+
github: any,
|
|
805
|
+
graphqlExtractor: any,
|
|
806
|
+
owner: string,
|
|
807
|
+
repo: string
|
|
808
|
+
): Promise<ExtractedTool[]> {
|
|
809
|
+
const tools: ExtractedTool[] = [];
|
|
810
|
+
|
|
811
|
+
try {
|
|
812
|
+
const schemaPatterns = [
|
|
813
|
+
'schema.graphql', 'schema.gql',
|
|
814
|
+
'graphql/schema.graphql', 'graphql/schema.gql',
|
|
815
|
+
'src/schema.graphql', 'src/schema.gql'
|
|
816
|
+
];
|
|
817
|
+
|
|
818
|
+
for (const pattern of schemaPatterns) {
|
|
819
|
+
try {
|
|
820
|
+
const content = await github.getFile(owner, repo, pattern);
|
|
821
|
+
if (content) {
|
|
822
|
+
const graphqlTools = await graphqlExtractor.extract(content, pattern);
|
|
823
|
+
tools.push(...graphqlTools);
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
826
|
+
} catch {
|
|
827
|
+
// File not found, continue
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
} catch {
|
|
831
|
+
// GraphQL extraction failed
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
return tools;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/**
|
|
838
|
+
* Introspect existing MCP server
|
|
839
|
+
*/
|
|
840
|
+
private async introspectMcpServer(
|
|
841
|
+
github: any,
|
|
842
|
+
mcpIntrospector: any,
|
|
843
|
+
owner: string,
|
|
844
|
+
repo: string
|
|
845
|
+
): Promise<ExtractedTool[]> {
|
|
846
|
+
const tools: ExtractedTool[] = [];
|
|
847
|
+
|
|
848
|
+
try {
|
|
849
|
+
const serverPatterns = [
|
|
850
|
+
'src/index.ts', 'src/server.ts', 'src/main.ts',
|
|
851
|
+
'index.ts', 'server.ts', 'main.ts',
|
|
852
|
+
'src/index.js', 'src/server.js'
|
|
853
|
+
];
|
|
854
|
+
|
|
855
|
+
for (const pattern of serverPatterns) {
|
|
856
|
+
try {
|
|
857
|
+
const content = await github.getFile(owner, repo, pattern);
|
|
858
|
+
if (content && (content.includes('server.tool') || content.includes('@mcp'))) {
|
|
859
|
+
const mcpTools = await mcpIntrospector.extract(content, pattern);
|
|
860
|
+
tools.push(...mcpTools);
|
|
861
|
+
}
|
|
862
|
+
} catch {
|
|
863
|
+
// File not found, continue
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
} catch {
|
|
867
|
+
// MCP introspection failed
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return tools;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Generate universal tools for any repository
|
|
875
|
+
*/
|
|
876
|
+
private generateUniversalTools(owner: string, repo: string): ExtractedTool[] {
|
|
877
|
+
return [
|
|
878
|
+
{
|
|
879
|
+
name: 'get_readme',
|
|
880
|
+
description: `Get the README file from ${owner}/${repo}`,
|
|
881
|
+
inputSchema: {
|
|
882
|
+
type: 'object',
|
|
883
|
+
properties: {},
|
|
884
|
+
required: []
|
|
885
|
+
},
|
|
886
|
+
source: { type: 'universal', file: '' }
|
|
887
|
+
},
|
|
888
|
+
{
|
|
889
|
+
name: 'list_files',
|
|
890
|
+
description: `List files in ${owner}/${repo}`,
|
|
891
|
+
inputSchema: {
|
|
892
|
+
type: 'object',
|
|
893
|
+
properties: {
|
|
894
|
+
path: {
|
|
895
|
+
type: 'string',
|
|
896
|
+
description: 'Directory path to list (default: root)',
|
|
897
|
+
default: ''
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
required: []
|
|
901
|
+
},
|
|
902
|
+
source: { type: 'universal', file: '' }
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
name: 'read_file',
|
|
906
|
+
description: `Read a file from ${owner}/${repo}`,
|
|
907
|
+
inputSchema: {
|
|
908
|
+
type: 'object',
|
|
909
|
+
properties: {
|
|
910
|
+
path: {
|
|
911
|
+
type: 'string',
|
|
912
|
+
description: 'Path to the file to read'
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
required: ['path']
|
|
916
|
+
},
|
|
917
|
+
source: { type: 'universal', file: '' }
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
name: 'search_code',
|
|
921
|
+
description: `Search for code patterns in ${owner}/${repo}`,
|
|
922
|
+
inputSchema: {
|
|
923
|
+
type: 'object',
|
|
924
|
+
properties: {
|
|
925
|
+
query: {
|
|
926
|
+
type: 'string',
|
|
927
|
+
description: 'Search query'
|
|
928
|
+
},
|
|
929
|
+
extension: {
|
|
930
|
+
type: 'string',
|
|
931
|
+
description: 'File extension to filter (e.g., "ts", "py")'
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
required: ['query']
|
|
935
|
+
},
|
|
936
|
+
source: { type: 'universal', file: '' }
|
|
937
|
+
}
|
|
938
|
+
];
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Deduplicate tools by name
|
|
943
|
+
*/
|
|
944
|
+
private deduplicateTools(tools: ExtractedTool[]): ExtractedTool[] {
|
|
945
|
+
const seen = new Map<string, ExtractedTool>();
|
|
946
|
+
|
|
947
|
+
for (const tool of tools) {
|
|
948
|
+
const existing = seen.get(tool.name);
|
|
949
|
+
if (!existing || (tool.confidence || 0) > (existing.confidence || 0)) {
|
|
950
|
+
seen.set(tool.name, tool);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
return Array.from(seen.values());
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Convenience function for streaming generation
|
|
960
|
+
*/
|
|
961
|
+
export async function* streamGenerate(
|
|
962
|
+
url: string,
|
|
963
|
+
options: StreamOptions = {}
|
|
964
|
+
): AsyncGenerator<StreamEvent, void, unknown> {
|
|
965
|
+
const generator = new StreamingGenerator(options);
|
|
966
|
+
yield* generator.generate(url);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* Collect all events from a streaming generation into an array
|
|
971
|
+
*/
|
|
972
|
+
export async function collectStreamEvents(
|
|
973
|
+
url: string,
|
|
974
|
+
options: StreamOptions = {}
|
|
975
|
+
): Promise<StreamEvent[]> {
|
|
976
|
+
const events: StreamEvent[] = [];
|
|
977
|
+
for await (const event of streamGenerate(url, options)) {
|
|
978
|
+
events.push(event);
|
|
979
|
+
}
|
|
980
|
+
return events;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Get just the final result from streaming generation
|
|
985
|
+
*/
|
|
986
|
+
export async function streamToResult(
|
|
987
|
+
url: string,
|
|
988
|
+
options: StreamOptions = {}
|
|
989
|
+
): Promise<GenerationResult | null> {
|
|
990
|
+
for await (const event of streamGenerate(url, options)) {
|
|
991
|
+
if (event.type === 'complete') {
|
|
992
|
+
return event.result;
|
|
993
|
+
}
|
|
994
|
+
if (event.type === 'error') {
|
|
995
|
+
throw event.error;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
return null;
|
|
999
|
+
}
|