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,924 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub to MCP Converter - Main Page
|
|
3
|
+
* @copyright 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use client';
|
|
8
|
+
|
|
9
|
+
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
10
|
+
import {
|
|
11
|
+
Github,
|
|
12
|
+
Zap,
|
|
13
|
+
Download,
|
|
14
|
+
Copy,
|
|
15
|
+
Check,
|
|
16
|
+
Loader2,
|
|
17
|
+
Server,
|
|
18
|
+
Box,
|
|
19
|
+
ExternalLink,
|
|
20
|
+
Star,
|
|
21
|
+
Clock,
|
|
22
|
+
FileCode,
|
|
23
|
+
ChevronDown,
|
|
24
|
+
ChevronRight,
|
|
25
|
+
Trash2,
|
|
26
|
+
History,
|
|
27
|
+
Settings,
|
|
28
|
+
AlertCircle,
|
|
29
|
+
CheckCircle,
|
|
30
|
+
Search,
|
|
31
|
+
X,
|
|
32
|
+
ArrowRight,
|
|
33
|
+
ArrowLeft,
|
|
34
|
+
Code2,
|
|
35
|
+
FileJson,
|
|
36
|
+
Terminal,
|
|
37
|
+
Cpu,
|
|
38
|
+
Package,
|
|
39
|
+
FolderOpen,
|
|
40
|
+
} from 'lucide-react';
|
|
41
|
+
import { Button } from '@/components/ui/button';
|
|
42
|
+
import { Input } from '@/components/ui/input';
|
|
43
|
+
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
|
44
|
+
import { Badge } from '@/components/ui/badge';
|
|
45
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
|
46
|
+
import { CodeBlock } from '@/components/ui/code-block';
|
|
47
|
+
import { cn } from '@/lib/utils';
|
|
48
|
+
import { copyToClipboard, downloadAsFile, formatRelativeTime, isValidGitHubUrl, parseGitHubUrl, truncate } from '@/lib/utils';
|
|
49
|
+
import { EXAMPLE_REPOS, CLASSIFICATION_LABELS, SOURCE_TYPE_LABELS, PLATFORMS, APP_NAME, GITHUB_URL } from '@/lib/constants';
|
|
50
|
+
import { useLocalStorage } from '@/hooks/use-local-storage';
|
|
51
|
+
import type { ConversionResult, Tool, ConversionHistory, ApiError, ConversionStatus } from '@/types';
|
|
52
|
+
|
|
53
|
+
// Loading steps
|
|
54
|
+
const LOADING_STEPS = [
|
|
55
|
+
{ id: 'fetch', label: 'Fetching repository', icon: Github },
|
|
56
|
+
{ id: 'analyze', label: 'Analyzing codebase', icon: Code2 },
|
|
57
|
+
{ id: 'extract', label: 'Extracting tools', icon: Package },
|
|
58
|
+
{ id: 'generate', label: 'Generating server', icon: Terminal },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// Features for landing
|
|
62
|
+
const FEATURES = [
|
|
63
|
+
{
|
|
64
|
+
icon: Code2,
|
|
65
|
+
title: 'Smart Extraction',
|
|
66
|
+
description: 'Extracts tools from README, source code, OpenAPI specs, and GraphQL schemas',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
icon: FileJson,
|
|
70
|
+
title: 'Ready-to-Use Config',
|
|
71
|
+
description: 'Generate configs for Claude Desktop, Cursor, and other MCP clients',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
icon: Terminal,
|
|
75
|
+
title: 'TypeScript & Python',
|
|
76
|
+
description: 'Get production-ready MCP server code in both languages',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
icon: Cpu,
|
|
80
|
+
title: 'Auto Classification',
|
|
81
|
+
description: 'Detects repo type: API SDK, CLI tool, library, MCP server',
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
export default function HomePage() {
|
|
86
|
+
// State
|
|
87
|
+
const [url, setUrl] = useState('');
|
|
88
|
+
const [status, setStatus] = useState<ConversionStatus>('idle');
|
|
89
|
+
const [result, setResult] = useState<ConversionResult | null>(null);
|
|
90
|
+
const [error, setError] = useState<ApiError | null>(null);
|
|
91
|
+
const [copiedItem, setCopiedItem] = useState<string | null>(null);
|
|
92
|
+
const [expandedTools, setExpandedTools] = useState<Set<string>>(new Set());
|
|
93
|
+
const [showHistory, setShowHistory] = useState(false);
|
|
94
|
+
const [activeConfigTab, setActiveConfigTab] = useState('claude');
|
|
95
|
+
const [history, setHistory] = useLocalStorage<ConversionHistory[]>('conversion-history', []);
|
|
96
|
+
const [searchFilter, setSearchFilter] = useState('');
|
|
97
|
+
const [loadingStep, setLoadingStep] = useState(0);
|
|
98
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
99
|
+
|
|
100
|
+
// Loading step animation
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (status === 'loading') {
|
|
103
|
+
setLoadingStep(0);
|
|
104
|
+
const interval = setInterval(() => {
|
|
105
|
+
setLoadingStep(prev => (prev < LOADING_STEPS.length - 1 ? prev + 1 : prev));
|
|
106
|
+
}, 1500);
|
|
107
|
+
return () => clearInterval(interval);
|
|
108
|
+
}
|
|
109
|
+
}, [status]);
|
|
110
|
+
|
|
111
|
+
// Convert handler
|
|
112
|
+
const handleConvert = useCallback(async () => {
|
|
113
|
+
if (!url.trim()) return;
|
|
114
|
+
|
|
115
|
+
if (!isValidGitHubUrl(url.trim())) {
|
|
116
|
+
setError({
|
|
117
|
+
error: 'Invalid GitHub URL',
|
|
118
|
+
code: 'INVALID_URL',
|
|
119
|
+
details: 'Please enter a valid GitHub repository URL (e.g., https://github.com/owner/repo)',
|
|
120
|
+
});
|
|
121
|
+
setStatus('error');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
setStatus('loading');
|
|
126
|
+
setError(null);
|
|
127
|
+
setResult(null);
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const response = await fetch('/api/convert', {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
headers: { 'Content-Type': 'application/json' },
|
|
133
|
+
body: JSON.stringify({ url: url.trim() }),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const data = await response.json();
|
|
137
|
+
|
|
138
|
+
if (!response.ok) {
|
|
139
|
+
setError({
|
|
140
|
+
error: data.error || 'Conversion failed',
|
|
141
|
+
code: data.code || 'UNKNOWN_ERROR',
|
|
142
|
+
details: data.details,
|
|
143
|
+
retryAfter: response.status === 429 ? parseInt(response.headers.get('Retry-After') || '60') : undefined,
|
|
144
|
+
});
|
|
145
|
+
setStatus('error');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
setResult(data);
|
|
150
|
+
setStatus('success');
|
|
151
|
+
|
|
152
|
+
// Add to history
|
|
153
|
+
const parsed = parseGitHubUrl(url.trim());
|
|
154
|
+
if (parsed) {
|
|
155
|
+
const entry: ConversionHistory = {
|
|
156
|
+
id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
157
|
+
url: url.trim(),
|
|
158
|
+
name: data.name,
|
|
159
|
+
toolCount: data.tools?.length || 0,
|
|
160
|
+
classification: data.classification?.type || 'unknown',
|
|
161
|
+
convertedAt: new Date().toISOString(),
|
|
162
|
+
};
|
|
163
|
+
setHistory(prev => [entry, ...prev.filter(h => h.url !== url.trim())].slice(0, 50));
|
|
164
|
+
}
|
|
165
|
+
} catch (err) {
|
|
166
|
+
setError({
|
|
167
|
+
error: err instanceof Error ? err.message : 'Network error',
|
|
168
|
+
code: 'NETWORK_ERROR',
|
|
169
|
+
details: 'Failed to connect to the server. Please check your internet connection.',
|
|
170
|
+
});
|
|
171
|
+
setStatus('error');
|
|
172
|
+
}
|
|
173
|
+
}, [url, setHistory]);
|
|
174
|
+
|
|
175
|
+
// Copy handler
|
|
176
|
+
const handleCopy = useCallback(async (text: string, id: string) => {
|
|
177
|
+
const success = await copyToClipboard(text);
|
|
178
|
+
if (success) {
|
|
179
|
+
setCopiedItem(id);
|
|
180
|
+
setTimeout(() => setCopiedItem(null), 2000);
|
|
181
|
+
}
|
|
182
|
+
}, []);
|
|
183
|
+
|
|
184
|
+
// Download handler
|
|
185
|
+
const handleDownload = useCallback(() => {
|
|
186
|
+
if (!result) return;
|
|
187
|
+
downloadAsFile(result.code, `${result.name}-mcp-server.ts`, 'text/typescript');
|
|
188
|
+
}, [result]);
|
|
189
|
+
|
|
190
|
+
// Toggle tool expansion
|
|
191
|
+
const toggleToolExpansion = useCallback((toolName: string) => {
|
|
192
|
+
setExpandedTools(prev => {
|
|
193
|
+
const next = new Set(prev);
|
|
194
|
+
if (next.has(toolName)) {
|
|
195
|
+
next.delete(toolName);
|
|
196
|
+
} else {
|
|
197
|
+
next.add(toolName);
|
|
198
|
+
}
|
|
199
|
+
return next;
|
|
200
|
+
});
|
|
201
|
+
}, []);
|
|
202
|
+
|
|
203
|
+
// Filter tools
|
|
204
|
+
const filteredTools = result?.tools.filter(tool =>
|
|
205
|
+
!searchFilter ||
|
|
206
|
+
tool.name.toLowerCase().includes(searchFilter.toLowerCase()) ||
|
|
207
|
+
tool.description.toLowerCase().includes(searchFilter.toLowerCase())
|
|
208
|
+
) || [];
|
|
209
|
+
|
|
210
|
+
// Classification info
|
|
211
|
+
const classificationInfo = result?.classification
|
|
212
|
+
? CLASSIFICATION_LABELS[result.classification.type] || CLASSIFICATION_LABELS.unknown
|
|
213
|
+
: null;
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<div className="min-h-screen bg-background text-foreground dark">
|
|
217
|
+
{/* Background */}
|
|
218
|
+
<div className="fixed inset-0 -z-10 bg-dots opacity-30" />
|
|
219
|
+
|
|
220
|
+
{/* Header */}
|
|
221
|
+
<header className="sticky top-0 z-50 border-b border-border bg-background/80 backdrop-blur-xl">
|
|
222
|
+
<div className="mx-auto max-w-5xl px-4 sm:px-6">
|
|
223
|
+
<div className="flex h-16 items-center justify-between">
|
|
224
|
+
{/* Logo */}
|
|
225
|
+
<a href="/" className="flex items-center gap-3">
|
|
226
|
+
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-foreground">
|
|
227
|
+
<Zap className="h-5 w-5 text-background" />
|
|
228
|
+
</div>
|
|
229
|
+
<span className="text-lg font-bold">{APP_NAME}</span>
|
|
230
|
+
</a>
|
|
231
|
+
|
|
232
|
+
{/* Actions */}
|
|
233
|
+
<div className="flex items-center gap-2">
|
|
234
|
+
<Button
|
|
235
|
+
variant="ghost"
|
|
236
|
+
size="icon-sm"
|
|
237
|
+
onClick={() => setShowHistory(!showHistory)}
|
|
238
|
+
className="relative"
|
|
239
|
+
>
|
|
240
|
+
<History className="h-4 w-4" />
|
|
241
|
+
{history.length > 0 && (
|
|
242
|
+
<span className="absolute -right-0.5 -top-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-foreground text-[10px] font-medium text-background">
|
|
243
|
+
{history.length > 9 ? '9+' : history.length}
|
|
244
|
+
</span>
|
|
245
|
+
)}
|
|
246
|
+
</Button>
|
|
247
|
+
<Button variant="ghost" size="sm" asChild>
|
|
248
|
+
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" className="gap-1.5">
|
|
249
|
+
<Github className="h-4 w-4" />
|
|
250
|
+
<span className="hidden sm:inline">GitHub</span>
|
|
251
|
+
</a>
|
|
252
|
+
</Button>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
</header>
|
|
257
|
+
|
|
258
|
+
<main className="mx-auto max-w-5xl px-4 sm:px-6 py-12">
|
|
259
|
+
{/* Hero Section */}
|
|
260
|
+
<section className="text-center mb-10">
|
|
261
|
+
<div className="inline-flex items-center gap-2 rounded-full border border-border bg-muted px-3 py-1 text-xs font-medium text-muted-foreground mb-6">
|
|
262
|
+
<span className="flex h-1.5 w-1.5 rounded-full bg-emerald-500" />
|
|
263
|
+
Free & Open Source
|
|
264
|
+
</div>
|
|
265
|
+
<h2 className="text-4xl sm:text-5xl font-bold tracking-tight mb-4">
|
|
266
|
+
Convert GitHub repos to
|
|
267
|
+
<span className="block text-muted-foreground">MCP servers</span>
|
|
268
|
+
</h2>
|
|
269
|
+
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
|
|
270
|
+
Extract tools from any GitHub repository and generate ready-to-use MCP servers for Claude, Cursor, and other AI assistants.
|
|
271
|
+
</p>
|
|
272
|
+
</section>
|
|
273
|
+
|
|
274
|
+
{/* Input Section */}
|
|
275
|
+
<div className="rounded-2xl border border-border bg-card p-6 mb-8">
|
|
276
|
+
<div className="flex flex-col sm:flex-row gap-3">
|
|
277
|
+
<div className="flex-1 relative">
|
|
278
|
+
<Input
|
|
279
|
+
ref={inputRef}
|
|
280
|
+
type="url"
|
|
281
|
+
value={url}
|
|
282
|
+
onChange={(e) => setUrl(e.target.value)}
|
|
283
|
+
placeholder="https://github.com/owner/repo"
|
|
284
|
+
leftIcon={<Github className="h-4 w-4" />}
|
|
285
|
+
rightIcon={
|
|
286
|
+
url ? (
|
|
287
|
+
<button onClick={() => setUrl('')} className="hover:text-foreground transition-colors">
|
|
288
|
+
<X className="h-4 w-4" />
|
|
289
|
+
</button>
|
|
290
|
+
) : null
|
|
291
|
+
}
|
|
292
|
+
onKeyDown={(e) => e.key === 'Enter' && handleConvert()}
|
|
293
|
+
disabled={status === 'loading'}
|
|
294
|
+
className="h-12 text-base"
|
|
295
|
+
/>
|
|
296
|
+
</div>
|
|
297
|
+
<Button
|
|
298
|
+
onClick={handleConvert}
|
|
299
|
+
disabled={status === 'loading' || !url.trim()}
|
|
300
|
+
size="lg"
|
|
301
|
+
className="h-12 min-w-[140px]"
|
|
302
|
+
>
|
|
303
|
+
{status === 'loading' ? (
|
|
304
|
+
<>
|
|
305
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
306
|
+
Converting...
|
|
307
|
+
</>
|
|
308
|
+
) : (
|
|
309
|
+
<>
|
|
310
|
+
Extract
|
|
311
|
+
<ArrowRight className="h-4 w-4" />
|
|
312
|
+
</>
|
|
313
|
+
)}
|
|
314
|
+
</Button>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
{/* Example Repos */}
|
|
318
|
+
<div className="mt-5 pt-5 border-t border-border">
|
|
319
|
+
<p className="text-xs text-muted-foreground mb-3">Try:</p>
|
|
320
|
+
<div className="flex flex-wrap gap-2">
|
|
321
|
+
{EXAMPLE_REPOS.map((repo) => (
|
|
322
|
+
<button
|
|
323
|
+
key={repo.url}
|
|
324
|
+
onClick={() => setUrl(repo.url)}
|
|
325
|
+
className="px-3 py-1.5 text-sm bg-muted border border-border rounded-lg hover:border-muted-foreground/50 hover:bg-muted/80 transition-all text-muted-foreground hover:text-foreground"
|
|
326
|
+
disabled={status === 'loading'}
|
|
327
|
+
>
|
|
328
|
+
{repo.name}
|
|
329
|
+
</button>
|
|
330
|
+
))}
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
|
|
335
|
+
{/* Error Display */}
|
|
336
|
+
{status === 'error' && error && (
|
|
337
|
+
<div className="mb-8 rounded-xl border border-red-500/20 bg-red-500/5 p-5">
|
|
338
|
+
<div className="flex items-start gap-3">
|
|
339
|
+
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-red-500/10">
|
|
340
|
+
<AlertCircle className="h-4 w-4 text-red-500" />
|
|
341
|
+
</div>
|
|
342
|
+
<div className="flex-1">
|
|
343
|
+
<h3 className="font-medium text-red-500">{error.error}</h3>
|
|
344
|
+
{error.details && (
|
|
345
|
+
<p className="mt-1 text-sm text-muted-foreground">{error.details}</p>
|
|
346
|
+
)}
|
|
347
|
+
{error.retryAfter && (
|
|
348
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
349
|
+
Rate limited. Try again in {error.retryAfter}s.
|
|
350
|
+
</p>
|
|
351
|
+
)}
|
|
352
|
+
</div>
|
|
353
|
+
<Button
|
|
354
|
+
variant="ghost"
|
|
355
|
+
size="icon-sm"
|
|
356
|
+
onClick={() => { setStatus('idle'); setError(null); }}
|
|
357
|
+
>
|
|
358
|
+
<X className="h-4 w-4" />
|
|
359
|
+
</Button>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
)}
|
|
363
|
+
|
|
364
|
+
{/* Loading State */}
|
|
365
|
+
{status === 'loading' && (
|
|
366
|
+
<div className="mb-8 rounded-2xl border border-border bg-card p-8">
|
|
367
|
+
<div className="flex flex-col items-center text-center mb-8">
|
|
368
|
+
<div className="relative mb-4">
|
|
369
|
+
<div className="h-16 w-16 rounded-2xl bg-muted flex items-center justify-center">
|
|
370
|
+
<Loader2 className="h-8 w-8 animate-spin" />
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
<h3 className="text-lg font-semibold mb-1">Converting Repository</h3>
|
|
374
|
+
<p className="text-sm text-muted-foreground">This usually takes 10-30 seconds</p>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
<div className="max-w-md mx-auto space-y-3">
|
|
378
|
+
{LOADING_STEPS.map((step, index) => {
|
|
379
|
+
const StepIcon = step.icon;
|
|
380
|
+
const isActive = index === loadingStep;
|
|
381
|
+
const isComplete = index < loadingStep;
|
|
382
|
+
|
|
383
|
+
return (
|
|
384
|
+
<div
|
|
385
|
+
key={step.id}
|
|
386
|
+
className={cn(
|
|
387
|
+
'flex items-center gap-3 p-3 rounded-lg transition-all duration-300',
|
|
388
|
+
isActive && 'bg-muted',
|
|
389
|
+
isComplete && 'opacity-60'
|
|
390
|
+
)}
|
|
391
|
+
>
|
|
392
|
+
<div className={cn(
|
|
393
|
+
'h-8 w-8 rounded-lg flex items-center justify-center transition-colors',
|
|
394
|
+
isActive && 'bg-foreground text-background',
|
|
395
|
+
isComplete && 'bg-emerald-500/20 text-emerald-600',
|
|
396
|
+
!isActive && !isComplete && 'bg-muted text-muted-foreground'
|
|
397
|
+
)}>
|
|
398
|
+
{isComplete ? (
|
|
399
|
+
<Check className="h-4 w-4" />
|
|
400
|
+
) : isActive ? (
|
|
401
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
402
|
+
) : (
|
|
403
|
+
<StepIcon className="h-4 w-4" />
|
|
404
|
+
)}
|
|
405
|
+
</div>
|
|
406
|
+
<span className={cn(
|
|
407
|
+
'text-sm font-medium',
|
|
408
|
+
isActive && 'text-foreground',
|
|
409
|
+
!isActive && 'text-muted-foreground'
|
|
410
|
+
)}>
|
|
411
|
+
{step.label}
|
|
412
|
+
</span>
|
|
413
|
+
{isActive && (
|
|
414
|
+
<div className="ml-auto flex gap-1">
|
|
415
|
+
<span className="h-1.5 w-1.5 rounded-full bg-foreground animate-bounce" style={{ animationDelay: '0ms' }} />
|
|
416
|
+
<span className="h-1.5 w-1.5 rounded-full bg-foreground animate-bounce" style={{ animationDelay: '150ms' }} />
|
|
417
|
+
<span className="h-1.5 w-1.5 rounded-full bg-foreground animate-bounce" style={{ animationDelay: '300ms' }} />
|
|
418
|
+
</div>
|
|
419
|
+
)}
|
|
420
|
+
</div>
|
|
421
|
+
);
|
|
422
|
+
})}
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
)}
|
|
426
|
+
|
|
427
|
+
{/* Features - Show when idle */}
|
|
428
|
+
{status === 'idle' && !result && (
|
|
429
|
+
<section className="mb-12">
|
|
430
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
431
|
+
{FEATURES.map((feature) => {
|
|
432
|
+
const FeatureIcon = feature.icon;
|
|
433
|
+
return (
|
|
434
|
+
<div key={feature.title} className="group rounded-xl border border-border p-5 bg-card hover:border-muted-foreground/30 transition-colors card-lift">
|
|
435
|
+
<div className="h-10 w-10 rounded-lg bg-muted flex items-center justify-center mb-3 group-hover:bg-muted/80 transition-colors">
|
|
436
|
+
<FeatureIcon className="h-5 w-5" />
|
|
437
|
+
</div>
|
|
438
|
+
<h3 className="font-semibold mb-1">{feature.title}</h3>
|
|
439
|
+
<p className="text-sm text-muted-foreground">{feature.description}</p>
|
|
440
|
+
</div>
|
|
441
|
+
);
|
|
442
|
+
})}
|
|
443
|
+
</div>
|
|
444
|
+
|
|
445
|
+
{/* How it works */}
|
|
446
|
+
<div className="mt-12 text-center">
|
|
447
|
+
<h3 className="text-lg font-semibold mb-6">How it works</h3>
|
|
448
|
+
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 sm:gap-8">
|
|
449
|
+
<div className="flex items-center gap-3">
|
|
450
|
+
<div className="h-10 w-10 rounded-lg bg-foreground text-background flex items-center justify-center text-sm font-bold">1</div>
|
|
451
|
+
<span className="text-sm text-muted-foreground">Paste a GitHub URL</span>
|
|
452
|
+
</div>
|
|
453
|
+
<ArrowRight className="h-4 w-4 text-muted-foreground hidden sm:block" />
|
|
454
|
+
<div className="flex items-center gap-3">
|
|
455
|
+
<div className="h-10 w-10 rounded-lg bg-foreground text-background flex items-center justify-center text-sm font-bold">2</div>
|
|
456
|
+
<span className="text-sm text-muted-foreground">We analyze the repo</span>
|
|
457
|
+
</div>
|
|
458
|
+
<ArrowRight className="h-4 w-4 text-muted-foreground hidden sm:block" />
|
|
459
|
+
<div className="flex items-center gap-3">
|
|
460
|
+
<div className="h-10 w-10 rounded-lg bg-foreground text-background flex items-center justify-center text-sm font-bold">3</div>
|
|
461
|
+
<span className="text-sm text-muted-foreground">Get your MCP server</span>
|
|
462
|
+
</div>
|
|
463
|
+
</div>
|
|
464
|
+
</div>
|
|
465
|
+
</section>
|
|
466
|
+
)}
|
|
467
|
+
|
|
468
|
+
{/* Results */}
|
|
469
|
+
{status === 'success' && result && (
|
|
470
|
+
<div className="space-y-6">
|
|
471
|
+
{/* Success Banner */}
|
|
472
|
+
<div className="flex items-center gap-3 p-4 rounded-xl bg-emerald-500/10 border border-emerald-500/20">
|
|
473
|
+
<div className="h-10 w-10 rounded-lg bg-emerald-500/20 flex items-center justify-center">
|
|
474
|
+
<CheckCircle className="h-5 w-5 text-emerald-500" />
|
|
475
|
+
</div>
|
|
476
|
+
<div className="flex-1">
|
|
477
|
+
<h3 className="font-medium text-emerald-600 dark:text-emerald-400">Conversion Complete!</h3>
|
|
478
|
+
<p className="text-sm text-emerald-600/70 dark:text-emerald-400/70">
|
|
479
|
+
Successfully extracted {result.tools.length} tools from {result.name}
|
|
480
|
+
</p>
|
|
481
|
+
</div>
|
|
482
|
+
<Button variant="outline" size="sm" onClick={() => { setStatus('idle'); setResult(null); }}>
|
|
483
|
+
Convert Another
|
|
484
|
+
</Button>
|
|
485
|
+
</div>
|
|
486
|
+
|
|
487
|
+
{/* Summary Card */}
|
|
488
|
+
<div className="rounded-2xl border border-border bg-card p-6">
|
|
489
|
+
<div className="flex flex-col sm:flex-row sm:items-start justify-between gap-4">
|
|
490
|
+
<div className="flex-1">
|
|
491
|
+
<div className="flex items-center gap-3 mb-3">
|
|
492
|
+
<h3 className="text-xl font-semibold">{result.name}</h3>
|
|
493
|
+
{result.repository?.url && (
|
|
494
|
+
<a
|
|
495
|
+
href={result.repository.url}
|
|
496
|
+
target="_blank"
|
|
497
|
+
rel="noopener noreferrer"
|
|
498
|
+
className="text-muted-foreground hover:text-foreground transition-colors"
|
|
499
|
+
>
|
|
500
|
+
<ExternalLink className="h-4 w-4" />
|
|
501
|
+
</a>
|
|
502
|
+
)}
|
|
503
|
+
</div>
|
|
504
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
505
|
+
{classificationInfo && (
|
|
506
|
+
<Badge>
|
|
507
|
+
{classificationInfo.icon} {classificationInfo.label}
|
|
508
|
+
</Badge>
|
|
509
|
+
)}
|
|
510
|
+
<Badge variant="secondary">
|
|
511
|
+
{Math.round((result.classification?.confidence || 0) * 100)}% confidence
|
|
512
|
+
</Badge>
|
|
513
|
+
{result.repository?.stars && (
|
|
514
|
+
<Badge variant="secondary">
|
|
515
|
+
<Star className="mr-1 h-3 w-3" />
|
|
516
|
+
{result.repository.stars.toLocaleString()}
|
|
517
|
+
</Badge>
|
|
518
|
+
)}
|
|
519
|
+
{result.repository?.language && (
|
|
520
|
+
<Badge variant="secondary">{result.repository.language}</Badge>
|
|
521
|
+
)}
|
|
522
|
+
</div>
|
|
523
|
+
</div>
|
|
524
|
+
<div className="flex items-center gap-3">
|
|
525
|
+
<div className="flex h-14 w-14 items-center justify-center rounded-xl bg-muted">
|
|
526
|
+
<span className="text-2xl font-bold">{result.tools.length}</span>
|
|
527
|
+
</div>
|
|
528
|
+
<div className="text-left">
|
|
529
|
+
<div className="text-sm font-medium">Tools</div>
|
|
530
|
+
<div className="text-xs text-muted-foreground">extracted</div>
|
|
531
|
+
</div>
|
|
532
|
+
</div>
|
|
533
|
+
</div>
|
|
534
|
+
|
|
535
|
+
{/* Source Breakdown */}
|
|
536
|
+
{result.sources && result.sources.length > 0 && (
|
|
537
|
+
<div className="flex flex-wrap gap-2 mt-4 pt-4 border-t border-border">
|
|
538
|
+
{result.sources.map((source) => {
|
|
539
|
+
const sourceInfo = SOURCE_TYPE_LABELS[source.type] || { label: source.type, color: 'gray' };
|
|
540
|
+
return (
|
|
541
|
+
<Badge key={source.type} variant="secondary">
|
|
542
|
+
{sourceInfo.label}: {source.count}
|
|
543
|
+
</Badge>
|
|
544
|
+
);
|
|
545
|
+
})}
|
|
546
|
+
</div>
|
|
547
|
+
)}
|
|
548
|
+
|
|
549
|
+
{result.description && (
|
|
550
|
+
<p className="text-muted-foreground mt-4 pt-4 border-t border-border">{result.description}</p>
|
|
551
|
+
)}
|
|
552
|
+
</div>
|
|
553
|
+
|
|
554
|
+
{/* Tools List */}
|
|
555
|
+
<div className="rounded-2xl border border-border bg-card">
|
|
556
|
+
<div className="p-6 border-b border-border">
|
|
557
|
+
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
|
558
|
+
<div className="flex items-center gap-2">
|
|
559
|
+
<Box className="h-4 w-4 text-muted-foreground" />
|
|
560
|
+
<h3 className="font-semibold">Extracted Tools</h3>
|
|
561
|
+
<Badge variant="secondary">{filteredTools.length}</Badge>
|
|
562
|
+
</div>
|
|
563
|
+
{result.tools.length > 5 && (
|
|
564
|
+
<div className="relative w-full sm:w-56">
|
|
565
|
+
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
566
|
+
<input
|
|
567
|
+
type="text"
|
|
568
|
+
placeholder="Filter tools..."
|
|
569
|
+
value={searchFilter}
|
|
570
|
+
onChange={(e) => setSearchFilter(e.target.value)}
|
|
571
|
+
className="w-full rounded-lg border border-border bg-background py-2 pl-9 pr-4 text-sm transition-colors focus:border-foreground focus:outline-none"
|
|
572
|
+
/>
|
|
573
|
+
</div>
|
|
574
|
+
)}
|
|
575
|
+
</div>
|
|
576
|
+
</div>
|
|
577
|
+
<div className="p-6">
|
|
578
|
+
<div className="max-h-[500px] space-y-2 overflow-y-auto pr-2">
|
|
579
|
+
{filteredTools.map((tool, i) => (
|
|
580
|
+
<ToolCard
|
|
581
|
+
key={`${tool.name}-${i}`}
|
|
582
|
+
tool={tool}
|
|
583
|
+
expanded={expandedTools.has(tool.name)}
|
|
584
|
+
onToggle={() => toggleToolExpansion(tool.name)}
|
|
585
|
+
/>
|
|
586
|
+
))}
|
|
587
|
+
{filteredTools.length === 0 && searchFilter && (
|
|
588
|
+
<div className="py-8 text-center text-muted-foreground">
|
|
589
|
+
No tools match "{searchFilter}"
|
|
590
|
+
</div>
|
|
591
|
+
)}
|
|
592
|
+
</div>
|
|
593
|
+
</div>
|
|
594
|
+
</div>
|
|
595
|
+
|
|
596
|
+
{/* Configuration Tabs */}
|
|
597
|
+
<div className="rounded-2xl border border-border bg-card">
|
|
598
|
+
<div className="p-6 border-b border-border">
|
|
599
|
+
<h3 className="font-semibold flex items-center gap-2">
|
|
600
|
+
<Settings className="h-4 w-4 text-muted-foreground" />
|
|
601
|
+
Configuration
|
|
602
|
+
</h3>
|
|
603
|
+
<p className="text-sm text-muted-foreground mt-1">
|
|
604
|
+
Copy the configuration for your AI assistant
|
|
605
|
+
</p>
|
|
606
|
+
</div>
|
|
607
|
+
<div className="p-6">
|
|
608
|
+
<Tabs value={activeConfigTab} onValueChange={setActiveConfigTab}>
|
|
609
|
+
<TabsList className="mb-4 w-full sm:w-auto">
|
|
610
|
+
{Object.entries(PLATFORMS).map(([key, platform]) => (
|
|
611
|
+
<TabsTrigger key={key} value={key}>
|
|
612
|
+
{platform.icon} {platform.name}
|
|
613
|
+
</TabsTrigger>
|
|
614
|
+
))}
|
|
615
|
+
</TabsList>
|
|
616
|
+
|
|
617
|
+
<TabsContent value="claude">
|
|
618
|
+
<div className="space-y-3">
|
|
619
|
+
<p className="text-sm text-muted-foreground">
|
|
620
|
+
Add to: <code className="rounded bg-muted px-1.5 py-0.5 text-xs font-mono">
|
|
621
|
+
{PLATFORMS.claude.configPath}
|
|
622
|
+
</code>
|
|
623
|
+
</p>
|
|
624
|
+
<CodeBlock
|
|
625
|
+
code={result.claudeConfig || '{}'}
|
|
626
|
+
language="json"
|
|
627
|
+
filename="claude_desktop_config.json"
|
|
628
|
+
showDownload
|
|
629
|
+
/>
|
|
630
|
+
</div>
|
|
631
|
+
</TabsContent>
|
|
632
|
+
|
|
633
|
+
<TabsContent value="cursor">
|
|
634
|
+
<div className="space-y-3">
|
|
635
|
+
<p className="text-sm text-muted-foreground">
|
|
636
|
+
Add to: <code className="rounded bg-muted px-1.5 py-0.5 text-xs font-mono">
|
|
637
|
+
{PLATFORMS.cursor.configPath}
|
|
638
|
+
</code>
|
|
639
|
+
</p>
|
|
640
|
+
<CodeBlock
|
|
641
|
+
code={result.cursorConfig || '{}'}
|
|
642
|
+
language="json"
|
|
643
|
+
filename="mcp.json"
|
|
644
|
+
showDownload
|
|
645
|
+
/>
|
|
646
|
+
</div>
|
|
647
|
+
</TabsContent>
|
|
648
|
+
|
|
649
|
+
<TabsContent value="openai">
|
|
650
|
+
<div className="space-y-3">
|
|
651
|
+
<p className="text-sm text-muted-foreground">
|
|
652
|
+
Configuration for OpenAI-compatible clients
|
|
653
|
+
</p>
|
|
654
|
+
<CodeBlock
|
|
655
|
+
code={result.openaiConfig || result.claudeConfig || '{}'}
|
|
656
|
+
language="json"
|
|
657
|
+
filename="openai-mcp-config.json"
|
|
658
|
+
showDownload
|
|
659
|
+
/>
|
|
660
|
+
</div>
|
|
661
|
+
</TabsContent>
|
|
662
|
+
</Tabs>
|
|
663
|
+
</div>
|
|
664
|
+
</div>
|
|
665
|
+
|
|
666
|
+
{/* Generated Code */}
|
|
667
|
+
<div className="rounded-2xl border border-border bg-card">
|
|
668
|
+
<div className="p-6 border-b border-border">
|
|
669
|
+
<div className="flex items-center justify-between">
|
|
670
|
+
<div>
|
|
671
|
+
<h3 className="font-semibold flex items-center gap-2">
|
|
672
|
+
<FileCode className="h-4 w-4 text-muted-foreground" />
|
|
673
|
+
Generated Server
|
|
674
|
+
</h3>
|
|
675
|
+
<p className="text-sm text-muted-foreground mt-1">
|
|
676
|
+
Ready-to-run MCP server implementation
|
|
677
|
+
</p>
|
|
678
|
+
</div>
|
|
679
|
+
<div className="flex gap-2">
|
|
680
|
+
<Button
|
|
681
|
+
variant="outline"
|
|
682
|
+
size="sm"
|
|
683
|
+
onClick={() => handleCopy(result.code, 'code')}
|
|
684
|
+
>
|
|
685
|
+
{copiedItem === 'code' ? (
|
|
686
|
+
<>
|
|
687
|
+
<Check className="h-3.5 w-3.5" />
|
|
688
|
+
Copied
|
|
689
|
+
</>
|
|
690
|
+
) : (
|
|
691
|
+
<>
|
|
692
|
+
<Copy className="h-3.5 w-3.5" />
|
|
693
|
+
Copy
|
|
694
|
+
</>
|
|
695
|
+
)}
|
|
696
|
+
</Button>
|
|
697
|
+
<Button size="sm" onClick={handleDownload}>
|
|
698
|
+
<Download className="h-3.5 w-3.5" />
|
|
699
|
+
Download
|
|
700
|
+
</Button>
|
|
701
|
+
</div>
|
|
702
|
+
</div>
|
|
703
|
+
</div>
|
|
704
|
+
<div className="p-6">
|
|
705
|
+
<Tabs defaultValue="typescript" className="w-full">
|
|
706
|
+
<TabsList className="mb-4">
|
|
707
|
+
<TabsTrigger value="typescript">TypeScript</TabsTrigger>
|
|
708
|
+
<TabsTrigger value="python">Python</TabsTrigger>
|
|
709
|
+
</TabsList>
|
|
710
|
+
<TabsContent value="typescript">
|
|
711
|
+
<CodeBlock
|
|
712
|
+
code={result.code}
|
|
713
|
+
language="typescript"
|
|
714
|
+
filename={`${result.name}-mcp-server.ts`}
|
|
715
|
+
maxHeight="400px"
|
|
716
|
+
showDownload
|
|
717
|
+
/>
|
|
718
|
+
</TabsContent>
|
|
719
|
+
<TabsContent value="python">
|
|
720
|
+
<CodeBlock
|
|
721
|
+
code={result.pythonCode || '# Python code generation not available'}
|
|
722
|
+
language="python"
|
|
723
|
+
filename={`${result.name}_mcp_server.py`}
|
|
724
|
+
maxHeight="400px"
|
|
725
|
+
showDownload
|
|
726
|
+
/>
|
|
727
|
+
</TabsContent>
|
|
728
|
+
</Tabs>
|
|
729
|
+
</div>
|
|
730
|
+
</div>
|
|
731
|
+
</div>
|
|
732
|
+
)}
|
|
733
|
+
|
|
734
|
+
{/* History Panel */}
|
|
735
|
+
{showHistory && (
|
|
736
|
+
<div className="fixed inset-y-0 right-0 z-50 w-full overflow-hidden bg-background border-l border-border shadow-2xl sm:w-80">
|
|
737
|
+
<div className="flex h-14 items-center justify-between border-b border-border px-4">
|
|
738
|
+
<h3 className="flex items-center gap-2 text-sm font-semibold">
|
|
739
|
+
<History className="h-4 w-4" />
|
|
740
|
+
History
|
|
741
|
+
</h3>
|
|
742
|
+
<Button variant="ghost" size="icon-sm" onClick={() => setShowHistory(false)}>
|
|
743
|
+
<X className="h-4 w-4" />
|
|
744
|
+
</Button>
|
|
745
|
+
</div>
|
|
746
|
+
<div className="space-y-2 overflow-y-auto p-4" style={{ maxHeight: 'calc(100vh - 120px)' }}>
|
|
747
|
+
{history.length === 0 ? (
|
|
748
|
+
<p className="py-8 text-center text-sm text-muted-foreground">No conversion history yet</p>
|
|
749
|
+
) : (
|
|
750
|
+
<>
|
|
751
|
+
<Button
|
|
752
|
+
variant="ghost"
|
|
753
|
+
size="sm"
|
|
754
|
+
className="w-full text-red-500 hover:bg-red-500/10 hover:text-red-500"
|
|
755
|
+
onClick={() => setHistory([])}
|
|
756
|
+
>
|
|
757
|
+
<Trash2 className="mr-2 h-3.5 w-3.5" />
|
|
758
|
+
Clear History
|
|
759
|
+
</Button>
|
|
760
|
+
{history.map((item) => (
|
|
761
|
+
<button
|
|
762
|
+
key={item.id}
|
|
763
|
+
onClick={() => {
|
|
764
|
+
setUrl(item.url);
|
|
765
|
+
setShowHistory(false);
|
|
766
|
+
}}
|
|
767
|
+
className="w-full rounded-lg border border-border bg-card p-3 text-left transition-colors hover:border-muted-foreground/50"
|
|
768
|
+
>
|
|
769
|
+
<div className="mb-1.5 flex items-center justify-between">
|
|
770
|
+
<span className="truncate text-sm font-medium">{item.name}</span>
|
|
771
|
+
<Badge variant="secondary" className="ml-2 shrink-0 text-[10px]">
|
|
772
|
+
{item.toolCount} tools
|
|
773
|
+
</Badge>
|
|
774
|
+
</div>
|
|
775
|
+
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
776
|
+
<Clock className="h-3 w-3" />
|
|
777
|
+
{formatRelativeTime(item.convertedAt)}
|
|
778
|
+
</div>
|
|
779
|
+
</button>
|
|
780
|
+
))}
|
|
781
|
+
</>
|
|
782
|
+
)}
|
|
783
|
+
</div>
|
|
784
|
+
</div>
|
|
785
|
+
)}
|
|
786
|
+
|
|
787
|
+
{/* History Overlay */}
|
|
788
|
+
{showHistory && (
|
|
789
|
+
<div
|
|
790
|
+
className="fixed inset-0 z-40 bg-background/80 backdrop-blur-sm"
|
|
791
|
+
onClick={() => setShowHistory(false)}
|
|
792
|
+
/>
|
|
793
|
+
)}
|
|
794
|
+
</main>
|
|
795
|
+
|
|
796
|
+
{/* Footer */}
|
|
797
|
+
<footer className="mt-auto border-t border-border py-8">
|
|
798
|
+
<div className="mx-auto max-w-5xl px-4 sm:px-6">
|
|
799
|
+
<div className="flex flex-col items-center justify-between gap-4 sm:flex-row">
|
|
800
|
+
<div className="flex items-center gap-4">
|
|
801
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-foreground">
|
|
802
|
+
<Zap className="h-4 w-4 text-background" />
|
|
803
|
+
</div>
|
|
804
|
+
<span className="text-sm text-muted-foreground">
|
|
805
|
+
Built by{' '}
|
|
806
|
+
<a
|
|
807
|
+
href="https://github.com/nirholas"
|
|
808
|
+
target="_blank"
|
|
809
|
+
rel="noopener noreferrer"
|
|
810
|
+
className="text-foreground hover:underline"
|
|
811
|
+
>
|
|
812
|
+
nirholas
|
|
813
|
+
</a>
|
|
814
|
+
</span>
|
|
815
|
+
</div>
|
|
816
|
+
|
|
817
|
+
<div className="flex items-center gap-6 text-sm text-muted-foreground">
|
|
818
|
+
<a
|
|
819
|
+
href={GITHUB_URL}
|
|
820
|
+
target="_blank"
|
|
821
|
+
rel="noopener noreferrer"
|
|
822
|
+
className="hover:text-foreground transition-colors"
|
|
823
|
+
>
|
|
824
|
+
GitHub
|
|
825
|
+
</a>
|
|
826
|
+
<a
|
|
827
|
+
href={`${GITHUB_URL}#readme`}
|
|
828
|
+
target="_blank"
|
|
829
|
+
rel="noopener noreferrer"
|
|
830
|
+
className="hover:text-foreground transition-colors"
|
|
831
|
+
>
|
|
832
|
+
Docs
|
|
833
|
+
</a>
|
|
834
|
+
<a
|
|
835
|
+
href={`${GITHUB_URL}/issues`}
|
|
836
|
+
target="_blank"
|
|
837
|
+
rel="noopener noreferrer"
|
|
838
|
+
className="hover:text-foreground transition-colors"
|
|
839
|
+
>
|
|
840
|
+
Issues
|
|
841
|
+
</a>
|
|
842
|
+
</div>
|
|
843
|
+
</div>
|
|
844
|
+
</div>
|
|
845
|
+
</footer>
|
|
846
|
+
</div>
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Tool Card Component
|
|
851
|
+
function ToolCard({
|
|
852
|
+
tool,
|
|
853
|
+
expanded,
|
|
854
|
+
onToggle,
|
|
855
|
+
}: {
|
|
856
|
+
tool: Tool;
|
|
857
|
+
expanded: boolean;
|
|
858
|
+
onToggle: () => void;
|
|
859
|
+
}) {
|
|
860
|
+
const sourceInfo = SOURCE_TYPE_LABELS[tool.source.type] || { label: tool.source.type, color: 'gray' };
|
|
861
|
+
const hasSchema = tool.inputSchema?.properties && Object.keys(tool.inputSchema.properties).length > 0;
|
|
862
|
+
|
|
863
|
+
return (
|
|
864
|
+
<div className="overflow-hidden rounded-xl border border-border bg-card transition-colors hover:border-muted-foreground/30">
|
|
865
|
+
<button
|
|
866
|
+
onClick={onToggle}
|
|
867
|
+
className="flex w-full items-start justify-between p-4 text-left transition-colors hover:bg-muted/50"
|
|
868
|
+
>
|
|
869
|
+
<div className="min-w-0 flex-1">
|
|
870
|
+
<div className="mb-1.5 flex items-center gap-2">
|
|
871
|
+
<code className="font-mono text-sm font-medium">
|
|
872
|
+
{tool.name}
|
|
873
|
+
</code>
|
|
874
|
+
<Badge variant="secondary" className="text-[10px]">
|
|
875
|
+
{sourceInfo.label}
|
|
876
|
+
</Badge>
|
|
877
|
+
</div>
|
|
878
|
+
<p className="line-clamp-2 text-sm text-muted-foreground">
|
|
879
|
+
{tool.description}
|
|
880
|
+
</p>
|
|
881
|
+
</div>
|
|
882
|
+
<div className="ml-4 shrink-0">
|
|
883
|
+
{expanded ? (
|
|
884
|
+
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
|
885
|
+
) : (
|
|
886
|
+
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
|
887
|
+
)}
|
|
888
|
+
</div>
|
|
889
|
+
</button>
|
|
890
|
+
|
|
891
|
+
{expanded && hasSchema && (
|
|
892
|
+
<div className="border-t border-border bg-muted/30 p-4">
|
|
893
|
+
<h4 className="mb-3 text-xs font-medium uppercase tracking-wide text-muted-foreground">Parameters</h4>
|
|
894
|
+
<div className="space-y-2">
|
|
895
|
+
{Object.entries(tool.inputSchema.properties || {}).map(([name, prop]: [string, any]) => (
|
|
896
|
+
<div key={name} className="flex flex-wrap items-center gap-2 text-sm">
|
|
897
|
+
<code className="font-mono text-xs">
|
|
898
|
+
{name}
|
|
899
|
+
</code>
|
|
900
|
+
<Badge variant="outline" className="text-[10px]">
|
|
901
|
+
{prop.type}
|
|
902
|
+
</Badge>
|
|
903
|
+
{tool.inputSchema.required?.includes(name) && (
|
|
904
|
+
<Badge variant="secondary" className="text-[10px] bg-red-500/10 text-red-500">
|
|
905
|
+
required
|
|
906
|
+
</Badge>
|
|
907
|
+
)}
|
|
908
|
+
{prop.description && (
|
|
909
|
+
<span className="text-xs text-muted-foreground">{prop.description}</span>
|
|
910
|
+
)}
|
|
911
|
+
</div>
|
|
912
|
+
))}
|
|
913
|
+
</div>
|
|
914
|
+
{tool.source.file && (
|
|
915
|
+
<p className="mt-4 text-xs text-muted-foreground">
|
|
916
|
+
Source: {tool.source.file}
|
|
917
|
+
{tool.source.line && `:${tool.source.line}`}
|
|
918
|
+
</p>
|
|
919
|
+
)}
|
|
920
|
+
</div>
|
|
921
|
+
)}
|
|
922
|
+
</div>
|
|
923
|
+
);
|
|
924
|
+
}
|