mcpmake 0.1.0 → 0.2.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/dist/commands/bundle.d.ts +1 -0
- package/dist/commands/bundle.d.ts.map +1 -0
- package/dist/commands/bundle.js +5 -4
- package/dist/commands/bundle.js.map +1 -0
- package/dist/commands/ci.d.ts +1 -0
- package/dist/commands/ci.d.ts.map +1 -0
- package/dist/commands/ci.js +3 -2
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/deploy.d.ts +1 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +4 -3
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/diff.d.ts +1 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +5 -4
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/from/describe.d.ts +1 -0
- package/dist/commands/from/describe.d.ts.map +1 -0
- package/dist/commands/from/describe.js +11 -10
- package/dist/commands/from/describe.js.map +1 -0
- package/dist/commands/from/har.d.ts +1 -0
- package/dist/commands/from/har.d.ts.map +1 -0
- package/dist/commands/from/har.js +14 -13
- package/dist/commands/from/har.js.map +1 -0
- package/dist/commands/from/openapi.d.ts +1 -0
- package/dist/commands/from/openapi.d.ts.map +1 -0
- package/dist/commands/from/openapi.js +17 -16
- package/dist/commands/from/openapi.js.map +1 -0
- package/dist/commands/from/postman.d.ts +1 -0
- package/dist/commands/from/postman.d.ts.map +1 -0
- package/dist/commands/from/postman.js +13 -12
- package/dist/commands/from/postman.js.map +1 -0
- package/dist/commands/from/stainless.d.ts +110 -0
- package/dist/commands/from/stainless.d.ts.map +1 -0
- package/dist/commands/from/stainless.js +272 -0
- package/dist/commands/from/stainless.js.map +1 -0
- package/dist/commands/from/target-support.d.ts +1 -0
- package/dist/commands/from/target-support.d.ts.map +1 -0
- package/dist/commands/from/target-support.js +2 -1
- package/dist/commands/from/target-support.js.map +1 -0
- package/dist/commands/from/url.d.ts +1 -0
- package/dist/commands/from/url.d.ts.map +1 -0
- package/dist/commands/from/url.js +14 -13
- package/dist/commands/from/url.js.map +1 -0
- package/dist/commands/from/website.d.ts +1 -0
- package/dist/commands/from/website.d.ts.map +1 -0
- package/dist/commands/from/website.js +17 -16
- package/dist/commands/from/website.js.map +1 -0
- package/dist/commands/lint.d.ts +1 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +6 -5
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/merge.d.ts +1 -0
- package/dist/commands/merge.d.ts.map +1 -0
- package/dist/commands/merge.js +3 -2
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/publish.d.ts +1 -0
- package/dist/commands/publish.d.ts.map +1 -0
- package/dist/commands/publish.js +4 -3
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/rescan.d.ts +1 -0
- package/dist/commands/rescan.d.ts.map +1 -0
- package/dist/commands/rescan.js +12 -11
- package/dist/commands/rescan.js.map +1 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +10 -9
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/verify.d.ts +1 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +7 -6
- package/dist/commands/verify.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -2
- package/dist/index.js.map +1 -0
- package/dist/registry/official-registry.d.ts +1 -0
- package/dist/registry/official-registry.d.ts.map +1 -0
- package/dist/registry/official-registry.js +1 -0
- package/dist/registry/official-registry.js.map +1 -0
- package/package.json +24 -42
- package/README.md +0 -691
- package/dist/analyzer/auth-detector.d.ts +0 -12
- package/dist/analyzer/auth-detector.js +0 -142
- package/dist/analyzer/dom-parser.d.ts +0 -10
- package/dist/analyzer/dom-parser.js +0 -259
- package/dist/analyzer/goal-crawler.d.ts +0 -25
- package/dist/analyzer/goal-crawler.js +0 -177
- package/dist/analyzer/hybrid-detector.d.ts +0 -28
- package/dist/analyzer/hybrid-detector.js +0 -96
- package/dist/analyzer/index.d.ts +0 -12
- package/dist/analyzer/index.js +0 -8
- package/dist/analyzer/screenshot-capture.d.ts +0 -29
- package/dist/analyzer/screenshot-capture.js +0 -42
- package/dist/analyzer/selector-builder.d.ts +0 -19
- package/dist/analyzer/selector-builder.js +0 -199
- package/dist/analyzer/semantic-analyzer.d.ts +0 -13
- package/dist/analyzer/semantic-analyzer.js +0 -145
- package/dist/analyzer/site-crawler.d.ts +0 -38
- package/dist/analyzer/site-crawler.js +0 -235
- package/dist/cloud/billing/billing-engine.d.ts +0 -44
- package/dist/cloud/billing/billing-engine.js +0 -81
- package/dist/cloud/billing/credit-store.d.ts +0 -64
- package/dist/cloud/billing/credit-store.js +0 -168
- package/dist/cloud/billing/index.d.ts +0 -4
- package/dist/cloud/billing/index.js +0 -2
- package/dist/cloud/billing/usage-store.d.ts +0 -42
- package/dist/cloud/billing/usage-store.js +0 -85
- package/dist/cloud/billing/usage-tracker.d.ts +0 -38
- package/dist/cloud/billing/usage-tracker.js +0 -95
- package/dist/cloud/build-pipeline.d.ts +0 -39
- package/dist/cloud/build-pipeline.js +0 -310
- package/dist/cloud/build-queue.d.ts +0 -30
- package/dist/cloud/build-queue.js +0 -70
- package/dist/cloud/caddy-manager.d.ts +0 -18
- package/dist/cloud/caddy-manager.js +0 -97
- package/dist/cloud/container-backend.d.ts +0 -62
- package/dist/cloud/container-backend.js +0 -59
- package/dist/cloud/container-manager.d.ts +0 -64
- package/dist/cloud/container-manager.js +0 -301
- package/dist/cloud/crypto.d.ts +0 -27
- package/dist/cloud/crypto.js +0 -63
- package/dist/cloud/db/index.d.ts +0 -27
- package/dist/cloud/db/index.js +0 -53
- package/dist/cloud/db/migrations.d.ts +0 -12
- package/dist/cloud/db/migrations.js +0 -329
- package/dist/cloud/db/pg-store.d.ts +0 -45
- package/dist/cloud/db/pg-store.js +0 -336
- package/dist/cloud/failure-tracker.d.ts +0 -51
- package/dist/cloud/failure-tracker.js +0 -102
- package/dist/cloud/idle-monitor.d.ts +0 -30
- package/dist/cloud/idle-monitor.js +0 -70
- package/dist/cloud/mailer.d.ts +0 -21
- package/dist/cloud/mailer.js +0 -193
- package/dist/cloud/mcp-proxy.d.ts +0 -58
- package/dist/cloud/mcp-proxy.js +0 -203
- package/dist/cloud/metric-samples.d.ts +0 -43
- package/dist/cloud/metric-samples.js +0 -85
- package/dist/cloud/metrics.d.ts +0 -26
- package/dist/cloud/metrics.js +0 -59
- package/dist/cloud/multipart.d.ts +0 -26
- package/dist/cloud/multipart.js +0 -132
- package/dist/cloud/observability.d.ts +0 -27
- package/dist/cloud/observability.js +0 -98
- package/dist/cloud/rate-limiter.d.ts +0 -31
- package/dist/cloud/rate-limiter.js +0 -58
- package/dist/cloud/request-security.d.ts +0 -5
- package/dist/cloud/request-security.js +0 -74
- package/dist/cloud/resource-monitor.d.ts +0 -69
- package/dist/cloud/resource-monitor.js +0 -130
- package/dist/cloud/secret-store.d.ts +0 -38
- package/dist/cloud/secret-store.js +0 -103
- package/dist/cloud/security.d.ts +0 -26
- package/dist/cloud/security.js +0 -142
- package/dist/cloud/server.d.ts +0 -21
- package/dist/cloud/server.js +0 -1079
- package/dist/cloud/shared-state.d.ts +0 -72
- package/dist/cloud/shared-state.js +0 -159
- package/dist/cloud/ssrf.d.ts +0 -43
- package/dist/cloud/ssrf.js +0 -150
- package/dist/cloud/store.d.ts +0 -41
- package/dist/cloud/store.js +0 -75
- package/dist/cloud/stripe.d.ts +0 -78
- package/dist/cloud/stripe.js +0 -317
- package/dist/cloud/telemetry-store.d.ts +0 -53
- package/dist/cloud/telemetry-store.js +0 -108
- package/dist/cloud/web/auth.d.ts +0 -225
- package/dist/cloud/web/auth.js +0 -555
- package/dist/cloud/web/charts.d.ts +0 -70
- package/dist/cloud/web/charts.js +0 -178
- package/dist/cloud/web/csrf.d.ts +0 -14
- package/dist/cloud/web/csrf.js +0 -22
- package/dist/cloud/web/docs.d.ts +0 -40
- package/dist/cloud/web/docs.js +0 -174
- package/dist/cloud/web/router.d.ts +0 -25
- package/dist/cloud/web/router.js +0 -1921
- package/dist/cloud/web/static/alpine.min.js +0 -5
- package/dist/cloud/web/static/favicon.svg +0 -4
- package/dist/cloud/web/static/htmx-sse.js +0 -290
- package/dist/cloud/web/static/htmx.min.js +0 -1
- package/dist/cloud/web/static/style.css +0 -2683
- package/dist/cloud/web/static-server.d.ts +0 -13
- package/dist/cloud/web/static-server.js +0 -73
- package/dist/cloud/web/template-engine.d.ts +0 -27
- package/dist/cloud/web/template-engine.js +0 -146
- package/dist/cloud/web/templates/layouts/admin.hbs +0 -57
- package/dist/cloud/web/templates/layouts/auth.hbs +0 -138
- package/dist/cloud/web/templates/layouts/base.hbs +0 -16
- package/dist/cloud/web/templates/layouts/dashboard.hbs +0 -39
- package/dist/cloud/web/templates/layouts/landing.hbs +0 -82
- package/dist/cloud/web/templates/pages/admin/overview.hbs +0 -123
- package/dist/cloud/web/templates/pages/admin/servers.hbs +0 -129
- package/dist/cloud/web/templates/pages/admin/telemetry.hbs +0 -39
- package/dist/cloud/web/templates/pages/admin/user-edit.hbs +0 -91
- package/dist/cloud/web/templates/pages/admin/users.hbs +0 -179
- package/dist/cloud/web/templates/pages/auth/forgot-password.hbs +0 -25
- package/dist/cloud/web/templates/pages/auth/login.hbs +0 -33
- package/dist/cloud/web/templates/pages/auth/register.hbs +0 -32
- package/dist/cloud/web/templates/pages/auth/reset-password.hbs +0 -34
- package/dist/cloud/web/templates/pages/dashboard/billing.hbs +0 -140
- package/dist/cloud/web/templates/pages/dashboard/create.hbs +0 -173
- package/dist/cloud/web/templates/pages/dashboard/index.hbs +0 -8
- package/dist/cloud/web/templates/pages/dashboard/server-detail.hbs +0 -280
- package/dist/cloud/web/templates/pages/dashboard/server-logs.hbs +0 -35
- package/dist/cloud/web/templates/pages/dashboard/server-metrics.hbs +0 -63
- package/dist/cloud/web/templates/pages/dashboard/servers-partial.hbs +0 -21
- package/dist/cloud/web/templates/pages/dashboard/servers.hbs +0 -44
- package/dist/cloud/web/templates/pages/docs/show.hbs +0 -16
- package/dist/cloud/web/templates/pages/errors/404.hbs +0 -9
- package/dist/cloud/web/templates/pages/errors/500.hbs +0 -8
- package/dist/cloud/web/templates/pages/landing/index.hbs +0 -223
- package/dist/cloud/web/templates/pages/legal/privacy.hbs +0 -71
- package/dist/cloud/web/templates/pages/legal/terms.hbs +0 -73
- package/dist/cloud/web/templates/partials/admin-stats.hbs +0 -52
- package/dist/cloud/web/templates/partials/flash-message.hbs +0 -6
- package/dist/cloud/web/templates/partials/pricing-table.hbs +0 -103
- package/dist/cloud/web/templates/partials/server-card.hbs +0 -19
- package/dist/cloud/web/templates/partials/status-badge.hbs +0 -1
- package/dist/config/configurable-command.d.ts +0 -13
- package/dist/config/configurable-command.js +0 -70
- package/dist/config/mcpmake-config.d.ts +0 -68
- package/dist/config/mcpmake-config.js +0 -207
- package/dist/docs/cli.md +0 -400
- package/dist/docs/mcp-2026-07-28-migration.md +0 -78
- package/dist/docs/migrate-from-stainless.md +0 -94
- package/dist/docs/quickstart.md +0 -166
- package/dist/docs/show-hn.md +0 -26
- package/dist/docs/website-servers.md +0 -169
- package/dist/emitter/code-writer.d.ts +0 -8
- package/dist/emitter/code-writer.js +0 -25
- package/dist/emitter/index.d.ts +0 -32
- package/dist/emitter/index.js +0 -280
- package/dist/emitter/mcpb-bundler.d.ts +0 -31
- package/dist/emitter/mcpb-bundler.js +0 -172
- package/dist/emitter/project-scaffolder.d.ts +0 -4
- package/dist/emitter/project-scaffolder.js +0 -89
- package/dist/emitter/python-template-loader.d.ts +0 -4
- package/dist/emitter/python-template-loader.js +0 -30
- package/dist/emitter/python-templates/dockerfile.hbs +0 -14
- package/dist/emitter/python-templates/env.example.hbs +0 -6
- package/dist/emitter/python-templates/requirements.txt.hbs +0 -4
- package/dist/emitter/python-templates/server.py.hbs +0 -77
- package/dist/emitter/site-scaffolder.d.ts +0 -13
- package/dist/emitter/site-scaffolder.js +0 -70
- package/dist/emitter/site-template-loader.d.ts +0 -5
- package/dist/emitter/site-template-loader.js +0 -47
- package/dist/emitter/site-templates/browser-manager.ts.hbs +0 -233
- package/dist/emitter/site-templates/config.ts.hbs +0 -28
- package/dist/emitter/site-templates/dockerfile.hbs +0 -31
- package/dist/emitter/site-templates/env.example.hbs +0 -19
- package/dist/emitter/site-templates/package.json.hbs +0 -26
- package/dist/emitter/site-templates/server-main-http.ts.hbs +0 -108
- package/dist/emitter/site-templates/server-main.ts.hbs +0 -23
- package/dist/emitter/site-templates/tool-handler-action.ts.hbs +0 -86
- package/dist/emitter/site-templates/tool-handler-form.ts.hbs +0 -116
- package/dist/emitter/site-templates/tool-handler-lifecycle.ts.hbs +0 -146
- package/dist/emitter/site-templates/tool-index.ts.hbs +0 -11
- package/dist/emitter/template-loader.d.ts +0 -1
- package/dist/emitter/template-loader.js +0 -27
- package/dist/emitter/templates/auth-provider.ts.hbs +0 -57
- package/dist/emitter/templates/config.ts.hbs +0 -63
- package/dist/emitter/templates/discovery.ts.hbs +0 -301
- package/dist/emitter/templates/dockerfile.hbs +0 -34
- package/dist/emitter/templates/env.example.hbs +0 -28
- package/dist/emitter/templates/gitignore.hbs +0 -5
- package/dist/emitter/templates/http-executor.ts.hbs +0 -117
- package/dist/emitter/templates/oauth.ts.hbs +0 -188
- package/dist/emitter/templates/package.json.hbs +0 -25
- package/dist/emitter/templates/prompts.ts.hbs +0 -22
- package/dist/emitter/templates/readme.md.hbs +0 -123
- package/dist/emitter/templates/resources.ts.hbs +0 -63
- package/dist/emitter/templates/server-main-http.ts.hbs +0 -407
- package/dist/emitter/templates/server-main.ts.hbs +0 -40
- package/dist/emitter/templates/task-handlers.ts.hbs +0 -189
- package/dist/emitter/templates/task-manager.ts.hbs +0 -139
- package/dist/emitter/templates/task-sse.ts.hbs +0 -105
- package/dist/emitter/templates/tool-handler.ts.hbs +0 -124
- package/dist/emitter/templates/tool-index.ts.hbs +0 -11
- package/dist/emitter/templates/tool-test.ts.hbs +0 -57
- package/dist/emitter/templates/trace.ts.hbs +0 -79
- package/dist/emitter/templates/tsconfig.json.hbs +0 -16
- package/dist/emitter/templates/types.ts.hbs +0 -5
- package/dist/emitter/worker-template-loader.d.ts +0 -5
- package/dist/emitter/worker-template-loader.js +0 -33
- package/dist/emitter/worker-templates/config.ts.hbs +0 -54
- package/dist/emitter/worker-templates/dev-vars.example.hbs +0 -10
- package/dist/emitter/worker-templates/gitignore.hbs +0 -6
- package/dist/emitter/worker-templates/package.json.hbs +0 -24
- package/dist/emitter/worker-templates/readme.md.hbs +0 -53
- package/dist/emitter/worker-templates/server.test.ts.hbs +0 -20
- package/dist/emitter/worker-templates/tool-handler.ts.hbs +0 -85
- package/dist/emitter/worker-templates/tool-index.ts.hbs +0 -28
- package/dist/emitter/worker-templates/tsconfig.json.hbs +0 -17
- package/dist/emitter/worker-templates/worker.ts.hbs +0 -242
- package/dist/emitter/worker-templates/wrangler.toml.hbs +0 -19
- package/dist/generator/spec-generator.d.ts +0 -6
- package/dist/generator/spec-generator.js +0 -50
- package/dist/parser/har-filter.d.ts +0 -8
- package/dist/parser/har-filter.js +0 -71
- package/dist/parser/har-loader.d.ts +0 -2
- package/dist/parser/har-loader.js +0 -14
- package/dist/parser/har-normalizer.d.ts +0 -20
- package/dist/parser/har-normalizer.js +0 -78
- package/dist/parser/index.d.ts +0 -10
- package/dist/parser/index.js +0 -6
- package/dist/parser/openapi-loader.d.ts +0 -6
- package/dist/parser/openapi-loader.js +0 -308
- package/dist/parser/operation-extractor.d.ts +0 -13
- package/dist/parser/operation-extractor.js +0 -155
- package/dist/parser/overlay-loader.d.ts +0 -10
- package/dist/parser/overlay-loader.js +0 -184
- package/dist/parser/postman-loader.d.ts +0 -9
- package/dist/parser/postman-loader.js +0 -106
- package/dist/parser/schema-converter.d.ts +0 -12
- package/dist/parser/schema-converter.js +0 -117
- package/dist/plugins/adapter.d.ts +0 -40
- package/dist/plugins/adapter.js +0 -15
- package/dist/plugins/loader.d.ts +0 -25
- package/dist/plugins/loader.js +0 -58
- package/dist/pricing.d.ts +0 -55
- package/dist/pricing.js +0 -133
- package/dist/providers/index.d.ts +0 -15
- package/dist/providers/index.js +0 -56
- package/dist/recorder/browser-recorder.d.ts +0 -22
- package/dist/recorder/browser-recorder.js +0 -205
- package/dist/rescan/diff-engine.d.ts +0 -5
- package/dist/rescan/diff-engine.js +0 -312
- package/dist/rescan/index.d.ts +0 -3
- package/dist/rescan/index.js +0 -2
- package/dist/rescan/rescan-runner.d.ts +0 -42
- package/dist/rescan/rescan-runner.js +0 -69
- package/dist/rescan/rescan-scheduler.d.ts +0 -41
- package/dist/rescan/rescan-scheduler.js +0 -179
- package/dist/site-transformer/browser-tools.d.ts +0 -10
- package/dist/site-transformer/browser-tools.js +0 -59
- package/dist/site-transformer/index.d.ts +0 -2
- package/dist/site-transformer/index.js +0 -2
- package/dist/site-transformer/selector-healer.d.ts +0 -8
- package/dist/site-transformer/selector-healer.js +0 -106
- package/dist/site-transformer/tool-generator.d.ts +0 -13
- package/dist/site-transformer/tool-generator.js +0 -245
- package/dist/transformer/auth-detector.d.ts +0 -13
- package/dist/transformer/auth-detector.js +0 -90
- package/dist/transformer/catalog-builder.d.ts +0 -18
- package/dist/transformer/catalog-builder.js +0 -56
- package/dist/transformer/client-compat.d.ts +0 -6
- package/dist/transformer/client-compat.js +0 -44
- package/dist/transformer/har-clusterer.d.ts +0 -9
- package/dist/transformer/har-clusterer.js +0 -27
- package/dist/transformer/har-dedup.d.ts +0 -10
- package/dist/transformer/har-dedup.js +0 -81
- package/dist/transformer/har-schema-inferrer.d.ts +0 -15
- package/dist/transformer/har-schema-inferrer.js +0 -90
- package/dist/transformer/har-to-operations.d.ts +0 -13
- package/dist/transformer/har-to-operations.js +0 -192
- package/dist/transformer/index.d.ts +0 -8
- package/dist/transformer/index.js +0 -6
- package/dist/transformer/llm-namer.d.ts +0 -6
- package/dist/transformer/llm-namer.js +0 -59
- package/dist/transformer/naming.d.ts +0 -4
- package/dist/transformer/naming.js +0 -30
- package/dist/transformer/operation-filter.d.ts +0 -13
- package/dist/transformer/operation-filter.js +0 -52
- package/dist/transformer/resource-builder.d.ts +0 -12
- package/dist/transformer/resource-builder.js +0 -80
- package/dist/transformer/schema-merger.d.ts +0 -14
- package/dist/transformer/schema-merger.js +0 -65
- package/dist/transformer/tool-builder.d.ts +0 -3
- package/dist/transformer/tool-builder.js +0 -114
- package/dist/types/index.d.ts +0 -131
- package/dist/types/index.js +0 -1
- package/dist/types/site.d.ts +0 -284
- package/dist/types/site.js +0 -8
- package/dist/utils/fail.d.ts +0 -48
- package/dist/utils/fail.js +0 -204
- package/dist/utils/fs.d.ts +0 -5
- package/dist/utils/fs.js +0 -28
- package/dist/utils/interactive.d.ts +0 -6
- package/dist/utils/interactive.js +0 -30
- package/dist/utils/logger.d.ts +0 -1
- package/dist/utils/logger.js +0 -2
- package/dist/utils/sanitize.d.ts +0 -28
- package/dist/utils/sanitize.js +0 -44
- package/dist/utils/watcher.d.ts +0 -11
- package/dist/utils/watcher.js +0 -36
package/dist/cloud/mailer.js
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal email sender for transactional emails (password reset, etc.).
|
|
3
|
-
*
|
|
4
|
-
* Supports two modes:
|
|
5
|
-
* 1. Console (default) — logs the email to stdout. Useful for development
|
|
6
|
-
* and self-hosted instances without email infrastructure.
|
|
7
|
-
* 2. SMTP — sends via an external SMTP relay. Requires env vars:
|
|
8
|
-
* SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, MAIL_FROM
|
|
9
|
-
*
|
|
10
|
-
* Uses Node.js built-in `net`/`tls` modules — zero external dependencies.
|
|
11
|
-
*/
|
|
12
|
-
import { createConnection } from 'node:net';
|
|
13
|
-
import { connect as tlsConnect } from 'node:tls';
|
|
14
|
-
import { logger } from '../utils/logger.js';
|
|
15
|
-
/**
|
|
16
|
-
* Send an email. Falls back to console logging when SMTP is not configured.
|
|
17
|
-
*/
|
|
18
|
-
export async function sendMail(options) {
|
|
19
|
-
const host = process.env.SMTP_HOST;
|
|
20
|
-
if (!host) {
|
|
21
|
-
logger.info(`[mailer] No SMTP configured — logging email to console`);
|
|
22
|
-
logger.info(`[mailer] To: ${options.to}`);
|
|
23
|
-
logger.info(`[mailer] Subject: ${options.subject}`);
|
|
24
|
-
logger.info(`[mailer] Body:\n${options.text}`);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
await sendSmtp(options);
|
|
28
|
-
}
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
// Minimal SMTP client (STARTTLS on 587, implicit TLS on 465, plain on 25)
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
async function sendSmtp(options) {
|
|
33
|
-
const host = process.env.SMTP_HOST;
|
|
34
|
-
const port = parseInt(process.env.SMTP_PORT ?? '587', 10);
|
|
35
|
-
const user = process.env.SMTP_USER ?? '';
|
|
36
|
-
const pass = process.env.SMTP_PASS ?? '';
|
|
37
|
-
const from = process.env.MAIL_FROM ?? 'noreply@mcpmake.dev';
|
|
38
|
-
const implicitTls = port === 465;
|
|
39
|
-
const boundary = `----mcpmake${Date.now()}`;
|
|
40
|
-
const body = options.html
|
|
41
|
-
? [
|
|
42
|
-
`MIME-Version: 1.0`,
|
|
43
|
-
`Content-Type: multipart/alternative; boundary="${boundary}"`,
|
|
44
|
-
``,
|
|
45
|
-
`--${boundary}`,
|
|
46
|
-
`Content-Type: text/plain; charset=utf-8`,
|
|
47
|
-
``,
|
|
48
|
-
options.text,
|
|
49
|
-
`--${boundary}`,
|
|
50
|
-
`Content-Type: text/html; charset=utf-8`,
|
|
51
|
-
``,
|
|
52
|
-
options.html,
|
|
53
|
-
`--${boundary}--`,
|
|
54
|
-
].join('\r\n')
|
|
55
|
-
: options.text;
|
|
56
|
-
const message = [
|
|
57
|
-
`From: ${from}`,
|
|
58
|
-
`To: ${options.to}`,
|
|
59
|
-
`Subject: ${options.subject}`,
|
|
60
|
-
`Date: ${new Date().toUTCString()}`,
|
|
61
|
-
...(options.html ? [] : [`MIME-Version: 1.0`, `Content-Type: text/plain; charset=utf-8`]),
|
|
62
|
-
``,
|
|
63
|
-
body,
|
|
64
|
-
].join('\r\n');
|
|
65
|
-
return new Promise((resolve, reject) => {
|
|
66
|
-
const timeout = setTimeout(() => {
|
|
67
|
-
reject(new Error('SMTP timeout after 30s'));
|
|
68
|
-
}, 30_000);
|
|
69
|
-
let socket;
|
|
70
|
-
let buffer = '';
|
|
71
|
-
function cleanup() {
|
|
72
|
-
clearTimeout(timeout);
|
|
73
|
-
socket?.removeAllListeners();
|
|
74
|
-
socket?.destroy();
|
|
75
|
-
}
|
|
76
|
-
function readLine() {
|
|
77
|
-
return new Promise((res) => {
|
|
78
|
-
const check = () => {
|
|
79
|
-
const idx = buffer.indexOf('\r\n');
|
|
80
|
-
if (idx !== -1) {
|
|
81
|
-
const line = buffer.slice(0, idx);
|
|
82
|
-
buffer = buffer.slice(idx + 2);
|
|
83
|
-
res(line);
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
socket.once('data', (chunk) => {
|
|
87
|
-
buffer += chunk.toString();
|
|
88
|
-
check();
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
check();
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
async function waitReply() {
|
|
96
|
-
let full = '';
|
|
97
|
-
// eslint-disable-next-line no-constant-condition
|
|
98
|
-
while (true) {
|
|
99
|
-
const line = await readLine();
|
|
100
|
-
full += line + '\r\n';
|
|
101
|
-
// Multi-line replies have "-" after the code, last line has " "
|
|
102
|
-
if (line.length >= 4 && line[3] === ' ') {
|
|
103
|
-
return { code: parseInt(line.slice(0, 3), 10), text: full };
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
function send(cmd) {
|
|
108
|
-
socket.write(cmd + '\r\n');
|
|
109
|
-
}
|
|
110
|
-
async function run() {
|
|
111
|
-
// Greeting
|
|
112
|
-
await waitReply();
|
|
113
|
-
send(`EHLO mcpmake`);
|
|
114
|
-
await waitReply();
|
|
115
|
-
// STARTTLS for port 587
|
|
116
|
-
if (!implicitTls && port === 587) {
|
|
117
|
-
send('STARTTLS');
|
|
118
|
-
const tlsReply = await waitReply();
|
|
119
|
-
if (tlsReply.code !== 220) {
|
|
120
|
-
throw new Error(`STARTTLS failed: ${tlsReply.text}`);
|
|
121
|
-
}
|
|
122
|
-
// Upgrade socket to TLS
|
|
123
|
-
const tlsSocket = tlsConnect({ socket, host, servername: host });
|
|
124
|
-
await new Promise((res, rej) => {
|
|
125
|
-
tlsSocket.once('secureConnect', res);
|
|
126
|
-
tlsSocket.once('error', rej);
|
|
127
|
-
});
|
|
128
|
-
socket = tlsSocket;
|
|
129
|
-
buffer = '';
|
|
130
|
-
send(`EHLO mcpmake`);
|
|
131
|
-
await waitReply();
|
|
132
|
-
}
|
|
133
|
-
// AUTH LOGIN
|
|
134
|
-
if (user && pass) {
|
|
135
|
-
send('AUTH LOGIN');
|
|
136
|
-
const authReply = await waitReply();
|
|
137
|
-
if (authReply.code !== 334) {
|
|
138
|
-
throw new Error(`AUTH LOGIN rejected: ${authReply.text}`);
|
|
139
|
-
}
|
|
140
|
-
send(Buffer.from(user).toString('base64'));
|
|
141
|
-
await waitReply();
|
|
142
|
-
send(Buffer.from(pass).toString('base64'));
|
|
143
|
-
const loginReply = await waitReply();
|
|
144
|
-
if (loginReply.code !== 235) {
|
|
145
|
-
throw new Error(`SMTP authentication failed: ${loginReply.text}`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
send(`MAIL FROM:<${from}>`);
|
|
149
|
-
const fromReply = await waitReply();
|
|
150
|
-
if (fromReply.code !== 250) {
|
|
151
|
-
throw new Error(`MAIL FROM rejected: ${fromReply.text}`);
|
|
152
|
-
}
|
|
153
|
-
send(`RCPT TO:<${options.to}>`);
|
|
154
|
-
const toReply = await waitReply();
|
|
155
|
-
if (toReply.code !== 250) {
|
|
156
|
-
throw new Error(`RCPT TO rejected: ${toReply.text}`);
|
|
157
|
-
}
|
|
158
|
-
send('DATA');
|
|
159
|
-
const dataReply = await waitReply();
|
|
160
|
-
if (dataReply.code !== 354) {
|
|
161
|
-
throw new Error(`DATA rejected: ${dataReply.text}`);
|
|
162
|
-
}
|
|
163
|
-
send(message + '\r\n.');
|
|
164
|
-
const msgReply = await waitReply();
|
|
165
|
-
if (msgReply.code !== 250) {
|
|
166
|
-
throw new Error(`Message rejected: ${msgReply.text}`);
|
|
167
|
-
}
|
|
168
|
-
send('QUIT');
|
|
169
|
-
cleanup();
|
|
170
|
-
resolve();
|
|
171
|
-
}
|
|
172
|
-
if (implicitTls) {
|
|
173
|
-
socket = tlsConnect({ host, port }, () => {
|
|
174
|
-
run().catch((err) => {
|
|
175
|
-
cleanup();
|
|
176
|
-
reject(err);
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
socket = createConnection({ host, port }, () => {
|
|
182
|
-
run().catch((err) => {
|
|
183
|
-
cleanup();
|
|
184
|
-
reject(err);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
socket.on('error', (err) => {
|
|
189
|
-
cleanup();
|
|
190
|
-
reject(new Error(`SMTP connection error: ${err.message}`));
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Backend reverse-proxy for served MCP traffic.
|
|
3
|
-
*
|
|
4
|
-
* Each hosted server is reached at `{slug}.{domain}`. Caddy proxies the
|
|
5
|
-
* wildcard subdomain to this backend (port 3001); the backend is the single
|
|
6
|
-
* authoritative entry point: it validates the hashed `mf_` bearer token,
|
|
7
|
-
* enforces quota, meters usage, and streams the request through to the
|
|
8
|
-
* container listening on `127.0.0.1:{port}`.
|
|
9
|
-
*
|
|
10
|
-
* This resolves the data-path question in the launch plan: Caddy routes the
|
|
11
|
-
* wildcard to the backend, the backend authenticates + meters, and the
|
|
12
|
-
* container (which also validates MCP_AUTH_TOKEN) is the in-depth backstop.
|
|
13
|
-
*/
|
|
14
|
-
import http from 'node:http';
|
|
15
|
-
/** Body buffered for metering must never exceed this (MCP JSON-RPC is small). */
|
|
16
|
-
export declare const MAX_PROXY_BODY: number;
|
|
17
|
-
/**
|
|
18
|
-
* Extract the server slug from a Host header given the apex domain.
|
|
19
|
-
*
|
|
20
|
-
* Returns the slug for `{slug}.{domain}` hosts, or null for the apex domain,
|
|
21
|
-
* `www`, multi-label subdomains, or anything that isn't a valid slug.
|
|
22
|
-
*/
|
|
23
|
-
export declare function extractSlug(host: string | undefined, domain: string): string | null;
|
|
24
|
-
export type AuthResult = {
|
|
25
|
-
ok: true;
|
|
26
|
-
} | {
|
|
27
|
-
ok: false;
|
|
28
|
-
status: number;
|
|
29
|
-
error: string;
|
|
30
|
-
};
|
|
31
|
-
/**
|
|
32
|
-
* Validate the request's bearer token against a server's stored SHA-256 hash.
|
|
33
|
-
*/
|
|
34
|
-
export declare function authorizeBearer(req: http.IncomingMessage, tokenHash: string): AuthResult;
|
|
35
|
-
/**
|
|
36
|
-
* Read a request body up to MAX_PROXY_BODY. Resolves `null` if the limit is
|
|
37
|
-
* exceeded (caller should respond 413). Returns an empty buffer for bodyless
|
|
38
|
-
* methods without touching the stream.
|
|
39
|
-
*/
|
|
40
|
-
export declare function readBoundedBody(req: http.IncomingMessage): Promise<Buffer | null>;
|
|
41
|
-
/**
|
|
42
|
-
* Count billable tool calls in an MCP JSON-RPC request body, and surface the
|
|
43
|
-
* primary tool name (for metrics). Best-effort: malformed bodies count as zero.
|
|
44
|
-
*/
|
|
45
|
-
export declare function inspectMcpBody(body: Buffer | undefined, contentType: string | undefined): {
|
|
46
|
-
toolCalls: number;
|
|
47
|
-
primaryTool?: string;
|
|
48
|
-
};
|
|
49
|
-
/**
|
|
50
|
-
* Stream-proxy a request to a local container upstream, preserving method,
|
|
51
|
-
* headers, body, and streaming the response (including long-lived SSE).
|
|
52
|
-
*
|
|
53
|
-
* @param bufferedBody Pre-read request body to forward (empty for GET/DELETE).
|
|
54
|
-
*/
|
|
55
|
-
export declare function proxyToUpstream(req: http.IncomingMessage, res: http.ServerResponse, upstreamPort: number, bufferedBody: Buffer, opts?: {
|
|
56
|
-
host?: string;
|
|
57
|
-
timeoutMs?: number;
|
|
58
|
-
}): Promise<void>;
|
package/dist/cloud/mcp-proxy.js
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Backend reverse-proxy for served MCP traffic.
|
|
3
|
-
*
|
|
4
|
-
* Each hosted server is reached at `{slug}.{domain}`. Caddy proxies the
|
|
5
|
-
* wildcard subdomain to this backend (port 3001); the backend is the single
|
|
6
|
-
* authoritative entry point: it validates the hashed `mf_` bearer token,
|
|
7
|
-
* enforces quota, meters usage, and streams the request through to the
|
|
8
|
-
* container listening on `127.0.0.1:{port}`.
|
|
9
|
-
*
|
|
10
|
-
* This resolves the data-path question in the launch plan: Caddy routes the
|
|
11
|
-
* wildcard to the backend, the backend authenticates + meters, and the
|
|
12
|
-
* container (which also validates MCP_AUTH_TOKEN) is the in-depth backstop.
|
|
13
|
-
*/
|
|
14
|
-
import http from 'node:http';
|
|
15
|
-
import { verifyToken } from './security.js';
|
|
16
|
-
/** Body buffered for metering must never exceed this (MCP JSON-RPC is small). */
|
|
17
|
-
export const MAX_PROXY_BODY = 8 * 1024 * 1024; // 8 MB
|
|
18
|
-
/** Methods that carry a request body we need to buffer for metering. */
|
|
19
|
-
const BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']);
|
|
20
|
-
/** Hop-by-hop headers that must not be forwarded through a proxy (RFC 7230 §6.1). */
|
|
21
|
-
const HOP_BY_HOP = new Set([
|
|
22
|
-
'connection',
|
|
23
|
-
'keep-alive',
|
|
24
|
-
'proxy-authenticate',
|
|
25
|
-
'proxy-authorization',
|
|
26
|
-
'te',
|
|
27
|
-
'trailers',
|
|
28
|
-
'transfer-encoding',
|
|
29
|
-
'upgrade',
|
|
30
|
-
]);
|
|
31
|
-
/**
|
|
32
|
-
* Extract the server slug from a Host header given the apex domain.
|
|
33
|
-
*
|
|
34
|
-
* Returns the slug for `{slug}.{domain}` hosts, or null for the apex domain,
|
|
35
|
-
* `www`, multi-label subdomains, or anything that isn't a valid slug.
|
|
36
|
-
*/
|
|
37
|
-
export function extractSlug(host, domain) {
|
|
38
|
-
if (!host)
|
|
39
|
-
return null;
|
|
40
|
-
let h = host.trim().toLowerCase();
|
|
41
|
-
if (!h)
|
|
42
|
-
return null;
|
|
43
|
-
// Strip a trailing :port (ignore IPv6 brackets — server hosts are never IPv6 here)
|
|
44
|
-
if (h.startsWith('['))
|
|
45
|
-
return null;
|
|
46
|
-
const colon = h.lastIndexOf(':');
|
|
47
|
-
if (colon !== -1)
|
|
48
|
-
h = h.slice(0, colon);
|
|
49
|
-
const suffix = '.' + domain.trim().toLowerCase();
|
|
50
|
-
if (!h.endsWith(suffix))
|
|
51
|
-
return null;
|
|
52
|
-
const label = h.slice(0, -suffix.length);
|
|
53
|
-
if (!label || label === 'www' || label.includes('.'))
|
|
54
|
-
return null;
|
|
55
|
-
if (!/^[a-z0-9][a-z0-9-]*$/.test(label))
|
|
56
|
-
return null;
|
|
57
|
-
return label;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Validate the request's bearer token against a server's stored SHA-256 hash.
|
|
61
|
-
*/
|
|
62
|
-
export function authorizeBearer(req, tokenHash) {
|
|
63
|
-
const header = req.headers.authorization;
|
|
64
|
-
if (!header || !header.startsWith('Bearer ')) {
|
|
65
|
-
return { ok: false, status: 401, error: 'Missing or invalid Authorization header' };
|
|
66
|
-
}
|
|
67
|
-
const token = header.slice(7).trim();
|
|
68
|
-
if (!token || !verifyToken(token, tokenHash)) {
|
|
69
|
-
return { ok: false, status: 403, error: 'Invalid bearer token' };
|
|
70
|
-
}
|
|
71
|
-
return { ok: true };
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Read a request body up to MAX_PROXY_BODY. Resolves `null` if the limit is
|
|
75
|
-
* exceeded (caller should respond 413). Returns an empty buffer for bodyless
|
|
76
|
-
* methods without touching the stream.
|
|
77
|
-
*/
|
|
78
|
-
export function readBoundedBody(req) {
|
|
79
|
-
if (!BODY_METHODS.has((req.method ?? 'GET').toUpperCase())) {
|
|
80
|
-
return Promise.resolve(Buffer.alloc(0));
|
|
81
|
-
}
|
|
82
|
-
return new Promise((resolve) => {
|
|
83
|
-
const chunks = [];
|
|
84
|
-
let total = 0;
|
|
85
|
-
let settled = false;
|
|
86
|
-
const finish = (value) => {
|
|
87
|
-
if (settled)
|
|
88
|
-
return;
|
|
89
|
-
settled = true;
|
|
90
|
-
resolve(value);
|
|
91
|
-
};
|
|
92
|
-
req.on('data', (chunk) => {
|
|
93
|
-
total += chunk.length;
|
|
94
|
-
if (total > MAX_PROXY_BODY) {
|
|
95
|
-
finish(null);
|
|
96
|
-
req.destroy();
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
chunks.push(chunk);
|
|
100
|
-
});
|
|
101
|
-
req.on('end', () => finish(Buffer.concat(chunks)));
|
|
102
|
-
req.on('error', () => finish(null));
|
|
103
|
-
req.on('aborted', () => finish(null));
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Count billable tool calls in an MCP JSON-RPC request body, and surface the
|
|
108
|
-
* primary tool name (for metrics). Best-effort: malformed bodies count as zero.
|
|
109
|
-
*/
|
|
110
|
-
export function inspectMcpBody(body, contentType) {
|
|
111
|
-
if (!body || body.length === 0)
|
|
112
|
-
return { toolCalls: 0 };
|
|
113
|
-
if (contentType && !contentType.includes('json'))
|
|
114
|
-
return { toolCalls: 0 };
|
|
115
|
-
let parsed;
|
|
116
|
-
try {
|
|
117
|
-
parsed = JSON.parse(body.toString('utf-8'));
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
return { toolCalls: 0 };
|
|
121
|
-
}
|
|
122
|
-
const messages = Array.isArray(parsed) ? parsed : [parsed];
|
|
123
|
-
let toolCalls = 0;
|
|
124
|
-
let primaryTool;
|
|
125
|
-
for (const msg of messages) {
|
|
126
|
-
if (msg && typeof msg === 'object' && msg.method === 'tools/call') {
|
|
127
|
-
toolCalls++;
|
|
128
|
-
const params = msg.params;
|
|
129
|
-
if (!primaryTool && params && typeof params.name === 'string') {
|
|
130
|
-
primaryTool = params.name;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return { toolCalls, primaryTool };
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Stream-proxy a request to a local container upstream, preserving method,
|
|
138
|
-
* headers, body, and streaming the response (including long-lived SSE).
|
|
139
|
-
*
|
|
140
|
-
* @param bufferedBody Pre-read request body to forward (empty for GET/DELETE).
|
|
141
|
-
*/
|
|
142
|
-
export function proxyToUpstream(req, res, upstreamPort, bufferedBody, opts = {}) {
|
|
143
|
-
const host = opts.host ?? '127.0.0.1';
|
|
144
|
-
const timeoutMs = opts.timeoutMs ?? 120_000;
|
|
145
|
-
return new Promise((resolve) => {
|
|
146
|
-
// Clone headers, overriding hop-by-hop / length headers we re-derive.
|
|
147
|
-
const headers = { ...req.headers };
|
|
148
|
-
delete headers['content-length'];
|
|
149
|
-
delete headers['connection'];
|
|
150
|
-
delete headers['keep-alive'];
|
|
151
|
-
delete headers['transfer-encoding'];
|
|
152
|
-
headers.host = `${host}:${upstreamPort}`;
|
|
153
|
-
const hasBody = BODY_METHODS.has((req.method ?? 'GET').toUpperCase());
|
|
154
|
-
if (hasBody) {
|
|
155
|
-
headers['content-length'] = String(bufferedBody.length);
|
|
156
|
-
}
|
|
157
|
-
let settled = false;
|
|
158
|
-
const done = () => {
|
|
159
|
-
if (settled)
|
|
160
|
-
return;
|
|
161
|
-
settled = true;
|
|
162
|
-
resolve();
|
|
163
|
-
};
|
|
164
|
-
const upstream = http.request({ host, port: upstreamPort, method: req.method, path: req.url, headers, timeout: timeoutMs }, (proxyRes) => {
|
|
165
|
-
// Strip hop-by-hop headers from the upstream response (RFC 7230 §6.1);
|
|
166
|
-
// Node manages connection framing for our client side independently.
|
|
167
|
-
const resHeaders = {};
|
|
168
|
-
for (const [k, v] of Object.entries(proxyRes.headers)) {
|
|
169
|
-
if (!HOP_BY_HOP.has(k.toLowerCase()) && v !== undefined)
|
|
170
|
-
resHeaders[k] = v;
|
|
171
|
-
}
|
|
172
|
-
res.writeHead(proxyRes.statusCode ?? 502, resHeaders);
|
|
173
|
-
proxyRes.pipe(res);
|
|
174
|
-
proxyRes.on('end', done);
|
|
175
|
-
proxyRes.on('error', () => {
|
|
176
|
-
res.destroy();
|
|
177
|
-
done();
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
upstream.on('timeout', () => {
|
|
181
|
-
upstream.destroy(new Error('upstream timeout'));
|
|
182
|
-
});
|
|
183
|
-
upstream.on('error', () => {
|
|
184
|
-
if (!res.headersSent) {
|
|
185
|
-
res.writeHead(502, { 'Content-Type': 'application/json' });
|
|
186
|
-
res.end(JSON.stringify({ error: 'Bad gateway: server is unreachable' }));
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
res.destroy();
|
|
190
|
-
}
|
|
191
|
-
done();
|
|
192
|
-
});
|
|
193
|
-
// If the client disconnects, tear down the upstream request.
|
|
194
|
-
res.on('close', () => {
|
|
195
|
-
if (!settled)
|
|
196
|
-
upstream.destroy();
|
|
197
|
-
});
|
|
198
|
-
if (hasBody)
|
|
199
|
-
upstream.end(bufferedBody);
|
|
200
|
-
else
|
|
201
|
-
upstream.end();
|
|
202
|
-
});
|
|
203
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durable time-series of host/API metrics for the admin charts.
|
|
3
|
-
*
|
|
4
|
-
* One row is written per sampler tick (every ~5 min by the ResourceMonitor's
|
|
5
|
-
* onSample hook). Backed by the `metric_samples` Postgres table when a database
|
|
6
|
-
* is configured; otherwise a bounded in-memory ring (dev / single-process).
|
|
7
|
-
*
|
|
8
|
-
* `requests`/`errors_*` are PER-INTERVAL deltas (read from the rolling
|
|
9
|
-
* FailureTracker window), not cumulative totals.
|
|
10
|
-
*/
|
|
11
|
-
import type { Database } from './db/index.js';
|
|
12
|
-
export interface MetricSample {
|
|
13
|
-
/** ISO timestamp. */
|
|
14
|
-
sampledAt: string;
|
|
15
|
-
requests: number;
|
|
16
|
-
errors4xx: number;
|
|
17
|
-
errors5xx: number;
|
|
18
|
-
activeContainers: number;
|
|
19
|
-
memUsedPct: number;
|
|
20
|
-
diskUsedPct: number;
|
|
21
|
-
totalUsers: number;
|
|
22
|
-
totalServers: number;
|
|
23
|
-
}
|
|
24
|
-
export interface MetricSampleStore {
|
|
25
|
-
record(sample: MetricSample): Promise<void>;
|
|
26
|
-
/** Samples with `sampledAt` within the last `sinceMs`, oldest first. */
|
|
27
|
-
recent(sinceMs: number): Promise<MetricSample[]>;
|
|
28
|
-
/** Delete samples older than `olderThanMs`. */
|
|
29
|
-
prune(olderThanMs: number): Promise<void>;
|
|
30
|
-
}
|
|
31
|
-
export declare class InMemoryMetricSampleStore implements MetricSampleStore {
|
|
32
|
-
private samples;
|
|
33
|
-
record(sample: MetricSample): Promise<void>;
|
|
34
|
-
recent(sinceMs: number): Promise<MetricSample[]>;
|
|
35
|
-
prune(olderThanMs: number): Promise<void>;
|
|
36
|
-
}
|
|
37
|
-
export declare class PgMetricSampleStore implements MetricSampleStore {
|
|
38
|
-
private db;
|
|
39
|
-
constructor(db: Database);
|
|
40
|
-
record(sample: MetricSample): Promise<void>;
|
|
41
|
-
recent(sinceMs: number): Promise<MetricSample[]>;
|
|
42
|
-
prune(olderThanMs: number): Promise<void>;
|
|
43
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durable time-series of host/API metrics for the admin charts.
|
|
3
|
-
*
|
|
4
|
-
* One row is written per sampler tick (every ~5 min by the ResourceMonitor's
|
|
5
|
-
* onSample hook). Backed by the `metric_samples` Postgres table when a database
|
|
6
|
-
* is configured; otherwise a bounded in-memory ring (dev / single-process).
|
|
7
|
-
*
|
|
8
|
-
* `requests`/`errors_*` are PER-INTERVAL deltas (read from the rolling
|
|
9
|
-
* FailureTracker window), not cumulative totals.
|
|
10
|
-
*/
|
|
11
|
-
import { randomBytes } from 'node:crypto';
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
// In-memory (dev / no database) — bounded ring
|
|
14
|
-
// ---------------------------------------------------------------------------
|
|
15
|
-
/** 24h at 5-min cadence. */
|
|
16
|
-
const MAX_INMEMORY_SAMPLES = 288;
|
|
17
|
-
export class InMemoryMetricSampleStore {
|
|
18
|
-
samples = [];
|
|
19
|
-
async record(sample) {
|
|
20
|
-
this.samples.push(sample);
|
|
21
|
-
if (this.samples.length > MAX_INMEMORY_SAMPLES) {
|
|
22
|
-
this.samples.splice(0, this.samples.length - MAX_INMEMORY_SAMPLES);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async recent(sinceMs) {
|
|
26
|
-
const cutoff = Date.now() - sinceMs;
|
|
27
|
-
return this.samples.filter((s) => Date.parse(s.sampledAt) >= cutoff);
|
|
28
|
-
}
|
|
29
|
-
async prune(olderThanMs) {
|
|
30
|
-
const cutoff = Date.now() - olderThanMs;
|
|
31
|
-
this.samples = this.samples.filter((s) => Date.parse(s.sampledAt) >= cutoff);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
// Postgres
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
export class PgMetricSampleStore {
|
|
38
|
-
db;
|
|
39
|
-
constructor(db) {
|
|
40
|
-
this.db = db;
|
|
41
|
-
}
|
|
42
|
-
async record(sample) {
|
|
43
|
-
await this.db.query(`INSERT INTO metric_samples
|
|
44
|
-
(id, sampled_at, requests, errors_4xx, errors_5xx, active_containers,
|
|
45
|
-
mem_used_pct, disk_used_pct, total_users, total_servers)
|
|
46
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, [
|
|
47
|
-
randomBytes(16).toString('hex'),
|
|
48
|
-
sample.sampledAt,
|
|
49
|
-
sample.requests,
|
|
50
|
-
sample.errors4xx,
|
|
51
|
-
sample.errors5xx,
|
|
52
|
-
sample.activeContainers,
|
|
53
|
-
sample.memUsedPct,
|
|
54
|
-
sample.diskUsedPct,
|
|
55
|
-
sample.totalUsers,
|
|
56
|
-
sample.totalServers,
|
|
57
|
-
]);
|
|
58
|
-
}
|
|
59
|
-
async recent(sinceMs) {
|
|
60
|
-
const since = new Date(Date.now() - sinceMs).toISOString();
|
|
61
|
-
const { rows } = await this.db.query(`SELECT sampled_at, requests, errors_4xx, errors_5xx, active_containers,
|
|
62
|
-
mem_used_pct, disk_used_pct, total_users, total_servers
|
|
63
|
-
FROM metric_samples WHERE sampled_at >= $1 ORDER BY sampled_at ASC`, [since]);
|
|
64
|
-
return rows.map(rowToSample);
|
|
65
|
-
}
|
|
66
|
-
async prune(olderThanMs) {
|
|
67
|
-
const cutoff = new Date(Date.now() - olderThanMs).toISOString();
|
|
68
|
-
await this.db.query('DELETE FROM metric_samples WHERE sampled_at < $1', [cutoff]);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
function rowToSample(row) {
|
|
72
|
-
const num = (v) => (typeof v === 'number' ? v : Number(v ?? 0));
|
|
73
|
-
const sampledAt = row.sampled_at;
|
|
74
|
-
return {
|
|
75
|
-
sampledAt: typeof sampledAt === 'string' ? sampledAt : new Date(sampledAt).toISOString(),
|
|
76
|
-
requests: num(row.requests),
|
|
77
|
-
errors4xx: num(row.errors_4xx),
|
|
78
|
-
errors5xx: num(row.errors_5xx),
|
|
79
|
-
activeContainers: num(row.active_containers),
|
|
80
|
-
memUsedPct: num(row.mem_used_pct),
|
|
81
|
-
diskUsedPct: num(row.disk_used_pct),
|
|
82
|
-
totalUsers: num(row.total_users),
|
|
83
|
-
totalServers: num(row.total_servers),
|
|
84
|
-
};
|
|
85
|
-
}
|
package/dist/cloud/metrics.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-memory usage metrics for hosted MCP servers.
|
|
3
|
-
* Tracks request counts, tool call breakdowns, and activity timestamps.
|
|
4
|
-
*/
|
|
5
|
-
export interface ServerMetrics {
|
|
6
|
-
slug: string;
|
|
7
|
-
totalRequests: number;
|
|
8
|
-
toolCalls: Record<string, number>;
|
|
9
|
-
lastActiveAt: string;
|
|
10
|
-
createdAt: string;
|
|
11
|
-
}
|
|
12
|
-
export declare class MetricsStore {
|
|
13
|
-
private metrics;
|
|
14
|
-
init(slug: string): void;
|
|
15
|
-
recordRequest(slug: string, toolName?: string): void;
|
|
16
|
-
get(slug: string): ServerMetrics | undefined;
|
|
17
|
-
getAll(): ServerMetrics[];
|
|
18
|
-
delete(slug: string): void;
|
|
19
|
-
/**
|
|
20
|
-
* Get top tools by call count for a given server.
|
|
21
|
-
*/
|
|
22
|
-
getTopTools(slug: string, limit?: number): Array<{
|
|
23
|
-
tool: string;
|
|
24
|
-
calls: number;
|
|
25
|
-
}>;
|
|
26
|
-
}
|
package/dist/cloud/metrics.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-memory usage metrics for hosted MCP servers.
|
|
3
|
-
* Tracks request counts, tool call breakdowns, and activity timestamps.
|
|
4
|
-
*/
|
|
5
|
-
export class MetricsStore {
|
|
6
|
-
metrics = new Map();
|
|
7
|
-
init(slug) {
|
|
8
|
-
if (this.metrics.has(slug))
|
|
9
|
-
return;
|
|
10
|
-
const now = new Date().toISOString();
|
|
11
|
-
this.metrics.set(slug, {
|
|
12
|
-
slug,
|
|
13
|
-
totalRequests: 0,
|
|
14
|
-
toolCalls: {},
|
|
15
|
-
lastActiveAt: now,
|
|
16
|
-
createdAt: now,
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
recordRequest(slug, toolName) {
|
|
20
|
-
let entry = this.metrics.get(slug);
|
|
21
|
-
if (!entry) {
|
|
22
|
-
this.init(slug);
|
|
23
|
-
entry = this.metrics.get(slug);
|
|
24
|
-
}
|
|
25
|
-
entry.totalRequests++;
|
|
26
|
-
entry.lastActiveAt = new Date().toISOString();
|
|
27
|
-
if (toolName) {
|
|
28
|
-
// Only track if the tool is already known or we haven't hit the cap
|
|
29
|
-
if (toolName in entry.toolCalls || Object.keys(entry.toolCalls).length < 500) {
|
|
30
|
-
entry.toolCalls[toolName] = (entry.toolCalls[toolName] ?? 0) + 1;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
get(slug) {
|
|
35
|
-
const entry = this.metrics.get(slug);
|
|
36
|
-
return entry ? { ...entry, toolCalls: { ...entry.toolCalls } } : undefined;
|
|
37
|
-
}
|
|
38
|
-
getAll() {
|
|
39
|
-
return [...this.metrics.values()].map((e) => ({
|
|
40
|
-
...e,
|
|
41
|
-
toolCalls: { ...e.toolCalls },
|
|
42
|
-
}));
|
|
43
|
-
}
|
|
44
|
-
delete(slug) {
|
|
45
|
-
this.metrics.delete(slug);
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Get top tools by call count for a given server.
|
|
49
|
-
*/
|
|
50
|
-
getTopTools(slug, limit = 10) {
|
|
51
|
-
const entry = this.metrics.get(slug);
|
|
52
|
-
if (!entry)
|
|
53
|
-
return [];
|
|
54
|
-
return Object.entries(entry.toolCalls)
|
|
55
|
-
.sort(([, a], [, b]) => b - a)
|
|
56
|
-
.slice(0, limit)
|
|
57
|
-
.map(([tool, calls]) => ({ tool, calls }));
|
|
58
|
-
}
|
|
59
|
-
}
|