mcpmake 0.1.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/README.md +691 -0
- package/bin/mcpmake.mjs +2 -0
- package/dist/analyzer/auth-detector.d.ts +12 -0
- package/dist/analyzer/auth-detector.js +142 -0
- package/dist/analyzer/dom-parser.d.ts +10 -0
- package/dist/analyzer/dom-parser.js +259 -0
- package/dist/analyzer/goal-crawler.d.ts +25 -0
- package/dist/analyzer/goal-crawler.js +177 -0
- package/dist/analyzer/hybrid-detector.d.ts +28 -0
- package/dist/analyzer/hybrid-detector.js +96 -0
- package/dist/analyzer/index.d.ts +12 -0
- package/dist/analyzer/index.js +8 -0
- package/dist/analyzer/screenshot-capture.d.ts +29 -0
- package/dist/analyzer/screenshot-capture.js +42 -0
- package/dist/analyzer/selector-builder.d.ts +19 -0
- package/dist/analyzer/selector-builder.js +199 -0
- package/dist/analyzer/semantic-analyzer.d.ts +13 -0
- package/dist/analyzer/semantic-analyzer.js +145 -0
- package/dist/analyzer/site-crawler.d.ts +38 -0
- package/dist/analyzer/site-crawler.js +235 -0
- package/dist/cloud/billing/billing-engine.d.ts +44 -0
- package/dist/cloud/billing/billing-engine.js +81 -0
- package/dist/cloud/billing/credit-store.d.ts +64 -0
- package/dist/cloud/billing/credit-store.js +168 -0
- package/dist/cloud/billing/index.d.ts +4 -0
- package/dist/cloud/billing/index.js +2 -0
- package/dist/cloud/billing/usage-store.d.ts +42 -0
- package/dist/cloud/billing/usage-store.js +85 -0
- package/dist/cloud/billing/usage-tracker.d.ts +38 -0
- package/dist/cloud/billing/usage-tracker.js +95 -0
- package/dist/cloud/build-pipeline.d.ts +39 -0
- package/dist/cloud/build-pipeline.js +310 -0
- package/dist/cloud/build-queue.d.ts +30 -0
- package/dist/cloud/build-queue.js +70 -0
- package/dist/cloud/caddy-manager.d.ts +18 -0
- package/dist/cloud/caddy-manager.js +97 -0
- package/dist/cloud/container-backend.d.ts +62 -0
- package/dist/cloud/container-backend.js +59 -0
- package/dist/cloud/container-manager.d.ts +64 -0
- package/dist/cloud/container-manager.js +301 -0
- package/dist/cloud/crypto.d.ts +27 -0
- package/dist/cloud/crypto.js +63 -0
- package/dist/cloud/db/index.d.ts +27 -0
- package/dist/cloud/db/index.js +53 -0
- package/dist/cloud/db/migrations.d.ts +12 -0
- package/dist/cloud/db/migrations.js +329 -0
- package/dist/cloud/db/pg-store.d.ts +45 -0
- package/dist/cloud/db/pg-store.js +336 -0
- package/dist/cloud/failure-tracker.d.ts +51 -0
- package/dist/cloud/failure-tracker.js +102 -0
- package/dist/cloud/idle-monitor.d.ts +30 -0
- package/dist/cloud/idle-monitor.js +70 -0
- package/dist/cloud/mailer.d.ts +21 -0
- package/dist/cloud/mailer.js +193 -0
- package/dist/cloud/mcp-proxy.d.ts +58 -0
- package/dist/cloud/mcp-proxy.js +203 -0
- package/dist/cloud/metric-samples.d.ts +43 -0
- package/dist/cloud/metric-samples.js +85 -0
- package/dist/cloud/metrics.d.ts +26 -0
- package/dist/cloud/metrics.js +59 -0
- package/dist/cloud/multipart.d.ts +26 -0
- package/dist/cloud/multipart.js +132 -0
- package/dist/cloud/observability.d.ts +27 -0
- package/dist/cloud/observability.js +98 -0
- package/dist/cloud/rate-limiter.d.ts +31 -0
- package/dist/cloud/rate-limiter.js +58 -0
- package/dist/cloud/request-security.d.ts +5 -0
- package/dist/cloud/request-security.js +74 -0
- package/dist/cloud/resource-monitor.d.ts +69 -0
- package/dist/cloud/resource-monitor.js +130 -0
- package/dist/cloud/secret-store.d.ts +38 -0
- package/dist/cloud/secret-store.js +103 -0
- package/dist/cloud/security.d.ts +26 -0
- package/dist/cloud/security.js +142 -0
- package/dist/cloud/server.d.ts +21 -0
- package/dist/cloud/server.js +1079 -0
- package/dist/cloud/shared-state.d.ts +72 -0
- package/dist/cloud/shared-state.js +159 -0
- package/dist/cloud/ssrf.d.ts +43 -0
- package/dist/cloud/ssrf.js +150 -0
- package/dist/cloud/store.d.ts +41 -0
- package/dist/cloud/store.js +75 -0
- package/dist/cloud/stripe.d.ts +78 -0
- package/dist/cloud/stripe.js +317 -0
- package/dist/cloud/telemetry-store.d.ts +53 -0
- package/dist/cloud/telemetry-store.js +108 -0
- package/dist/cloud/web/auth.d.ts +225 -0
- package/dist/cloud/web/auth.js +555 -0
- package/dist/cloud/web/charts.d.ts +70 -0
- package/dist/cloud/web/charts.js +178 -0
- package/dist/cloud/web/csrf.d.ts +14 -0
- package/dist/cloud/web/csrf.js +22 -0
- package/dist/cloud/web/docs.d.ts +40 -0
- package/dist/cloud/web/docs.js +174 -0
- package/dist/cloud/web/router.d.ts +25 -0
- package/dist/cloud/web/router.js +1921 -0
- package/dist/cloud/web/static/alpine.min.js +5 -0
- package/dist/cloud/web/static/favicon.svg +4 -0
- package/dist/cloud/web/static/htmx-sse.js +290 -0
- package/dist/cloud/web/static/htmx.min.js +1 -0
- package/dist/cloud/web/static/style.css +2683 -0
- package/dist/cloud/web/static-server.d.ts +13 -0
- package/dist/cloud/web/static-server.js +73 -0
- package/dist/cloud/web/template-engine.d.ts +27 -0
- package/dist/cloud/web/template-engine.js +146 -0
- package/dist/cloud/web/templates/layouts/admin.hbs +57 -0
- package/dist/cloud/web/templates/layouts/auth.hbs +138 -0
- package/dist/cloud/web/templates/layouts/base.hbs +16 -0
- package/dist/cloud/web/templates/layouts/dashboard.hbs +39 -0
- package/dist/cloud/web/templates/layouts/landing.hbs +82 -0
- package/dist/cloud/web/templates/pages/admin/overview.hbs +123 -0
- package/dist/cloud/web/templates/pages/admin/servers.hbs +129 -0
- package/dist/cloud/web/templates/pages/admin/telemetry.hbs +39 -0
- package/dist/cloud/web/templates/pages/admin/user-edit.hbs +91 -0
- package/dist/cloud/web/templates/pages/admin/users.hbs +179 -0
- package/dist/cloud/web/templates/pages/auth/forgot-password.hbs +25 -0
- package/dist/cloud/web/templates/pages/auth/login.hbs +33 -0
- package/dist/cloud/web/templates/pages/auth/register.hbs +32 -0
- package/dist/cloud/web/templates/pages/auth/reset-password.hbs +34 -0
- package/dist/cloud/web/templates/pages/dashboard/billing.hbs +140 -0
- package/dist/cloud/web/templates/pages/dashboard/create.hbs +173 -0
- package/dist/cloud/web/templates/pages/dashboard/index.hbs +8 -0
- package/dist/cloud/web/templates/pages/dashboard/server-detail.hbs +280 -0
- package/dist/cloud/web/templates/pages/dashboard/server-logs.hbs +35 -0
- package/dist/cloud/web/templates/pages/dashboard/server-metrics.hbs +63 -0
- package/dist/cloud/web/templates/pages/dashboard/servers-partial.hbs +21 -0
- package/dist/cloud/web/templates/pages/dashboard/servers.hbs +44 -0
- package/dist/cloud/web/templates/pages/docs/show.hbs +16 -0
- package/dist/cloud/web/templates/pages/errors/404.hbs +9 -0
- package/dist/cloud/web/templates/pages/errors/500.hbs +8 -0
- package/dist/cloud/web/templates/pages/landing/index.hbs +223 -0
- package/dist/cloud/web/templates/pages/legal/privacy.hbs +71 -0
- package/dist/cloud/web/templates/pages/legal/terms.hbs +73 -0
- package/dist/cloud/web/templates/partials/admin-stats.hbs +52 -0
- package/dist/cloud/web/templates/partials/flash-message.hbs +6 -0
- package/dist/cloud/web/templates/partials/pricing-table.hbs +103 -0
- package/dist/cloud/web/templates/partials/server-card.hbs +19 -0
- package/dist/cloud/web/templates/partials/status-badge.hbs +1 -0
- package/dist/commands/bundle.d.ts +18 -0
- package/dist/commands/bundle.js +82 -0
- package/dist/commands/ci.d.ts +25 -0
- package/dist/commands/ci.js +149 -0
- package/dist/commands/deploy.d.ts +24 -0
- package/dist/commands/deploy.js +145 -0
- package/dist/commands/diff.d.ts +18 -0
- package/dist/commands/diff.js +185 -0
- package/dist/commands/from/describe.d.ts +65 -0
- package/dist/commands/from/describe.js +173 -0
- package/dist/commands/from/har.d.ts +81 -0
- package/dist/commands/from/har.js +255 -0
- package/dist/commands/from/openapi.d.ts +105 -0
- package/dist/commands/from/openapi.js +302 -0
- package/dist/commands/from/postman.d.ts +51 -0
- package/dist/commands/from/postman.js +146 -0
- package/dist/commands/from/target-support.d.ts +11 -0
- package/dist/commands/from/target-support.js +33 -0
- package/dist/commands/from/url.d.ts +75 -0
- package/dist/commands/from/url.js +244 -0
- package/dist/commands/from/website.d.ts +75 -0
- package/dist/commands/from/website.js +284 -0
- package/dist/commands/lint.d.ts +24 -0
- package/dist/commands/lint.js +184 -0
- package/dist/commands/merge.d.ts +18 -0
- package/dist/commands/merge.js +161 -0
- package/dist/commands/publish.d.ts +27 -0
- package/dist/commands/publish.js +334 -0
- package/dist/commands/rescan.d.ts +40 -0
- package/dist/commands/rescan.js +255 -0
- package/dist/commands/update.d.ts +14 -0
- package/dist/commands/update.js +87 -0
- package/dist/commands/verify.d.ts +14 -0
- package/dist/commands/verify.js +71 -0
- package/dist/config/configurable-command.d.ts +13 -0
- package/dist/config/configurable-command.js +70 -0
- package/dist/config/mcpmake-config.d.ts +68 -0
- package/dist/config/mcpmake-config.js +207 -0
- package/dist/docs/cli.md +400 -0
- package/dist/docs/mcp-2026-07-28-migration.md +78 -0
- package/dist/docs/migrate-from-stainless.md +94 -0
- package/dist/docs/quickstart.md +166 -0
- package/dist/docs/show-hn.md +26 -0
- package/dist/docs/website-servers.md +169 -0
- package/dist/emitter/code-writer.d.ts +8 -0
- package/dist/emitter/code-writer.js +25 -0
- package/dist/emitter/index.d.ts +32 -0
- package/dist/emitter/index.js +280 -0
- package/dist/emitter/mcpb-bundler.d.ts +31 -0
- package/dist/emitter/mcpb-bundler.js +172 -0
- package/dist/emitter/project-scaffolder.d.ts +4 -0
- package/dist/emitter/project-scaffolder.js +89 -0
- package/dist/emitter/python-template-loader.d.ts +4 -0
- package/dist/emitter/python-template-loader.js +30 -0
- package/dist/emitter/python-templates/dockerfile.hbs +14 -0
- package/dist/emitter/python-templates/env.example.hbs +6 -0
- package/dist/emitter/python-templates/requirements.txt.hbs +4 -0
- package/dist/emitter/python-templates/server.py.hbs +77 -0
- package/dist/emitter/site-scaffolder.d.ts +13 -0
- package/dist/emitter/site-scaffolder.js +70 -0
- package/dist/emitter/site-template-loader.d.ts +5 -0
- package/dist/emitter/site-template-loader.js +47 -0
- package/dist/emitter/site-templates/browser-manager.ts.hbs +233 -0
- package/dist/emitter/site-templates/config.ts.hbs +28 -0
- package/dist/emitter/site-templates/dockerfile.hbs +31 -0
- package/dist/emitter/site-templates/env.example.hbs +19 -0
- package/dist/emitter/site-templates/package.json.hbs +26 -0
- package/dist/emitter/site-templates/server-main-http.ts.hbs +108 -0
- package/dist/emitter/site-templates/server-main.ts.hbs +23 -0
- package/dist/emitter/site-templates/tool-handler-action.ts.hbs +86 -0
- package/dist/emitter/site-templates/tool-handler-form.ts.hbs +116 -0
- package/dist/emitter/site-templates/tool-handler-lifecycle.ts.hbs +146 -0
- package/dist/emitter/site-templates/tool-index.ts.hbs +11 -0
- package/dist/emitter/template-loader.d.ts +1 -0
- package/dist/emitter/template-loader.js +27 -0
- package/dist/emitter/templates/auth-provider.ts.hbs +57 -0
- package/dist/emitter/templates/config.ts.hbs +63 -0
- package/dist/emitter/templates/discovery.ts.hbs +301 -0
- package/dist/emitter/templates/dockerfile.hbs +34 -0
- package/dist/emitter/templates/env.example.hbs +28 -0
- package/dist/emitter/templates/gitignore.hbs +5 -0
- package/dist/emitter/templates/http-executor.ts.hbs +117 -0
- package/dist/emitter/templates/oauth.ts.hbs +188 -0
- package/dist/emitter/templates/package.json.hbs +25 -0
- package/dist/emitter/templates/prompts.ts.hbs +22 -0
- package/dist/emitter/templates/readme.md.hbs +123 -0
- package/dist/emitter/templates/resources.ts.hbs +63 -0
- package/dist/emitter/templates/server-main-http.ts.hbs +407 -0
- package/dist/emitter/templates/server-main.ts.hbs +40 -0
- package/dist/emitter/templates/task-handlers.ts.hbs +189 -0
- package/dist/emitter/templates/task-manager.ts.hbs +139 -0
- package/dist/emitter/templates/task-sse.ts.hbs +105 -0
- package/dist/emitter/templates/tool-handler.ts.hbs +124 -0
- package/dist/emitter/templates/tool-index.ts.hbs +11 -0
- package/dist/emitter/templates/tool-test.ts.hbs +57 -0
- package/dist/emitter/templates/trace.ts.hbs +79 -0
- package/dist/emitter/templates/tsconfig.json.hbs +16 -0
- package/dist/emitter/templates/types.ts.hbs +5 -0
- package/dist/emitter/worker-template-loader.d.ts +5 -0
- package/dist/emitter/worker-template-loader.js +33 -0
- package/dist/emitter/worker-templates/config.ts.hbs +54 -0
- package/dist/emitter/worker-templates/dev-vars.example.hbs +10 -0
- package/dist/emitter/worker-templates/gitignore.hbs +6 -0
- package/dist/emitter/worker-templates/package.json.hbs +24 -0
- package/dist/emitter/worker-templates/readme.md.hbs +53 -0
- package/dist/emitter/worker-templates/server.test.ts.hbs +20 -0
- package/dist/emitter/worker-templates/tool-handler.ts.hbs +85 -0
- package/dist/emitter/worker-templates/tool-index.ts.hbs +28 -0
- package/dist/emitter/worker-templates/tsconfig.json.hbs +17 -0
- package/dist/emitter/worker-templates/worker.ts.hbs +242 -0
- package/dist/emitter/worker-templates/wrangler.toml.hbs +19 -0
- package/dist/generator/spec-generator.d.ts +6 -0
- package/dist/generator/spec-generator.js +50 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +64 -0
- package/dist/parser/har-filter.d.ts +8 -0
- package/dist/parser/har-filter.js +71 -0
- package/dist/parser/har-loader.d.ts +2 -0
- package/dist/parser/har-loader.js +14 -0
- package/dist/parser/har-normalizer.d.ts +20 -0
- package/dist/parser/har-normalizer.js +78 -0
- package/dist/parser/index.d.ts +10 -0
- package/dist/parser/index.js +6 -0
- package/dist/parser/openapi-loader.d.ts +6 -0
- package/dist/parser/openapi-loader.js +308 -0
- package/dist/parser/operation-extractor.d.ts +13 -0
- package/dist/parser/operation-extractor.js +155 -0
- package/dist/parser/overlay-loader.d.ts +10 -0
- package/dist/parser/overlay-loader.js +184 -0
- package/dist/parser/postman-loader.d.ts +9 -0
- package/dist/parser/postman-loader.js +106 -0
- package/dist/parser/schema-converter.d.ts +12 -0
- package/dist/parser/schema-converter.js +117 -0
- package/dist/plugins/adapter.d.ts +40 -0
- package/dist/plugins/adapter.js +15 -0
- package/dist/plugins/loader.d.ts +25 -0
- package/dist/plugins/loader.js +58 -0
- package/dist/pricing.d.ts +55 -0
- package/dist/pricing.js +133 -0
- package/dist/providers/index.d.ts +15 -0
- package/dist/providers/index.js +56 -0
- package/dist/recorder/browser-recorder.d.ts +22 -0
- package/dist/recorder/browser-recorder.js +205 -0
- package/dist/registry/official-registry.d.ts +90 -0
- package/dist/registry/official-registry.js +129 -0
- package/dist/rescan/diff-engine.d.ts +5 -0
- package/dist/rescan/diff-engine.js +312 -0
- package/dist/rescan/index.d.ts +3 -0
- package/dist/rescan/index.js +2 -0
- package/dist/rescan/rescan-runner.d.ts +42 -0
- package/dist/rescan/rescan-runner.js +69 -0
- package/dist/rescan/rescan-scheduler.d.ts +41 -0
- package/dist/rescan/rescan-scheduler.js +179 -0
- package/dist/site-transformer/browser-tools.d.ts +10 -0
- package/dist/site-transformer/browser-tools.js +59 -0
- package/dist/site-transformer/index.d.ts +2 -0
- package/dist/site-transformer/index.js +2 -0
- package/dist/site-transformer/selector-healer.d.ts +8 -0
- package/dist/site-transformer/selector-healer.js +106 -0
- package/dist/site-transformer/tool-generator.d.ts +13 -0
- package/dist/site-transformer/tool-generator.js +245 -0
- package/dist/transformer/auth-detector.d.ts +13 -0
- package/dist/transformer/auth-detector.js +90 -0
- package/dist/transformer/catalog-builder.d.ts +18 -0
- package/dist/transformer/catalog-builder.js +56 -0
- package/dist/transformer/client-compat.d.ts +6 -0
- package/dist/transformer/client-compat.js +44 -0
- package/dist/transformer/har-clusterer.d.ts +9 -0
- package/dist/transformer/har-clusterer.js +27 -0
- package/dist/transformer/har-dedup.d.ts +10 -0
- package/dist/transformer/har-dedup.js +81 -0
- package/dist/transformer/har-schema-inferrer.d.ts +15 -0
- package/dist/transformer/har-schema-inferrer.js +90 -0
- package/dist/transformer/har-to-operations.d.ts +13 -0
- package/dist/transformer/har-to-operations.js +192 -0
- package/dist/transformer/index.d.ts +8 -0
- package/dist/transformer/index.js +6 -0
- package/dist/transformer/llm-namer.d.ts +6 -0
- package/dist/transformer/llm-namer.js +59 -0
- package/dist/transformer/naming.d.ts +4 -0
- package/dist/transformer/naming.js +30 -0
- package/dist/transformer/operation-filter.d.ts +13 -0
- package/dist/transformer/operation-filter.js +52 -0
- package/dist/transformer/resource-builder.d.ts +12 -0
- package/dist/transformer/resource-builder.js +80 -0
- package/dist/transformer/schema-merger.d.ts +14 -0
- package/dist/transformer/schema-merger.js +65 -0
- package/dist/transformer/tool-builder.d.ts +3 -0
- package/dist/transformer/tool-builder.js +114 -0
- package/dist/types/index.d.ts +131 -0
- package/dist/types/index.js +1 -0
- package/dist/types/site.d.ts +284 -0
- package/dist/types/site.js +8 -0
- package/dist/utils/fail.d.ts +48 -0
- package/dist/utils/fail.js +204 -0
- package/dist/utils/fs.d.ts +5 -0
- package/dist/utils/fs.js +28 -0
- package/dist/utils/interactive.d.ts +6 -0
- package/dist/utils/interactive.js +30 -0
- package/dist/utils/logger.d.ts +1 -0
- package/dist/utils/logger.js +2 -0
- package/dist/utils/sanitize.d.ts +28 -0
- package/dist/utils/sanitize.js +44 -0
- package/dist/utils/watcher.d.ts +11 -0
- package/dist/utils/watcher.js +36 -0
- package/package.json +65 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export function detectAuthSchemes(securitySchemes) {
|
|
2
|
+
const authSchemes = [];
|
|
3
|
+
const envVars = [];
|
|
4
|
+
const oauthFlows = [];
|
|
5
|
+
for (const [name, scheme] of Object.entries(securitySchemes)) {
|
|
6
|
+
if (scheme.type === 'apiKey') {
|
|
7
|
+
authSchemes.push({
|
|
8
|
+
type: 'apiKey',
|
|
9
|
+
envVarName: 'API_KEY',
|
|
10
|
+
headerName: scheme.name,
|
|
11
|
+
in: scheme.in,
|
|
12
|
+
description: `API key for ${name}`,
|
|
13
|
+
});
|
|
14
|
+
envVars.push({
|
|
15
|
+
name: 'API_KEY',
|
|
16
|
+
description: `API key (sent as ${scheme.in} "${scheme.name}")`,
|
|
17
|
+
required: true,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
else if (scheme.type === 'http' && scheme.scheme === 'bearer') {
|
|
21
|
+
authSchemes.push({
|
|
22
|
+
type: 'http-bearer',
|
|
23
|
+
envVarName: 'BEARER_TOKEN',
|
|
24
|
+
description: `Bearer token for ${name}`,
|
|
25
|
+
});
|
|
26
|
+
envVars.push({
|
|
27
|
+
name: 'BEARER_TOKEN',
|
|
28
|
+
description: 'Bearer authentication token',
|
|
29
|
+
required: true,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
else if (scheme.type === 'http' && scheme.scheme === 'basic') {
|
|
33
|
+
authSchemes.push({
|
|
34
|
+
type: 'http-basic',
|
|
35
|
+
envVarName: 'BASIC_USERNAME',
|
|
36
|
+
description: `Basic auth for ${name}`,
|
|
37
|
+
});
|
|
38
|
+
envVars.push({ name: 'BASIC_USERNAME', description: 'Basic auth username', required: true }, { name: 'BASIC_PASSWORD', description: 'Basic auth password', required: true });
|
|
39
|
+
}
|
|
40
|
+
else if (scheme.type === 'oauth2') {
|
|
41
|
+
authSchemes.push({
|
|
42
|
+
type: 'oauth2',
|
|
43
|
+
envVarName: 'OAUTH2_CLIENT_ID',
|
|
44
|
+
description: `OAuth2 for ${name}`,
|
|
45
|
+
});
|
|
46
|
+
// Extract flow details
|
|
47
|
+
const flows = scheme.flows;
|
|
48
|
+
if (flows?.authorizationCode) {
|
|
49
|
+
const flow = flows.authorizationCode;
|
|
50
|
+
oauthFlows.push({
|
|
51
|
+
authorizationUrl: flow.authorizationUrl,
|
|
52
|
+
tokenUrl: flow.tokenUrl,
|
|
53
|
+
scopes: Object.keys(flow.scopes ?? {}),
|
|
54
|
+
flowType: 'authorizationCode',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (flows?.clientCredentials) {
|
|
58
|
+
const flow = flows.clientCredentials;
|
|
59
|
+
oauthFlows.push({
|
|
60
|
+
tokenUrl: flow.tokenUrl,
|
|
61
|
+
scopes: Object.keys(flow.scopes ?? {}),
|
|
62
|
+
flowType: 'clientCredentials',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Emit OAuth env vars
|
|
66
|
+
envVars.push({ name: 'OAUTH2_CLIENT_ID', description: 'OAuth2 client ID', required: true }, { name: 'OAUTH2_CLIENT_SECRET', description: 'OAuth2 client secret', required: false }, {
|
|
67
|
+
name: 'OAUTH2_TOKEN',
|
|
68
|
+
description: 'Pre-obtained OAuth2 token (alternative to client credentials)',
|
|
69
|
+
required: false,
|
|
70
|
+
});
|
|
71
|
+
if (flows?.authorizationCode) {
|
|
72
|
+
envVars.push({
|
|
73
|
+
name: 'OAUTH2_REDIRECT_URI',
|
|
74
|
+
description: 'OAuth2 redirect URI for authorization code flow',
|
|
75
|
+
required: false,
|
|
76
|
+
example: 'http://localhost:3000/callback',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Deduplicate env vars by name
|
|
82
|
+
const seen = new Set();
|
|
83
|
+
const uniqueEnvVars = envVars.filter((v) => {
|
|
84
|
+
if (seen.has(v.name))
|
|
85
|
+
return false;
|
|
86
|
+
seen.add(v.name);
|
|
87
|
+
return true;
|
|
88
|
+
});
|
|
89
|
+
return { authSchemes, envVars: uniqueEnvVars, oauthFlows };
|
|
90
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ToolDefinition } from '../types/index.js';
|
|
2
|
+
export interface CatalogEntry {
|
|
3
|
+
name: string;
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
inputSchema: Record<string, unknown>;
|
|
9
|
+
pathParams: string[];
|
|
10
|
+
queryParams: string[];
|
|
11
|
+
hasRequestBody: boolean;
|
|
12
|
+
requestBodyContentType: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build a tool catalog JSON from tool definitions.
|
|
16
|
+
* Used by dynamic discovery mode to avoid registering all tools upfront.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildCatalog(tools: ToolDefinition[]): CatalogEntry[];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a tool catalog JSON from tool definitions.
|
|
3
|
+
* Used by dynamic discovery mode to avoid registering all tools upfront.
|
|
4
|
+
*/
|
|
5
|
+
export function buildCatalog(tools) {
|
|
6
|
+
return tools.map((tool) => ({
|
|
7
|
+
name: tool.name,
|
|
8
|
+
title: tool.title,
|
|
9
|
+
description: tool.description,
|
|
10
|
+
method: tool.method,
|
|
11
|
+
path: tool.pathTemplate,
|
|
12
|
+
inputSchema: parseInputSchema(tool.inputSchemaCode),
|
|
13
|
+
pathParams: tool.pathParams,
|
|
14
|
+
queryParams: tool.queryParams,
|
|
15
|
+
hasRequestBody: tool.hasRequestBody,
|
|
16
|
+
requestBodyContentType: tool.requestBodyContentType,
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse the Zod code string into a JSON Schema-like object for the catalog.
|
|
21
|
+
* This is a best-effort conversion for display purposes.
|
|
22
|
+
*/
|
|
23
|
+
function parseInputSchema(zodCode) {
|
|
24
|
+
// Extract field names and types from the Zod code
|
|
25
|
+
const fields = {};
|
|
26
|
+
const fieldPattern = /(\w+):\s*z\.(\w+)/g;
|
|
27
|
+
let match;
|
|
28
|
+
while ((match = fieldPattern.exec(zodCode)) !== null) {
|
|
29
|
+
fields[match[1]] = match[2];
|
|
30
|
+
}
|
|
31
|
+
if (Object.keys(fields).length === 0) {
|
|
32
|
+
return { type: 'object', properties: {} };
|
|
33
|
+
}
|
|
34
|
+
const properties = {};
|
|
35
|
+
for (const [name, type] of Object.entries(fields)) {
|
|
36
|
+
properties[name] = { type: mapZodType(type) };
|
|
37
|
+
}
|
|
38
|
+
return { type: 'object', properties };
|
|
39
|
+
}
|
|
40
|
+
function mapZodType(zodType) {
|
|
41
|
+
switch (zodType) {
|
|
42
|
+
case 'string':
|
|
43
|
+
return 'string';
|
|
44
|
+
case 'number':
|
|
45
|
+
case 'int':
|
|
46
|
+
return 'number';
|
|
47
|
+
case 'boolean':
|
|
48
|
+
return 'boolean';
|
|
49
|
+
case 'array':
|
|
50
|
+
return 'array';
|
|
51
|
+
case 'object':
|
|
52
|
+
return 'object';
|
|
53
|
+
default:
|
|
54
|
+
return 'string';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ToolDefinition } from '../types/index.js';
|
|
2
|
+
export type ClientMode = 'cursor' | 'claude' | 'openai';
|
|
3
|
+
/**
|
|
4
|
+
* Apply client-specific compatibility transforms to tool definitions.
|
|
5
|
+
*/
|
|
6
|
+
export declare function applyClientCompat(tools: ToolDefinition[], client: ClientMode): ToolDefinition[];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { logger } from '../utils/logger.js';
|
|
2
|
+
const CLIENT_LIMITS = {
|
|
3
|
+
cursor: { maxToolNameLength: 60, maxTools: 40 },
|
|
4
|
+
claude: { maxToolNameLength: 128, maxTools: 1000 },
|
|
5
|
+
openai: { maxToolNameLength: 128, maxTools: 128 },
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Apply client-specific compatibility transforms to tool definitions.
|
|
9
|
+
*/
|
|
10
|
+
export function applyClientCompat(tools, client) {
|
|
11
|
+
const limits = CLIENT_LIMITS[client];
|
|
12
|
+
let result = tools.map((t) => ({ ...t }));
|
|
13
|
+
// Truncate tool names to client limit
|
|
14
|
+
const renamed = new Map();
|
|
15
|
+
for (const tool of result) {
|
|
16
|
+
if (tool.name.length > limits.maxToolNameLength) {
|
|
17
|
+
const original = tool.name;
|
|
18
|
+
tool.name = tool.name.slice(0, limits.maxToolNameLength);
|
|
19
|
+
renamed.set(original, tool.name);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Deduplicate after truncation
|
|
23
|
+
const nameCount = new Map();
|
|
24
|
+
for (const t of result) {
|
|
25
|
+
nameCount.set(t.name, (nameCount.get(t.name) ?? 0) + 1);
|
|
26
|
+
}
|
|
27
|
+
for (const tool of result) {
|
|
28
|
+
if ((nameCount.get(tool.name) ?? 0) > 1) {
|
|
29
|
+
// Append a hash suffix to make unique, staying within limit
|
|
30
|
+
const suffix = `_${tool.method}`;
|
|
31
|
+
const maxBase = limits.maxToolNameLength - suffix.length;
|
|
32
|
+
tool.name = tool.name.slice(0, maxBase) + suffix;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (renamed.size > 0) {
|
|
36
|
+
logger.warn(`[${client}] Truncated ${renamed.size} tool name(s) to ${limits.maxToolNameLength} chars`);
|
|
37
|
+
}
|
|
38
|
+
// Enforce max tool count
|
|
39
|
+
if (result.length > limits.maxTools) {
|
|
40
|
+
logger.warn(`[${client}] Tool count (${result.length}) exceeds limit (${limits.maxTools}). Keeping first ${limits.maxTools}.`);
|
|
41
|
+
result = result.slice(0, limits.maxTools);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NormalizedEntry } from '../parser/har-normalizer.js';
|
|
2
|
+
export interface EntryCluster {
|
|
3
|
+
signature: string;
|
|
4
|
+
method: string;
|
|
5
|
+
normalizedPath: string;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
entries: NormalizedEntry[];
|
|
8
|
+
}
|
|
9
|
+
export declare function clusterEntries(entries: NormalizedEntry[]): EntryCluster[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function clusterEntries(entries) {
|
|
2
|
+
const groups = new Map();
|
|
3
|
+
for (const entry of entries) {
|
|
4
|
+
const method = entry.entry.request.method.toUpperCase();
|
|
5
|
+
const signature = `${method} ${entry.normalizedPath}`;
|
|
6
|
+
const existing = groups.get(signature);
|
|
7
|
+
if (existing) {
|
|
8
|
+
existing.push(entry);
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
groups.set(signature, [entry]);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const clusters = [];
|
|
15
|
+
for (const [signature, clusterEntries] of groups) {
|
|
16
|
+
const [method, ...pathParts] = signature.split(' ');
|
|
17
|
+
const normalizedPath = pathParts.join(' ');
|
|
18
|
+
clusters.push({
|
|
19
|
+
signature,
|
|
20
|
+
method: method.toLowerCase(),
|
|
21
|
+
normalizedPath,
|
|
22
|
+
baseUrl: clusterEntries[0].baseUrl,
|
|
23
|
+
entries: clusterEntries,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return clusters;
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { NormalizedEntry } from '../parser/har-normalizer.js';
|
|
2
|
+
/**
|
|
3
|
+
* Deduplicate HAR entries that are likely pagination or retries.
|
|
4
|
+
*
|
|
5
|
+
* Strategy:
|
|
6
|
+
* - Group by method + normalizedPath (same as clustering)
|
|
7
|
+
* - Within each group, detect pagination patterns (same URL with different page/offset/cursor params)
|
|
8
|
+
* - Remove retries: consecutive requests to the same URL within 5s with same method
|
|
9
|
+
*/
|
|
10
|
+
export declare function deduplicateEntries(entries: NormalizedEntry[]): NormalizedEntry[];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deduplicate HAR entries that are likely pagination or retries.
|
|
3
|
+
*
|
|
4
|
+
* Strategy:
|
|
5
|
+
* - Group by method + normalizedPath (same as clustering)
|
|
6
|
+
* - Within each group, detect pagination patterns (same URL with different page/offset/cursor params)
|
|
7
|
+
* - Remove retries: consecutive requests to the same URL within 5s with same method
|
|
8
|
+
*/
|
|
9
|
+
export function deduplicateEntries(entries) {
|
|
10
|
+
const result = [];
|
|
11
|
+
const seenSignatures = new Map();
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
const url = entry.entry.request.url;
|
|
14
|
+
const method = entry.entry.request.method;
|
|
15
|
+
const signature = `${method} ${stripPaginationParams(url)}`;
|
|
16
|
+
const timestamp = new Date(entry.entry.startedDateTime).getTime();
|
|
17
|
+
const seen = seenSignatures.get(signature);
|
|
18
|
+
if (seen) {
|
|
19
|
+
// Skip retries: same signature within 5 seconds
|
|
20
|
+
if (timestamp - seen.lastTime < 5000) {
|
|
21
|
+
seen.lastTime = timestamp;
|
|
22
|
+
seen.count++;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
// Skip excessive pagination: keep at most 3 examples per endpoint
|
|
26
|
+
if (isPaginationVariant(url, entries, entry) && seen.count >= 3) {
|
|
27
|
+
seen.lastTime = timestamp;
|
|
28
|
+
seen.count++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
seenSignatures.set(signature, {
|
|
33
|
+
lastTime: timestamp,
|
|
34
|
+
count: (seen?.count ?? 0) + 1,
|
|
35
|
+
});
|
|
36
|
+
result.push(entry);
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
const PAGINATION_PARAMS = new Set([
|
|
41
|
+
'page',
|
|
42
|
+
'offset',
|
|
43
|
+
'limit',
|
|
44
|
+
'cursor',
|
|
45
|
+
'after',
|
|
46
|
+
'before',
|
|
47
|
+
'skip',
|
|
48
|
+
'take',
|
|
49
|
+
'per_page',
|
|
50
|
+
'page_size',
|
|
51
|
+
'pagesize',
|
|
52
|
+
'start',
|
|
53
|
+
'count',
|
|
54
|
+
'from',
|
|
55
|
+
'next_token',
|
|
56
|
+
'continuation',
|
|
57
|
+
]);
|
|
58
|
+
function stripPaginationParams(url) {
|
|
59
|
+
try {
|
|
60
|
+
const parsed = new URL(url);
|
|
61
|
+
for (const key of [...parsed.searchParams.keys()]) {
|
|
62
|
+
if (PAGINATION_PARAMS.has(key.toLowerCase())) {
|
|
63
|
+
parsed.searchParams.delete(key);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return `${parsed.origin}${parsed.pathname}${parsed.search}`;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return url;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function isPaginationVariant(url, allEntries, current) {
|
|
73
|
+
try {
|
|
74
|
+
const parsed = new URL(url);
|
|
75
|
+
const paramKeys = [...parsed.searchParams.keys()].map((k) => k.toLowerCase());
|
|
76
|
+
return paramKeys.some((k) => PAGINATION_PARAMS.has(k));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { JsonSchema } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Infer a JSON Schema from a sample JSON value.
|
|
4
|
+
* Handles objects, arrays, and primitives.
|
|
5
|
+
*/
|
|
6
|
+
export declare function inferJsonSchema(value: unknown, depth?: number): JsonSchema;
|
|
7
|
+
/**
|
|
8
|
+
* Try to parse a response body as JSON and infer its schema.
|
|
9
|
+
* Returns undefined if not JSON or parsing fails.
|
|
10
|
+
*/
|
|
11
|
+
export declare function inferResponseSchema(body: string | undefined, mimeType: string): JsonSchema | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Try to parse a request body and infer its schema.
|
|
14
|
+
*/
|
|
15
|
+
export declare function inferRequestBodySchema(text: string | undefined, mimeType: string): JsonSchema | undefined;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { isDangerousKey } from '../utils/sanitize.js';
|
|
2
|
+
const MAX_INFERENCE_DEPTH = 20;
|
|
3
|
+
/**
|
|
4
|
+
* Infer a JSON Schema from a sample JSON value.
|
|
5
|
+
* Handles objects, arrays, and primitives.
|
|
6
|
+
*/
|
|
7
|
+
export function inferJsonSchema(value, depth = 0) {
|
|
8
|
+
if (depth > MAX_INFERENCE_DEPTH) {
|
|
9
|
+
return { type: 'object' };
|
|
10
|
+
}
|
|
11
|
+
if (value === null || value === undefined) {
|
|
12
|
+
return { type: 'string' };
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
if (value.length === 0) {
|
|
16
|
+
return { type: 'array', items: {} };
|
|
17
|
+
}
|
|
18
|
+
return { type: 'array', items: inferJsonSchema(value[0], depth + 1) };
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === 'object') {
|
|
21
|
+
const properties = {};
|
|
22
|
+
const required = [];
|
|
23
|
+
for (const [key, val] of Object.entries(value)) {
|
|
24
|
+
if (isDangerousKey(key))
|
|
25
|
+
continue;
|
|
26
|
+
properties[key] = inferJsonSchema(val, depth + 1);
|
|
27
|
+
if (val !== null && val !== undefined) {
|
|
28
|
+
required.push(key);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties,
|
|
34
|
+
...(required.length > 0 ? { required } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (typeof value === 'number') {
|
|
38
|
+
return Number.isInteger(value) ? { type: 'integer' } : { type: 'number' };
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === 'boolean') {
|
|
41
|
+
return { type: 'boolean' };
|
|
42
|
+
}
|
|
43
|
+
return { type: 'string' };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Try to parse a response body as JSON and infer its schema.
|
|
47
|
+
* Returns undefined if not JSON or parsing fails.
|
|
48
|
+
*/
|
|
49
|
+
export function inferResponseSchema(body, mimeType) {
|
|
50
|
+
if (!body || !mimeType.includes('json'))
|
|
51
|
+
return undefined;
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(body);
|
|
54
|
+
return inferJsonSchema(parsed);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Try to parse a request body and infer its schema.
|
|
62
|
+
*/
|
|
63
|
+
export function inferRequestBodySchema(text, mimeType) {
|
|
64
|
+
if (!text)
|
|
65
|
+
return undefined;
|
|
66
|
+
if (mimeType.includes('json')) {
|
|
67
|
+
try {
|
|
68
|
+
return inferJsonSchema(JSON.parse(text));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (mimeType.includes('x-www-form-urlencoded')) {
|
|
75
|
+
const params = new URLSearchParams(text);
|
|
76
|
+
const properties = {};
|
|
77
|
+
const required = [];
|
|
78
|
+
for (const [key, value] of params) {
|
|
79
|
+
properties[key] = { type: 'string' };
|
|
80
|
+
required.push(key);
|
|
81
|
+
// Try to detect numbers/booleans
|
|
82
|
+
if (/^\d+$/.test(value))
|
|
83
|
+
properties[key] = { type: 'integer' };
|
|
84
|
+
else if (value === 'true' || value === 'false')
|
|
85
|
+
properties[key] = { type: 'boolean' };
|
|
86
|
+
}
|
|
87
|
+
return { type: 'object', properties, required };
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { OperationDescriptor } from '../types/index.js';
|
|
2
|
+
import type { EntryCluster } from './har-clusterer.js';
|
|
3
|
+
export interface HarConversionResult {
|
|
4
|
+
operations: OperationDescriptor[];
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
detectedAuth: DetectedAuth[];
|
|
7
|
+
}
|
|
8
|
+
export interface DetectedAuth {
|
|
9
|
+
type: 'bearer' | 'basic' | 'apiKey';
|
|
10
|
+
headerName: string;
|
|
11
|
+
exampleValue?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function clustersToOperations(clusters: EntryCluster[]): HarConversionResult;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { mergeRequestBodySchemas } from './schema-merger.js';
|
|
2
|
+
const AUTH_HEADER_PATTERNS = [
|
|
3
|
+
{ pattern: /^Bearer\s+/i, schemeName: 'bearer' },
|
|
4
|
+
{ pattern: /^Basic\s+/i, schemeName: 'basic' },
|
|
5
|
+
{ pattern: /^Token\s+/i, schemeName: 'token' },
|
|
6
|
+
];
|
|
7
|
+
const CUSTOM_AUTH_HEADERS = [
|
|
8
|
+
'x-api-key',
|
|
9
|
+
'api-key',
|
|
10
|
+
'authorization-token',
|
|
11
|
+
'x-auth-token',
|
|
12
|
+
'x-access-token',
|
|
13
|
+
'x-csrf-token',
|
|
14
|
+
'x-session-token',
|
|
15
|
+
];
|
|
16
|
+
const SESSION_COOKIE_PATTERNS = [
|
|
17
|
+
/^sess/i,
|
|
18
|
+
/^sid$/i,
|
|
19
|
+
/^session/i,
|
|
20
|
+
/^token/i,
|
|
21
|
+
/^auth/i,
|
|
22
|
+
/^jwt/i,
|
|
23
|
+
/^connect\.sid$/i,
|
|
24
|
+
];
|
|
25
|
+
export function clustersToOperations(clusters) {
|
|
26
|
+
const operations = [];
|
|
27
|
+
const allAuth = new Map();
|
|
28
|
+
let baseUrl = '';
|
|
29
|
+
for (const cluster of clusters) {
|
|
30
|
+
if (!baseUrl)
|
|
31
|
+
baseUrl = cluster.baseUrl;
|
|
32
|
+
const canonical = pickCanonicalEntry(cluster.entries);
|
|
33
|
+
const entry = canonical.entry;
|
|
34
|
+
const auth = detectAuth(entry);
|
|
35
|
+
for (const a of auth) {
|
|
36
|
+
allAuth.set(a.headerName.toLowerCase(), a);
|
|
37
|
+
}
|
|
38
|
+
const operationId = generateOperationId(cluster.method, cluster.normalizedPath);
|
|
39
|
+
const parameters = [];
|
|
40
|
+
// Path params from normalization
|
|
41
|
+
for (const pp of canonical.pathParams) {
|
|
42
|
+
const schemaType = pp.inferredType === 'integer' ? 'integer' : 'string';
|
|
43
|
+
const schema = pp.inferredType === 'uuid' ? { type: 'string', format: 'uuid' } : { type: schemaType };
|
|
44
|
+
parameters.push({
|
|
45
|
+
name: pp.name,
|
|
46
|
+
in: 'path',
|
|
47
|
+
required: true,
|
|
48
|
+
description: pp.name,
|
|
49
|
+
schema,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Query params merged across all entries in cluster
|
|
53
|
+
const queryMap = new Map();
|
|
54
|
+
for (const ne of cluster.entries) {
|
|
55
|
+
for (const qp of ne.queryParams) {
|
|
56
|
+
const existing = queryMap.get(qp.name);
|
|
57
|
+
if (existing) {
|
|
58
|
+
existing.values.add(qp.exampleValue);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
queryMap.set(qp.name, { values: new Set([qp.exampleValue]), type: qp.inferredType });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const [name, info] of queryMap) {
|
|
66
|
+
parameters.push({
|
|
67
|
+
name,
|
|
68
|
+
in: 'query',
|
|
69
|
+
required: false,
|
|
70
|
+
schema: { type: info.type },
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// Request body — merge schemas across all entries in the cluster
|
|
74
|
+
let requestBody;
|
|
75
|
+
const bodyEntries = cluster.entries
|
|
76
|
+
.map((ne) => ne.entry.request.postData)
|
|
77
|
+
.filter((pd) => !!pd?.text)
|
|
78
|
+
.map((pd) => ({ text: pd.text, mimeType: pd.mimeType }));
|
|
79
|
+
if (bodyEntries.length > 0) {
|
|
80
|
+
const mergedSchema = mergeRequestBodySchemas(bodyEntries);
|
|
81
|
+
if (mergedSchema) {
|
|
82
|
+
requestBody = {
|
|
83
|
+
required: true,
|
|
84
|
+
contentType: bodyEntries[0].mimeType.split(';')[0].trim(),
|
|
85
|
+
schema: mergedSchema,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Security
|
|
90
|
+
const security = auth.map((a) => ({
|
|
91
|
+
schemeName: a.type,
|
|
92
|
+
scopes: [],
|
|
93
|
+
}));
|
|
94
|
+
operations.push({
|
|
95
|
+
operationId,
|
|
96
|
+
method: cluster.method,
|
|
97
|
+
path: cluster.normalizedPath,
|
|
98
|
+
summary: `${cluster.method.toUpperCase()} ${cluster.normalizedPath}`,
|
|
99
|
+
tags: [extractTag(cluster.normalizedPath)],
|
|
100
|
+
parameters,
|
|
101
|
+
requestBody,
|
|
102
|
+
responses: [],
|
|
103
|
+
security,
|
|
104
|
+
deprecated: false,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
operations,
|
|
109
|
+
baseUrl,
|
|
110
|
+
detectedAuth: [...allAuth.values()],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function pickCanonicalEntry(entries) {
|
|
114
|
+
// Prefer entries with 2xx responses
|
|
115
|
+
const successful = entries.filter((e) => e.entry.response.status >= 200 && e.entry.response.status < 300);
|
|
116
|
+
return successful[0] ?? entries[0];
|
|
117
|
+
}
|
|
118
|
+
function detectAuth(entry) {
|
|
119
|
+
const result = [];
|
|
120
|
+
for (const header of entry.request.headers) {
|
|
121
|
+
const name = header.name.toLowerCase();
|
|
122
|
+
if (name === 'authorization') {
|
|
123
|
+
for (const { pattern, schemeName } of AUTH_HEADER_PATTERNS) {
|
|
124
|
+
if (pattern.test(header.value)) {
|
|
125
|
+
result.push({
|
|
126
|
+
type: schemeName,
|
|
127
|
+
headerName: 'Authorization',
|
|
128
|
+
exampleValue: '[REDACTED]',
|
|
129
|
+
});
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (CUSTOM_AUTH_HEADERS.includes(name)) {
|
|
135
|
+
result.push({
|
|
136
|
+
type: 'apiKey',
|
|
137
|
+
headerName: header.name,
|
|
138
|
+
exampleValue: '[REDACTED]',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// Detect cookie-based session auth
|
|
142
|
+
if (name === 'cookie') {
|
|
143
|
+
const cookies = header.value.split(';').map((c) => c.trim().split('=')[0]);
|
|
144
|
+
for (const cookieName of cookies) {
|
|
145
|
+
if (SESSION_COOKIE_PATTERNS.some((p) => p.test(cookieName))) {
|
|
146
|
+
result.push({
|
|
147
|
+
type: 'apiKey',
|
|
148
|
+
headerName: 'Cookie',
|
|
149
|
+
exampleValue: '[REDACTED]',
|
|
150
|
+
});
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
function generateOperationId(method, path) {
|
|
159
|
+
const segments = path
|
|
160
|
+
.replace(/\{[^}]+\}/g, '')
|
|
161
|
+
.split('/')
|
|
162
|
+
.filter(Boolean);
|
|
163
|
+
const resource = segments[segments.length - 1] ?? 'resource';
|
|
164
|
+
switch (method.toLowerCase()) {
|
|
165
|
+
case 'get':
|
|
166
|
+
return path.includes('{') ? `get_${singularize(resource)}` : `list_${resource}`;
|
|
167
|
+
case 'post':
|
|
168
|
+
return `create_${singularize(resource)}`;
|
|
169
|
+
case 'put':
|
|
170
|
+
case 'patch':
|
|
171
|
+
return `update_${singularize(resource)}`;
|
|
172
|
+
case 'delete':
|
|
173
|
+
return `delete_${singularize(resource)}`;
|
|
174
|
+
default:
|
|
175
|
+
return `${method.toLowerCase()}_${resource}`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function extractTag(path) {
|
|
179
|
+
const segments = path.split('/').filter((s) => s && !s.startsWith('{'));
|
|
180
|
+
// Skip api/v1/v2 prefixes
|
|
181
|
+
const meaningful = segments.filter((s) => !/^(api|v\d+)$/i.test(s));
|
|
182
|
+
return meaningful[0] ?? 'default';
|
|
183
|
+
}
|
|
184
|
+
function singularize(word) {
|
|
185
|
+
if (word.endsWith('ies'))
|
|
186
|
+
return word.slice(0, -3) + 'y';
|
|
187
|
+
if (word.endsWith('ses') || word.endsWith('xes') || word.endsWith('zes'))
|
|
188
|
+
return word.slice(0, -2);
|
|
189
|
+
if (word.endsWith('s') && !word.endsWith('ss'))
|
|
190
|
+
return word.slice(0, -1);
|
|
191
|
+
return word;
|
|
192
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { toToolName, toToolTitle, toFileName, toFunctionName } from './naming.js';
|
|
2
|
+
export { detectAuthSchemes } from './auth-detector.js';
|
|
3
|
+
export { buildToolDefinition, buildAllTools } from './tool-builder.js';
|
|
4
|
+
export { clusterEntries } from './har-clusterer.js';
|
|
5
|
+
export type { EntryCluster } from './har-clusterer.js';
|
|
6
|
+
export { clustersToOperations } from './har-to-operations.js';
|
|
7
|
+
export type { HarConversionResult, DetectedAuth } from './har-to-operations.js';
|
|
8
|
+
export { inferJsonSchema, inferResponseSchema, inferRequestBodySchema, } from './har-schema-inferrer.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { toToolName, toToolTitle, toFileName, toFunctionName } from './naming.js';
|
|
2
|
+
export { detectAuthSchemes } from './auth-detector.js';
|
|
3
|
+
export { buildToolDefinition, buildAllTools } from './tool-builder.js';
|
|
4
|
+
export { clusterEntries } from './har-clusterer.js';
|
|
5
|
+
export { clustersToOperations } from './har-to-operations.js';
|
|
6
|
+
export { inferJsonSchema, inferResponseSchema, inferRequestBodySchema, } from './har-schema-inferrer.js';
|