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,822 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Command-line interface
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* GitHub to MCP CLI
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
import { generateFromGithub, generateFromGithubBatch, GenerationResult, ExtractedTool } from './index';
|
|
13
|
+
import ora from 'ora';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import * as fs from 'fs/promises';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
|
|
18
|
+
// Dynamic imports for optional dependencies
|
|
19
|
+
async function importPrompts() {
|
|
20
|
+
const prompts = await import('prompts');
|
|
21
|
+
return prompts.default;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function importChokidar() {
|
|
25
|
+
const chokidar = await import('chokidar');
|
|
26
|
+
return chokidar.default;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function importTable() {
|
|
30
|
+
const Table = await import('cli-table3');
|
|
31
|
+
return Table.default;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const program = new Command();
|
|
35
|
+
|
|
36
|
+
// Logging utilities
|
|
37
|
+
let isQuiet = false;
|
|
38
|
+
let isVerbose = false;
|
|
39
|
+
|
|
40
|
+
function log(message: string): void {
|
|
41
|
+
if (!isQuiet) {
|
|
42
|
+
console.log(message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function debug(message: string): void {
|
|
47
|
+
if (isVerbose && !isQuiet) {
|
|
48
|
+
console.log(chalk.gray(`[debug] ${message}`));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function errorLog(message: string): void {
|
|
53
|
+
console.error(chalk.red(message));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Display a summary box for conversion results
|
|
58
|
+
*/
|
|
59
|
+
async function displayResultBox(result: GenerationResult): Promise<void> {
|
|
60
|
+
const Table = await importTable();
|
|
61
|
+
|
|
62
|
+
const table = new Table({
|
|
63
|
+
chars: {
|
|
64
|
+
'top': '─', 'top-mid': '┬', 'top-left': '┌', 'top-right': '┐',
|
|
65
|
+
'bottom': '─', 'bottom-mid': '┴', 'bottom-left': '└', 'bottom-right': '┘',
|
|
66
|
+
'left': '│', 'left-mid': '├', 'mid': '─', 'mid-mid': '┼',
|
|
67
|
+
'right': '│', 'right-mid': '┤', 'middle': '│'
|
|
68
|
+
},
|
|
69
|
+
style: { head: ['cyan'], border: ['gray'] }
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const confidence = result.classification
|
|
73
|
+
? Math.round(result.classification.confidence * 100)
|
|
74
|
+
: 0;
|
|
75
|
+
|
|
76
|
+
table.push(
|
|
77
|
+
[{ colSpan: 2, content: chalk.bold.cyan(' GitHub to MCP Converter '), hAlign: 'center' }],
|
|
78
|
+
[chalk.gray('Repository'), chalk.white(result.name)],
|
|
79
|
+
[chalk.gray('Tools Found'), chalk.green(result.tools.length.toString())],
|
|
80
|
+
[chalk.gray('Confidence'), chalk.yellow(`${confidence}%`)],
|
|
81
|
+
[chalk.gray('Type'), chalk.blue(result.classification?.type || 'unknown')]
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
log(table.toString());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Interactive mode - prompts for all options
|
|
89
|
+
*/
|
|
90
|
+
async function runInteractive(): Promise<void> {
|
|
91
|
+
const prompts = await importPrompts();
|
|
92
|
+
|
|
93
|
+
log(chalk.bold.cyan('\n GitHub to MCP - Interactive Mode\n'));
|
|
94
|
+
|
|
95
|
+
const response = await prompts([
|
|
96
|
+
{
|
|
97
|
+
type: 'text',
|
|
98
|
+
name: 'url',
|
|
99
|
+
message: 'GitHub repository URL:',
|
|
100
|
+
validate: (value: string) => {
|
|
101
|
+
if (!value) return 'URL is required';
|
|
102
|
+
if (!value.match(/^https?:\/\/github\.com\/[\w-]+\/[\w.-]+/)) {
|
|
103
|
+
return 'Invalid GitHub URL format';
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
type: 'select',
|
|
110
|
+
name: 'output',
|
|
111
|
+
message: 'Output format:',
|
|
112
|
+
choices: [
|
|
113
|
+
{ title: 'TypeScript', value: 'typescript' },
|
|
114
|
+
{ title: 'Python', value: 'python' },
|
|
115
|
+
{ title: 'JSON', value: 'json' }
|
|
116
|
+
],
|
|
117
|
+
initial: 0
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
type: 'select',
|
|
121
|
+
name: 'platform',
|
|
122
|
+
message: 'Target platform:',
|
|
123
|
+
choices: [
|
|
124
|
+
{ title: 'Claude Desktop', value: 'claude' },
|
|
125
|
+
{ title: 'Cursor', value: 'cursor' },
|
|
126
|
+
{ title: 'None (just generate)', value: 'none' }
|
|
127
|
+
],
|
|
128
|
+
initial: 0
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
type: 'multiselect',
|
|
132
|
+
name: 'sources',
|
|
133
|
+
message: 'Sources to extract from:',
|
|
134
|
+
choices: [
|
|
135
|
+
{ title: 'README', value: 'readme', selected: true },
|
|
136
|
+
{ title: 'OpenAPI specs', value: 'openapi', selected: true },
|
|
137
|
+
{ title: 'Code analysis', value: 'code', selected: true }
|
|
138
|
+
],
|
|
139
|
+
min: 1
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
type: 'text',
|
|
143
|
+
name: 'outputDir',
|
|
144
|
+
message: 'Output directory:',
|
|
145
|
+
initial: './mcp-tools'
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
type: 'confirm',
|
|
149
|
+
name: 'proceed',
|
|
150
|
+
message: 'Proceed with conversion?',
|
|
151
|
+
initial: true
|
|
152
|
+
}
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
if (!response.proceed || !response.url) {
|
|
156
|
+
log(chalk.yellow('Cancelled.'));
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const spinner = ora('Converting repository...').start();
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const result = await generateFromGithub(response.url, {
|
|
164
|
+
sources: response.sources,
|
|
165
|
+
outputLanguage: response.output === 'python' ? 'python' : 'typescript'
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
spinner.succeed(chalk.green('Conversion complete!'));
|
|
169
|
+
|
|
170
|
+
await displayResultBox(result);
|
|
171
|
+
|
|
172
|
+
// Save based on output format
|
|
173
|
+
const outputDir = `${response.outputDir}/${result.name}`;
|
|
174
|
+
|
|
175
|
+
if (response.output === 'json') {
|
|
176
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
177
|
+
const jsonOutput = {
|
|
178
|
+
name: result.name,
|
|
179
|
+
tools: result.tools.map((t: ExtractedTool) => ({
|
|
180
|
+
name: t.name,
|
|
181
|
+
description: t.description,
|
|
182
|
+
inputSchema: t.inputSchema
|
|
183
|
+
})),
|
|
184
|
+
classification: result.classification
|
|
185
|
+
};
|
|
186
|
+
await fs.writeFile(
|
|
187
|
+
path.join(outputDir, 'tools.json'),
|
|
188
|
+
JSON.stringify(jsonOutput, null, 2)
|
|
189
|
+
);
|
|
190
|
+
log(chalk.green(`\n✓ Saved JSON to ${outputDir}/tools.json`));
|
|
191
|
+
} else {
|
|
192
|
+
await result.save(outputDir);
|
|
193
|
+
log(chalk.green(`\n✓ Saved to ${outputDir}`));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Show platform config if requested
|
|
197
|
+
if (response.platform !== 'none') {
|
|
198
|
+
log(chalk.cyan('\nMCP Configuration:'));
|
|
199
|
+
const config = generatePlatformConfig(result.name, response.platform);
|
|
200
|
+
log(chalk.gray(config));
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
spinner.fail(chalk.red('Conversion failed'));
|
|
204
|
+
errorLog(`Error: ${(error as Error).message}`);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Watch mode - monitor local directory for changes
|
|
211
|
+
*/
|
|
212
|
+
async function runWatchMode(localPath: string, options: Record<string, unknown>): Promise<void> {
|
|
213
|
+
const chokidar = await importChokidar();
|
|
214
|
+
|
|
215
|
+
log(chalk.cyan(`\n Watching ${localPath} for changes...\n`));
|
|
216
|
+
log(chalk.gray(' Press Ctrl+C to stop\n'));
|
|
217
|
+
|
|
218
|
+
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
219
|
+
const debounceMs = 500;
|
|
220
|
+
|
|
221
|
+
async function processChange(): Promise<void> {
|
|
222
|
+
console.clear();
|
|
223
|
+
log(chalk.cyan(`\n File change detected - Re-analyzing...\n`));
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
// For local paths, we'd need to implement local analysis
|
|
227
|
+
// For now, show a message about this feature
|
|
228
|
+
log(chalk.yellow(' Watch mode analyzes local directories for tool definitions.'));
|
|
229
|
+
log(chalk.gray(` Watching: ${path.resolve(localPath)}`));
|
|
230
|
+
log(chalk.gray(` Last update: ${new Date().toLocaleTimeString()}`));
|
|
231
|
+
|
|
232
|
+
// In a full implementation, this would:
|
|
233
|
+
// 1. Scan the local directory for OpenAPI specs, code files, etc.
|
|
234
|
+
// 2. Extract tool definitions
|
|
235
|
+
// 3. Regenerate the MCP server
|
|
236
|
+
|
|
237
|
+
const generateOptions = {
|
|
238
|
+
sources: (options.sources as string || 'readme,openapi,code').split(','),
|
|
239
|
+
outputLanguage: options.output === 'python' ? 'python' as const : 'typescript' as const
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
debug(`Options: ${JSON.stringify(generateOptions)}`);
|
|
243
|
+
|
|
244
|
+
} catch (error) {
|
|
245
|
+
errorLog(`Error processing changes: ${(error as Error).message}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const watcher = chokidar.watch(localPath, {
|
|
250
|
+
ignored: /(^|[/\\])\..|(node_modules|dist|\.git)/,
|
|
251
|
+
persistent: true,
|
|
252
|
+
ignoreInitial: false
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
watcher.on('all', (event, filePath) => {
|
|
256
|
+
debug(`${event}: ${filePath}`);
|
|
257
|
+
|
|
258
|
+
if (debounceTimer) {
|
|
259
|
+
clearTimeout(debounceTimer);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
debounceTimer = setTimeout(() => {
|
|
263
|
+
processChange();
|
|
264
|
+
}, debounceMs);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
watcher.on('error', (error) => {
|
|
268
|
+
errorLog(`Watcher error: ${error.message}`);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Keep the process running
|
|
272
|
+
process.on('SIGINT', () => {
|
|
273
|
+
log(chalk.yellow('\n Stopping watch mode...'));
|
|
274
|
+
watcher.close();
|
|
275
|
+
process.exit(0);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Validate command - check generated MCP server
|
|
281
|
+
*/
|
|
282
|
+
async function runValidate(filePath: string, options: Record<string, unknown>): Promise<void> {
|
|
283
|
+
const Table = await importTable();
|
|
284
|
+
|
|
285
|
+
log(chalk.cyan('\n Validating MCP Server\n'));
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
let content: string;
|
|
289
|
+
|
|
290
|
+
if (options.url) {
|
|
291
|
+
// Validate from URL - not supported yet
|
|
292
|
+
errorLog('URL validation not yet supported. Please provide a local file path.');
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
content = await fs.readFile(filePath, 'utf-8');
|
|
297
|
+
|
|
298
|
+
const issues: Array<{ severity: 'error' | 'warning'; message: string; line?: number }> = [];
|
|
299
|
+
const lines = content.split('\n');
|
|
300
|
+
|
|
301
|
+
// Detect file type
|
|
302
|
+
const isPython = filePath.endsWith('.py');
|
|
303
|
+
const isTypeScript = filePath.endsWith('.ts') || filePath.endsWith('.js');
|
|
304
|
+
|
|
305
|
+
if (isTypeScript) {
|
|
306
|
+
// Check for MCP SDK import
|
|
307
|
+
if (!content.includes('@modelcontextprotocol/sdk')) {
|
|
308
|
+
issues.push({ severity: 'error', message: 'Missing @modelcontextprotocol/sdk import' });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Check for Server instantiation
|
|
312
|
+
if (!content.includes('new Server(')) {
|
|
313
|
+
issues.push({ severity: 'error', message: 'Missing Server instantiation' });
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Check for ListToolsRequestSchema
|
|
317
|
+
if (!content.includes('ListToolsRequestSchema')) {
|
|
318
|
+
issues.push({ severity: 'warning', message: 'Missing ListToolsRequestSchema handler' });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Check for CallToolRequestSchema
|
|
322
|
+
if (!content.includes('CallToolRequestSchema')) {
|
|
323
|
+
issues.push({ severity: 'warning', message: 'Missing CallToolRequestSchema handler' });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Check for transport
|
|
327
|
+
if (!content.includes('StdioServerTransport') && !content.includes('transport')) {
|
|
328
|
+
issues.push({ severity: 'warning', message: 'Missing transport configuration' });
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Check for tool definitions
|
|
332
|
+
const toolMatches = content.match(/name:\s*['"`][\w_-]+['"`]/g);
|
|
333
|
+
if (toolMatches) {
|
|
334
|
+
debug(`Found ${toolMatches.length} tool definitions`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Check for syntax issues
|
|
338
|
+
lines.forEach((line, index) => {
|
|
339
|
+
if (line.includes('async function') && line.includes('{') && !line.includes('}')) {
|
|
340
|
+
// Check if next line has the closing brace or more code
|
|
341
|
+
const nextLine = lines[index + 1];
|
|
342
|
+
if (nextLine && nextLine.trim() === '') {
|
|
343
|
+
issues.push({ severity: 'warning', message: 'Possible incomplete function', line: index + 1 });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
} else if (isPython) {
|
|
348
|
+
// Check for mcp import
|
|
349
|
+
if (!content.includes('from mcp') && !content.includes('import mcp')) {
|
|
350
|
+
issues.push({ severity: 'error', message: 'Missing mcp module import' });
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Check for Server
|
|
354
|
+
if (!content.includes('Server(')) {
|
|
355
|
+
issues.push({ severity: 'error', message: 'Missing Server instantiation' });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Check for handlers
|
|
359
|
+
if (!content.includes('list_tools')) {
|
|
360
|
+
issues.push({ severity: 'warning', message: 'Missing list_tools handler' });
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (!content.includes('call_tool')) {
|
|
364
|
+
issues.push({ severity: 'warning', message: 'Missing call_tool handler' });
|
|
365
|
+
}
|
|
366
|
+
} else {
|
|
367
|
+
issues.push({ severity: 'warning', message: 'Unknown file type - limited validation' });
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Display results
|
|
371
|
+
if (issues.length === 0) {
|
|
372
|
+
log(chalk.green(' ✓ Validation passed! No issues found.\n'));
|
|
373
|
+
} else {
|
|
374
|
+
const table = new Table({
|
|
375
|
+
head: [chalk.white('Severity'), chalk.white('Line'), chalk.white('Message')],
|
|
376
|
+
style: { head: ['cyan'] }
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
for (const issue of issues) {
|
|
380
|
+
const icon = issue.severity === 'error' ? chalk.red('✗ ERROR') : chalk.yellow('⚠ WARN');
|
|
381
|
+
table.push([icon, issue.line?.toString() || '-', issue.message]);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
log(table.toString());
|
|
385
|
+
log('');
|
|
386
|
+
|
|
387
|
+
const errorCount = issues.filter(i => i.severity === 'error').length;
|
|
388
|
+
const warnCount = issues.filter(i => i.severity === 'warning').length;
|
|
389
|
+
|
|
390
|
+
if (errorCount > 0) {
|
|
391
|
+
log(chalk.red(` ${errorCount} error(s), ${warnCount} warning(s)\n`));
|
|
392
|
+
process.exit(1);
|
|
393
|
+
} else {
|
|
394
|
+
log(chalk.yellow(` ${warnCount} warning(s)\n`));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
} catch (error) {
|
|
398
|
+
errorLog(`Validation failed: ${(error as Error).message}`);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Generate platform-specific config
|
|
405
|
+
*/
|
|
406
|
+
function generatePlatformConfig(repoName: string, platform: string): string {
|
|
407
|
+
const serverName = repoName.includes('/') ? repoName.split('/')[1] : repoName;
|
|
408
|
+
|
|
409
|
+
switch (platform) {
|
|
410
|
+
case 'claude':
|
|
411
|
+
return JSON.stringify({
|
|
412
|
+
mcpServers: {
|
|
413
|
+
[serverName]: {
|
|
414
|
+
command: 'node',
|
|
415
|
+
args: [`${serverName}-mcp/index.js`]
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}, null, 2);
|
|
419
|
+
|
|
420
|
+
case 'cursor':
|
|
421
|
+
return JSON.stringify({
|
|
422
|
+
mcp: {
|
|
423
|
+
servers: {
|
|
424
|
+
[serverName]: {
|
|
425
|
+
command: 'node',
|
|
426
|
+
args: [`${serverName}-mcp/index.js`]
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}, null, 2);
|
|
431
|
+
|
|
432
|
+
default:
|
|
433
|
+
return JSON.stringify({
|
|
434
|
+
name: serverName,
|
|
435
|
+
command: 'node',
|
|
436
|
+
args: [`${serverName}-mcp/index.js`]
|
|
437
|
+
}, null, 2);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Get config file path for each platform
|
|
443
|
+
*/
|
|
444
|
+
function getConfigPath(platform: string): string {
|
|
445
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '~';
|
|
446
|
+
const isWindows = process.platform === 'win32';
|
|
447
|
+
const isMac = process.platform === 'darwin';
|
|
448
|
+
|
|
449
|
+
switch (platform) {
|
|
450
|
+
case 'claude':
|
|
451
|
+
if (isMac) {
|
|
452
|
+
return path.join(homeDir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
453
|
+
} else if (isWindows) {
|
|
454
|
+
return path.join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');
|
|
455
|
+
} else {
|
|
456
|
+
return path.join(homeDir, '.config', 'claude', 'claude_desktop_config.json');
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
case 'cursor':
|
|
460
|
+
return path.join(homeDir, '.cursor', 'mcp.json');
|
|
461
|
+
|
|
462
|
+
case 'vscode':
|
|
463
|
+
if (isMac) {
|
|
464
|
+
return path.join(homeDir, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'mcp_settings.json');
|
|
465
|
+
} else if (isWindows) {
|
|
466
|
+
return path.join(process.env.APPDATA || '', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'mcp_settings.json');
|
|
467
|
+
} else {
|
|
468
|
+
return path.join(homeDir, '.config', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'mcp_settings.json');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
default:
|
|
472
|
+
return '';
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Install command - One-click install MCP server to platforms
|
|
478
|
+
*/
|
|
479
|
+
async function runInstall(url: string, options: Record<string, unknown>): Promise<void> {
|
|
480
|
+
const Table = await importTable();
|
|
481
|
+
|
|
482
|
+
log(chalk.bold.cyan('\n GitHub to MCP - One-Click Install\n'));
|
|
483
|
+
|
|
484
|
+
// Determine which platforms to install to
|
|
485
|
+
const platforms: string[] = [];
|
|
486
|
+
if (options.all) {
|
|
487
|
+
platforms.push('claude', 'cursor', 'vscode');
|
|
488
|
+
} else {
|
|
489
|
+
if (options.claude) platforms.push('claude');
|
|
490
|
+
if (options.cursor) platforms.push('cursor');
|
|
491
|
+
if (options.vscode) platforms.push('vscode');
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Default to Claude if no platform specified
|
|
495
|
+
if (platforms.length === 0) {
|
|
496
|
+
platforms.push('claude');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const spinner = ora('Converting repository...').start();
|
|
500
|
+
|
|
501
|
+
try {
|
|
502
|
+
// Generate MCP server
|
|
503
|
+
const result = await generateFromGithub(url, {
|
|
504
|
+
sources: ['readme', 'openapi', 'code'],
|
|
505
|
+
outputLanguage: 'typescript'
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
spinner.succeed(chalk.green(`Generated ${result.tools.length} tools from ${result.name}`));
|
|
509
|
+
|
|
510
|
+
// Expand home directory
|
|
511
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '~';
|
|
512
|
+
const installDir = (options.dir as string).replace('~', homeDir);
|
|
513
|
+
const serverDir = path.join(installDir, `${result.name}-mcp`);
|
|
514
|
+
|
|
515
|
+
if (options.dryRun) {
|
|
516
|
+
log(chalk.yellow('\n Dry run - no changes will be made\n'));
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Save the server code
|
|
520
|
+
if (!options.dryRun) {
|
|
521
|
+
await fs.mkdir(serverDir, { recursive: true });
|
|
522
|
+
const code = result.generate();
|
|
523
|
+
await fs.writeFile(path.join(serverDir, 'index.ts'), code);
|
|
524
|
+
|
|
525
|
+
// Create package.json
|
|
526
|
+
const packageJson = {
|
|
527
|
+
name: `${result.name}-mcp`,
|
|
528
|
+
version: '1.0.0',
|
|
529
|
+
type: 'module',
|
|
530
|
+
scripts: {
|
|
531
|
+
start: 'tsx index.ts'
|
|
532
|
+
},
|
|
533
|
+
dependencies: {
|
|
534
|
+
'@modelcontextprotocol/sdk': '^1.0.0',
|
|
535
|
+
'tsx': '^4.0.0'
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
await fs.writeFile(
|
|
539
|
+
path.join(serverDir, 'package.json'),
|
|
540
|
+
JSON.stringify(packageJson, null, 2)
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
log(chalk.green(` ✓ Saved server to ${serverDir}`));
|
|
544
|
+
} else {
|
|
545
|
+
log(chalk.gray(` Would save server to ${serverDir}`));
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Install to each platform
|
|
549
|
+
log(chalk.cyan('\n Installing to platforms:\n'));
|
|
550
|
+
|
|
551
|
+
for (const platform of platforms) {
|
|
552
|
+
const configPath = getConfigPath(platform);
|
|
553
|
+
const platformName = platform === 'claude' ? 'Claude Desktop' :
|
|
554
|
+
platform === 'cursor' ? 'Cursor' : 'VS Code';
|
|
555
|
+
|
|
556
|
+
try {
|
|
557
|
+
// Read existing config or create new
|
|
558
|
+
let config: Record<string, unknown> = {};
|
|
559
|
+
try {
|
|
560
|
+
const existingConfig = await fs.readFile(configPath, 'utf-8');
|
|
561
|
+
config = JSON.parse(existingConfig);
|
|
562
|
+
} catch {
|
|
563
|
+
// Config doesn't exist, will create new
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Add our server to the config
|
|
567
|
+
const serverName = result.name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
568
|
+
|
|
569
|
+
if (platform === 'cursor') {
|
|
570
|
+
if (!config.mcp) config.mcp = { servers: {} };
|
|
571
|
+
if (!(config.mcp as Record<string, unknown>).servers) {
|
|
572
|
+
(config.mcp as Record<string, unknown>).servers = {};
|
|
573
|
+
}
|
|
574
|
+
((config.mcp as Record<string, unknown>).servers as Record<string, unknown>)[serverName] = {
|
|
575
|
+
command: 'npx',
|
|
576
|
+
args: ['tsx', path.join(serverDir, 'index.ts')]
|
|
577
|
+
};
|
|
578
|
+
} else {
|
|
579
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
580
|
+
(config.mcpServers as Record<string, unknown>)[serverName] = {
|
|
581
|
+
command: 'npx',
|
|
582
|
+
args: ['tsx', path.join(serverDir, 'index.ts')]
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (!options.dryRun) {
|
|
587
|
+
// Ensure config directory exists
|
|
588
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
589
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
590
|
+
log(chalk.green(` ✓ ${platformName}: Added to ${configPath}`));
|
|
591
|
+
} else {
|
|
592
|
+
log(chalk.gray(` Would add to ${platformName}: ${configPath}`));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
} catch (error) {
|
|
596
|
+
log(chalk.yellow(` ⚠ ${platformName}: ${(error as Error).message}`));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Summary
|
|
601
|
+
log(chalk.cyan('\n Installation Summary:\n'));
|
|
602
|
+
|
|
603
|
+
const table = new Table({
|
|
604
|
+
head: [chalk.white('Item'), chalk.white('Value')],
|
|
605
|
+
style: { head: ['cyan'] }
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
table.push(
|
|
609
|
+
['Server Name', result.name],
|
|
610
|
+
['Tools', result.tools.length.toString()],
|
|
611
|
+
['Location', serverDir],
|
|
612
|
+
['Platforms', platforms.join(', ')]
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
log(table.toString());
|
|
616
|
+
|
|
617
|
+
// Next steps
|
|
618
|
+
log(chalk.cyan('\n Next Steps:\n'));
|
|
619
|
+
log(chalk.white(' 1. Restart your MCP client (Claude Desktop, Cursor, etc.)'));
|
|
620
|
+
log(chalk.white(' 2. The new tools will be available automatically'));
|
|
621
|
+
log(chalk.white(` 3. Run ${chalk.yellow(`cd ${serverDir} && npm install`)} to install dependencies`));
|
|
622
|
+
log('');
|
|
623
|
+
|
|
624
|
+
if (!options.dryRun) {
|
|
625
|
+
log(chalk.green.bold(' ✓ Installation complete!\n'));
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
} catch (error) {
|
|
629
|
+
spinner.fail(chalk.red('Installation failed'));
|
|
630
|
+
errorLog(`Error: ${(error as Error).message}`);
|
|
631
|
+
process.exit(1);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Main program setup
|
|
636
|
+
program
|
|
637
|
+
.name('github-to-mcp')
|
|
638
|
+
.description('Convert GitHub repositories to MCP tools')
|
|
639
|
+
.version('1.0.0');
|
|
640
|
+
|
|
641
|
+
// Global options
|
|
642
|
+
program
|
|
643
|
+
.option('-q, --quiet', 'Minimal output')
|
|
644
|
+
.option('-v, --verbose', 'Debug logging')
|
|
645
|
+
.option('--no-cache', 'Skip cache');
|
|
646
|
+
|
|
647
|
+
// Default convert command
|
|
648
|
+
program
|
|
649
|
+
.argument('[urls...]', 'GitHub repository URLs')
|
|
650
|
+
.option('-o, --output <format>', 'Output format (typescript, json, python)', 'typescript')
|
|
651
|
+
.option('-d, --dir <path>', 'Output directory', './mcp-tools')
|
|
652
|
+
.option('-f, --file <path>', 'File containing list of GitHub URLs')
|
|
653
|
+
.option('-i, --interactive', 'Interactive mode')
|
|
654
|
+
.option('-w, --watch', 'Watch mode for local directory')
|
|
655
|
+
.option('--follow-docs', 'Follow documentation links', false)
|
|
656
|
+
.option('--depth <number>', 'Documentation scraping depth', '2')
|
|
657
|
+
.option('--sources <types>', 'Sources to extract from (comma-separated)', 'readme,openapi,code')
|
|
658
|
+
.option('--format <format>', 'Output format (deprecated, use -o)', 'typescript')
|
|
659
|
+
.option('--token <token>', 'GitHub token for private repos')
|
|
660
|
+
.option('--config <file>', 'Use config file')
|
|
661
|
+
.action(async (urls, options) => {
|
|
662
|
+
// Set logging modes
|
|
663
|
+
isQuiet = options.quiet || false;
|
|
664
|
+
isVerbose = options.verbose || false;
|
|
665
|
+
|
|
666
|
+
debug(`Options: ${JSON.stringify(options)}`);
|
|
667
|
+
|
|
668
|
+
// Interactive mode
|
|
669
|
+
if (options.interactive) {
|
|
670
|
+
await runInteractive();
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Watch mode
|
|
675
|
+
if (options.watch && urls.length > 0) {
|
|
676
|
+
await runWatchMode(urls[0], options);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
try {
|
|
681
|
+
let repoUrls = urls;
|
|
682
|
+
|
|
683
|
+
// Load URLs from file if specified
|
|
684
|
+
if (options.file) {
|
|
685
|
+
const content = await fs.readFile(options.file, 'utf-8');
|
|
686
|
+
const fileUrls = content.split('\n').filter((line: string) => line.trim());
|
|
687
|
+
repoUrls = [...repoUrls, ...fileUrls];
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Load config file if specified
|
|
691
|
+
if (options.config) {
|
|
692
|
+
try {
|
|
693
|
+
const configContent = await fs.readFile(options.config, 'utf-8');
|
|
694
|
+
const config = JSON.parse(configContent);
|
|
695
|
+
debug(`Loaded config: ${JSON.stringify(config)}`);
|
|
696
|
+
// Merge config with options (command line takes precedence)
|
|
697
|
+
Object.assign(options, { ...config, ...options });
|
|
698
|
+
} catch (e) {
|
|
699
|
+
errorLog(`Failed to load config file: ${(e as Error).message}`);
|
|
700
|
+
process.exit(1);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (repoUrls.length === 0) {
|
|
705
|
+
errorLog('No GitHub URLs provided. Use -i for interactive mode or provide URLs.');
|
|
706
|
+
program.help();
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const spinner = ora('Generating MCP tools...').start();
|
|
711
|
+
|
|
712
|
+
const outputFormat = options.output || options.format || 'typescript';
|
|
713
|
+
const generateOptions = {
|
|
714
|
+
sources: options.sources.split(','),
|
|
715
|
+
followDocs: options.followDocs,
|
|
716
|
+
depth: parseInt(options.depth),
|
|
717
|
+
outputLanguage: outputFormat === 'python' ? 'python' as const : 'typescript' as const,
|
|
718
|
+
githubToken: options.token,
|
|
719
|
+
cache: options.cache !== false
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
debug(`Generate options: ${JSON.stringify(generateOptions)}`);
|
|
723
|
+
|
|
724
|
+
if (repoUrls.length === 1) {
|
|
725
|
+
// Single repo
|
|
726
|
+
const result = await generateFromGithub(repoUrls[0], generateOptions);
|
|
727
|
+
|
|
728
|
+
spinner.succeed(chalk.green(`Generated ${result.tools.length} tools from ${result.name}`));
|
|
729
|
+
|
|
730
|
+
if (!isQuiet) {
|
|
731
|
+
await displayResultBox(result);
|
|
732
|
+
|
|
733
|
+
// Show breakdown
|
|
734
|
+
log('\nSources:');
|
|
735
|
+
result.sources.forEach(source => {
|
|
736
|
+
log(chalk.blue(` ${source.type}: ${source.count} tools`));
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Save based on output format
|
|
741
|
+
const outputDir = `${options.dir}/${result.name}`;
|
|
742
|
+
|
|
743
|
+
if (outputFormat === 'json') {
|
|
744
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
745
|
+
const jsonOutput = {
|
|
746
|
+
name: result.name,
|
|
747
|
+
tools: result.tools.map((t: ExtractedTool) => ({
|
|
748
|
+
name: t.name,
|
|
749
|
+
description: t.description,
|
|
750
|
+
inputSchema: t.inputSchema
|
|
751
|
+
})),
|
|
752
|
+
classification: result.classification,
|
|
753
|
+
sources: result.sources
|
|
754
|
+
};
|
|
755
|
+
await fs.writeFile(
|
|
756
|
+
path.join(outputDir, 'tools.json'),
|
|
757
|
+
JSON.stringify(jsonOutput, null, 2)
|
|
758
|
+
);
|
|
759
|
+
log(chalk.green(`\n✓ Saved JSON to ${outputDir}/tools.json`));
|
|
760
|
+
} else if (outputFormat === 'python') {
|
|
761
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
762
|
+
const pythonCode = result.generatePython?.() ?? result.generate();
|
|
763
|
+
await fs.writeFile(path.join(outputDir, 'server.py'), pythonCode);
|
|
764
|
+
log(chalk.green(`\n✓ Saved Python to ${outputDir}/server.py`));
|
|
765
|
+
} else {
|
|
766
|
+
await result.save(outputDir);
|
|
767
|
+
log(chalk.green(`\n✓ Saved to ${outputDir}`));
|
|
768
|
+
}
|
|
769
|
+
} else {
|
|
770
|
+
// Batch
|
|
771
|
+
const results = await generateFromGithubBatch(repoUrls, generateOptions);
|
|
772
|
+
|
|
773
|
+
spinner.succeed(chalk.green(`Generated tools from ${results.length} repositories`));
|
|
774
|
+
|
|
775
|
+
if (!isQuiet) {
|
|
776
|
+
log('\nResults:');
|
|
777
|
+
for (const result of results) {
|
|
778
|
+
const typeLabel = result.classification ? ` [${result.classification.type}]` : '';
|
|
779
|
+
log(chalk.blue(` ${result.name}${typeLabel}: ${result.tools.length} tools`));
|
|
780
|
+
|
|
781
|
+
const outputDir = `${options.dir}/${result.name}`;
|
|
782
|
+
await result.save(outputDir);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
log(chalk.green(`\n✓ Saved all to ${options.dir}/`));
|
|
787
|
+
}
|
|
788
|
+
} catch (error) {
|
|
789
|
+
errorLog(`Error: ${(error as Error).message}`);
|
|
790
|
+
process.exit(1);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
// Validate subcommand
|
|
795
|
+
program
|
|
796
|
+
.command('validate <file>')
|
|
797
|
+
.description('Validate a generated MCP server file')
|
|
798
|
+
.option('--url <url>', 'Validate from URL instead of file')
|
|
799
|
+
.action(async (file, options) => {
|
|
800
|
+
isQuiet = program.opts().quiet || false;
|
|
801
|
+
isVerbose = program.opts().verbose || false;
|
|
802
|
+
await runValidate(file, options);
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
// Install subcommand - One-click install to MCP clients
|
|
806
|
+
program
|
|
807
|
+
.command('install <url>')
|
|
808
|
+
.description('Install MCP server directly to Claude Desktop, Cursor, or other MCP clients')
|
|
809
|
+
.option('--claude', 'Install to Claude Desktop')
|
|
810
|
+
.option('--cursor', 'Install to Cursor')
|
|
811
|
+
.option('--vscode', 'Install to VS Code (Cline/Roo)')
|
|
812
|
+
.option('--all', 'Install to all supported platforms')
|
|
813
|
+
.option('-d, --dir <path>', 'Installation directory', '~/.mcp-servers')
|
|
814
|
+
.option('--dry-run', 'Show what would be installed without making changes')
|
|
815
|
+
.action(async (url, options) => {
|
|
816
|
+
isQuiet = program.opts().quiet || false;
|
|
817
|
+
isVerbose = program.opts().verbose || false;
|
|
818
|
+
await runInstall(url, options);
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
program.parse();
|
|
822
|
+
|