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,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlaygroundToolTester Component - Test individual tools with dynamic input forms
|
|
3
|
+
* @copyright 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use client';
|
|
8
|
+
|
|
9
|
+
import { useState, useCallback, useMemo } from 'react';
|
|
10
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
11
|
+
import { Play, Loader2, AlertCircle, Check, Copy, ChevronDown, Clock } from 'lucide-react';
|
|
12
|
+
import { Button } from '@/components/ui/button';
|
|
13
|
+
import { Input } from '@/components/ui/input';
|
|
14
|
+
import type { Tool } from '@/types';
|
|
15
|
+
import { copyToClipboard } from '@/lib/utils';
|
|
16
|
+
import type { ExecuteToolResult } from '@/hooks/use-mcp-execution';
|
|
17
|
+
|
|
18
|
+
interface PlaygroundToolTesterProps {
|
|
19
|
+
tool: Tool;
|
|
20
|
+
/** Handler for executing the tool. If not provided, mock execution is used in demo mode */
|
|
21
|
+
onExecute?: (tool: Tool, params: Record<string, unknown>) => Promise<ExecuteToolResult | unknown>;
|
|
22
|
+
/** Whether we're in demo mode (not connected to real server) */
|
|
23
|
+
isDemoMode?: boolean;
|
|
24
|
+
/** Whether an execution is currently in progress */
|
|
25
|
+
isExecuting?: boolean;
|
|
26
|
+
className?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type InputFieldType = 'string' | 'number' | 'boolean' | 'array' | 'object';
|
|
30
|
+
|
|
31
|
+
interface SchemaProperty {
|
|
32
|
+
type: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
enum?: string[];
|
|
35
|
+
default?: unknown;
|
|
36
|
+
items?: { type: string };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default function PlaygroundToolTester({
|
|
40
|
+
tool,
|
|
41
|
+
onExecute,
|
|
42
|
+
isDemoMode = false,
|
|
43
|
+
isExecuting: externalIsExecuting,
|
|
44
|
+
className = '',
|
|
45
|
+
}: PlaygroundToolTesterProps) {
|
|
46
|
+
const [params, setParams] = useState<Record<string, unknown>>({});
|
|
47
|
+
const [internalIsExecuting, setInternalIsExecuting] = useState(false);
|
|
48
|
+
const [result, setResult] = useState<unknown>(null);
|
|
49
|
+
const [error, setError] = useState<string | null>(null);
|
|
50
|
+
const [executionTime, setExecutionTime] = useState<number | null>(null);
|
|
51
|
+
const [copied, setCopied] = useState(false);
|
|
52
|
+
const [showSchema, setShowSchema] = useState(false);
|
|
53
|
+
|
|
54
|
+
// Use external executing state if provided, otherwise use internal
|
|
55
|
+
const isExecuting = externalIsExecuting ?? internalIsExecuting;
|
|
56
|
+
|
|
57
|
+
// Get properties from input schema
|
|
58
|
+
const properties = useMemo(() => {
|
|
59
|
+
return tool.inputSchema?.properties || {};
|
|
60
|
+
}, [tool]);
|
|
61
|
+
|
|
62
|
+
const requiredFields = useMemo(() => {
|
|
63
|
+
return new Set(tool.inputSchema?.required || []);
|
|
64
|
+
}, [tool]);
|
|
65
|
+
|
|
66
|
+
// Initialize default values
|
|
67
|
+
const initializeParams = useCallback(() => {
|
|
68
|
+
const initial: Record<string, unknown> = {};
|
|
69
|
+
Object.entries(properties).forEach(([key, prop]) => {
|
|
70
|
+
const property = prop as SchemaProperty;
|
|
71
|
+
if (property.default !== undefined) {
|
|
72
|
+
initial[key] = property.default;
|
|
73
|
+
} else if (property.enum && property.enum.length > 0) {
|
|
74
|
+
initial[key] = property.enum[0];
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
setParams(initial);
|
|
78
|
+
}, [properties]);
|
|
79
|
+
|
|
80
|
+
const handleParamChange = useCallback((key: string, value: unknown) => {
|
|
81
|
+
setParams(prev => ({ ...prev, [key]: value }));
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
const handleExecute = useCallback(async () => {
|
|
85
|
+
setInternalIsExecuting(true);
|
|
86
|
+
setError(null);
|
|
87
|
+
setResult(null);
|
|
88
|
+
setExecutionTime(null);
|
|
89
|
+
|
|
90
|
+
const startTime = Date.now();
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
if (!onExecute || isDemoMode) {
|
|
94
|
+
// Mock execution for demo mode
|
|
95
|
+
await new Promise(resolve => setTimeout(resolve, 800 + Math.random() * 400));
|
|
96
|
+
|
|
97
|
+
const mockResult = {
|
|
98
|
+
success: true,
|
|
99
|
+
tool: tool.name,
|
|
100
|
+
params: params,
|
|
101
|
+
response: {
|
|
102
|
+
message: `[Demo] Successfully executed ${tool.name}`,
|
|
103
|
+
timestamp: new Date().toISOString(),
|
|
104
|
+
data: Object.keys(params).length > 0 ? params : { example: 'result' },
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
setResult(mockResult);
|
|
108
|
+
setExecutionTime(Date.now() - startTime);
|
|
109
|
+
} else {
|
|
110
|
+
// Real execution via the provided handler
|
|
111
|
+
const response = await onExecute(tool, params);
|
|
112
|
+
|
|
113
|
+
// Handle ExecuteToolResult type
|
|
114
|
+
if (response && typeof response === 'object' && 'success' in response) {
|
|
115
|
+
const execResult = response as ExecuteToolResult;
|
|
116
|
+
if (!execResult.success && execResult.error) {
|
|
117
|
+
setError(execResult.error);
|
|
118
|
+
} else {
|
|
119
|
+
setResult(execResult.result ?? execResult);
|
|
120
|
+
}
|
|
121
|
+
setExecutionTime(execResult.executionTime || (Date.now() - startTime));
|
|
122
|
+
} else {
|
|
123
|
+
setResult(response);
|
|
124
|
+
setExecutionTime(Date.now() - startTime);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
setError(err instanceof Error ? err.message : 'Execution failed');
|
|
129
|
+
setExecutionTime(Date.now() - startTime);
|
|
130
|
+
} finally {
|
|
131
|
+
setInternalIsExecuting(false);
|
|
132
|
+
}
|
|
133
|
+
}, [tool, params, onExecute, isDemoMode]);
|
|
134
|
+
|
|
135
|
+
const handleCopyResult = useCallback(async () => {
|
|
136
|
+
if (result) {
|
|
137
|
+
const success = await copyToClipboard(JSON.stringify(result, null, 2));
|
|
138
|
+
if (success) {
|
|
139
|
+
setCopied(true);
|
|
140
|
+
setTimeout(() => setCopied(false), 2000);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}, [result]);
|
|
144
|
+
|
|
145
|
+
const handleReset = useCallback(() => {
|
|
146
|
+
setParams({});
|
|
147
|
+
setResult(null);
|
|
148
|
+
setError(null);
|
|
149
|
+
setExecutionTime(null);
|
|
150
|
+
initializeParams();
|
|
151
|
+
}, [initializeParams]);
|
|
152
|
+
|
|
153
|
+
// Validate required fields
|
|
154
|
+
const isValid = useMemo(() => {
|
|
155
|
+
for (const field of requiredFields) {
|
|
156
|
+
const value = params[field];
|
|
157
|
+
if (value === undefined || value === '' || value === null) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
}, [params, requiredFields]);
|
|
163
|
+
|
|
164
|
+
const renderField = useCallback((key: string, prop: SchemaProperty) => {
|
|
165
|
+
const isRequired = requiredFields.has(key);
|
|
166
|
+
const value = params[key];
|
|
167
|
+
|
|
168
|
+
// Enum field - render as select
|
|
169
|
+
if (prop.enum && prop.enum.length > 0) {
|
|
170
|
+
return (
|
|
171
|
+
<div key={key} className="space-y-1.5">
|
|
172
|
+
<label className="text-sm font-medium text-neutral-300 flex items-center gap-1">
|
|
173
|
+
{key}
|
|
174
|
+
{isRequired && <span className="text-red-400">*</span>}
|
|
175
|
+
</label>
|
|
176
|
+
{prop.description && (
|
|
177
|
+
<p className="text-xs text-neutral-500">{prop.description}</p>
|
|
178
|
+
)}
|
|
179
|
+
<select
|
|
180
|
+
value={value as string || ''}
|
|
181
|
+
onChange={(e) => handleParamChange(key, e.target.value)}
|
|
182
|
+
className="w-full h-10 px-3 rounded-lg border border-neutral-700 bg-black text-white text-sm focus:outline-none focus:ring-2 focus:ring-white/10"
|
|
183
|
+
>
|
|
184
|
+
<option value="">Select...</option>
|
|
185
|
+
{prop.enum.map((opt) => (
|
|
186
|
+
<option key={opt} value={opt}>{opt}</option>
|
|
187
|
+
))}
|
|
188
|
+
</select>
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Boolean field - render as checkbox
|
|
194
|
+
if (prop.type === 'boolean') {
|
|
195
|
+
return (
|
|
196
|
+
<div key={key} className="space-y-1.5">
|
|
197
|
+
<label className="flex items-center gap-2 cursor-pointer">
|
|
198
|
+
<input
|
|
199
|
+
type="checkbox"
|
|
200
|
+
checked={value as boolean || false}
|
|
201
|
+
onChange={(e) => handleParamChange(key, e.target.checked)}
|
|
202
|
+
className="w-4 h-4 rounded border-neutral-700 bg-black text-white focus:ring-white/20"
|
|
203
|
+
/>
|
|
204
|
+
<span className="text-sm font-medium text-neutral-300">
|
|
205
|
+
{key}
|
|
206
|
+
{isRequired && <span className="text-red-400 ml-1">*</span>}
|
|
207
|
+
</span>
|
|
208
|
+
</label>
|
|
209
|
+
{prop.description && (
|
|
210
|
+
<p className="text-xs text-neutral-500 ml-6">{prop.description}</p>
|
|
211
|
+
)}
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Number field
|
|
217
|
+
if (prop.type === 'number' || prop.type === 'integer') {
|
|
218
|
+
return (
|
|
219
|
+
<div key={key} className="space-y-1.5">
|
|
220
|
+
<label className="text-sm font-medium text-neutral-300 flex items-center gap-1">
|
|
221
|
+
{key}
|
|
222
|
+
{isRequired && <span className="text-red-400">*</span>}
|
|
223
|
+
</label>
|
|
224
|
+
{prop.description && (
|
|
225
|
+
<p className="text-xs text-neutral-500">{prop.description}</p>
|
|
226
|
+
)}
|
|
227
|
+
<Input
|
|
228
|
+
type="number"
|
|
229
|
+
value={value as number || ''}
|
|
230
|
+
onChange={(e) => handleParamChange(key, e.target.value ? Number(e.target.value) : undefined)}
|
|
231
|
+
placeholder={`Enter ${key}...`}
|
|
232
|
+
/>
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Array or object field - render as textarea
|
|
238
|
+
if (prop.type === 'array' || prop.type === 'object') {
|
|
239
|
+
return (
|
|
240
|
+
<div key={key} className="space-y-1.5">
|
|
241
|
+
<label className="text-sm font-medium text-neutral-300 flex items-center gap-1">
|
|
242
|
+
{key}
|
|
243
|
+
{isRequired && <span className="text-red-400">*</span>}
|
|
244
|
+
<span className="text-xs text-neutral-500 font-normal">({prop.type})</span>
|
|
245
|
+
</label>
|
|
246
|
+
{prop.description && (
|
|
247
|
+
<p className="text-xs text-neutral-500">{prop.description}</p>
|
|
248
|
+
)}
|
|
249
|
+
<textarea
|
|
250
|
+
value={typeof value === 'object' ? JSON.stringify(value, null, 2) : (value as string || '')}
|
|
251
|
+
onChange={(e) => {
|
|
252
|
+
try {
|
|
253
|
+
const parsed = JSON.parse(e.target.value);
|
|
254
|
+
handleParamChange(key, parsed);
|
|
255
|
+
} catch {
|
|
256
|
+
handleParamChange(key, e.target.value);
|
|
257
|
+
}
|
|
258
|
+
}}
|
|
259
|
+
placeholder={`Enter ${prop.type === 'array' ? '["item1", "item2"]' : '{"key": "value"}'}`}
|
|
260
|
+
rows={3}
|
|
261
|
+
className="w-full px-3 py-2 rounded-lg border border-neutral-700 bg-black text-white text-sm font-mono focus:outline-none focus:ring-2 focus:ring-white/10 resize-y"
|
|
262
|
+
/>
|
|
263
|
+
</div>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Default string field
|
|
268
|
+
return (
|
|
269
|
+
<div key={key} className="space-y-1.5">
|
|
270
|
+
<label className="text-sm font-medium text-neutral-300 flex items-center gap-1">
|
|
271
|
+
{key}
|
|
272
|
+
{isRequired && <span className="text-red-400">*</span>}
|
|
273
|
+
</label>
|
|
274
|
+
{prop.description && (
|
|
275
|
+
<p className="text-xs text-neutral-500">{prop.description}</p>
|
|
276
|
+
)}
|
|
277
|
+
<Input
|
|
278
|
+
type="text"
|
|
279
|
+
value={value as string || ''}
|
|
280
|
+
onChange={(e) => handleParamChange(key, e.target.value)}
|
|
281
|
+
placeholder={`Enter ${key}...`}
|
|
282
|
+
/>
|
|
283
|
+
</div>
|
|
284
|
+
);
|
|
285
|
+
}, [params, requiredFields, handleParamChange]);
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
<div className={`rounded-xl border border-neutral-800 bg-neutral-900/50 overflow-hidden ${className}`}>
|
|
289
|
+
{/* Tool header */}
|
|
290
|
+
<div className="p-4 border-b border-neutral-800">
|
|
291
|
+
<div className="flex items-start justify-between gap-4">
|
|
292
|
+
<div>
|
|
293
|
+
<h3 className="font-semibold text-white">{tool.name}</h3>
|
|
294
|
+
<p className="text-sm text-neutral-400 mt-1">{tool.description}</p>
|
|
295
|
+
</div>
|
|
296
|
+
<button
|
|
297
|
+
onClick={() => setShowSchema(!showSchema)}
|
|
298
|
+
className="flex items-center gap-1 text-xs text-neutral-500 hover:text-white transition-colors"
|
|
299
|
+
>
|
|
300
|
+
Schema
|
|
301
|
+
<ChevronDown className={`w-3 h-3 transition-transform ${showSchema ? 'rotate-180' : ''}`} />
|
|
302
|
+
</button>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<AnimatePresence>
|
|
306
|
+
{showSchema && (
|
|
307
|
+
<motion.div
|
|
308
|
+
initial={{ height: 0, opacity: 0 }}
|
|
309
|
+
animate={{ height: 'auto', opacity: 1 }}
|
|
310
|
+
exit={{ height: 0, opacity: 0 }}
|
|
311
|
+
className="overflow-hidden"
|
|
312
|
+
>
|
|
313
|
+
<pre className="mt-3 p-3 bg-black/50 rounded-lg text-xs text-neutral-400 overflow-x-auto">
|
|
314
|
+
{JSON.stringify(tool.inputSchema, null, 2)}
|
|
315
|
+
</pre>
|
|
316
|
+
</motion.div>
|
|
317
|
+
)}
|
|
318
|
+
</AnimatePresence>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
{/* Input form */}
|
|
322
|
+
<div className="p-4 space-y-4">
|
|
323
|
+
{Object.keys(properties).length === 0 ? (
|
|
324
|
+
<p className="text-sm text-neutral-500 italic">This tool has no input parameters</p>
|
|
325
|
+
) : (
|
|
326
|
+
Object.entries(properties).map(([key, prop]) =>
|
|
327
|
+
renderField(key, prop as SchemaProperty)
|
|
328
|
+
)
|
|
329
|
+
)}
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
{/* Actions */}
|
|
333
|
+
<div className="p-4 border-t border-neutral-800 flex items-center gap-3">
|
|
334
|
+
<Button
|
|
335
|
+
onClick={handleExecute}
|
|
336
|
+
disabled={!isValid || isExecuting}
|
|
337
|
+
loading={isExecuting}
|
|
338
|
+
leftIcon={<Play className="w-4 h-4" />}
|
|
339
|
+
>
|
|
340
|
+
{isExecuting ? 'Executing...' : 'Execute'}
|
|
341
|
+
</Button>
|
|
342
|
+
<Button
|
|
343
|
+
variant="outline"
|
|
344
|
+
onClick={handleReset}
|
|
345
|
+
disabled={isExecuting}
|
|
346
|
+
>
|
|
347
|
+
Reset
|
|
348
|
+
</Button>
|
|
349
|
+
</div>
|
|
350
|
+
|
|
351
|
+
{/* Result/Error */}
|
|
352
|
+
<AnimatePresence>
|
|
353
|
+
{(result || error) && (
|
|
354
|
+
<motion.div
|
|
355
|
+
initial={{ height: 0, opacity: 0 }}
|
|
356
|
+
animate={{ height: 'auto', opacity: 1 }}
|
|
357
|
+
exit={{ height: 0, opacity: 0 }}
|
|
358
|
+
className="overflow-hidden"
|
|
359
|
+
>
|
|
360
|
+
<div className={`p-4 border-t ${error ? 'border-red-500/30 bg-red-500/10' : 'border-green-500/30 bg-green-500/10'}`}>
|
|
361
|
+
<div className="flex items-start justify-between gap-2 mb-2">
|
|
362
|
+
<div className="flex items-center gap-2 flex-wrap">
|
|
363
|
+
{error ? (
|
|
364
|
+
<>
|
|
365
|
+
<AlertCircle className="w-4 h-4 text-red-400" />
|
|
366
|
+
<span className="text-sm font-medium text-red-400">Error</span>
|
|
367
|
+
</>
|
|
368
|
+
) : (
|
|
369
|
+
<>
|
|
370
|
+
<Check className="w-4 h-4 text-green-400" />
|
|
371
|
+
<span className="text-sm font-medium text-green-400">Success</span>
|
|
372
|
+
</>
|
|
373
|
+
)}
|
|
374
|
+
{executionTime !== null && (
|
|
375
|
+
<span className="flex items-center gap-1 text-xs text-neutral-500">
|
|
376
|
+
<Clock className="w-3 h-3" />
|
|
377
|
+
{executionTime}ms
|
|
378
|
+
</span>
|
|
379
|
+
)}
|
|
380
|
+
{isDemoMode && (
|
|
381
|
+
<span className="px-1.5 py-0.5 text-xs rounded bg-yellow-500/20 text-yellow-400">
|
|
382
|
+
Demo
|
|
383
|
+
</span>
|
|
384
|
+
)}
|
|
385
|
+
</div>
|
|
386
|
+
{result !== null && result !== undefined && (
|
|
387
|
+
<Button
|
|
388
|
+
variant="ghost"
|
|
389
|
+
size="sm"
|
|
390
|
+
onClick={handleCopyResult}
|
|
391
|
+
leftIcon={copied ? <Check className="w-3 h-3" /> : <Copy className="w-3 h-3" />}
|
|
392
|
+
>
|
|
393
|
+
{copied ? 'Copied!' : 'Copy'}
|
|
394
|
+
</Button>
|
|
395
|
+
)}
|
|
396
|
+
</div>
|
|
397
|
+
<pre className="p-3 bg-black/30 rounded-lg text-xs overflow-x-auto">
|
|
398
|
+
{error ? (
|
|
399
|
+
<span className="text-red-300">{error}</span>
|
|
400
|
+
) : (
|
|
401
|
+
<span className="text-neutral-300">{JSON.stringify(result, null, 2)}</span>
|
|
402
|
+
)}
|
|
403
|
+
</pre>
|
|
404
|
+
</div>
|
|
405
|
+
</motion.div>
|
|
406
|
+
)}
|
|
407
|
+
</AnimatePresence>
|
|
408
|
+
</div>
|
|
409
|
+
);
|
|
410
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product Cards Component - Landing page feature cards
|
|
3
|
+
* @copyright 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use client';
|
|
8
|
+
|
|
9
|
+
import { motion } from 'framer-motion';
|
|
10
|
+
import { ArrowRight, Github, Terminal, FileJson, BookOpen, Layers, Play, Sparkles, Cloud } from 'lucide-react';
|
|
11
|
+
import Link from 'next/link';
|
|
12
|
+
|
|
13
|
+
const PRODUCTS = [
|
|
14
|
+
{
|
|
15
|
+
id: 'convert',
|
|
16
|
+
icon: Github,
|
|
17
|
+
title: 'Convert Repository',
|
|
18
|
+
description: 'Transform any GitHub repo into a fully functional MCP server. Extracts tools from README, code, and API specs.',
|
|
19
|
+
href: '/convert',
|
|
20
|
+
badge: 'Popular',
|
|
21
|
+
badgeColor: 'bg-green-500/20 text-green-300',
|
|
22
|
+
features: ['Auto-detection', 'TypeScript & Python', 'Ready configs'],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'playground',
|
|
26
|
+
icon: Play,
|
|
27
|
+
title: 'Interactive Playground',
|
|
28
|
+
description: 'Test your generated MCP tools in real-time. Execute tools, view results, and debug before deployment.',
|
|
29
|
+
href: '/playground',
|
|
30
|
+
badge: 'Try It',
|
|
31
|
+
badgeColor: 'bg-orange-500/20 text-orange-300',
|
|
32
|
+
features: ['Live testing', 'Real-time results', 'Debug mode'],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'dashboard',
|
|
36
|
+
icon: Cloud,
|
|
37
|
+
title: 'Cloud Deploy',
|
|
38
|
+
description: 'One-click deploy your MCP server to the cloud. Get instant endpoints, usage analytics, and API management.',
|
|
39
|
+
href: '/dashboard',
|
|
40
|
+
badge: 'New',
|
|
41
|
+
badgeColor: 'bg-pink-500/20 text-pink-300',
|
|
42
|
+
features: ['Instant deploy', 'Usage dashboard', 'API keys'],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'batch',
|
|
46
|
+
icon: Layers,
|
|
47
|
+
title: 'Batch Convert',
|
|
48
|
+
description: 'Convert multiple repositories at once. Perfect for teams or creating comprehensive tool collections.',
|
|
49
|
+
href: '/batch',
|
|
50
|
+
badge: 'New',
|
|
51
|
+
badgeColor: 'bg-purple-500/20 text-purple-300',
|
|
52
|
+
features: ['Multiple repos', 'Parallel processing', 'Bulk export'],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'vscode',
|
|
56
|
+
icon: Sparkles,
|
|
57
|
+
title: 'VS Code Extension',
|
|
58
|
+
description: 'Convert repos directly from VS Code. Right-click any GitHub URL or use the command palette.',
|
|
59
|
+
href: 'https://marketplace.visualstudio.com/items?itemName=nirholas.github-to-mcp',
|
|
60
|
+
badge: 'Extension',
|
|
61
|
+
badgeColor: 'bg-blue-500/20 text-blue-300',
|
|
62
|
+
features: ['One-click convert', 'Inline results', 'Auto-config'],
|
|
63
|
+
external: true,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'cli',
|
|
67
|
+
icon: Terminal,
|
|
68
|
+
title: 'CLI Tool',
|
|
69
|
+
description: 'Use the command-line tool for local development and CI/CD integration. Full control over the conversion process.',
|
|
70
|
+
href: 'https://github.com/nirholas/github-to-mcp#cli',
|
|
71
|
+
badge: 'Dev',
|
|
72
|
+
badgeColor: 'bg-cyan-500/20 text-cyan-300',
|
|
73
|
+
features: ['npm install', 'CI/CD ready', 'Batch processing'],
|
|
74
|
+
external: true,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'api',
|
|
78
|
+
icon: FileJson,
|
|
79
|
+
title: 'REST API',
|
|
80
|
+
description: 'Programmatic access to the conversion engine. Build custom integrations and automated workflows.',
|
|
81
|
+
href: 'https://github.com/nirholas/github-to-mcp#api',
|
|
82
|
+
badge: null,
|
|
83
|
+
badgeColor: '',
|
|
84
|
+
features: ['JSON responses', 'Rate limited', 'OpenAPI spec'],
|
|
85
|
+
external: true,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'docs',
|
|
89
|
+
icon: BookOpen,
|
|
90
|
+
title: 'Documentation',
|
|
91
|
+
description: 'Learn how to use github-to-mcp effectively. Guides, examples, and best practices.',
|
|
92
|
+
href: 'https://github.com/nirholas/github-to-mcp#readme',
|
|
93
|
+
badge: null,
|
|
94
|
+
badgeColor: '',
|
|
95
|
+
features: ['Getting started', 'Examples', 'FAQ'],
|
|
96
|
+
external: true,
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
export default function ProductCards() {
|
|
101
|
+
return (
|
|
102
|
+
<section className="py-16">
|
|
103
|
+
<div className="text-center mb-12">
|
|
104
|
+
<motion.h2
|
|
105
|
+
initial={{ opacity: 0, y: 20 }}
|
|
106
|
+
whileInView={{ opacity: 1, y: 0 }}
|
|
107
|
+
viewport={{ once: true }}
|
|
108
|
+
className="text-3xl md:text-4xl font-bold text-white mb-4"
|
|
109
|
+
>
|
|
110
|
+
All Tools & Features
|
|
111
|
+
</motion.h2>
|
|
112
|
+
<motion.p
|
|
113
|
+
initial={{ opacity: 0, y: 20 }}
|
|
114
|
+
whileInView={{ opacity: 1, y: 0 }}
|
|
115
|
+
viewport={{ once: true }}
|
|
116
|
+
transition={{ delay: 0.1 }}
|
|
117
|
+
className="text-lg text-neutral-400 max-w-2xl mx-auto"
|
|
118
|
+
>
|
|
119
|
+
Convert GitHub repos to MCP servers, test interactively, and deploy
|
|
120
|
+
</motion.p>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto">
|
|
124
|
+
{PRODUCTS.map((product, index) => {
|
|
125
|
+
const CardWrapper = product.external ? 'a' : Link;
|
|
126
|
+
const cardProps = product.external
|
|
127
|
+
? { href: product.href, target: '_blank', rel: 'noopener noreferrer' }
|
|
128
|
+
: { href: product.href };
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<motion.div
|
|
132
|
+
key={product.id}
|
|
133
|
+
initial={{ opacity: 0, y: 20 }}
|
|
134
|
+
whileInView={{ opacity: 1, y: 0 }}
|
|
135
|
+
viewport={{ once: true }}
|
|
136
|
+
transition={{ delay: index * 0.1 }}
|
|
137
|
+
>
|
|
138
|
+
<CardWrapper
|
|
139
|
+
{...cardProps}
|
|
140
|
+
className="group block h-full rounded-xl border border-neutral-800 p-6 bg-neutral-900/50 backdrop-blur-sm hover:border-neutral-600 hover:bg-neutral-900/80 transition-all"
|
|
141
|
+
>
|
|
142
|
+
<div className="flex items-start justify-between mb-4">
|
|
143
|
+
<div className="w-12 h-12 rounded-xl bg-white/5 border border-neutral-800 flex items-center justify-center group-hover:border-neutral-600 group-hover:bg-white/10 transition-all">
|
|
144
|
+
<product.icon className="w-6 h-6 text-white" />
|
|
145
|
+
</div>
|
|
146
|
+
{product.badge && (
|
|
147
|
+
<span className={`px-2.5 py-1 text-xs font-medium rounded-full ${product.badgeColor}`}>
|
|
148
|
+
{product.badge}
|
|
149
|
+
</span>
|
|
150
|
+
)}
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<h3 className="text-xl font-semibold text-white mb-2 group-hover:text-white transition-colors flex items-center gap-2">
|
|
154
|
+
{product.title}
|
|
155
|
+
<ArrowRight className="w-4 h-4 opacity-0 -translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all" />
|
|
156
|
+
</h3>
|
|
157
|
+
|
|
158
|
+
<p className="text-neutral-400 text-sm mb-4 leading-relaxed">
|
|
159
|
+
{product.description}
|
|
160
|
+
</p>
|
|
161
|
+
|
|
162
|
+
<div className="flex flex-wrap gap-2">
|
|
163
|
+
{product.features.map((feature) => (
|
|
164
|
+
<span
|
|
165
|
+
key={feature}
|
|
166
|
+
className="px-2 py-1 text-xs bg-white/5 border border-neutral-800 rounded text-neutral-400"
|
|
167
|
+
>
|
|
168
|
+
{feature}
|
|
169
|
+
</span>
|
|
170
|
+
))}
|
|
171
|
+
</div>
|
|
172
|
+
</CardWrapper>
|
|
173
|
+
</motion.div>
|
|
174
|
+
);
|
|
175
|
+
})}
|
|
176
|
+
</div>
|
|
177
|
+
</section>
|
|
178
|
+
);
|
|
179
|
+
}
|