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,696 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview code-extractor module implementation
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Code Extractor
|
|
9
|
+
* Extract tools from SDK source code
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { ExtractedTool, SdkMethod } from './types';
|
|
13
|
+
import { GoExtractor } from './extractors/go-extractor';
|
|
14
|
+
import { JavaExtractor } from './extractors/java-extractor';
|
|
15
|
+
import { RustExtractor } from './extractors/rust-extractor';
|
|
16
|
+
|
|
17
|
+
export class CodeExtractor {
|
|
18
|
+
private goExtractor: GoExtractor;
|
|
19
|
+
private javaExtractor: JavaExtractor;
|
|
20
|
+
private rustExtractor: RustExtractor;
|
|
21
|
+
|
|
22
|
+
constructor() {
|
|
23
|
+
this.goExtractor = new GoExtractor();
|
|
24
|
+
this.javaExtractor = new JavaExtractor();
|
|
25
|
+
this.rustExtractor = new RustExtractor();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Extract tools from code files
|
|
30
|
+
*/
|
|
31
|
+
async extract(code: string, filename: string): Promise<ExtractedTool[]> {
|
|
32
|
+
const tools: ExtractedTool[] = [];
|
|
33
|
+
|
|
34
|
+
// Detect language
|
|
35
|
+
const lang = this.detectLanguage(filename);
|
|
36
|
+
|
|
37
|
+
if (lang === 'typescript' || lang === 'javascript') {
|
|
38
|
+
const methods = this.extractMethods(code, filename);
|
|
39
|
+
tools.push(...methods.map(m => this.methodToTool(m)));
|
|
40
|
+
} else if (lang === 'python') {
|
|
41
|
+
const pythonTools = this.extractPythonMcpTools(code, filename);
|
|
42
|
+
tools.push(...pythonTools);
|
|
43
|
+
} else if (lang === 'go') {
|
|
44
|
+
const goTools = await this.goExtractor.extract(code, filename);
|
|
45
|
+
tools.push(...goTools);
|
|
46
|
+
} else if (lang === 'java' || lang === 'kotlin') {
|
|
47
|
+
const javaTools = await this.javaExtractor.extract(code, filename);
|
|
48
|
+
tools.push(...javaTools);
|
|
49
|
+
} else if (lang === 'rust') {
|
|
50
|
+
const rustTools = await this.rustExtractor.extract(code, filename);
|
|
51
|
+
tools.push(...rustTools);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return tools;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Extract MCP tools from Python code using @mcp.tool decorators
|
|
59
|
+
*/
|
|
60
|
+
private extractPythonMcpTools(code: string, filename: string): ExtractedTool[] {
|
|
61
|
+
const tools: ExtractedTool[] = [];
|
|
62
|
+
const lines = code.split('\n');
|
|
63
|
+
|
|
64
|
+
// Pattern for @mcp.tool decorator with various formats
|
|
65
|
+
const decoratorPatterns = [
|
|
66
|
+
// @mcp.tool(name="ToolName", description="...")
|
|
67
|
+
/@(?:mcp|server|app)\.tool\s*\(\s*name\s*=\s*["']([^"']+)["']\s*,\s*description\s*=\s*["']([^"']+)["']/g,
|
|
68
|
+
// @mcp.tool(description="...", name="ToolName")
|
|
69
|
+
/@(?:mcp|server|app)\.tool\s*\(\s*description\s*=\s*["']([^"']+)["']\s*,\s*name\s*=\s*["']([^"']+)["']/g,
|
|
70
|
+
// @server.tool("ToolName")
|
|
71
|
+
/@(?:mcp|server|app)\.tool\s*\(\s*["']([^"']+)["']\s*(?:,\s*description\s*=\s*["']([^"']+)["'])?\s*\)/g,
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < lines.length; i++) {
|
|
75
|
+
const line = lines[i];
|
|
76
|
+
|
|
77
|
+
for (const pattern of decoratorPatterns) {
|
|
78
|
+
pattern.lastIndex = 0;
|
|
79
|
+
const match = pattern.exec(line);
|
|
80
|
+
if (match) {
|
|
81
|
+
// Extract function signature on next lines
|
|
82
|
+
const funcInfo = this.extractPythonFunction(lines, i + 1);
|
|
83
|
+
|
|
84
|
+
// Handle different match groups based on pattern
|
|
85
|
+
let name: string;
|
|
86
|
+
let description: string | undefined;
|
|
87
|
+
|
|
88
|
+
if (match[1] && match[2]) {
|
|
89
|
+
// Check which pattern matched
|
|
90
|
+
if (line.includes('name=') && line.indexOf('name=') < line.indexOf('description=')) {
|
|
91
|
+
name = match[1];
|
|
92
|
+
description = match[2];
|
|
93
|
+
} else if (line.includes('description=') && line.indexOf('description=') < line.indexOf('name=')) {
|
|
94
|
+
description = match[1];
|
|
95
|
+
name = match[2];
|
|
96
|
+
} else {
|
|
97
|
+
name = match[1];
|
|
98
|
+
description = match[2];
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
name = match[1];
|
|
102
|
+
description = match[2];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
tools.push({
|
|
106
|
+
name,
|
|
107
|
+
description: description || funcInfo.docstring || `${name} tool`,
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: this.pythonParamsToSchema(funcInfo.params),
|
|
111
|
+
required: funcInfo.required
|
|
112
|
+
},
|
|
113
|
+
source: {
|
|
114
|
+
type: 'code',
|
|
115
|
+
file: filename,
|
|
116
|
+
line: i + 1
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Also look for Tool class instantiations
|
|
124
|
+
const toolClassPattern = /Tool\s*\(\s*name\s*=\s*["']([^"']+)["']\s*,\s*description\s*=\s*["']([^"']+)["']/g;
|
|
125
|
+
let match;
|
|
126
|
+
while ((match = toolClassPattern.exec(code)) !== null) {
|
|
127
|
+
const lineNum = code.substring(0, match.index).split('\n').length;
|
|
128
|
+
tools.push({
|
|
129
|
+
name: match[1],
|
|
130
|
+
description: match[2],
|
|
131
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
132
|
+
source: { type: 'code', file: filename, line: lineNum }
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return tools;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Extract Python function info from lines after decorator
|
|
141
|
+
*/
|
|
142
|
+
private extractPythonFunction(lines: string[], startLine: number): {
|
|
143
|
+
name: string;
|
|
144
|
+
params: Array<{ name: string; type: string; default?: string }>;
|
|
145
|
+
required: string[];
|
|
146
|
+
docstring?: string;
|
|
147
|
+
} {
|
|
148
|
+
const result = {
|
|
149
|
+
name: '',
|
|
150
|
+
params: [] as Array<{ name: string; type: string; default?: string }>,
|
|
151
|
+
required: [] as string[],
|
|
152
|
+
docstring: undefined as string | undefined
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Find async def or def line and collect full function signature (may span multiple lines)
|
|
156
|
+
let funcSignature = '';
|
|
157
|
+
let funcStartLine = -1;
|
|
158
|
+
let parenDepth = 0;
|
|
159
|
+
let foundDef = false;
|
|
160
|
+
|
|
161
|
+
for (let i = startLine; i < Math.min(startLine + 20, lines.length); i++) {
|
|
162
|
+
const line = lines[i];
|
|
163
|
+
|
|
164
|
+
if (!foundDef) {
|
|
165
|
+
const defMatch = line.match(/(?:async\s+)?def\s+(\w+)\s*\(/);
|
|
166
|
+
if (defMatch) {
|
|
167
|
+
foundDef = true;
|
|
168
|
+
funcStartLine = i;
|
|
169
|
+
result.name = defMatch[1];
|
|
170
|
+
funcSignature = line;
|
|
171
|
+
// Count parentheses
|
|
172
|
+
for (const char of line) {
|
|
173
|
+
if (char === '(') parenDepth++;
|
|
174
|
+
if (char === ')') parenDepth--;
|
|
175
|
+
}
|
|
176
|
+
if (parenDepth === 0) break;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
funcSignature += ' ' + line.trim();
|
|
181
|
+
for (const char of line) {
|
|
182
|
+
if (char === '(') parenDepth++;
|
|
183
|
+
if (char === ')') parenDepth--;
|
|
184
|
+
}
|
|
185
|
+
if (parenDepth === 0) break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Extract params from the full signature
|
|
190
|
+
if (funcSignature) {
|
|
191
|
+
const paramsMatch = funcSignature.match(/\(([^)]*)\)/);
|
|
192
|
+
if (paramsMatch) {
|
|
193
|
+
const paramsStr = paramsMatch[1];
|
|
194
|
+
|
|
195
|
+
// Parse parameters using bracket-aware splitting (skip 'self' and context params)
|
|
196
|
+
const params = this.splitPythonParams(paramsStr).filter(p =>
|
|
197
|
+
p && !p.startsWith('self') && !p.includes('Context') && !p.includes('ctx')
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
for (const param of params) {
|
|
201
|
+
// Parse: name: type = default or name = default or just name
|
|
202
|
+
const paramMatch = param.match(/(\w+)(?:\s*:\s*([^=]+))?(?:\s*=\s*(.+))?/);
|
|
203
|
+
if (paramMatch) {
|
|
204
|
+
const [, name, type, defaultVal] = paramMatch;
|
|
205
|
+
result.params.push({
|
|
206
|
+
name,
|
|
207
|
+
type: type?.trim() || 'string',
|
|
208
|
+
default: defaultVal?.trim()
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (!defaultVal) {
|
|
212
|
+
result.required.push(name);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Look for docstring after function definition
|
|
219
|
+
const docstringStartLine = funcStartLine >= 0 ? funcStartLine + 1 : startLine + 1;
|
|
220
|
+
for (let j = docstringStartLine; j < Math.min(docstringStartLine + 10, lines.length); j++) {
|
|
221
|
+
const docLine = lines[j].trim();
|
|
222
|
+
if (docLine.startsWith('"""') || docLine.startsWith("'''")) {
|
|
223
|
+
// Single line docstring
|
|
224
|
+
const docMatch = docLine.match(/['"]{3}(.+?)['"]{3}/);
|
|
225
|
+
if (docMatch) {
|
|
226
|
+
result.docstring = docMatch[1].trim();
|
|
227
|
+
} else {
|
|
228
|
+
// Multi-line docstring - get first line
|
|
229
|
+
result.docstring = docLine.replace(/^['"]{3}/, '').trim();
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Convert Python params to JSON schema properties
|
|
241
|
+
*/
|
|
242
|
+
private pythonParamsToSchema(params: Array<{ name: string; type: string; default?: string }>): Record<string, any> {
|
|
243
|
+
const properties: Record<string, any> = {};
|
|
244
|
+
|
|
245
|
+
for (const param of params) {
|
|
246
|
+
properties[param.name] = {
|
|
247
|
+
type: this.pythonTypeToJsonSchema(param.type),
|
|
248
|
+
description: `${param.name} parameter`
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
if (param.default !== undefined) {
|
|
252
|
+
properties[param.name].default = this.parsePythonDefault(param.default);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return properties;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Convert Python type to JSON schema type
|
|
261
|
+
* Handles complex typing patterns including:
|
|
262
|
+
* - Container types: List, Dict, Set, Tuple, Sequence, Mapping
|
|
263
|
+
* - Union types: Union[X, Y], X | Y (Python 3.10+)
|
|
264
|
+
* - Optional: Optional[X] (equivalent to Union[X, None])
|
|
265
|
+
* - Literal types: Literal["a", "b"]
|
|
266
|
+
* - Any, None types
|
|
267
|
+
* - Nested generics: List[Dict[str, int]]
|
|
268
|
+
*/
|
|
269
|
+
private pythonTypeToJsonSchema(pythonType: string): string {
|
|
270
|
+
const type = pythonType.toLowerCase().trim();
|
|
271
|
+
|
|
272
|
+
// Handle empty or whitespace-only types
|
|
273
|
+
if (!type) return 'string';
|
|
274
|
+
|
|
275
|
+
// Check container types first (they may contain other type names like 'str')
|
|
276
|
+
// List-like types
|
|
277
|
+
if (type.startsWith('list[') || type === 'list' ||
|
|
278
|
+
type.startsWith('sequence[') || type === 'sequence' ||
|
|
279
|
+
type.startsWith('set[') || type === 'set' ||
|
|
280
|
+
type.startsWith('frozenset[') || type === 'frozenset' ||
|
|
281
|
+
type.startsWith('tuple[') || type === 'tuple' ||
|
|
282
|
+
type.startsWith('iterable[') || type === 'iterable' ||
|
|
283
|
+
type.includes('[]')) {
|
|
284
|
+
return 'array';
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Dict-like types
|
|
288
|
+
if (type.startsWith('dict[') || type === 'dict' ||
|
|
289
|
+
type.startsWith('mapping[') || type === 'mapping' ||
|
|
290
|
+
type.startsWith('mutablemapping[') || type === 'mutablemapping' ||
|
|
291
|
+
type.startsWith('typeddict') || type === 'typeddict' ||
|
|
292
|
+
type.startsWith('record[')) {
|
|
293
|
+
return 'object';
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Handle Optional[X] - extract inner type
|
|
297
|
+
if (type.startsWith('optional[')) {
|
|
298
|
+
const inner = type.match(/optional\[(.+)\]$/i);
|
|
299
|
+
if (inner) return this.pythonTypeToJsonSchema(inner[1]);
|
|
300
|
+
return 'string';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Handle Union[X, Y, ...] - return type of first non-None type
|
|
304
|
+
if (type.startsWith('union[')) {
|
|
305
|
+
const inner = type.match(/union\[(.+)\]$/i);
|
|
306
|
+
if (inner) {
|
|
307
|
+
const types = this.splitGenericArgs(inner[1]);
|
|
308
|
+
const nonNoneType = types.find(t => t.trim().toLowerCase() !== 'none');
|
|
309
|
+
if (nonNoneType) return this.pythonTypeToJsonSchema(nonNoneType);
|
|
310
|
+
}
|
|
311
|
+
return 'string';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Handle Python 3.10+ union syntax: X | Y
|
|
315
|
+
if (type.includes(' | ')) {
|
|
316
|
+
const types = type.split('|').map(t => t.trim());
|
|
317
|
+
const nonNoneType = types.find(t => t !== 'none');
|
|
318
|
+
if (nonNoneType) return this.pythonTypeToJsonSchema(nonNoneType);
|
|
319
|
+
return 'string';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Handle Literal["a", "b"] - these are strings with enum constraint
|
|
323
|
+
if (type.startsWith('literal[')) {
|
|
324
|
+
// Check what's inside the literal
|
|
325
|
+
const inner = type.match(/literal\[(.+)\]/i);
|
|
326
|
+
if (inner) {
|
|
327
|
+
const firstValue = inner[1].split(',')[0].trim();
|
|
328
|
+
// Check for quoted strings
|
|
329
|
+
if (firstValue.startsWith('"') || firstValue.startsWith("'")) return 'string';
|
|
330
|
+
// Check for numeric values (including negative numbers and floats)
|
|
331
|
+
if (/^-?\d+(\.\d+)?$/.test(firstValue)) return 'number';
|
|
332
|
+
// Check for boolean values (case-insensitive since we lowercased)
|
|
333
|
+
if (firstValue === 'true' || firstValue === 'false') return 'boolean';
|
|
334
|
+
}
|
|
335
|
+
return 'string';
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Handle Callable - return 'string' as we can't represent functions in JSON Schema
|
|
339
|
+
if (type.startsWith('callable[') || type === 'callable') {
|
|
340
|
+
return 'string';
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Handle None/NoneType
|
|
344
|
+
if (type === 'none' || type === 'nonetype') {
|
|
345
|
+
return 'null';
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Handle Any
|
|
349
|
+
if (type === 'any') {
|
|
350
|
+
return 'object';
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Check primitive types - use exact match or startsWith for type annotations with whitespace
|
|
354
|
+
// String types
|
|
355
|
+
if (type === 'str' || type === 'string' || type === 'text' ||
|
|
356
|
+
type === 'bytes' || type === 'bytearray') {
|
|
357
|
+
return 'string';
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Integer types (map to 'number' for JSON Schema compatibility)
|
|
361
|
+
if (type === 'int' || type === 'integer') {
|
|
362
|
+
return 'number';
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Float/decimal types
|
|
366
|
+
if (type === 'float' || type === 'double' || type === 'decimal' ||
|
|
367
|
+
type === 'number' || type === 'complex') {
|
|
368
|
+
return 'number';
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Boolean type
|
|
372
|
+
if (type === 'bool' || type === 'boolean') {
|
|
373
|
+
return 'boolean';
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Object types
|
|
377
|
+
if (type === 'object' || type === 'jsonobject') {
|
|
378
|
+
return 'object';
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Path types - treat as strings
|
|
382
|
+
if (type === 'path' || type === 'pathlike' || type.startsWith('path[')) {
|
|
383
|
+
return 'string';
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// UUID - treat as string
|
|
387
|
+
if (type === 'uuid') {
|
|
388
|
+
return 'string';
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Date/time types - treat as strings (ISO format)
|
|
392
|
+
if (type === 'datetime' || type === 'date' || type === 'time' ||
|
|
393
|
+
type === 'timedelta' || type === 'timestamp') {
|
|
394
|
+
return 'string';
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Default to string for unknown types
|
|
398
|
+
return 'string';
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Split generic type arguments, handling nested brackets
|
|
403
|
+
* e.g., "str, Dict[str, int], bool" -> ["str", "Dict[str, int]", "bool"]
|
|
404
|
+
*/
|
|
405
|
+
private splitGenericArgs(args: string): string[] {
|
|
406
|
+
const result: string[] = [];
|
|
407
|
+
let current = '';
|
|
408
|
+
let depth = 0;
|
|
409
|
+
|
|
410
|
+
for (const char of args) {
|
|
411
|
+
if (char === '[') {
|
|
412
|
+
depth++;
|
|
413
|
+
current += char;
|
|
414
|
+
} else if (char === ']') {
|
|
415
|
+
depth--;
|
|
416
|
+
current += char;
|
|
417
|
+
} else if (char === ',' && depth === 0) {
|
|
418
|
+
result.push(current.trim());
|
|
419
|
+
current = '';
|
|
420
|
+
} else {
|
|
421
|
+
current += char;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (current.trim()) {
|
|
426
|
+
result.push(current.trim());
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return result;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Split Python function parameters, handling nested brackets and quotes
|
|
434
|
+
* This correctly handles types like List[str], Dict[str, int], Literal["a", "b"]
|
|
435
|
+
* e.g., "name: str, items: List[int], status: Literal['a', 'b']"
|
|
436
|
+
* -> ["name: str", "items: List[int]", "status: Literal['a', 'b']"]
|
|
437
|
+
*/
|
|
438
|
+
private splitPythonParams(paramsStr: string): string[] {
|
|
439
|
+
const result: string[] = [];
|
|
440
|
+
let current = '';
|
|
441
|
+
let bracketDepth = 0;
|
|
442
|
+
let inSingleQuote = false;
|
|
443
|
+
let inDoubleQuote = false;
|
|
444
|
+
|
|
445
|
+
for (let i = 0; i < paramsStr.length; i++) {
|
|
446
|
+
const char = paramsStr[i];
|
|
447
|
+
const prevChar = i > 0 ? paramsStr[i - 1] : '';
|
|
448
|
+
|
|
449
|
+
// Track quotes (but not escaped ones)
|
|
450
|
+
if (char === "'" && prevChar !== '\\' && !inDoubleQuote) {
|
|
451
|
+
inSingleQuote = !inSingleQuote;
|
|
452
|
+
} else if (char === '"' && prevChar !== '\\' && !inSingleQuote) {
|
|
453
|
+
inDoubleQuote = !inDoubleQuote;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Track bracket depth (only when not in quotes)
|
|
457
|
+
if (!inSingleQuote && !inDoubleQuote) {
|
|
458
|
+
if (char === '[' || char === '(' || char === '{') {
|
|
459
|
+
bracketDepth++;
|
|
460
|
+
} else if (char === ']' || char === ')' || char === '}') {
|
|
461
|
+
bracketDepth--;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Split on comma only at top level (depth 0, not in quotes)
|
|
466
|
+
if (char === ',' && bracketDepth === 0 && !inSingleQuote && !inDoubleQuote) {
|
|
467
|
+
const trimmed = current.trim();
|
|
468
|
+
if (trimmed) {
|
|
469
|
+
result.push(trimmed);
|
|
470
|
+
}
|
|
471
|
+
current = '';
|
|
472
|
+
} else {
|
|
473
|
+
current += char;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Don't forget the last param
|
|
478
|
+
const trimmed = current.trim();
|
|
479
|
+
if (trimmed) {
|
|
480
|
+
result.push(trimmed);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return result;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Parse Python default value
|
|
488
|
+
*/
|
|
489
|
+
private parsePythonDefault(value: string): any {
|
|
490
|
+
if (value === 'None' || value === 'null') return null;
|
|
491
|
+
if (value === 'True') return true;
|
|
492
|
+
if (value === 'False') return false;
|
|
493
|
+
if (/^["']/.test(value)) return value.slice(1, -1);
|
|
494
|
+
if (/^\d+$/.test(value)) return parseInt(value);
|
|
495
|
+
if (/^\d+\.\d+$/.test(value)) return parseFloat(value);
|
|
496
|
+
return value;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Detect programming language
|
|
501
|
+
*/
|
|
502
|
+
private detectLanguage(filename: string): string {
|
|
503
|
+
if (filename.endsWith('.ts') || filename.endsWith('.tsx')) return 'typescript';
|
|
504
|
+
if (filename.endsWith('.js') || filename.endsWith('.jsx') || filename.endsWith('.mjs')) return 'javascript';
|
|
505
|
+
if (filename.endsWith('.py') || filename.endsWith('.pyi')) return 'python';
|
|
506
|
+
if (filename.endsWith('.go')) return 'go';
|
|
507
|
+
if (filename.endsWith('.java')) return 'java';
|
|
508
|
+
if (filename.endsWith('.kt') || filename.endsWith('.kts')) return 'kotlin';
|
|
509
|
+
if (filename.endsWith('.rs')) return 'rust';
|
|
510
|
+
if (filename.endsWith('.proto')) return 'protobuf';
|
|
511
|
+
return 'unknown';
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Extract methods from TypeScript/JavaScript
|
|
516
|
+
*/
|
|
517
|
+
private extractMethods(code: string, filename: string): SdkMethod[] {
|
|
518
|
+
const methods: SdkMethod[] = [];
|
|
519
|
+
|
|
520
|
+
// Match class methods
|
|
521
|
+
const methodPattern = /(?:async\s+)?(\w+)\s*\(([^)]*)\)\s*:\s*([^{]+)\s*\{/g;
|
|
522
|
+
|
|
523
|
+
let match;
|
|
524
|
+
let lineNumber = 1;
|
|
525
|
+
|
|
526
|
+
while ((match = methodPattern.exec(code)) !== null) {
|
|
527
|
+
const [fullMatch, name, paramsStr, returnType] = match;
|
|
528
|
+
|
|
529
|
+
// Skip constructors and private methods
|
|
530
|
+
if (name === 'constructor' || name.startsWith('_')) {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Parse parameters
|
|
535
|
+
const parameters = this.parseParameters(paramsStr);
|
|
536
|
+
|
|
537
|
+
// Get description from JSDoc
|
|
538
|
+
const description = this.extractJsDoc(code, match.index);
|
|
539
|
+
|
|
540
|
+
// Calculate line number
|
|
541
|
+
const codeBeforeMatch = code.substring(0, match.index);
|
|
542
|
+
lineNumber = (codeBeforeMatch.match(/\n/g) || []).length + 1;
|
|
543
|
+
|
|
544
|
+
methods.push({
|
|
545
|
+
name,
|
|
546
|
+
parameters,
|
|
547
|
+
returnType: returnType.trim(),
|
|
548
|
+
description,
|
|
549
|
+
file: filename,
|
|
550
|
+
line: lineNumber
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return methods;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Parse function parameters
|
|
559
|
+
*/
|
|
560
|
+
private parseParameters(paramsStr: string): Array<{
|
|
561
|
+
name: string;
|
|
562
|
+
type: string;
|
|
563
|
+
required: boolean;
|
|
564
|
+
description?: string;
|
|
565
|
+
}> {
|
|
566
|
+
if (!paramsStr.trim()) {
|
|
567
|
+
return [];
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const params = paramsStr.split(',').map(p => p.trim());
|
|
571
|
+
|
|
572
|
+
return params.map(param => {
|
|
573
|
+
// Parse: "name: type" or "name?: type" or "name: type = default"
|
|
574
|
+
const match = param.match(/(\w+)(\??):\s*([^=]+)(?:\s*=\s*(.+))?/);
|
|
575
|
+
|
|
576
|
+
if (!match) {
|
|
577
|
+
return {
|
|
578
|
+
name: param,
|
|
579
|
+
type: 'any',
|
|
580
|
+
required: true
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const [, name, optional, type, defaultValue] = match;
|
|
585
|
+
|
|
586
|
+
return {
|
|
587
|
+
name,
|
|
588
|
+
type: type.trim(),
|
|
589
|
+
required: !optional && !defaultValue,
|
|
590
|
+
description: undefined
|
|
591
|
+
};
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Extract JSDoc comment before code
|
|
597
|
+
*/
|
|
598
|
+
private extractJsDoc(code: string, index: number): string | undefined {
|
|
599
|
+
const codeBeforeMethod = code.substring(0, index);
|
|
600
|
+
const jsdocMatch = codeBeforeMethod.match(/\/\*\*\s*([\s\S]*?)\s*\*\/\s*$/);
|
|
601
|
+
|
|
602
|
+
if (jsdocMatch) {
|
|
603
|
+
const jsdoc = jsdocMatch[1];
|
|
604
|
+
|
|
605
|
+
// Extract description (first line before @tags)
|
|
606
|
+
const lines = jsdoc.split('\n').map(l => l.replace(/^\s*\*\s?/, ''));
|
|
607
|
+
const descriptionLines = [];
|
|
608
|
+
|
|
609
|
+
for (const line of lines) {
|
|
610
|
+
if (line.startsWith('@')) break;
|
|
611
|
+
descriptionLines.push(line);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return descriptionLines.join(' ').trim();
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return undefined;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Convert SDK method to MCP tool
|
|
622
|
+
*/
|
|
623
|
+
private methodToTool(method: SdkMethod): ExtractedTool {
|
|
624
|
+
// Build input schema
|
|
625
|
+
const properties: Record<string, any> = {};
|
|
626
|
+
const required: string[] = [];
|
|
627
|
+
|
|
628
|
+
for (const param of method.parameters) {
|
|
629
|
+
properties[param.name] = {
|
|
630
|
+
type: this.typeScriptToJsonSchema(param.type),
|
|
631
|
+
description: param.description || `${param.name} parameter`
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
if (param.required) {
|
|
635
|
+
required.push(param.name);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Generate implementation
|
|
640
|
+
const implementation = this.generateImplementation(method);
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
name: this.toToolName(method.name),
|
|
644
|
+
description: method.description || `Call ${method.name}`,
|
|
645
|
+
inputSchema: {
|
|
646
|
+
type: 'object',
|
|
647
|
+
properties,
|
|
648
|
+
required
|
|
649
|
+
},
|
|
650
|
+
implementation,
|
|
651
|
+
source: {
|
|
652
|
+
type: 'code',
|
|
653
|
+
file: method.file,
|
|
654
|
+
line: method.line
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Convert TypeScript type to JSON Schema type
|
|
661
|
+
*/
|
|
662
|
+
private typeScriptToJsonSchema(tsType: string): string {
|
|
663
|
+
const type = tsType.toLowerCase();
|
|
664
|
+
|
|
665
|
+
if (type.includes('string')) return 'string';
|
|
666
|
+
if (type.includes('number')) return 'number';
|
|
667
|
+
if (type.includes('boolean')) return 'boolean';
|
|
668
|
+
if (type.includes('array') || type.includes('[]')) return 'array';
|
|
669
|
+
if (type.includes('object') || type.includes('{}')) return 'object';
|
|
670
|
+
|
|
671
|
+
return 'string'; // Default
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Convert method name to tool name
|
|
676
|
+
*/
|
|
677
|
+
private toToolName(methodName: string): string {
|
|
678
|
+
return methodName.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Generate implementation code
|
|
683
|
+
*/
|
|
684
|
+
private generateImplementation(method: SdkMethod): string {
|
|
685
|
+
const paramNames = method.parameters.map(p => p.name);
|
|
686
|
+
|
|
687
|
+
return `async function ${method.name}(args: any) {
|
|
688
|
+
const { ${paramNames.join(', ')} } = args;
|
|
689
|
+
|
|
690
|
+
// Call SDK method
|
|
691
|
+
const result = await sdk.${method.name}(${paramNames.join(', ')});
|
|
692
|
+
|
|
693
|
+
return result;
|
|
694
|
+
}`;
|
|
695
|
+
}
|
|
696
|
+
}
|