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,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview GitHub API client for VS Code extension
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as vscode from 'vscode';
|
|
8
|
+
|
|
9
|
+
interface GitHubRepo {
|
|
10
|
+
name: string;
|
|
11
|
+
full_name: string;
|
|
12
|
+
description: string | null;
|
|
13
|
+
html_url: string;
|
|
14
|
+
default_branch: string;
|
|
15
|
+
language: string | null;
|
|
16
|
+
stargazers_count: number;
|
|
17
|
+
forks_count: number;
|
|
18
|
+
topics: string[];
|
|
19
|
+
owner: {
|
|
20
|
+
login: string;
|
|
21
|
+
avatar_url: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface GitHubContent {
|
|
26
|
+
name: string;
|
|
27
|
+
path: string;
|
|
28
|
+
type: 'file' | 'dir';
|
|
29
|
+
size?: number;
|
|
30
|
+
download_url?: string;
|
|
31
|
+
content?: string;
|
|
32
|
+
encoding?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface GitHubRateLimit {
|
|
36
|
+
limit: number;
|
|
37
|
+
remaining: number;
|
|
38
|
+
reset: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* GitHub API client for the VS Code extension
|
|
43
|
+
*/
|
|
44
|
+
export class GitHubApiClient {
|
|
45
|
+
private static instance: GitHubApiClient;
|
|
46
|
+
private baseUrl = 'https://api.github.com';
|
|
47
|
+
private token: string | undefined;
|
|
48
|
+
|
|
49
|
+
private constructor() {
|
|
50
|
+
this.loadToken();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get singleton instance
|
|
55
|
+
*/
|
|
56
|
+
static getInstance(): GitHubApiClient {
|
|
57
|
+
if (!GitHubApiClient.instance) {
|
|
58
|
+
GitHubApiClient.instance = new GitHubApiClient();
|
|
59
|
+
}
|
|
60
|
+
return GitHubApiClient.instance;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Load GitHub token from VS Code settings or environment
|
|
65
|
+
*/
|
|
66
|
+
private loadToken(): void {
|
|
67
|
+
const config = vscode.workspace.getConfiguration('github-to-mcp');
|
|
68
|
+
this.token = config.get<string>('githubToken') || process.env.GITHUB_TOKEN;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Set GitHub token
|
|
73
|
+
*/
|
|
74
|
+
setToken(token: string): void {
|
|
75
|
+
this.token = token;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if authenticated
|
|
80
|
+
*/
|
|
81
|
+
isAuthenticated(): boolean {
|
|
82
|
+
return !!this.token;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Make a request to GitHub API
|
|
87
|
+
*/
|
|
88
|
+
private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
|
|
89
|
+
const headers: Record<string, string> = {
|
|
90
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
91
|
+
'User-Agent': 'GitHub-to-MCP-VSCode-Extension'
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
if (this.token) {
|
|
95
|
+
headers['Authorization'] = `Bearer ${this.token}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const response = await fetch(`${this.baseUrl}${endpoint}`, {
|
|
99
|
+
...options,
|
|
100
|
+
headers: {
|
|
101
|
+
...headers,
|
|
102
|
+
...options.headers
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
if (response.status === 403) {
|
|
108
|
+
const rateLimitRemaining = response.headers.get('X-RateLimit-Remaining');
|
|
109
|
+
if (rateLimitRemaining === '0') {
|
|
110
|
+
throw new Error('GitHub API rate limit exceeded. Please try again later or add a GitHub token.');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (response.status === 404) {
|
|
114
|
+
throw new Error('Repository not found. Check the URL and make sure the repository is public.');
|
|
115
|
+
}
|
|
116
|
+
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return response.json() as Promise<T>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Parse GitHub URL to extract owner and repo
|
|
124
|
+
*/
|
|
125
|
+
parseGitHubUrl(url: string): { owner: string; repo: string } | null {
|
|
126
|
+
const match = url.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
|
|
127
|
+
if (!match) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return { owner: match[1], repo: match[2] };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get repository information
|
|
135
|
+
*/
|
|
136
|
+
async getRepository(owner: string, repo: string): Promise<GitHubRepo> {
|
|
137
|
+
return this.request<GitHubRepo>(`/repos/${owner}/${repo}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get repository from URL
|
|
142
|
+
*/
|
|
143
|
+
async getRepositoryFromUrl(url: string): Promise<GitHubRepo> {
|
|
144
|
+
const parsed = this.parseGitHubUrl(url);
|
|
145
|
+
if (!parsed) {
|
|
146
|
+
throw new Error('Invalid GitHub URL');
|
|
147
|
+
}
|
|
148
|
+
return this.getRepository(parsed.owner, parsed.repo);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get repository contents at a path
|
|
153
|
+
*/
|
|
154
|
+
async getContents(owner: string, repo: string, path: string = ''): Promise<GitHubContent[]> {
|
|
155
|
+
const result = await this.request<GitHubContent | GitHubContent[]>(
|
|
156
|
+
`/repos/${owner}/${repo}/contents/${path}`
|
|
157
|
+
);
|
|
158
|
+
return Array.isArray(result) ? result : [result];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get file content
|
|
163
|
+
*/
|
|
164
|
+
async getFileContent(owner: string, repo: string, path: string): Promise<string> {
|
|
165
|
+
const content = await this.request<GitHubContent>(
|
|
166
|
+
`/repos/${owner}/${repo}/contents/${path}`
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
if (content.content && content.encoding === 'base64') {
|
|
170
|
+
return Buffer.from(content.content, 'base64').toString('utf-8');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (content.download_url) {
|
|
174
|
+
const response = await fetch(content.download_url);
|
|
175
|
+
return response.text();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
throw new Error(`Could not retrieve content for ${path}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get README content
|
|
183
|
+
*/
|
|
184
|
+
async getReadme(owner: string, repo: string): Promise<string | null> {
|
|
185
|
+
try {
|
|
186
|
+
const readme = await this.request<GitHubContent>(
|
|
187
|
+
`/repos/${owner}/${repo}/readme`
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (readme.content && readme.encoding === 'base64') {
|
|
191
|
+
return Buffer.from(readme.content, 'base64').toString('utf-8');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (readme.download_url) {
|
|
195
|
+
const response = await fetch(readme.download_url);
|
|
196
|
+
return response.text();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return null;
|
|
200
|
+
} catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Search for files in repository
|
|
207
|
+
*/
|
|
208
|
+
async searchFiles(
|
|
209
|
+
owner: string,
|
|
210
|
+
repo: string,
|
|
211
|
+
query: string
|
|
212
|
+
): Promise<Array<{ name: string; path: string }>> {
|
|
213
|
+
try {
|
|
214
|
+
const result = await this.request<{
|
|
215
|
+
items: Array<{ name: string; path: string }>;
|
|
216
|
+
}>(`/search/code?q=${encodeURIComponent(query)}+repo:${owner}/${repo}`);
|
|
217
|
+
return result.items || [];
|
|
218
|
+
} catch {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Find OpenAPI/Swagger files in repository
|
|
225
|
+
*/
|
|
226
|
+
async findOpenApiFiles(owner: string, repo: string): Promise<string[]> {
|
|
227
|
+
const commonPaths = [
|
|
228
|
+
'openapi.json',
|
|
229
|
+
'openapi.yaml',
|
|
230
|
+
'openapi.yml',
|
|
231
|
+
'swagger.json',
|
|
232
|
+
'swagger.yaml',
|
|
233
|
+
'swagger.yml',
|
|
234
|
+
'api/openapi.json',
|
|
235
|
+
'api/openapi.yaml',
|
|
236
|
+
'docs/openapi.json',
|
|
237
|
+
'docs/openapi.yaml',
|
|
238
|
+
'spec/openapi.json',
|
|
239
|
+
'spec/openapi.yaml'
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
const found: string[] = [];
|
|
243
|
+
|
|
244
|
+
for (const path of commonPaths) {
|
|
245
|
+
try {
|
|
246
|
+
await this.request(`/repos/${owner}/${repo}/contents/${path}`);
|
|
247
|
+
found.push(path);
|
|
248
|
+
} catch {
|
|
249
|
+
// File doesn't exist, continue
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return found;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Get rate limit status
|
|
258
|
+
*/
|
|
259
|
+
async getRateLimit(): Promise<GitHubRateLimit> {
|
|
260
|
+
const result = await this.request<{ rate: GitHubRateLimit }>('/rate_limit');
|
|
261
|
+
return result.rate;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Check rate limit and warn if low
|
|
266
|
+
*/
|
|
267
|
+
async checkRateLimit(): Promise<boolean> {
|
|
268
|
+
try {
|
|
269
|
+
const limit = await this.getRateLimit();
|
|
270
|
+
|
|
271
|
+
if (limit.remaining < 10) {
|
|
272
|
+
const resetDate = new Date(limit.reset * 1000);
|
|
273
|
+
const message = `GitHub API rate limit low (${limit.remaining} remaining). ` +
|
|
274
|
+
`Resets at ${resetDate.toLocaleTimeString()}.`;
|
|
275
|
+
|
|
276
|
+
if (this.token) {
|
|
277
|
+
vscode.window.showWarningMessage(message);
|
|
278
|
+
} else {
|
|
279
|
+
vscode.window.showWarningMessage(
|
|
280
|
+
`${message} Add a GitHub token for higher limits.`,
|
|
281
|
+
'Add Token'
|
|
282
|
+
).then(selection => {
|
|
283
|
+
if (selection === 'Add Token') {
|
|
284
|
+
vscode.commands.executeCommand('workbench.action.openSettings', 'github-to-mcp.githubToken');
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return limit.remaining > 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return true;
|
|
293
|
+
} catch {
|
|
294
|
+
// Can't check rate limit, proceed anyway
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Get the GitHub API client instance
|
|
302
|
+
*/
|
|
303
|
+
export function getGitHubClient(): GitHubApiClient {
|
|
304
|
+
return GitHubApiClient.getInstance();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Prompt user to enter GitHub token
|
|
309
|
+
*/
|
|
310
|
+
export async function promptForGitHubToken(): Promise<string | undefined> {
|
|
311
|
+
const token = await vscode.window.showInputBox({
|
|
312
|
+
prompt: 'Enter your GitHub Personal Access Token',
|
|
313
|
+
placeHolder: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
|
314
|
+
password: true,
|
|
315
|
+
ignoreFocusOut: true,
|
|
316
|
+
validateInput: (value) => {
|
|
317
|
+
if (!value) {
|
|
318
|
+
return 'Token is required';
|
|
319
|
+
}
|
|
320
|
+
if (!value.startsWith('ghp_') && !value.startsWith('github_pat_')) {
|
|
321
|
+
return 'Invalid token format';
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
if (token) {
|
|
328
|
+
const config = vscode.workspace.getConfiguration('github-to-mcp');
|
|
329
|
+
await config.update('githubToken', token, vscode.ConfigurationTarget.Global);
|
|
330
|
+
getGitHubClient().setToken(token);
|
|
331
|
+
vscode.window.showInformationMessage('GitHub token saved successfully!');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return token;
|
|
335
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Utility exports
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { StorageService, type ConversionResult } from './storage';
|
|
8
|
+
export { GitHubApiClient, getGitHubClient, promptForGitHubToken } from './github-api';
|
|
9
|
+
export {
|
|
10
|
+
getMcpConfigPath,
|
|
11
|
+
readMcpConfig,
|
|
12
|
+
writeMcpConfig,
|
|
13
|
+
addMcpServer,
|
|
14
|
+
removeMcpServer,
|
|
15
|
+
listMcpServers,
|
|
16
|
+
generateServerConfig,
|
|
17
|
+
formatConfigForClient,
|
|
18
|
+
openMcpConfigInEditor,
|
|
19
|
+
validateServerConfig,
|
|
20
|
+
type McpServerConfig,
|
|
21
|
+
type ClaudeDesktopConfig,
|
|
22
|
+
type CursorConfig,
|
|
23
|
+
type McpClientType
|
|
24
|
+
} from './mcp-config';
|
|
25
|
+
export {
|
|
26
|
+
generateMcpServerFiles,
|
|
27
|
+
type GeneratedFiles,
|
|
28
|
+
type GeneratorOptions
|
|
29
|
+
} from './file-generator';
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview MCP configuration management for Claude Desktop and other clients
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as vscode from 'vscode';
|
|
8
|
+
import * as os from 'os';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
|
|
11
|
+
export interface McpServerConfig {
|
|
12
|
+
command: string;
|
|
13
|
+
args?: string[];
|
|
14
|
+
env?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ClaudeDesktopConfig {
|
|
18
|
+
mcpServers?: Record<string, McpServerConfig>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface CursorConfig {
|
|
22
|
+
mcp?: {
|
|
23
|
+
servers?: Record<string, McpServerConfig>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type McpClientType = 'claude' | 'cursor' | 'custom';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get the config file path for a specific MCP client
|
|
31
|
+
*/
|
|
32
|
+
export function getMcpConfigPath(client: McpClientType): string | null {
|
|
33
|
+
const platform = os.platform();
|
|
34
|
+
const homeDir = os.homedir();
|
|
35
|
+
|
|
36
|
+
switch (client) {
|
|
37
|
+
case 'claude':
|
|
38
|
+
switch (platform) {
|
|
39
|
+
case 'darwin':
|
|
40
|
+
return path.join(homeDir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
41
|
+
case 'win32':
|
|
42
|
+
return path.join(homeDir, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
|
|
43
|
+
case 'linux':
|
|
44
|
+
return path.join(homeDir, '.config', 'Claude', 'claude_desktop_config.json');
|
|
45
|
+
default:
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
case 'cursor':
|
|
50
|
+
switch (platform) {
|
|
51
|
+
case 'darwin':
|
|
52
|
+
return path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'settings.json');
|
|
53
|
+
case 'win32':
|
|
54
|
+
return path.join(homeDir, 'AppData', 'Roaming', 'Cursor', 'User', 'settings.json');
|
|
55
|
+
case 'linux':
|
|
56
|
+
return path.join(homeDir, '.config', 'Cursor', 'User', 'settings.json');
|
|
57
|
+
default:
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
default:
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Read MCP configuration from file
|
|
68
|
+
*/
|
|
69
|
+
export async function readMcpConfig(client: McpClientType): Promise<ClaudeDesktopConfig | CursorConfig | null> {
|
|
70
|
+
const configPath = getMcpConfigPath(client);
|
|
71
|
+
if (!configPath) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const configUri = vscode.Uri.file(configPath);
|
|
77
|
+
const content = await vscode.workspace.fs.readFile(configUri);
|
|
78
|
+
return JSON.parse(Buffer.from(content).toString('utf-8'));
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Write MCP configuration to file
|
|
86
|
+
*/
|
|
87
|
+
export async function writeMcpConfig(
|
|
88
|
+
client: McpClientType,
|
|
89
|
+
config: ClaudeDesktopConfig | CursorConfig
|
|
90
|
+
): Promise<void> {
|
|
91
|
+
const configPath = getMcpConfigPath(client);
|
|
92
|
+
if (!configPath) {
|
|
93
|
+
throw new Error(`Unsupported platform for ${client}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const configUri = vscode.Uri.file(configPath);
|
|
97
|
+
const configDir = path.dirname(configPath);
|
|
98
|
+
|
|
99
|
+
// Ensure directory exists
|
|
100
|
+
try {
|
|
101
|
+
await vscode.workspace.fs.createDirectory(vscode.Uri.file(configDir));
|
|
102
|
+
} catch {
|
|
103
|
+
// Directory may already exist
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Write config
|
|
107
|
+
await vscode.workspace.fs.writeFile(
|
|
108
|
+
configUri,
|
|
109
|
+
Buffer.from(JSON.stringify(config, null, 2), 'utf-8')
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Add an MCP server to client configuration
|
|
115
|
+
*/
|
|
116
|
+
export async function addMcpServer(
|
|
117
|
+
client: McpClientType,
|
|
118
|
+
serverName: string,
|
|
119
|
+
serverConfig: McpServerConfig
|
|
120
|
+
): Promise<void> {
|
|
121
|
+
let config = await readMcpConfig(client);
|
|
122
|
+
|
|
123
|
+
if (client === 'claude') {
|
|
124
|
+
const claudeConfig = (config as ClaudeDesktopConfig) || {};
|
|
125
|
+
claudeConfig.mcpServers = claudeConfig.mcpServers || {};
|
|
126
|
+
claudeConfig.mcpServers[serverName] = serverConfig;
|
|
127
|
+
await writeMcpConfig(client, claudeConfig);
|
|
128
|
+
} else if (client === 'cursor') {
|
|
129
|
+
const cursorConfig = (config as CursorConfig) || {};
|
|
130
|
+
cursorConfig.mcp = cursorConfig.mcp || {};
|
|
131
|
+
cursorConfig.mcp.servers = cursorConfig.mcp.servers || {};
|
|
132
|
+
cursorConfig.mcp.servers[serverName] = serverConfig;
|
|
133
|
+
await writeMcpConfig(client, cursorConfig);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Remove an MCP server from client configuration
|
|
139
|
+
*/
|
|
140
|
+
export async function removeMcpServer(
|
|
141
|
+
client: McpClientType,
|
|
142
|
+
serverName: string
|
|
143
|
+
): Promise<boolean> {
|
|
144
|
+
const config = await readMcpConfig(client);
|
|
145
|
+
if (!config) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let removed = false;
|
|
150
|
+
|
|
151
|
+
if (client === 'claude') {
|
|
152
|
+
const claudeConfig = config as ClaudeDesktopConfig;
|
|
153
|
+
if (claudeConfig.mcpServers?.[serverName]) {
|
|
154
|
+
delete claudeConfig.mcpServers[serverName];
|
|
155
|
+
await writeMcpConfig(client, claudeConfig);
|
|
156
|
+
removed = true;
|
|
157
|
+
}
|
|
158
|
+
} else if (client === 'cursor') {
|
|
159
|
+
const cursorConfig = config as CursorConfig;
|
|
160
|
+
if (cursorConfig.mcp?.servers?.[serverName]) {
|
|
161
|
+
delete cursorConfig.mcp.servers[serverName];
|
|
162
|
+
await writeMcpConfig(client, cursorConfig);
|
|
163
|
+
removed = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return removed;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* List all configured MCP servers for a client
|
|
172
|
+
*/
|
|
173
|
+
export async function listMcpServers(client: McpClientType): Promise<Record<string, McpServerConfig>> {
|
|
174
|
+
const config = await readMcpConfig(client);
|
|
175
|
+
if (!config) {
|
|
176
|
+
return {};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (client === 'claude') {
|
|
180
|
+
return (config as ClaudeDesktopConfig).mcpServers || {};
|
|
181
|
+
} else if (client === 'cursor') {
|
|
182
|
+
return (config as CursorConfig).mcp?.servers || {};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Generate MCP server configuration object
|
|
190
|
+
*/
|
|
191
|
+
export function generateServerConfig(
|
|
192
|
+
serverPath: string,
|
|
193
|
+
options?: {
|
|
194
|
+
runtime?: 'node' | 'python' | 'deno';
|
|
195
|
+
env?: Record<string, string>;
|
|
196
|
+
args?: string[];
|
|
197
|
+
}
|
|
198
|
+
): McpServerConfig {
|
|
199
|
+
const runtime = options?.runtime || detectRuntime(serverPath);
|
|
200
|
+
|
|
201
|
+
let command: string;
|
|
202
|
+
let args: string[];
|
|
203
|
+
|
|
204
|
+
switch (runtime) {
|
|
205
|
+
case 'node':
|
|
206
|
+
command = 'node';
|
|
207
|
+
args = [serverPath, ...(options?.args || [])];
|
|
208
|
+
break;
|
|
209
|
+
case 'python':
|
|
210
|
+
command = 'python';
|
|
211
|
+
args = [serverPath, ...(options?.args || [])];
|
|
212
|
+
break;
|
|
213
|
+
case 'deno':
|
|
214
|
+
command = 'deno';
|
|
215
|
+
args = ['run', '--allow-all', serverPath, ...(options?.args || [])];
|
|
216
|
+
break;
|
|
217
|
+
default:
|
|
218
|
+
command = serverPath;
|
|
219
|
+
args = options?.args || [];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const config: McpServerConfig = { command, args };
|
|
223
|
+
|
|
224
|
+
if (options?.env && Object.keys(options.env).length > 0) {
|
|
225
|
+
config.env = options.env;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return config;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Detect runtime from file extension
|
|
233
|
+
*/
|
|
234
|
+
function detectRuntime(filePath: string): 'node' | 'python' | 'deno' | null {
|
|
235
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
236
|
+
|
|
237
|
+
switch (ext) {
|
|
238
|
+
case '.js':
|
|
239
|
+
case '.mjs':
|
|
240
|
+
case '.cjs':
|
|
241
|
+
return 'node';
|
|
242
|
+
case '.ts':
|
|
243
|
+
// Check if Deno is preferred
|
|
244
|
+
return 'node'; // Default to node with ts-node
|
|
245
|
+
case '.py':
|
|
246
|
+
return 'python';
|
|
247
|
+
default:
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Format config for display/clipboard based on client type
|
|
254
|
+
*/
|
|
255
|
+
export function formatConfigForClient(
|
|
256
|
+
serverName: string,
|
|
257
|
+
serverConfig: McpServerConfig,
|
|
258
|
+
client: McpClientType
|
|
259
|
+
): string {
|
|
260
|
+
if (client === 'claude') {
|
|
261
|
+
return JSON.stringify({
|
|
262
|
+
mcpServers: {
|
|
263
|
+
[serverName]: serverConfig
|
|
264
|
+
}
|
|
265
|
+
}, null, 2);
|
|
266
|
+
} else if (client === 'cursor') {
|
|
267
|
+
return JSON.stringify({
|
|
268
|
+
mcp: {
|
|
269
|
+
servers: {
|
|
270
|
+
[serverName]: serverConfig
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}, null, 2);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return JSON.stringify({
|
|
277
|
+
[serverName]: serverConfig
|
|
278
|
+
}, null, 2);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Open MCP config file in editor
|
|
283
|
+
*/
|
|
284
|
+
export async function openMcpConfigInEditor(client: McpClientType): Promise<void> {
|
|
285
|
+
const configPath = getMcpConfigPath(client);
|
|
286
|
+
if (!configPath) {
|
|
287
|
+
vscode.window.showErrorMessage(`Could not determine config path for ${client}`);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(configPath));
|
|
293
|
+
await vscode.window.showTextDocument(doc);
|
|
294
|
+
} catch {
|
|
295
|
+
// File doesn't exist, create it
|
|
296
|
+
const create = await vscode.window.showInformationMessage(
|
|
297
|
+
`${client} config file does not exist. Create it?`,
|
|
298
|
+
'Create',
|
|
299
|
+
'Cancel'
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
if (create === 'Create') {
|
|
303
|
+
const initialConfig = client === 'claude'
|
|
304
|
+
? { mcpServers: {} }
|
|
305
|
+
: { mcp: { servers: {} } };
|
|
306
|
+
|
|
307
|
+
await writeMcpConfig(client, initialConfig);
|
|
308
|
+
|
|
309
|
+
const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(configPath));
|
|
310
|
+
await vscode.window.showTextDocument(doc);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Validate MCP server config
|
|
317
|
+
*/
|
|
318
|
+
export function validateServerConfig(config: McpServerConfig): string[] {
|
|
319
|
+
const errors: string[] = [];
|
|
320
|
+
|
|
321
|
+
if (!config.command) {
|
|
322
|
+
errors.push('Command is required');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (config.args && !Array.isArray(config.args)) {
|
|
326
|
+
errors.push('Args must be an array');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (config.env && typeof config.env !== 'object') {
|
|
330
|
+
errors.push('Env must be an object');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return errors;
|
|
334
|
+
}
|