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,937 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Java code extractor for extracting tools from Java codebases
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ExtractedTool, ParsedDocumentation } from '../types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extracted Java method information
|
|
11
|
+
*/
|
|
12
|
+
interface JavaMethod {
|
|
13
|
+
name: string;
|
|
14
|
+
className: string;
|
|
15
|
+
visibility: 'public' | 'protected' | 'private' | 'package';
|
|
16
|
+
isStatic: boolean;
|
|
17
|
+
returnType: string;
|
|
18
|
+
params: Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
type: string;
|
|
21
|
+
annotations: string[];
|
|
22
|
+
}>;
|
|
23
|
+
annotations: string[];
|
|
24
|
+
documentation: ParsedDocumentation | null;
|
|
25
|
+
line: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Java endpoint info (for Spring/JAX-RS)
|
|
30
|
+
*/
|
|
31
|
+
interface JavaEndpoint {
|
|
32
|
+
method: string;
|
|
33
|
+
path: string;
|
|
34
|
+
handler: JavaMethod | null;
|
|
35
|
+
produces?: string[];
|
|
36
|
+
consumes?: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* JavaExtractor extracts tools from Java codebases
|
|
41
|
+
* Supports:
|
|
42
|
+
* - Spring Boot @RequestMapping, @GetMapping, etc.
|
|
43
|
+
* - JAX-RS @Path, @GET, @POST, etc.
|
|
44
|
+
* - Micronaut @Controller routes
|
|
45
|
+
* - Public methods with Javadoc
|
|
46
|
+
*/
|
|
47
|
+
export class JavaExtractor {
|
|
48
|
+
/**
|
|
49
|
+
* Extract tools from Java code
|
|
50
|
+
*/
|
|
51
|
+
async extract(code: string, filename: string): Promise<ExtractedTool[]> {
|
|
52
|
+
const tools: ExtractedTool[] = [];
|
|
53
|
+
|
|
54
|
+
// Extract from web framework annotations
|
|
55
|
+
const springTools = this.extractSpringEndpoints(code, filename);
|
|
56
|
+
const jaxrsTools = this.extractJaxRsEndpoints(code, filename);
|
|
57
|
+
const micronautTools = this.extractMicronautEndpoints(code, filename);
|
|
58
|
+
|
|
59
|
+
tools.push(...springTools, ...jaxrsTools, ...micronautTools);
|
|
60
|
+
|
|
61
|
+
// Extract from public methods with Javadoc
|
|
62
|
+
const publicMethods = this.extractPublicMethods(code, filename);
|
|
63
|
+
tools.push(...publicMethods);
|
|
64
|
+
|
|
65
|
+
return tools;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Extract Spring Boot endpoints
|
|
70
|
+
* Annotations:
|
|
71
|
+
* - @RequestMapping
|
|
72
|
+
* - @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping
|
|
73
|
+
* - @RestController, @Controller
|
|
74
|
+
*/
|
|
75
|
+
private extractSpringEndpoints(code: string, filename: string): ExtractedTool[] {
|
|
76
|
+
const tools: ExtractedTool[] = [];
|
|
77
|
+
const lines = code.split('\n');
|
|
78
|
+
|
|
79
|
+
// Find class-level @RequestMapping for base path
|
|
80
|
+
let basePath = '';
|
|
81
|
+
const classRequestMapping = code.match(/@RequestMapping\s*\(\s*(?:value\s*=\s*)?"([^"]+)"/);
|
|
82
|
+
if (classRequestMapping) {
|
|
83
|
+
basePath = classRequestMapping[1];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Method-level mapping annotations
|
|
87
|
+
const mappingAnnotations = [
|
|
88
|
+
{ pattern: /@GetMapping\s*\(\s*(?:value\s*=\s*)?"([^"]+)"/, method: 'GET' },
|
|
89
|
+
{ pattern: /@PostMapping\s*\(\s*(?:value\s*=\s*)?"([^"]+)"/, method: 'POST' },
|
|
90
|
+
{ pattern: /@PutMapping\s*\(\s*(?:value\s*=\s*)?"([^"]+)"/, method: 'PUT' },
|
|
91
|
+
{ pattern: /@DeleteMapping\s*\(\s*(?:value\s*=\s*)?"([^"]+)"/, method: 'DELETE' },
|
|
92
|
+
{ pattern: /@PatchMapping\s*\(\s*(?:value\s*=\s*)?"([^"]+)"/, method: 'PATCH' },
|
|
93
|
+
{ pattern: /@GetMapping\s*(?:\(\s*\))?(?!\s*\()/, method: 'GET', noPath: true },
|
|
94
|
+
{ pattern: /@PostMapping\s*(?:\(\s*\))?(?!\s*\()/, method: 'POST', noPath: true },
|
|
95
|
+
{ pattern: /@PutMapping\s*(?:\(\s*\))?(?!\s*\()/, method: 'PUT', noPath: true },
|
|
96
|
+
{ pattern: /@DeleteMapping\s*(?:\(\s*\))?(?!\s*\()/, method: 'DELETE', noPath: true },
|
|
97
|
+
{ pattern: /@PatchMapping\s*(?:\(\s*\))?(?!\s*\()/, method: 'PATCH', noPath: true },
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
// Also handle @RequestMapping with method attribute
|
|
101
|
+
const requestMappingPattern = /@RequestMapping\s*\(\s*(?:value\s*=\s*)?"([^"]+)"(?:\s*,\s*method\s*=\s*RequestMethod\.(\w+))?/g;
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < lines.length; i++) {
|
|
104
|
+
const line = lines[i];
|
|
105
|
+
|
|
106
|
+
// Check for specific mapping annotations
|
|
107
|
+
for (const { pattern, method, noPath } of mappingAnnotations) {
|
|
108
|
+
const match = line.match(pattern);
|
|
109
|
+
if (match) {
|
|
110
|
+
const path = noPath ? '' : (match[1] || '');
|
|
111
|
+
const fullPath = this.joinPaths(basePath, path);
|
|
112
|
+
|
|
113
|
+
// Find the method definition
|
|
114
|
+
const methodInfo = this.findNextMethod(lines, i + 1);
|
|
115
|
+
|
|
116
|
+
if (methodInfo) {
|
|
117
|
+
tools.push({
|
|
118
|
+
name: this.generateToolName(method, fullPath, methodInfo.name),
|
|
119
|
+
description: methodInfo.documentation?.description || `${method} ${fullPath}`,
|
|
120
|
+
inputSchema: this.buildSpringInputSchema(methodInfo, fullPath),
|
|
121
|
+
source: {
|
|
122
|
+
type: 'code',
|
|
123
|
+
file: filename,
|
|
124
|
+
line: i + 1
|
|
125
|
+
},
|
|
126
|
+
confidence: 0.85
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check for @RequestMapping
|
|
133
|
+
requestMappingPattern.lastIndex = 0;
|
|
134
|
+
const rmMatch = requestMappingPattern.exec(line);
|
|
135
|
+
if (rmMatch && !line.includes('@interface') && !this.isClassLevel(lines, i)) {
|
|
136
|
+
const path = rmMatch[1] || '';
|
|
137
|
+
const method = rmMatch[2] || 'GET';
|
|
138
|
+
const fullPath = this.joinPaths(basePath, path);
|
|
139
|
+
|
|
140
|
+
const methodInfo = this.findNextMethod(lines, i + 1);
|
|
141
|
+
|
|
142
|
+
if (methodInfo) {
|
|
143
|
+
tools.push({
|
|
144
|
+
name: this.generateToolName(method, fullPath, methodInfo.name),
|
|
145
|
+
description: methodInfo.documentation?.description || `${method} ${fullPath}`,
|
|
146
|
+
inputSchema: this.buildSpringInputSchema(methodInfo, fullPath),
|
|
147
|
+
source: {
|
|
148
|
+
type: 'code',
|
|
149
|
+
file: filename,
|
|
150
|
+
line: i + 1
|
|
151
|
+
},
|
|
152
|
+
confidence: 0.85
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return tools;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Extract JAX-RS endpoints
|
|
163
|
+
* Annotations:
|
|
164
|
+
* - @Path
|
|
165
|
+
* - @GET, @POST, @PUT, @DELETE, @PATCH, @HEAD, @OPTIONS
|
|
166
|
+
* - @Produces, @Consumes
|
|
167
|
+
*/
|
|
168
|
+
private extractJaxRsEndpoints(code: string, filename: string): ExtractedTool[] {
|
|
169
|
+
const tools: ExtractedTool[] = [];
|
|
170
|
+
const lines = code.split('\n');
|
|
171
|
+
|
|
172
|
+
// Find class-level @Path
|
|
173
|
+
let basePath = '';
|
|
174
|
+
const classPath = code.match(/@Path\s*\(\s*"([^"]+)"\s*\)/);
|
|
175
|
+
if (classPath) {
|
|
176
|
+
basePath = classPath[1];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Check if this is a JAX-RS file
|
|
180
|
+
if (!code.includes('@Path') && !code.includes('@GET') && !code.includes('@POST')) {
|
|
181
|
+
return tools;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const httpMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
|
185
|
+
|
|
186
|
+
for (let i = 0; i < lines.length; i++) {
|
|
187
|
+
const line = lines[i];
|
|
188
|
+
|
|
189
|
+
// Look for HTTP method annotations
|
|
190
|
+
for (const httpMethod of httpMethods) {
|
|
191
|
+
if (line.includes(`@${httpMethod}`)) {
|
|
192
|
+
// Find @Path annotation near this line
|
|
193
|
+
let methodPath = '';
|
|
194
|
+
for (let j = Math.max(0, i - 3); j <= Math.min(lines.length - 1, i + 3); j++) {
|
|
195
|
+
const pathMatch = lines[j].match(/@Path\s*\(\s*"([^"]+)"\s*\)/);
|
|
196
|
+
if (pathMatch && j !== this.getClassPathLine(lines)) {
|
|
197
|
+
methodPath = pathMatch[1];
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const fullPath = this.joinPaths(basePath, methodPath);
|
|
203
|
+
const methodInfo = this.findNextMethod(lines, i + 1);
|
|
204
|
+
|
|
205
|
+
if (methodInfo) {
|
|
206
|
+
tools.push({
|
|
207
|
+
name: this.generateToolName(httpMethod, fullPath, methodInfo.name),
|
|
208
|
+
description: methodInfo.documentation?.description || `${httpMethod} ${fullPath}`,
|
|
209
|
+
inputSchema: this.buildJaxRsInputSchema(methodInfo, fullPath),
|
|
210
|
+
source: {
|
|
211
|
+
type: 'code',
|
|
212
|
+
file: filename,
|
|
213
|
+
line: i + 1
|
|
214
|
+
},
|
|
215
|
+
confidence: 0.85
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return tools;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Extract Micronaut endpoints
|
|
228
|
+
* Annotations:
|
|
229
|
+
* - @Controller
|
|
230
|
+
* - @Get, @Post, @Put, @Delete, @Patch
|
|
231
|
+
*/
|
|
232
|
+
private extractMicronautEndpoints(code: string, filename: string): ExtractedTool[] {
|
|
233
|
+
const tools: ExtractedTool[] = [];
|
|
234
|
+
const lines = code.split('\n');
|
|
235
|
+
|
|
236
|
+
// Check if this is a Micronaut file
|
|
237
|
+
if (!code.includes('@Controller') && !code.includes('io.micronaut')) {
|
|
238
|
+
return tools;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Find class-level @Controller path
|
|
242
|
+
let basePath = '';
|
|
243
|
+
const controllerMatch = code.match(/@Controller\s*\(\s*"([^"]+)"\s*\)/);
|
|
244
|
+
if (controllerMatch) {
|
|
245
|
+
basePath = controllerMatch[1];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const mappingAnnotations = [
|
|
249
|
+
{ pattern: /@Get\s*\(\s*"([^"]+)"\s*\)/, method: 'GET' },
|
|
250
|
+
{ pattern: /@Post\s*\(\s*"([^"]+)"\s*\)/, method: 'POST' },
|
|
251
|
+
{ pattern: /@Put\s*\(\s*"([^"]+)"\s*\)/, method: 'PUT' },
|
|
252
|
+
{ pattern: /@Delete\s*\(\s*"([^"]+)"\s*\)/, method: 'DELETE' },
|
|
253
|
+
{ pattern: /@Patch\s*\(\s*"([^"]+)"\s*\)/, method: 'PATCH' },
|
|
254
|
+
{ pattern: /@Get\s*(?:\(\s*\))?(?!\s*\()/, method: 'GET', noPath: true },
|
|
255
|
+
{ pattern: /@Post\s*(?:\(\s*\))?(?!\s*\()/, method: 'POST', noPath: true },
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
for (let i = 0; i < lines.length; i++) {
|
|
259
|
+
const line = lines[i];
|
|
260
|
+
|
|
261
|
+
for (const { pattern, method, noPath } of mappingAnnotations) {
|
|
262
|
+
const match = line.match(pattern);
|
|
263
|
+
if (match) {
|
|
264
|
+
const path = noPath ? '' : (match[1] || '');
|
|
265
|
+
const fullPath = this.joinPaths(basePath, path);
|
|
266
|
+
|
|
267
|
+
const methodInfo = this.findNextMethod(lines, i + 1);
|
|
268
|
+
|
|
269
|
+
if (methodInfo) {
|
|
270
|
+
tools.push({
|
|
271
|
+
name: this.generateToolName(method, fullPath, methodInfo.name),
|
|
272
|
+
description: methodInfo.documentation?.description || `${method} ${fullPath}`,
|
|
273
|
+
inputSchema: this.buildMicronautInputSchema(methodInfo, fullPath),
|
|
274
|
+
source: {
|
|
275
|
+
type: 'code',
|
|
276
|
+
file: filename,
|
|
277
|
+
line: i + 1
|
|
278
|
+
},
|
|
279
|
+
confidence: 0.8
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return tools;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Extract public methods with Javadoc
|
|
291
|
+
*/
|
|
292
|
+
private extractPublicMethods(code: string, filename: string): ExtractedTool[] {
|
|
293
|
+
const tools: ExtractedTool[] = [];
|
|
294
|
+
const lines = code.split('\n');
|
|
295
|
+
|
|
296
|
+
for (let i = 0; i < lines.length; i++) {
|
|
297
|
+
const line = lines[i];
|
|
298
|
+
|
|
299
|
+
// Look for public method declarations
|
|
300
|
+
if (/^\s*public\s+(?:static\s+)?(?!class|interface|enum)[\w<>\[\],\s]+\s+\w+\s*\(/.test(line)) {
|
|
301
|
+
// Skip if it has web framework annotations (already processed)
|
|
302
|
+
let hasWebAnnotation = false;
|
|
303
|
+
for (let j = Math.max(0, i - 5); j < i; j++) {
|
|
304
|
+
if (/@(Get|Post|Put|Delete|Patch|RequestMapping|Path|GET|POST|PUT|DELETE)/.test(lines[j])) {
|
|
305
|
+
hasWebAnnotation = true;
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (hasWebAnnotation) continue;
|
|
311
|
+
|
|
312
|
+
const methodInfo = this.parseMethod(lines, i);
|
|
313
|
+
|
|
314
|
+
if (methodInfo && methodInfo.documentation && methodInfo.visibility === 'public') {
|
|
315
|
+
tools.push({
|
|
316
|
+
name: methodInfo.name,
|
|
317
|
+
description: methodInfo.documentation.description || methodInfo.name,
|
|
318
|
+
inputSchema: this.buildMethodInputSchema(methodInfo),
|
|
319
|
+
source: {
|
|
320
|
+
type: 'code',
|
|
321
|
+
file: filename,
|
|
322
|
+
line: i + 1
|
|
323
|
+
},
|
|
324
|
+
confidence: 0.6,
|
|
325
|
+
confidenceFactors: {
|
|
326
|
+
documentation: 0.8,
|
|
327
|
+
types: 0.9,
|
|
328
|
+
examples: methodInfo.documentation.examples?.length ? 0.7 : 0.2,
|
|
329
|
+
source: 0.7
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return tools;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Find the next method definition
|
|
341
|
+
*/
|
|
342
|
+
private findNextMethod(lines: string[], startLine: number): JavaMethod | null {
|
|
343
|
+
for (let i = startLine; i < Math.min(startLine + 10, lines.length); i++) {
|
|
344
|
+
const line = lines[i];
|
|
345
|
+
// Look for method signature (not class/interface declaration)
|
|
346
|
+
if (/^\s*(public|protected|private)?\s*(static\s+)?[\w<>\[\],\s]+\s+\w+\s*\(/.test(line) &&
|
|
347
|
+
!line.includes('class ') && !line.includes('interface ') && !line.includes('enum ')) {
|
|
348
|
+
return this.parseMethod(lines, i);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Parse a Java method definition
|
|
356
|
+
*/
|
|
357
|
+
private parseMethod(lines: string[], lineIndex: number): JavaMethod | null {
|
|
358
|
+
let methodLine = lines[lineIndex];
|
|
359
|
+
|
|
360
|
+
// Handle multi-line signatures
|
|
361
|
+
let parenDepth = 0;
|
|
362
|
+
let i = lineIndex;
|
|
363
|
+
while (i < lines.length) {
|
|
364
|
+
for (const char of lines[i]) {
|
|
365
|
+
if (char === '(') parenDepth++;
|
|
366
|
+
if (char === ')') parenDepth--;
|
|
367
|
+
}
|
|
368
|
+
if (parenDepth === 0 && i > lineIndex) {
|
|
369
|
+
methodLine = lines.slice(lineIndex, i + 1).join(' ');
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
if (parenDepth === 0) break;
|
|
373
|
+
i++;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Parse method signature - but need to handle params with nested parens
|
|
377
|
+
// First, extract the header part (before opening paren)
|
|
378
|
+
const headerMatch = methodLine.match(
|
|
379
|
+
/^\s*(public|protected|private)?\s*(static\s+)?([\w<>\[\],\s]+)\s+(\w+)\s*\(/
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
if (!headerMatch) return null;
|
|
383
|
+
|
|
384
|
+
const [headerPart, visibility, isStatic, returnType, name] = headerMatch;
|
|
385
|
+
|
|
386
|
+
// Extract parameters by finding the matching closing paren
|
|
387
|
+
let paramsStart = headerPart.length;
|
|
388
|
+
let depth = 1;
|
|
389
|
+
let paramsEnd = paramsStart;
|
|
390
|
+
|
|
391
|
+
for (let idx = paramsStart; idx < methodLine.length; idx++) {
|
|
392
|
+
const char = methodLine[idx];
|
|
393
|
+
if (char === '(') depth++;
|
|
394
|
+
else if (char === ')') {
|
|
395
|
+
depth--;
|
|
396
|
+
if (depth === 0) {
|
|
397
|
+
paramsEnd = idx;
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const paramsStr = methodLine.substring(paramsStart, paramsEnd);
|
|
404
|
+
|
|
405
|
+
// Parse parameters
|
|
406
|
+
const params = this.parseParameters(paramsStr, lines, lineIndex);
|
|
407
|
+
|
|
408
|
+
// Get class name from file
|
|
409
|
+
const classMatch = lines.join('\n').match(/class\s+(\w+)/);
|
|
410
|
+
const className = classMatch ? classMatch[1] : 'Unknown';
|
|
411
|
+
|
|
412
|
+
// Get annotations
|
|
413
|
+
const annotations = this.getAnnotations(lines, lineIndex);
|
|
414
|
+
|
|
415
|
+
// Get Javadoc
|
|
416
|
+
const documentation = this.parseJavadoc(lines, lineIndex);
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
name,
|
|
420
|
+
className,
|
|
421
|
+
visibility: (visibility as JavaMethod['visibility']) || 'package',
|
|
422
|
+
isStatic: !!isStatic,
|
|
423
|
+
returnType: returnType.trim(),
|
|
424
|
+
params,
|
|
425
|
+
annotations,
|
|
426
|
+
documentation,
|
|
427
|
+
line: lineIndex + 1
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Parse method parameters
|
|
433
|
+
*/
|
|
434
|
+
private parseParameters(
|
|
435
|
+
paramsStr: string,
|
|
436
|
+
lines: string[],
|
|
437
|
+
methodLine: number
|
|
438
|
+
): JavaMethod['params'] {
|
|
439
|
+
const params: JavaMethod['params'] = [];
|
|
440
|
+
if (!paramsStr.trim()) return params;
|
|
441
|
+
|
|
442
|
+
// Split by comma, handling generics
|
|
443
|
+
const paramParts = this.splitParams(paramsStr);
|
|
444
|
+
|
|
445
|
+
for (const part of paramParts) {
|
|
446
|
+
const trimmed = part.trim();
|
|
447
|
+
if (!trimmed) continue;
|
|
448
|
+
|
|
449
|
+
// Extract annotations
|
|
450
|
+
const annotations: string[] = [];
|
|
451
|
+
let cleanPart = trimmed;
|
|
452
|
+
|
|
453
|
+
const annotationMatches = trimmed.matchAll(/@(\w+)(?:\([^)]*\))?\s*/g);
|
|
454
|
+
for (const match of annotationMatches) {
|
|
455
|
+
annotations.push(match[0].trim());
|
|
456
|
+
cleanPart = cleanPart.replace(match[0], '');
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Parse type and name
|
|
460
|
+
const paramMatch = cleanPart.trim().match(/^([\w<>\[\],\s.]+)\s+(\w+)$/);
|
|
461
|
+
if (paramMatch) {
|
|
462
|
+
params.push({
|
|
463
|
+
type: paramMatch[1].trim(),
|
|
464
|
+
name: paramMatch[2],
|
|
465
|
+
annotations
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return params;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Split parameters handling generics
|
|
475
|
+
*/
|
|
476
|
+
private splitParams(paramsStr: string): string[] {
|
|
477
|
+
const params: string[] = [];
|
|
478
|
+
let current = '';
|
|
479
|
+
let depth = 0;
|
|
480
|
+
|
|
481
|
+
for (const char of paramsStr) {
|
|
482
|
+
if (char === '<' || char === '(' || char === '[') {
|
|
483
|
+
depth++;
|
|
484
|
+
current += char;
|
|
485
|
+
} else if (char === '>' || char === ')' || char === ']') {
|
|
486
|
+
depth--;
|
|
487
|
+
current += char;
|
|
488
|
+
} else if (char === ',' && depth === 0) {
|
|
489
|
+
params.push(current.trim());
|
|
490
|
+
current = '';
|
|
491
|
+
} else {
|
|
492
|
+
current += char;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (current.trim()) {
|
|
497
|
+
params.push(current.trim());
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return params;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Get annotations above a method
|
|
505
|
+
*/
|
|
506
|
+
private getAnnotations(lines: string[], methodLine: number): string[] {
|
|
507
|
+
const annotations: string[] = [];
|
|
508
|
+
let i = methodLine - 1;
|
|
509
|
+
|
|
510
|
+
while (i >= 0) {
|
|
511
|
+
const line = lines[i].trim();
|
|
512
|
+
if (line.startsWith('@')) {
|
|
513
|
+
annotations.unshift(line);
|
|
514
|
+
i--;
|
|
515
|
+
} else if (line === '' || line.startsWith('*') || line.startsWith('/*') || line.startsWith('//')) {
|
|
516
|
+
i--;
|
|
517
|
+
} else {
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return annotations;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Parse Javadoc comments
|
|
527
|
+
*/
|
|
528
|
+
private parseJavadoc(lines: string[], methodLine: number): ParsedDocumentation | null {
|
|
529
|
+
// Find start of Javadoc
|
|
530
|
+
let javadocEnd = -1;
|
|
531
|
+
let i = methodLine - 1;
|
|
532
|
+
|
|
533
|
+
// Skip annotations
|
|
534
|
+
while (i >= 0 && (lines[i].trim().startsWith('@') || lines[i].trim() === '')) {
|
|
535
|
+
i--;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Look for */
|
|
539
|
+
while (i >= 0) {
|
|
540
|
+
if (lines[i].includes('*/')) {
|
|
541
|
+
javadocEnd = i;
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
if (!lines[i].trim().startsWith('*') && !lines[i].trim().startsWith('@') && lines[i].trim() !== '') {
|
|
545
|
+
return null; // No Javadoc
|
|
546
|
+
}
|
|
547
|
+
i--;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (javadocEnd === -1) return null;
|
|
551
|
+
|
|
552
|
+
// Find start /**
|
|
553
|
+
let javadocStart = javadocEnd;
|
|
554
|
+
while (javadocStart >= 0) {
|
|
555
|
+
if (lines[javadocStart].includes('/**')) {
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
javadocStart--;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (javadocStart < 0) return null;
|
|
562
|
+
|
|
563
|
+
// Parse Javadoc content
|
|
564
|
+
const javadocLines: string[] = [];
|
|
565
|
+
for (let j = javadocStart; j <= javadocEnd; j++) {
|
|
566
|
+
let line = lines[j]
|
|
567
|
+
.replace(/\/\*\*/, '')
|
|
568
|
+
.replace(/\*\//, '')
|
|
569
|
+
.replace(/^\s*\*\s?/, '')
|
|
570
|
+
.trim();
|
|
571
|
+
if (line) {
|
|
572
|
+
javadocLines.push(line);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return this.parseDocumentation(javadocLines);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Parse documentation content
|
|
581
|
+
*/
|
|
582
|
+
private parseDocumentation(docLines: string[]): ParsedDocumentation {
|
|
583
|
+
const doc: ParsedDocumentation = {
|
|
584
|
+
params: [],
|
|
585
|
+
examples: []
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
let description = '';
|
|
589
|
+
let inDescription = true;
|
|
590
|
+
|
|
591
|
+
for (const line of docLines) {
|
|
592
|
+
// @param tag
|
|
593
|
+
const paramMatch = line.match(/^@param\s+(\w+)\s+(.*)/);
|
|
594
|
+
if (paramMatch) {
|
|
595
|
+
inDescription = false;
|
|
596
|
+
doc.params.push({
|
|
597
|
+
name: paramMatch[1],
|
|
598
|
+
description: paramMatch[2]
|
|
599
|
+
});
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// @return tag
|
|
604
|
+
const returnMatch = line.match(/^@returns?\s+(.*)/);
|
|
605
|
+
if (returnMatch) {
|
|
606
|
+
inDescription = false;
|
|
607
|
+
doc.returns = { description: returnMatch[1] };
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// @throws/@exception tag
|
|
612
|
+
const throwsMatch = line.match(/^@(?:throws|exception)\s+(.*)/);
|
|
613
|
+
if (throwsMatch) {
|
|
614
|
+
inDescription = false;
|
|
615
|
+
if (!doc.throws) doc.throws = [];
|
|
616
|
+
doc.throws.push(throwsMatch[1]);
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// @deprecated tag
|
|
621
|
+
if (line.startsWith('@deprecated')) {
|
|
622
|
+
inDescription = false;
|
|
623
|
+
doc.deprecated = true;
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// @since tag
|
|
628
|
+
const sinceMatch = line.match(/^@since\s+(.*)/);
|
|
629
|
+
if (sinceMatch) {
|
|
630
|
+
inDescription = false;
|
|
631
|
+
doc.since = sinceMatch[1];
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Skip other @ tags
|
|
636
|
+
if (line.startsWith('@')) {
|
|
637
|
+
inDescription = false;
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// Add to description
|
|
642
|
+
if (inDescription) {
|
|
643
|
+
description += (description ? ' ' : '') + line;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
doc.description = description.trim() || undefined;
|
|
648
|
+
return doc;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Check if current line is class-level (not method-level)
|
|
653
|
+
*/
|
|
654
|
+
private isClassLevel(lines: string[], lineIndex: number): boolean {
|
|
655
|
+
// Look ahead for class keyword before next method
|
|
656
|
+
for (let i = lineIndex + 1; i < Math.min(lineIndex + 10, lines.length); i++) {
|
|
657
|
+
if (/^\s*public\s+class\s+/.test(lines[i])) {
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
if (/^\s*(public|protected|private)?\s*(static\s+)?[\w<>\[\],\s]+\s+\w+\s*\(/.test(lines[i])) {
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Get line number of class-level @Path annotation
|
|
669
|
+
*/
|
|
670
|
+
private getClassPathLine(lines: string[]): number {
|
|
671
|
+
for (let i = 0; i < lines.length; i++) {
|
|
672
|
+
if (lines[i].includes('@Path') && this.isClassLevel(lines, i)) {
|
|
673
|
+
return i;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return -1;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Join path segments
|
|
681
|
+
*/
|
|
682
|
+
private joinPaths(base: string, path: string): string {
|
|
683
|
+
if (!base && !path) return '/';
|
|
684
|
+
if (!base) return path.startsWith('/') ? path : '/' + path;
|
|
685
|
+
if (!path) return base.startsWith('/') ? base : '/' + base;
|
|
686
|
+
|
|
687
|
+
const normalizedBase = base.endsWith('/') ? base.slice(0, -1) : base;
|
|
688
|
+
const normalizedPath = path.startsWith('/') ? path : '/' + path;
|
|
689
|
+
|
|
690
|
+
return (normalizedBase.startsWith('/') ? '' : '/') + normalizedBase + normalizedPath;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Generate tool name
|
|
695
|
+
*/
|
|
696
|
+
private generateToolName(method: string, path: string, handlerName: string): string {
|
|
697
|
+
const pathParts = path
|
|
698
|
+
.split('/')
|
|
699
|
+
.filter(p => p && !p.startsWith('{') && !p.startsWith(':'))
|
|
700
|
+
.map(p => p.replace(/-/g, '_').toLowerCase());
|
|
701
|
+
|
|
702
|
+
if (pathParts.length > 0) {
|
|
703
|
+
return `${method.toLowerCase()}_${pathParts.join('_')}`;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Convert method name from camelCase to snake_case
|
|
707
|
+
return handlerName.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Build input schema for Spring endpoints
|
|
712
|
+
*/
|
|
713
|
+
private buildSpringInputSchema(
|
|
714
|
+
methodInfo: JavaMethod,
|
|
715
|
+
path: string
|
|
716
|
+
): { type: 'object'; properties: Record<string, any>; required: string[] } {
|
|
717
|
+
const properties: Record<string, any> = {};
|
|
718
|
+
const required: string[] = [];
|
|
719
|
+
|
|
720
|
+
// Extract path parameters
|
|
721
|
+
const pathParams = path.match(/\{(\w+)\}/g) || [];
|
|
722
|
+
for (const param of pathParams) {
|
|
723
|
+
const name = param.slice(1, -1);
|
|
724
|
+
properties[name] = {
|
|
725
|
+
type: 'string',
|
|
726
|
+
description: `Path parameter: ${name}`
|
|
727
|
+
};
|
|
728
|
+
required.push(name);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Add method parameters
|
|
732
|
+
for (const param of methodInfo.params) {
|
|
733
|
+
// Skip framework types
|
|
734
|
+
if (this.isSpringFrameworkType(param.type)) continue;
|
|
735
|
+
|
|
736
|
+
// Check for @RequestBody, @RequestParam, @PathVariable annotations
|
|
737
|
+
const isPathVariable = param.annotations.some(a => a.includes('@PathVariable'));
|
|
738
|
+
const isRequestBody = param.annotations.some(a => a.includes('@RequestBody'));
|
|
739
|
+
const isRequestParam = param.annotations.some(a => a.includes('@RequestParam'));
|
|
740
|
+
|
|
741
|
+
if (isPathVariable) continue; // Already handled
|
|
742
|
+
|
|
743
|
+
const paramDescription = methodInfo.documentation?.params.find(p => p.name === param.name)?.description;
|
|
744
|
+
|
|
745
|
+
if (isRequestBody) {
|
|
746
|
+
properties[param.name] = {
|
|
747
|
+
type: 'object',
|
|
748
|
+
description: paramDescription || `Request body: ${param.name}`
|
|
749
|
+
};
|
|
750
|
+
required.push(param.name);
|
|
751
|
+
} else if (isRequestParam) {
|
|
752
|
+
// Check if required
|
|
753
|
+
const reqMatch = param.annotations.find(a => a.includes('@RequestParam'))?.match(/required\s*=\s*(true|false)/);
|
|
754
|
+
const isRequired = !reqMatch || reqMatch[1] === 'true';
|
|
755
|
+
|
|
756
|
+
properties[param.name] = {
|
|
757
|
+
type: this.javaTypeToJsonSchema(param.type),
|
|
758
|
+
description: paramDescription || param.name
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
if (isRequired) {
|
|
762
|
+
required.push(param.name);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return { type: 'object', properties, required };
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Build input schema for JAX-RS endpoints
|
|
772
|
+
*/
|
|
773
|
+
private buildJaxRsInputSchema(
|
|
774
|
+
methodInfo: JavaMethod,
|
|
775
|
+
path: string
|
|
776
|
+
): { type: 'object'; properties: Record<string, any>; required: string[] } {
|
|
777
|
+
const properties: Record<string, any> = {};
|
|
778
|
+
const required: string[] = [];
|
|
779
|
+
|
|
780
|
+
// Extract path parameters
|
|
781
|
+
const pathParams = path.match(/\{(\w+)\}/g) || [];
|
|
782
|
+
for (const param of pathParams) {
|
|
783
|
+
const name = param.slice(1, -1);
|
|
784
|
+
properties[name] = {
|
|
785
|
+
type: 'string',
|
|
786
|
+
description: `Path parameter: ${name}`
|
|
787
|
+
};
|
|
788
|
+
required.push(name);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// Add method parameters
|
|
792
|
+
for (const param of methodInfo.params) {
|
|
793
|
+
const isPathParam = param.annotations.some(a => a.includes('@PathParam'));
|
|
794
|
+
const isQueryParam = param.annotations.some(a => a.includes('@QueryParam'));
|
|
795
|
+
const isFormParam = param.annotations.some(a => a.includes('@FormParam'));
|
|
796
|
+
|
|
797
|
+
if (isPathParam) continue;
|
|
798
|
+
|
|
799
|
+
const paramDescription = methodInfo.documentation?.params.find(p => p.name === param.name)?.description;
|
|
800
|
+
|
|
801
|
+
if (isQueryParam || isFormParam) {
|
|
802
|
+
properties[param.name] = {
|
|
803
|
+
type: this.javaTypeToJsonSchema(param.type),
|
|
804
|
+
description: paramDescription || param.name
|
|
805
|
+
};
|
|
806
|
+
} else if (!this.isJaxRsFrameworkType(param.type)) {
|
|
807
|
+
// Assume it's a request body
|
|
808
|
+
properties[param.name] = {
|
|
809
|
+
type: 'object',
|
|
810
|
+
description: paramDescription || `Request body: ${param.name}`
|
|
811
|
+
};
|
|
812
|
+
required.push(param.name);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
return { type: 'object', properties, required };
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Build input schema for Micronaut endpoints
|
|
821
|
+
*/
|
|
822
|
+
private buildMicronautInputSchema(
|
|
823
|
+
methodInfo: JavaMethod,
|
|
824
|
+
path: string
|
|
825
|
+
): { type: 'object'; properties: Record<string, any>; required: string[] } {
|
|
826
|
+
// Micronaut is similar to Spring
|
|
827
|
+
return this.buildSpringInputSchema(methodInfo, path);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Build input schema from method parameters
|
|
832
|
+
*/
|
|
833
|
+
private buildMethodInputSchema(
|
|
834
|
+
methodInfo: JavaMethod
|
|
835
|
+
): { type: 'object'; properties: Record<string, any>; required: string[] } {
|
|
836
|
+
const properties: Record<string, any> = {};
|
|
837
|
+
const required: string[] = [];
|
|
838
|
+
|
|
839
|
+
for (const param of methodInfo.params) {
|
|
840
|
+
const paramDescription = methodInfo.documentation?.params.find(p => p.name === param.name)?.description;
|
|
841
|
+
|
|
842
|
+
properties[param.name] = {
|
|
843
|
+
type: this.javaTypeToJsonSchema(param.type),
|
|
844
|
+
description: paramDescription || param.name
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
// Primitive types are required, objects are optional
|
|
848
|
+
if (!param.type.includes('.') && !param.type.startsWith('Optional')) {
|
|
849
|
+
required.push(param.name);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
return { type: 'object', properties, required };
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* Check if type is a Spring framework type
|
|
858
|
+
*/
|
|
859
|
+
private isSpringFrameworkType(type: string): boolean {
|
|
860
|
+
const frameworkTypes = [
|
|
861
|
+
'HttpServletRequest', 'HttpServletResponse',
|
|
862
|
+
'Model', 'ModelMap', 'ModelAndView',
|
|
863
|
+
'BindingResult', 'Errors',
|
|
864
|
+
'RedirectAttributes', 'SessionStatus',
|
|
865
|
+
'Principal', 'Authentication',
|
|
866
|
+
'UriComponentsBuilder', 'HttpEntity',
|
|
867
|
+
'MultipartFile'
|
|
868
|
+
];
|
|
869
|
+
return frameworkTypes.some(ft => type.includes(ft));
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Check if type is a JAX-RS framework type
|
|
874
|
+
*/
|
|
875
|
+
private isJaxRsFrameworkType(type: string): boolean {
|
|
876
|
+
const frameworkTypes = [
|
|
877
|
+
'HttpServletRequest', 'HttpServletResponse',
|
|
878
|
+
'UriInfo', 'HttpHeaders', 'SecurityContext',
|
|
879
|
+
'Request', 'Response', 'ContainerRequestContext'
|
|
880
|
+
];
|
|
881
|
+
return frameworkTypes.some(ft => type.includes(ft));
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Convert Java type to JSON Schema type
|
|
886
|
+
*/
|
|
887
|
+
private javaTypeToJsonSchema(javaType: string): string {
|
|
888
|
+
const type = javaType.trim();
|
|
889
|
+
|
|
890
|
+
// Handle arrays
|
|
891
|
+
if (type.endsWith('[]') || type.startsWith('List<') || type.startsWith('Set<') || type.startsWith('Collection<')) {
|
|
892
|
+
return 'array';
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// Handle maps
|
|
896
|
+
if (type.startsWith('Map<') || type.startsWith('HashMap<')) {
|
|
897
|
+
return 'object';
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// Handle Optional
|
|
901
|
+
if (type.startsWith('Optional<')) {
|
|
902
|
+
const inner = type.match(/Optional<(.+)>/);
|
|
903
|
+
if (inner) return this.javaTypeToJsonSchema(inner[1]);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// Primitive and wrapper type mapping
|
|
907
|
+
const typeMap: Record<string, string> = {
|
|
908
|
+
'String': 'string',
|
|
909
|
+
'CharSequence': 'string',
|
|
910
|
+
'int': 'integer',
|
|
911
|
+
'Integer': 'integer',
|
|
912
|
+
'long': 'integer',
|
|
913
|
+
'Long': 'integer',
|
|
914
|
+
'short': 'integer',
|
|
915
|
+
'Short': 'integer',
|
|
916
|
+
'byte': 'integer',
|
|
917
|
+
'Byte': 'integer',
|
|
918
|
+
'BigInteger': 'integer',
|
|
919
|
+
'float': 'number',
|
|
920
|
+
'Float': 'number',
|
|
921
|
+
'double': 'number',
|
|
922
|
+
'Double': 'number',
|
|
923
|
+
'BigDecimal': 'number',
|
|
924
|
+
'boolean': 'boolean',
|
|
925
|
+
'Boolean': 'boolean',
|
|
926
|
+
'char': 'string',
|
|
927
|
+
'Character': 'string'
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
if (typeMap[type]) {
|
|
931
|
+
return typeMap[type];
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// Default to object for complex types
|
|
935
|
+
return 'object';
|
|
936
|
+
}
|
|
937
|
+
}
|