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,723 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview HAR (HTTP Archive) file parser
|
|
3
|
+
* Reverse-engineers API definitions from captured HTTP traffic
|
|
4
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
5
|
+
* @license MIT
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Maximum length for a numeric ID segment to be considered a path parameter */
|
|
9
|
+
const MAX_NUMERIC_ID_LENGTH = 20;
|
|
10
|
+
|
|
11
|
+
/** Maximum length for Base64-like ID segments to be considered path parameters */
|
|
12
|
+
const MAX_BASE64_ID_LENGTH = 50;
|
|
13
|
+
|
|
14
|
+
export interface HarFile {
|
|
15
|
+
log: {
|
|
16
|
+
version: string;
|
|
17
|
+
creator: HarCreator;
|
|
18
|
+
browser?: HarBrowser;
|
|
19
|
+
pages?: HarPage[];
|
|
20
|
+
entries: HarEntry[];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface HarCreator {
|
|
25
|
+
name: string;
|
|
26
|
+
version: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface HarBrowser {
|
|
30
|
+
name: string;
|
|
31
|
+
version: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface HarPage {
|
|
35
|
+
id: string;
|
|
36
|
+
startedDateTime: string;
|
|
37
|
+
title: string;
|
|
38
|
+
pageTimings?: any;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface HarEntry {
|
|
42
|
+
startedDateTime: string;
|
|
43
|
+
time: number;
|
|
44
|
+
request: HarRequest;
|
|
45
|
+
response: HarResponse;
|
|
46
|
+
cache?: any;
|
|
47
|
+
timings?: any;
|
|
48
|
+
serverIPAddress?: string;
|
|
49
|
+
connection?: string;
|
|
50
|
+
pageref?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface HarRequest {
|
|
54
|
+
method: string;
|
|
55
|
+
url: string;
|
|
56
|
+
httpVersion: string;
|
|
57
|
+
cookies: HarCookie[];
|
|
58
|
+
headers: HarHeader[];
|
|
59
|
+
queryString: HarQueryParam[];
|
|
60
|
+
postData?: HarPostData;
|
|
61
|
+
headersSize: number;
|
|
62
|
+
bodySize: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface HarResponse {
|
|
66
|
+
status: number;
|
|
67
|
+
statusText: string;
|
|
68
|
+
httpVersion: string;
|
|
69
|
+
cookies: HarCookie[];
|
|
70
|
+
headers: HarHeader[];
|
|
71
|
+
content: HarContent;
|
|
72
|
+
redirectURL: string;
|
|
73
|
+
headersSize: number;
|
|
74
|
+
bodySize: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface HarCookie {
|
|
78
|
+
name: string;
|
|
79
|
+
value: string;
|
|
80
|
+
path?: string;
|
|
81
|
+
domain?: string;
|
|
82
|
+
expires?: string;
|
|
83
|
+
httpOnly?: boolean;
|
|
84
|
+
secure?: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface HarHeader {
|
|
88
|
+
name: string;
|
|
89
|
+
value: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface HarQueryParam {
|
|
93
|
+
name: string;
|
|
94
|
+
value: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface HarPostData {
|
|
98
|
+
mimeType: string;
|
|
99
|
+
text?: string;
|
|
100
|
+
params?: Array<{ name: string; value?: string; fileName?: string; contentType?: string }>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface HarContent {
|
|
104
|
+
size: number;
|
|
105
|
+
mimeType: string;
|
|
106
|
+
text?: string;
|
|
107
|
+
encoding?: string;
|
|
108
|
+
compression?: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface HarEndpointGroup {
|
|
112
|
+
pattern: string;
|
|
113
|
+
method: string;
|
|
114
|
+
entries: HarEntry[];
|
|
115
|
+
pathParams: string[];
|
|
116
|
+
queryParams: Set<string>;
|
|
117
|
+
requestBodyExamples: any[];
|
|
118
|
+
responseBodyExamples: any[];
|
|
119
|
+
headers: Map<string, Set<string>>;
|
|
120
|
+
authPatterns: Set<string>;
|
|
121
|
+
statusCodes: Set<number>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface HarToolDefinition {
|
|
125
|
+
name: string;
|
|
126
|
+
description: string;
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: 'object';
|
|
129
|
+
properties: Record<string, any>;
|
|
130
|
+
required?: string[];
|
|
131
|
+
};
|
|
132
|
+
metadata: {
|
|
133
|
+
endpoint: {
|
|
134
|
+
path: string;
|
|
135
|
+
method: string;
|
|
136
|
+
};
|
|
137
|
+
confidence: 'high' | 'medium' | 'low';
|
|
138
|
+
sampleCount: number;
|
|
139
|
+
statusCodes: number[];
|
|
140
|
+
auth?: {
|
|
141
|
+
type: string;
|
|
142
|
+
envVar?: string;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
examples?: Array<{ input?: any; output?: any }>;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface HarParseResult {
|
|
149
|
+
format: 'har';
|
|
150
|
+
info: {
|
|
151
|
+
creator: string;
|
|
152
|
+
browser?: string;
|
|
153
|
+
entryCount: number;
|
|
154
|
+
endpointCount: number;
|
|
155
|
+
};
|
|
156
|
+
tools: HarToolDefinition[];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Parser for HAR (HTTP Archive) files
|
|
161
|
+
* Reverse-engineers APIs from captured traffic
|
|
162
|
+
*/
|
|
163
|
+
export class HarParser {
|
|
164
|
+
private har: HarFile | null = null;
|
|
165
|
+
private endpoints: Map<string, HarEndpointGroup> = new Map();
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Parse HAR file from JSON string
|
|
169
|
+
*/
|
|
170
|
+
parse(input: string): HarFile {
|
|
171
|
+
try {
|
|
172
|
+
this.har = JSON.parse(input);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
throw new Error(`Failed to parse HAR file: ${error instanceof Error ? error.message : 'Invalid JSON'}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!this.isValidHar(this.har)) {
|
|
178
|
+
throw new Error('Invalid HAR file: missing required fields');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.analyzeEntries(this.har.log.entries);
|
|
182
|
+
return this.har;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Parse from object (already loaded)
|
|
187
|
+
*/
|
|
188
|
+
parseObject(har: HarFile): HarFile {
|
|
189
|
+
if (!this.isValidHar(har)) {
|
|
190
|
+
throw new Error('Invalid HAR file: missing required fields');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.har = har;
|
|
194
|
+
this.analyzeEntries(har.log.entries);
|
|
195
|
+
return har;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Validate HAR structure
|
|
200
|
+
*/
|
|
201
|
+
private isValidHar(har: any): har is HarFile {
|
|
202
|
+
return (
|
|
203
|
+
har &&
|
|
204
|
+
typeof har.log === 'object' &&
|
|
205
|
+
typeof har.log.version === 'string' &&
|
|
206
|
+
Array.isArray(har.log.entries)
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Analyze entries and group by endpoint pattern
|
|
212
|
+
*/
|
|
213
|
+
private analyzeEntries(entries: HarEntry[]): void {
|
|
214
|
+
this.endpoints.clear();
|
|
215
|
+
|
|
216
|
+
for (const entry of entries) {
|
|
217
|
+
// Skip non-API requests
|
|
218
|
+
if (!this.isApiRequest(entry)) continue;
|
|
219
|
+
|
|
220
|
+
const { pattern, pathParams } = this.extractEndpointPattern(entry.request.url);
|
|
221
|
+
const key = `${entry.request.method}:${pattern}`;
|
|
222
|
+
|
|
223
|
+
if (!this.endpoints.has(key)) {
|
|
224
|
+
this.endpoints.set(key, {
|
|
225
|
+
pattern,
|
|
226
|
+
method: entry.request.method,
|
|
227
|
+
entries: [],
|
|
228
|
+
pathParams,
|
|
229
|
+
queryParams: new Set(),
|
|
230
|
+
requestBodyExamples: [],
|
|
231
|
+
responseBodyExamples: [],
|
|
232
|
+
headers: new Map(),
|
|
233
|
+
authPatterns: new Set(),
|
|
234
|
+
statusCodes: new Set(),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const group = this.endpoints.get(key)!;
|
|
239
|
+
group.entries.push(entry);
|
|
240
|
+
group.statusCodes.add(entry.response.status);
|
|
241
|
+
|
|
242
|
+
// Collect query parameters
|
|
243
|
+
for (const param of entry.request.queryString) {
|
|
244
|
+
group.queryParams.add(param.name);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Collect headers
|
|
248
|
+
for (const header of entry.request.headers) {
|
|
249
|
+
const lowerName = header.name.toLowerCase();
|
|
250
|
+
if (!group.headers.has(lowerName)) {
|
|
251
|
+
group.headers.set(lowerName, new Set());
|
|
252
|
+
}
|
|
253
|
+
group.headers.get(lowerName)!.add(header.value);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Detect auth patterns
|
|
257
|
+
this.detectAuthPattern(entry, group);
|
|
258
|
+
|
|
259
|
+
// Collect request body examples
|
|
260
|
+
if (entry.request.postData?.text) {
|
|
261
|
+
try {
|
|
262
|
+
const body = JSON.parse(entry.request.postData.text);
|
|
263
|
+
group.requestBodyExamples.push(body);
|
|
264
|
+
} catch {
|
|
265
|
+
// Ignore non-JSON
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Collect response body examples
|
|
270
|
+
if (entry.response.content.text) {
|
|
271
|
+
try {
|
|
272
|
+
const body = JSON.parse(entry.response.content.text);
|
|
273
|
+
group.responseBodyExamples.push(body);
|
|
274
|
+
} catch {
|
|
275
|
+
// Ignore non-JSON
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Check if entry is likely an API request
|
|
283
|
+
*/
|
|
284
|
+
private isApiRequest(entry: HarEntry): boolean {
|
|
285
|
+
const contentType = entry.response.content.mimeType || '';
|
|
286
|
+
const url = entry.request.url;
|
|
287
|
+
|
|
288
|
+
// Include JSON/XML responses
|
|
289
|
+
if (contentType.includes('json') || contentType.includes('xml')) {
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Exclude common static assets
|
|
294
|
+
const staticExtensions = ['.js', '.css', '.html', '.png', '.jpg', '.gif', '.ico', '.svg', '.woff', '.ttf'];
|
|
295
|
+
if (staticExtensions.some(ext => url.toLowerCase().endsWith(ext))) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Include if URL contains common API patterns
|
|
300
|
+
const apiPatterns = ['/api/', '/v1/', '/v2/', '/v3/', '/rest/', '/graphql'];
|
|
301
|
+
if (apiPatterns.some(pattern => url.includes(pattern))) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Include POST, PUT, PATCH, DELETE by default
|
|
306
|
+
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(entry.request.method)) {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Extract endpoint pattern from URL (detect path parameters)
|
|
315
|
+
*/
|
|
316
|
+
private extractEndpointPattern(urlString: string): { pattern: string; pathParams: string[] } {
|
|
317
|
+
let url: URL;
|
|
318
|
+
try {
|
|
319
|
+
url = new URL(urlString);
|
|
320
|
+
} catch {
|
|
321
|
+
return { pattern: urlString, pathParams: [] };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const pathParts = url.pathname.split('/').filter(Boolean);
|
|
325
|
+
const pathParams: string[] = [];
|
|
326
|
+
const patternParts: string[] = [];
|
|
327
|
+
|
|
328
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
329
|
+
const part = pathParts[i];
|
|
330
|
+
|
|
331
|
+
// Detect path parameters (UUIDs, numeric IDs, etc.)
|
|
332
|
+
if (this.isLikelyPathParam(part)) {
|
|
333
|
+
const paramName = this.guessParamName(pathParts, i);
|
|
334
|
+
pathParams.push(paramName);
|
|
335
|
+
patternParts.push(`{${paramName}}`);
|
|
336
|
+
} else {
|
|
337
|
+
patternParts.push(part);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
pattern: '/' + patternParts.join('/'),
|
|
343
|
+
pathParams,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Check if path segment is likely a parameter
|
|
349
|
+
*/
|
|
350
|
+
private isLikelyPathParam(segment: string): boolean {
|
|
351
|
+
// UUID pattern
|
|
352
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(segment)) {
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Numeric ID
|
|
357
|
+
if (/^\d+$/.test(segment) && segment.length <= MAX_NUMERIC_ID_LENGTH) {
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Short alphanumeric ID (like GitHub's short SHA)
|
|
362
|
+
if (/^[a-f0-9]{7,40}$/i.test(segment)) {
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Base64-like strings (common for encoded IDs)
|
|
367
|
+
if (/^[A-Za-z0-9_-]{20,}$/.test(segment) && segment.length <= MAX_BASE64_ID_LENGTH) {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Guess parameter name from context
|
|
376
|
+
*/
|
|
377
|
+
private guessParamName(pathParts: string[], index: number): string {
|
|
378
|
+
// Use previous segment as hint
|
|
379
|
+
if (index > 0) {
|
|
380
|
+
const prev = pathParts[index - 1];
|
|
381
|
+
// Common patterns: /users/123, /posts/abc
|
|
382
|
+
if (prev.endsWith('s')) {
|
|
383
|
+
return prev.slice(0, -1) + '_id';
|
|
384
|
+
}
|
|
385
|
+
return prev + '_id';
|
|
386
|
+
}
|
|
387
|
+
return 'id';
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Detect authentication pattern from entry
|
|
392
|
+
*/
|
|
393
|
+
private detectAuthPattern(entry: HarEntry, group: HarEndpointGroup): void {
|
|
394
|
+
const headers = entry.request.headers;
|
|
395
|
+
|
|
396
|
+
for (const header of headers) {
|
|
397
|
+
const name = header.name.toLowerCase();
|
|
398
|
+
const value = header.value;
|
|
399
|
+
|
|
400
|
+
if (name === 'authorization') {
|
|
401
|
+
if (value.toLowerCase().startsWith('bearer ')) {
|
|
402
|
+
group.authPatterns.add('bearer');
|
|
403
|
+
} else if (value.toLowerCase().startsWith('basic ')) {
|
|
404
|
+
group.authPatterns.add('basic');
|
|
405
|
+
} else if (value.toLowerCase().startsWith('apikey ')) {
|
|
406
|
+
group.authPatterns.add('apikey');
|
|
407
|
+
}
|
|
408
|
+
} else if (name === 'x-api-key' || name === 'api-key') {
|
|
409
|
+
group.authPatterns.add('apikey');
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Check cookies for session tokens
|
|
414
|
+
if (entry.request.cookies.length > 0) {
|
|
415
|
+
const sessionCookies = entry.request.cookies.filter(c =>
|
|
416
|
+
c.name.toLowerCase().includes('session') ||
|
|
417
|
+
c.name.toLowerCase().includes('token') ||
|
|
418
|
+
c.name.toLowerCase().includes('auth')
|
|
419
|
+
);
|
|
420
|
+
if (sessionCookies.length > 0) {
|
|
421
|
+
group.authPatterns.add('cookie');
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Convert to MCP tool definitions
|
|
428
|
+
*/
|
|
429
|
+
toMcpTools(): HarToolDefinition[] {
|
|
430
|
+
const tools: HarToolDefinition[] = [];
|
|
431
|
+
|
|
432
|
+
for (const group of this.endpoints.values()) {
|
|
433
|
+
// Skip endpoints with too few samples
|
|
434
|
+
if (group.entries.length < 1) continue;
|
|
435
|
+
|
|
436
|
+
const tool = this.groupToTool(group);
|
|
437
|
+
tools.push(tool);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return tools;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Convert endpoint group to MCP tool
|
|
445
|
+
*/
|
|
446
|
+
private groupToTool(group: HarEndpointGroup): HarToolDefinition {
|
|
447
|
+
const properties: Record<string, any> = {};
|
|
448
|
+
const required: string[] = [];
|
|
449
|
+
|
|
450
|
+
// Add path parameters
|
|
451
|
+
for (const param of group.pathParams) {
|
|
452
|
+
properties[param] = {
|
|
453
|
+
type: 'string',
|
|
454
|
+
description: `Path parameter: ${param}`,
|
|
455
|
+
};
|
|
456
|
+
required.push(param);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Add query parameters (infer from all samples)
|
|
460
|
+
for (const param of group.queryParams) {
|
|
461
|
+
properties[param] = {
|
|
462
|
+
type: 'string',
|
|
463
|
+
description: `Query parameter: ${param}`,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Infer body schema from examples
|
|
468
|
+
if (group.requestBodyExamples.length > 0) {
|
|
469
|
+
const bodySchema = this.mergeSchemas(
|
|
470
|
+
group.requestBodyExamples.map(ex => this.inferSchemaFromExample(ex))
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
if (bodySchema.properties && Object.keys(bodySchema.properties).length <= 5) {
|
|
474
|
+
Object.assign(properties, bodySchema.properties);
|
|
475
|
+
} else {
|
|
476
|
+
properties.body = bodySchema;
|
|
477
|
+
required.push('body');
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Determine confidence level
|
|
482
|
+
const confidence = this.calculateConfidence(group);
|
|
483
|
+
|
|
484
|
+
// Determine auth
|
|
485
|
+
const auth = this.extractAuth(group.authPatterns);
|
|
486
|
+
|
|
487
|
+
// Extract examples
|
|
488
|
+
const examples = this.extractExamples(group);
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
name: this.generateToolName(group.method, group.pattern),
|
|
492
|
+
description: this.generateDescription(group),
|
|
493
|
+
inputSchema: {
|
|
494
|
+
type: 'object',
|
|
495
|
+
properties,
|
|
496
|
+
required: required.length > 0 ? required : undefined,
|
|
497
|
+
},
|
|
498
|
+
metadata: {
|
|
499
|
+
endpoint: {
|
|
500
|
+
path: group.pattern,
|
|
501
|
+
method: group.method,
|
|
502
|
+
},
|
|
503
|
+
confidence,
|
|
504
|
+
sampleCount: group.entries.length,
|
|
505
|
+
statusCodes: Array.from(group.statusCodes),
|
|
506
|
+
auth,
|
|
507
|
+
},
|
|
508
|
+
examples: examples.length > 0 ? examples : undefined,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Infer JSON Schema from example value
|
|
514
|
+
*/
|
|
515
|
+
private inferSchemaFromExample(value: any): any {
|
|
516
|
+
if (value === null) {
|
|
517
|
+
return { type: 'null' };
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (Array.isArray(value)) {
|
|
521
|
+
return {
|
|
522
|
+
type: 'array',
|
|
523
|
+
items: value.length > 0 ? this.inferSchemaFromExample(value[0]) : {},
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (typeof value === 'object') {
|
|
528
|
+
const properties: Record<string, any> = {};
|
|
529
|
+
for (const [key, val] of Object.entries(value)) {
|
|
530
|
+
properties[key] = this.inferSchemaFromExample(val);
|
|
531
|
+
}
|
|
532
|
+
return { type: 'object', properties };
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (typeof value === 'number') {
|
|
536
|
+
return Number.isInteger(value) ? { type: 'integer' } : { type: 'number' };
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (typeof value === 'boolean') {
|
|
540
|
+
return { type: 'boolean' };
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return { type: 'string' };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Merge multiple schemas into one
|
|
548
|
+
*/
|
|
549
|
+
private mergeSchemas(schemas: any[]): any {
|
|
550
|
+
if (schemas.length === 0) return { type: 'object', properties: {} };
|
|
551
|
+
if (schemas.length === 1) return schemas[0];
|
|
552
|
+
|
|
553
|
+
const merged: any = { type: 'object', properties: {} };
|
|
554
|
+
|
|
555
|
+
for (const schema of schemas) {
|
|
556
|
+
if (schema.properties) {
|
|
557
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
558
|
+
if (!merged.properties[key]) {
|
|
559
|
+
merged.properties[key] = value;
|
|
560
|
+
}
|
|
561
|
+
// Could merge types here for more accuracy
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return merged;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Calculate confidence level based on data quality
|
|
571
|
+
*/
|
|
572
|
+
private calculateConfidence(group: HarEndpointGroup): 'high' | 'medium' | 'low' {
|
|
573
|
+
let score = 0;
|
|
574
|
+
|
|
575
|
+
// More samples = higher confidence
|
|
576
|
+
if (group.entries.length >= 10) score += 3;
|
|
577
|
+
else if (group.entries.length >= 5) score += 2;
|
|
578
|
+
else if (group.entries.length >= 2) score += 1;
|
|
579
|
+
|
|
580
|
+
// Has successful responses
|
|
581
|
+
if (group.statusCodes.has(200) || group.statusCodes.has(201)) score += 2;
|
|
582
|
+
|
|
583
|
+
// Has consistent structure
|
|
584
|
+
if (group.requestBodyExamples.length >= 2) score += 1;
|
|
585
|
+
if (group.responseBodyExamples.length >= 2) score += 1;
|
|
586
|
+
|
|
587
|
+
// Clear auth pattern
|
|
588
|
+
if (group.authPatterns.size > 0) score += 1;
|
|
589
|
+
|
|
590
|
+
if (score >= 6) return 'high';
|
|
591
|
+
if (score >= 3) return 'medium';
|
|
592
|
+
return 'low';
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Extract auth info
|
|
597
|
+
*/
|
|
598
|
+
private extractAuth(patterns: Set<string>): { type: string; envVar?: string } | undefined {
|
|
599
|
+
if (patterns.size === 0) return undefined;
|
|
600
|
+
|
|
601
|
+
const pattern = patterns.values().next().value as string;
|
|
602
|
+
const authMappings: Record<string, { type: string; envVar: string }> = {
|
|
603
|
+
bearer: { type: 'bearer', envVar: 'API_TOKEN' },
|
|
604
|
+
basic: { type: 'basic', envVar: 'API_BASIC_AUTH' },
|
|
605
|
+
apikey: { type: 'apiKey', envVar: 'API_KEY' },
|
|
606
|
+
cookie: { type: 'cookie', envVar: 'SESSION_COOKIE' },
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
return authMappings[pattern] || { type: pattern };
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Extract examples from group
|
|
614
|
+
*/
|
|
615
|
+
private extractExamples(group: HarEndpointGroup): Array<{ input?: any; output?: any }> {
|
|
616
|
+
const examples: Array<{ input?: any; output?: any }> = [];
|
|
617
|
+
|
|
618
|
+
// Take up to 3 examples
|
|
619
|
+
const count = Math.min(3, group.entries.length);
|
|
620
|
+
for (let i = 0; i < count; i++) {
|
|
621
|
+
const entry = group.entries[i];
|
|
622
|
+
const example: { input?: any; output?: any } = {};
|
|
623
|
+
|
|
624
|
+
if (group.requestBodyExamples[i]) {
|
|
625
|
+
example.input = group.requestBodyExamples[i];
|
|
626
|
+
}
|
|
627
|
+
if (group.responseBodyExamples[i]) {
|
|
628
|
+
example.output = group.responseBodyExamples[i];
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (example.input || example.output) {
|
|
632
|
+
examples.push(example);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return examples;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Generate tool name from method and pattern
|
|
641
|
+
*/
|
|
642
|
+
private generateToolName(method: string, pattern: string): string {
|
|
643
|
+
const pathParts = pattern
|
|
644
|
+
.split('/')
|
|
645
|
+
.filter(Boolean)
|
|
646
|
+
.filter(part => !part.startsWith('{'))
|
|
647
|
+
.join('_');
|
|
648
|
+
|
|
649
|
+
return this.toSnakeCase(`${method.toLowerCase()}_${pathParts}`);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Generate description from group data
|
|
654
|
+
*/
|
|
655
|
+
private generateDescription(group: HarEndpointGroup): string {
|
|
656
|
+
const actionWords: Record<string, string> = {
|
|
657
|
+
GET: 'Get',
|
|
658
|
+
POST: 'Create',
|
|
659
|
+
PUT: 'Update',
|
|
660
|
+
PATCH: 'Modify',
|
|
661
|
+
DELETE: 'Delete',
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
const action = actionWords[group.method] || group.method;
|
|
665
|
+
const resource = group.pattern
|
|
666
|
+
.split('/')
|
|
667
|
+
.filter(Boolean)
|
|
668
|
+
.filter(part => !part.startsWith('{'))
|
|
669
|
+
.pop() || 'resource';
|
|
670
|
+
|
|
671
|
+
return `${action} ${resource} (inferred from ${group.entries.length} captured request${group.entries.length > 1 ? 's' : ''})`;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Convert string to snake_case
|
|
676
|
+
*/
|
|
677
|
+
private toSnakeCase(str: string): string {
|
|
678
|
+
return str
|
|
679
|
+
.replace(/([A-Z])/g, '_$1')
|
|
680
|
+
.toLowerCase()
|
|
681
|
+
.replace(/[^a-z0-9_]/g, '_')
|
|
682
|
+
.replace(/[_]+/g, '_')
|
|
683
|
+
.replace(/^_/, '')
|
|
684
|
+
.replace(/_$/, '');
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Get complete parse result
|
|
689
|
+
*/
|
|
690
|
+
getParseResult(): HarParseResult {
|
|
691
|
+
if (!this.har) {
|
|
692
|
+
throw new Error('No HAR file parsed. Call parse() first.');
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return {
|
|
696
|
+
format: 'har',
|
|
697
|
+
info: {
|
|
698
|
+
creator: `${this.har.log.creator.name} ${this.har.log.creator.version}`,
|
|
699
|
+
browser: this.har.log.browser
|
|
700
|
+
? `${this.har.log.browser.name} ${this.har.log.browser.version}`
|
|
701
|
+
: undefined,
|
|
702
|
+
entryCount: this.har.log.entries.length,
|
|
703
|
+
endpointCount: this.endpoints.size,
|
|
704
|
+
},
|
|
705
|
+
tools: this.toMcpTools(),
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Convenience function to parse HAR file
|
|
712
|
+
*/
|
|
713
|
+
export function parseHAR(input: string | object): HarParseResult {
|
|
714
|
+
const parser = new HarParser();
|
|
715
|
+
|
|
716
|
+
if (typeof input === 'string') {
|
|
717
|
+
parser.parse(input);
|
|
718
|
+
} else {
|
|
719
|
+
parser.parseObject(input as HarFile);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
return parser.getParseResult();
|
|
723
|
+
}
|