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,744 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Rust code extractor for extracting tools from Rust codebases
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ExtractedTool, ParsedDocumentation } from '../types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extracted Rust function information
|
|
11
|
+
*/
|
|
12
|
+
interface RustFunction {
|
|
13
|
+
name: string;
|
|
14
|
+
isPublic: boolean;
|
|
15
|
+
isAsync: boolean;
|
|
16
|
+
params: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
type: string;
|
|
19
|
+
isMutable: boolean;
|
|
20
|
+
isReference: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
returnType: string | null;
|
|
23
|
+
documentation: ParsedDocumentation | null;
|
|
24
|
+
attributes: string[];
|
|
25
|
+
line: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Rust route handler info (for web frameworks)
|
|
30
|
+
*/
|
|
31
|
+
interface RustRouteHandler {
|
|
32
|
+
method: string;
|
|
33
|
+
path: string;
|
|
34
|
+
handler: string;
|
|
35
|
+
function: RustFunction | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* RustExtractor extracts tools from Rust codebases
|
|
40
|
+
* Supports:
|
|
41
|
+
* - Actix-web route handlers
|
|
42
|
+
* - Axum handlers
|
|
43
|
+
* - Rocket routes
|
|
44
|
+
* - Regular public functions with doc comments
|
|
45
|
+
*/
|
|
46
|
+
export class RustExtractor {
|
|
47
|
+
/**
|
|
48
|
+
* Extract tools from Rust code
|
|
49
|
+
*/
|
|
50
|
+
async extract(code: string, filename: string): Promise<ExtractedTool[]> {
|
|
51
|
+
const tools: ExtractedTool[] = [];
|
|
52
|
+
|
|
53
|
+
// Extract from web framework routes
|
|
54
|
+
const actixTools = this.extractActixRoutes(code, filename);
|
|
55
|
+
const axumTools = this.extractAxumRoutes(code, filename);
|
|
56
|
+
const rocketTools = this.extractRocketRoutes(code, filename);
|
|
57
|
+
|
|
58
|
+
tools.push(...actixTools, ...axumTools, ...rocketTools);
|
|
59
|
+
|
|
60
|
+
// Extract from public functions with documentation
|
|
61
|
+
const publicFunctions = this.extractPublicFunctions(code, filename);
|
|
62
|
+
tools.push(...publicFunctions);
|
|
63
|
+
|
|
64
|
+
return tools;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Extract Actix-web route handlers
|
|
69
|
+
* Patterns:
|
|
70
|
+
* - #[get("/path")]
|
|
71
|
+
* - #[post("/path")]
|
|
72
|
+
* - web::resource("/path").route(web::get().to(handler))
|
|
73
|
+
*/
|
|
74
|
+
private extractActixRoutes(code: string, filename: string): ExtractedTool[] {
|
|
75
|
+
const tools: ExtractedTool[] = [];
|
|
76
|
+
const lines = code.split('\n');
|
|
77
|
+
|
|
78
|
+
// Pattern for attribute-based routes
|
|
79
|
+
const routeAttrPattern = /#\[(get|post|put|delete|patch|head|options)\s*\(\s*"([^"]+)"\s*(?:,\s*[^)]+)?\)\]/gi;
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < lines.length; i++) {
|
|
82
|
+
const line = lines[i];
|
|
83
|
+
let match;
|
|
84
|
+
|
|
85
|
+
routeAttrPattern.lastIndex = 0;
|
|
86
|
+
while ((match = routeAttrPattern.exec(line)) !== null) {
|
|
87
|
+
const method = match[1].toUpperCase();
|
|
88
|
+
const path = match[2];
|
|
89
|
+
|
|
90
|
+
// Look for the function definition on the next lines
|
|
91
|
+
const funcInfo = this.findNextFunction(lines, i + 1);
|
|
92
|
+
|
|
93
|
+
if (funcInfo) {
|
|
94
|
+
const toolName = this.generateToolName(method, path, funcInfo.name);
|
|
95
|
+
const description = funcInfo.documentation?.description ||
|
|
96
|
+
`${method} ${path} - ${funcInfo.name}`;
|
|
97
|
+
|
|
98
|
+
tools.push({
|
|
99
|
+
name: toolName,
|
|
100
|
+
description,
|
|
101
|
+
inputSchema: this.buildInputSchema(funcInfo, path),
|
|
102
|
+
source: {
|
|
103
|
+
type: 'code',
|
|
104
|
+
file: filename,
|
|
105
|
+
line: i + 1
|
|
106
|
+
},
|
|
107
|
+
confidence: 0.8,
|
|
108
|
+
confidenceFactors: {
|
|
109
|
+
documentation: funcInfo.documentation ? 0.9 : 0.5,
|
|
110
|
+
types: 0.8,
|
|
111
|
+
examples: funcInfo.documentation?.examples?.length ? 0.8 : 0.3,
|
|
112
|
+
source: 0.9
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Pattern for web::scope and web::resource
|
|
120
|
+
const resourcePattern = /web::(resource|scope)\s*\(\s*"([^"]+)"\s*\)/g;
|
|
121
|
+
const routeToPattern = /\.route\s*\(\s*web::(get|post|put|delete|patch)\s*\(\s*\)\s*\.to\s*\(\s*(\w+)\s*\)\s*\)/gi;
|
|
122
|
+
|
|
123
|
+
let resourceMatch;
|
|
124
|
+
while ((resourceMatch = resourcePattern.exec(code)) !== null) {
|
|
125
|
+
const basePath = resourceMatch[2];
|
|
126
|
+
const lineNum = code.substring(0, resourceMatch.index).split('\n').length;
|
|
127
|
+
|
|
128
|
+
// Find route definitions that follow
|
|
129
|
+
const remainingCode = code.substring(resourceMatch.index);
|
|
130
|
+
let routeMatch;
|
|
131
|
+
|
|
132
|
+
while ((routeMatch = routeToPattern.exec(remainingCode)) !== null) {
|
|
133
|
+
const method = routeMatch[1].toUpperCase();
|
|
134
|
+
const handlerName = routeMatch[2];
|
|
135
|
+
|
|
136
|
+
// Try to find the handler function
|
|
137
|
+
const funcInfo = this.findFunctionByName(code, handlerName);
|
|
138
|
+
|
|
139
|
+
tools.push({
|
|
140
|
+
name: this.generateToolName(method, basePath, handlerName),
|
|
141
|
+
description: funcInfo?.documentation?.description || `${method} ${basePath}`,
|
|
142
|
+
inputSchema: funcInfo ? this.buildInputSchema(funcInfo, basePath) : { type: 'object', properties: {}, required: [] },
|
|
143
|
+
source: {
|
|
144
|
+
type: 'code',
|
|
145
|
+
file: filename,
|
|
146
|
+
line: lineNum
|
|
147
|
+
},
|
|
148
|
+
confidence: 0.7
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return tools;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Extract Axum route handlers
|
|
158
|
+
* Patterns:
|
|
159
|
+
* - Router::new().route("/path", get(handler))
|
|
160
|
+
* - .route("/path", post(handler))
|
|
161
|
+
*/
|
|
162
|
+
private extractAxumRoutes(code: string, filename: string): ExtractedTool[] {
|
|
163
|
+
const tools: ExtractedTool[] = [];
|
|
164
|
+
|
|
165
|
+
// Pattern for .route() calls
|
|
166
|
+
const routePattern = /\.route\s*\(\s*"([^"]+)"\s*,\s*(get|post|put|delete|patch|head|options)\s*\(\s*(\w+)\s*\)/gi;
|
|
167
|
+
|
|
168
|
+
let match;
|
|
169
|
+
while ((match = routePattern.exec(code)) !== null) {
|
|
170
|
+
const path = match[1];
|
|
171
|
+
const method = match[2].toUpperCase();
|
|
172
|
+
const handlerName = match[3];
|
|
173
|
+
const lineNum = code.substring(0, match.index).split('\n').length;
|
|
174
|
+
|
|
175
|
+
const funcInfo = this.findFunctionByName(code, handlerName);
|
|
176
|
+
|
|
177
|
+
tools.push({
|
|
178
|
+
name: this.generateToolName(method, path, handlerName),
|
|
179
|
+
description: funcInfo?.documentation?.description || `${method} ${path}`,
|
|
180
|
+
inputSchema: funcInfo ? this.buildInputSchema(funcInfo, path) : { type: 'object', properties: {}, required: [] },
|
|
181
|
+
source: {
|
|
182
|
+
type: 'code',
|
|
183
|
+
file: filename,
|
|
184
|
+
line: lineNum
|
|
185
|
+
},
|
|
186
|
+
confidence: 0.8
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Axum also supports method routing with handler attributes
|
|
191
|
+
const axumAttrPattern = /#\[axum::(debug_handler|handler)]/gi;
|
|
192
|
+
|
|
193
|
+
// Look for #[debug_handler] followed by async fn
|
|
194
|
+
const debugHandlerMatches = code.matchAll(/#\[debug_handler\]\s*(?:pub\s+)?async\s+fn\s+(\w+)/g);
|
|
195
|
+
for (const match of debugHandlerMatches) {
|
|
196
|
+
const handlerName = match[1];
|
|
197
|
+
const lineNum = code.substring(0, match.index!).split('\n').length;
|
|
198
|
+
const funcInfo = this.findFunctionByName(code, handlerName);
|
|
199
|
+
|
|
200
|
+
if (funcInfo) {
|
|
201
|
+
tools.push({
|
|
202
|
+
name: handlerName,
|
|
203
|
+
description: funcInfo.documentation?.description || `Axum handler: ${handlerName}`,
|
|
204
|
+
inputSchema: this.buildInputSchema(funcInfo, ''),
|
|
205
|
+
source: {
|
|
206
|
+
type: 'code',
|
|
207
|
+
file: filename,
|
|
208
|
+
line: lineNum
|
|
209
|
+
},
|
|
210
|
+
confidence: 0.7
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return tools;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Extract Rocket route handlers
|
|
220
|
+
* Patterns:
|
|
221
|
+
* - #[get("/path")]
|
|
222
|
+
* - #[post("/path", data = "<input>")]
|
|
223
|
+
*/
|
|
224
|
+
private extractRocketRoutes(code: string, filename: string): ExtractedTool[] {
|
|
225
|
+
const tools: ExtractedTool[] = [];
|
|
226
|
+
const lines = code.split('\n');
|
|
227
|
+
|
|
228
|
+
// Rocket uses similar attribute syntax but with more options
|
|
229
|
+
const rocketRoutePattern = /#\[(get|post|put|delete|patch|head|options)\s*\(\s*"([^"]+)"(?:\s*,\s*([^)]+))?\)\]/gi;
|
|
230
|
+
|
|
231
|
+
for (let i = 0; i < lines.length; i++) {
|
|
232
|
+
const line = lines[i];
|
|
233
|
+
let match;
|
|
234
|
+
|
|
235
|
+
rocketRoutePattern.lastIndex = 0;
|
|
236
|
+
while ((match = rocketRoutePattern.exec(line)) !== null) {
|
|
237
|
+
const method = match[1].toUpperCase();
|
|
238
|
+
const path = match[2];
|
|
239
|
+
const options = match[3] || '';
|
|
240
|
+
|
|
241
|
+
// Parse data parameter if present
|
|
242
|
+
const dataMatch = options.match(/data\s*=\s*"<(\w+)>"/);
|
|
243
|
+
const dataParam = dataMatch ? dataMatch[1] : null;
|
|
244
|
+
|
|
245
|
+
// Find the function
|
|
246
|
+
const funcInfo = this.findNextFunction(lines, i + 1);
|
|
247
|
+
|
|
248
|
+
if (funcInfo) {
|
|
249
|
+
tools.push({
|
|
250
|
+
name: this.generateToolName(method, path, funcInfo.name),
|
|
251
|
+
description: funcInfo.documentation?.description || `${method} ${path}`,
|
|
252
|
+
inputSchema: this.buildInputSchema(funcInfo, path, dataParam),
|
|
253
|
+
source: {
|
|
254
|
+
type: 'code',
|
|
255
|
+
file: filename,
|
|
256
|
+
line: i + 1
|
|
257
|
+
},
|
|
258
|
+
confidence: 0.8
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return tools;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Extract public functions with doc comments
|
|
269
|
+
*/
|
|
270
|
+
private extractPublicFunctions(code: string, filename: string): ExtractedTool[] {
|
|
271
|
+
const tools: ExtractedTool[] = [];
|
|
272
|
+
const lines = code.split('\n');
|
|
273
|
+
|
|
274
|
+
for (let i = 0; i < lines.length; i++) {
|
|
275
|
+
const line = lines[i];
|
|
276
|
+
|
|
277
|
+
// Look for pub fn or pub async fn with preceding doc comments
|
|
278
|
+
if (/^\s*pub\s+(async\s+)?fn\s+\w+/.test(line)) {
|
|
279
|
+
// Check if this function has route attributes (already processed)
|
|
280
|
+
if (i > 0) {
|
|
281
|
+
const prevLine = lines[i - 1];
|
|
282
|
+
if (/#\[(get|post|put|delete|patch|head|options|route)/.test(prevLine)) {
|
|
283
|
+
continue; // Skip, already handled by route extractors
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const funcInfo = this.parseFunction(lines, i);
|
|
288
|
+
if (funcInfo && funcInfo.documentation) {
|
|
289
|
+
// Only include functions with documentation
|
|
290
|
+
tools.push({
|
|
291
|
+
name: funcInfo.name,
|
|
292
|
+
description: funcInfo.documentation.description || funcInfo.name,
|
|
293
|
+
inputSchema: this.buildInputSchemaFromFunction(funcInfo),
|
|
294
|
+
source: {
|
|
295
|
+
type: 'code',
|
|
296
|
+
file: filename,
|
|
297
|
+
line: i + 1
|
|
298
|
+
},
|
|
299
|
+
confidence: 0.6,
|
|
300
|
+
confidenceFactors: {
|
|
301
|
+
documentation: 0.8,
|
|
302
|
+
types: 0.9,
|
|
303
|
+
examples: funcInfo.documentation.examples?.length ? 0.7 : 0.2,
|
|
304
|
+
source: 0.7
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return tools;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Find the next function definition after the given line
|
|
316
|
+
*/
|
|
317
|
+
private findNextFunction(lines: string[], startLine: number): RustFunction | null {
|
|
318
|
+
for (let i = startLine; i < Math.min(startLine + 5, lines.length); i++) {
|
|
319
|
+
const line = lines[i];
|
|
320
|
+
if (/^\s*(pub\s+)?(async\s+)?fn\s+\w+/.test(line)) {
|
|
321
|
+
return this.parseFunction(lines, i);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Find a function by name in the code
|
|
329
|
+
*/
|
|
330
|
+
private findFunctionByName(code: string, name: string): RustFunction | null {
|
|
331
|
+
const lines = code.split('\n');
|
|
332
|
+
const pattern = new RegExp(`^\\s*(pub\\s+)?(async\\s+)?fn\\s+${name}\\s*[<(]`);
|
|
333
|
+
|
|
334
|
+
for (let i = 0; i < lines.length; i++) {
|
|
335
|
+
if (pattern.test(lines[i])) {
|
|
336
|
+
return this.parseFunction(lines, i);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Parse a Rust function definition
|
|
344
|
+
*/
|
|
345
|
+
private parseFunction(lines: string[], lineIndex: number): RustFunction | null {
|
|
346
|
+
const line = lines[lineIndex];
|
|
347
|
+
|
|
348
|
+
// First, try to match single-line function signature
|
|
349
|
+
let funcMatch = line.match(/^\s*(pub\s+)?(async\s+)?fn\s+(\w+)\s*(?:<[^>]*>)?\s*\(([^)]*)\)(?:\s*->\s*(.+?))?(?:\s*(?:where|{))?/);
|
|
350
|
+
|
|
351
|
+
// If no match, try multi-line function signature
|
|
352
|
+
if (!funcMatch) {
|
|
353
|
+
// Get the function name and check for multi-line signature
|
|
354
|
+
const headerMatch = line.match(/^\s*(pub\s+)?(async\s+)?fn\s+(\w+)\s*(?:<[^>]*>)?\s*\(/);
|
|
355
|
+
if (!headerMatch) return null;
|
|
356
|
+
|
|
357
|
+
// Collect lines until we find the closing paren
|
|
358
|
+
let combinedParams = '';
|
|
359
|
+
let returnType: string | undefined;
|
|
360
|
+
let depth = 1;
|
|
361
|
+
|
|
362
|
+
// Get params from first line after the opening paren
|
|
363
|
+
const afterParen = line.substring(line.indexOf('(') + 1);
|
|
364
|
+
combinedParams += afterParen;
|
|
365
|
+
|
|
366
|
+
for (let i = lineIndex + 1; i < lines.length && depth > 0; i++) {
|
|
367
|
+
const currentLine = lines[i];
|
|
368
|
+
for (const char of currentLine) {
|
|
369
|
+
if (char === '(') depth++;
|
|
370
|
+
else if (char === ')') depth--;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (depth > 0) {
|
|
374
|
+
combinedParams += ' ' + currentLine.trim();
|
|
375
|
+
} else {
|
|
376
|
+
// Found the closing paren - extract up to it and check for return type
|
|
377
|
+
const closingIdx = currentLine.indexOf(')');
|
|
378
|
+
combinedParams += ' ' + currentLine.substring(0, closingIdx).trim();
|
|
379
|
+
|
|
380
|
+
// Check for return type
|
|
381
|
+
const afterClose = currentLine.substring(closingIdx + 1);
|
|
382
|
+
const returnMatch = afterClose.match(/^\s*->\s*(.+?)(?:\s*(?:where|{))?$/);
|
|
383
|
+
if (returnMatch) {
|
|
384
|
+
returnType = returnMatch[1].trim();
|
|
385
|
+
}
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
funcMatch = [
|
|
391
|
+
line,
|
|
392
|
+
headerMatch[1],
|
|
393
|
+
headerMatch[2],
|
|
394
|
+
headerMatch[3],
|
|
395
|
+
combinedParams,
|
|
396
|
+
returnType
|
|
397
|
+
] as RegExpMatchArray;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (!funcMatch) return null;
|
|
401
|
+
|
|
402
|
+
const [, pubKw, asyncKw, name, paramsStr, returnTypeStr] = funcMatch;
|
|
403
|
+
|
|
404
|
+
// Parse parameters
|
|
405
|
+
const params = this.parseParameters(paramsStr);
|
|
406
|
+
|
|
407
|
+
// Look for doc comments above the function
|
|
408
|
+
const documentation = this.parseDocComments(lines, lineIndex);
|
|
409
|
+
|
|
410
|
+
// Look for attributes
|
|
411
|
+
const attributes = this.parseAttributes(lines, lineIndex);
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
name,
|
|
415
|
+
isPublic: !!pubKw,
|
|
416
|
+
isAsync: !!asyncKw,
|
|
417
|
+
params,
|
|
418
|
+
returnType: returnTypeStr?.trim() || null,
|
|
419
|
+
documentation,
|
|
420
|
+
attributes,
|
|
421
|
+
line: lineIndex + 1
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Parse function parameters
|
|
427
|
+
*/
|
|
428
|
+
private parseParameters(paramsStr: string): RustFunction['params'] {
|
|
429
|
+
const params: RustFunction['params'] = [];
|
|
430
|
+
if (!paramsStr.trim()) return params;
|
|
431
|
+
|
|
432
|
+
// Handle multi-line and complex types
|
|
433
|
+
const paramParts = this.splitParams(paramsStr);
|
|
434
|
+
|
|
435
|
+
for (const part of paramParts) {
|
|
436
|
+
const trimmed = part.trim();
|
|
437
|
+
if (!trimmed || trimmed === 'self' || trimmed === '&self' || trimmed === '&mut self') {
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Match: name: Type or mut name: Type or &name: Type
|
|
442
|
+
const paramMatch = trimmed.match(/^(&)?(\s*mut\s+)?(\w+)\s*:\s*(.+)$/);
|
|
443
|
+
if (paramMatch) {
|
|
444
|
+
const [, isRef, isMut, name, type] = paramMatch;
|
|
445
|
+
params.push({
|
|
446
|
+
name,
|
|
447
|
+
type: type.trim(),
|
|
448
|
+
isMutable: !!isMut,
|
|
449
|
+
isReference: !!isRef
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return params;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Split parameters handling nested generics
|
|
459
|
+
*/
|
|
460
|
+
private splitParams(paramsStr: string): string[] {
|
|
461
|
+
const params: string[] = [];
|
|
462
|
+
let current = '';
|
|
463
|
+
let depth = 0;
|
|
464
|
+
|
|
465
|
+
for (const char of paramsStr) {
|
|
466
|
+
if (char === '<' || char === '(' || char === '[' || char === '{') {
|
|
467
|
+
depth++;
|
|
468
|
+
current += char;
|
|
469
|
+
} else if (char === '>' || char === ')' || char === ']' || char === '}') {
|
|
470
|
+
depth--;
|
|
471
|
+
current += char;
|
|
472
|
+
} else if (char === ',' && depth === 0) {
|
|
473
|
+
params.push(current.trim());
|
|
474
|
+
current = '';
|
|
475
|
+
} else {
|
|
476
|
+
current += char;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (current.trim()) {
|
|
481
|
+
params.push(current.trim());
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return params;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Parse doc comments (///) above a function
|
|
489
|
+
*/
|
|
490
|
+
private parseDocComments(lines: string[], funcLineIndex: number): ParsedDocumentation | null {
|
|
491
|
+
const docLines: string[] = [];
|
|
492
|
+
let i = funcLineIndex - 1;
|
|
493
|
+
|
|
494
|
+
// Skip attributes
|
|
495
|
+
while (i >= 0 && lines[i].trim().startsWith('#[')) {
|
|
496
|
+
i--;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Collect doc comments
|
|
500
|
+
while (i >= 0) {
|
|
501
|
+
const line = lines[i].trim();
|
|
502
|
+
if (line.startsWith('///')) {
|
|
503
|
+
docLines.unshift(line.substring(3).trim());
|
|
504
|
+
i--;
|
|
505
|
+
} else if (line.startsWith('//!') || line === '') {
|
|
506
|
+
i--;
|
|
507
|
+
} else {
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (docLines.length === 0) return null;
|
|
513
|
+
|
|
514
|
+
return this.parseDocumentation(docLines);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Parse documentation content
|
|
519
|
+
*/
|
|
520
|
+
private parseDocumentation(docLines: string[]): ParsedDocumentation {
|
|
521
|
+
const doc: ParsedDocumentation = {
|
|
522
|
+
params: [],
|
|
523
|
+
examples: []
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
let description = '';
|
|
527
|
+
let inExample = false;
|
|
528
|
+
let currentExample = '';
|
|
529
|
+
|
|
530
|
+
for (const line of docLines) {
|
|
531
|
+
// Check for argument documentation
|
|
532
|
+
const argMatch = line.match(/^#?\s*(?:Arguments?|Params?)\s*$/i);
|
|
533
|
+
if (argMatch) continue;
|
|
534
|
+
|
|
535
|
+
const paramMatch = line.match(/^\*?\s*`(\w+)`\s*[-:]?\s*(.+)/);
|
|
536
|
+
if (paramMatch) {
|
|
537
|
+
doc.params.push({
|
|
538
|
+
name: paramMatch[1],
|
|
539
|
+
description: paramMatch[2]
|
|
540
|
+
});
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Check for examples
|
|
545
|
+
if (line.match(/^#?\s*(?:Example|Examples)\s*$/i)) {
|
|
546
|
+
inExample = true;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (line.startsWith('```')) {
|
|
551
|
+
if (inExample && currentExample) {
|
|
552
|
+
doc.examples!.push(currentExample.trim());
|
|
553
|
+
currentExample = '';
|
|
554
|
+
}
|
|
555
|
+
inExample = !inExample;
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (inExample) {
|
|
560
|
+
currentExample += line + '\n';
|
|
561
|
+
} else if (!line.startsWith('#')) {
|
|
562
|
+
description += (description ? ' ' : '') + line;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
doc.description = description.trim() || undefined;
|
|
567
|
+
return doc;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Parse attributes above a function
|
|
572
|
+
*/
|
|
573
|
+
private parseAttributes(lines: string[], funcLineIndex: number): string[] {
|
|
574
|
+
const attributes: string[] = [];
|
|
575
|
+
let i = funcLineIndex - 1;
|
|
576
|
+
|
|
577
|
+
while (i >= 0) {
|
|
578
|
+
const line = lines[i].trim();
|
|
579
|
+
if (line.startsWith('#[')) {
|
|
580
|
+
attributes.unshift(line);
|
|
581
|
+
i--;
|
|
582
|
+
} else if (line.startsWith('///') || line === '') {
|
|
583
|
+
i--;
|
|
584
|
+
} else {
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return attributes;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Generate a tool name from HTTP method, path, and handler name
|
|
594
|
+
*/
|
|
595
|
+
private generateToolName(method: string, path: string, handlerName: string): string {
|
|
596
|
+
// Convert path to snake_case parts
|
|
597
|
+
const pathParts = path
|
|
598
|
+
.split('/')
|
|
599
|
+
.filter(p => p && !p.startsWith('{') && !p.startsWith('<') && !p.startsWith(':'))
|
|
600
|
+
.map(p => p.replace(/-/g, '_').toLowerCase());
|
|
601
|
+
|
|
602
|
+
if (pathParts.length > 0) {
|
|
603
|
+
return `${method.toLowerCase()}_${pathParts.join('_')}`;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return handlerName;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Build input schema from function info and route path
|
|
611
|
+
*/
|
|
612
|
+
private buildInputSchema(
|
|
613
|
+
funcInfo: RustFunction,
|
|
614
|
+
path: string,
|
|
615
|
+
dataParam?: string | null
|
|
616
|
+
): { type: 'object'; properties: Record<string, any>; required: string[] } {
|
|
617
|
+
const properties: Record<string, any> = {};
|
|
618
|
+
const required: string[] = [];
|
|
619
|
+
|
|
620
|
+
// Extract path parameters
|
|
621
|
+
const pathParams = path.match(/\{(\w+)\}|<(\w+)>|:(\w+)/g) || [];
|
|
622
|
+
for (const param of pathParams) {
|
|
623
|
+
const name = param.replace(/[{}<>:]/g, '');
|
|
624
|
+
properties[name] = {
|
|
625
|
+
type: 'string',
|
|
626
|
+
description: `Path parameter: ${name}`
|
|
627
|
+
};
|
|
628
|
+
required.push(name);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Add function parameters (excluding web framework types)
|
|
632
|
+
for (const param of funcInfo.params) {
|
|
633
|
+
if (this.isWebFrameworkType(param.type)) continue;
|
|
634
|
+
if (pathParams.some(p => p.includes(param.name))) continue;
|
|
635
|
+
|
|
636
|
+
properties[param.name] = {
|
|
637
|
+
type: this.rustTypeToJsonSchema(param.type),
|
|
638
|
+
description: funcInfo.documentation?.params.find(p => p.name === param.name)?.description || param.name
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
// If it's the data parameter or not Option, it's required
|
|
642
|
+
if (param.name === dataParam || !param.type.startsWith('Option<')) {
|
|
643
|
+
required.push(param.name);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
return { type: 'object', properties, required };
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Build input schema from function only (no route info)
|
|
652
|
+
*/
|
|
653
|
+
private buildInputSchemaFromFunction(
|
|
654
|
+
funcInfo: RustFunction
|
|
655
|
+
): { type: 'object'; properties: Record<string, any>; required: string[] } {
|
|
656
|
+
const properties: Record<string, any> = {};
|
|
657
|
+
const required: string[] = [];
|
|
658
|
+
|
|
659
|
+
for (const param of funcInfo.params) {
|
|
660
|
+
const isOptional = param.type.startsWith('Option<');
|
|
661
|
+
|
|
662
|
+
properties[param.name] = {
|
|
663
|
+
type: this.rustTypeToJsonSchema(param.type),
|
|
664
|
+
description: funcInfo.documentation?.params.find(p => p.name === param.name)?.description || param.name
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
if (!isOptional) {
|
|
668
|
+
required.push(param.name);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return { type: 'object', properties, required };
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Check if a type is a web framework type that should be ignored
|
|
677
|
+
*/
|
|
678
|
+
private isWebFrameworkType(type: string): boolean {
|
|
679
|
+
const frameworkTypes = [
|
|
680
|
+
'HttpRequest', 'HttpResponse', 'Request', 'Response',
|
|
681
|
+
'State', 'Data', 'Query', 'Path', 'Json', 'Form',
|
|
682
|
+
'Extension', 'Headers', 'Cookies', 'Session',
|
|
683
|
+
'web::Data', 'web::Path', 'web::Query', 'web::Json',
|
|
684
|
+
'actix_web::', 'axum::', 'rocket::'
|
|
685
|
+
];
|
|
686
|
+
|
|
687
|
+
return frameworkTypes.some(ft => type.includes(ft));
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Convert Rust type to JSON Schema type
|
|
692
|
+
*/
|
|
693
|
+
private rustTypeToJsonSchema(rustType: string): string {
|
|
694
|
+
const type = rustType.trim();
|
|
695
|
+
|
|
696
|
+
// Handle Option<T>
|
|
697
|
+
if (type.startsWith('Option<')) {
|
|
698
|
+
const inner = type.match(/Option<(.+)>$/);
|
|
699
|
+
if (inner) return this.rustTypeToJsonSchema(inner[1]);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Handle Vec<T>
|
|
703
|
+
if (type.startsWith('Vec<') || type.startsWith('&[') || type.includes('[]')) {
|
|
704
|
+
return 'array';
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Handle HashMap, BTreeMap
|
|
708
|
+
if (type.includes('Map<') || type.includes('map<')) {
|
|
709
|
+
return 'object';
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// Primitive types
|
|
713
|
+
const typeMap: Record<string, string> = {
|
|
714
|
+
'String': 'string',
|
|
715
|
+
'&str': 'string',
|
|
716
|
+
'str': 'string',
|
|
717
|
+
'i8': 'integer',
|
|
718
|
+
'i16': 'integer',
|
|
719
|
+
'i32': 'integer',
|
|
720
|
+
'i64': 'integer',
|
|
721
|
+
'i128': 'integer',
|
|
722
|
+
'isize': 'integer',
|
|
723
|
+
'u8': 'integer',
|
|
724
|
+
'u16': 'integer',
|
|
725
|
+
'u32': 'integer',
|
|
726
|
+
'u64': 'integer',
|
|
727
|
+
'u128': 'integer',
|
|
728
|
+
'usize': 'integer',
|
|
729
|
+
'f32': 'number',
|
|
730
|
+
'f64': 'number',
|
|
731
|
+
'bool': 'boolean',
|
|
732
|
+
'char': 'string'
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
for (const [rust, json] of Object.entries(typeMap)) {
|
|
736
|
+
if (type === rust || type.endsWith(`::${rust}`)) {
|
|
737
|
+
return json;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Default to object for complex types
|
|
742
|
+
return 'object';
|
|
743
|
+
}
|
|
744
|
+
}
|