mcpmake 0.1.1 → 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 +20 -46
- 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
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
"""{{serverName}} MCP Server — generated by mcpmake."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import json
|
|
5
|
-
import httpx
|
|
6
|
-
from dotenv import load_dotenv
|
|
7
|
-
from mcp.server import Server
|
|
8
|
-
from mcp.server.stdio import stdio_server
|
|
9
|
-
from mcp.types import TextContent
|
|
10
|
-
|
|
11
|
-
load_dotenv()
|
|
12
|
-
|
|
13
|
-
BASE_URL = os.environ.get("BASE_URL", "{{{baseUrl}}}").rstrip("/")
|
|
14
|
-
{{#each authSchemes}}
|
|
15
|
-
{{#if (eq type "http-bearer")}}
|
|
16
|
-
BEARER_TOKEN = os.environ.get("BEARER_TOKEN", "")
|
|
17
|
-
{{/if}}
|
|
18
|
-
{{#if (eq type "apiKey")}}
|
|
19
|
-
API_KEY = os.environ.get("API_KEY", "")
|
|
20
|
-
{{/if}}
|
|
21
|
-
{{/each}}
|
|
22
|
-
|
|
23
|
-
server = Server("{{serverName}}")
|
|
24
|
-
client = httpx.AsyncClient(timeout=30.0)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def _headers() -> dict[str, str]:
|
|
28
|
-
headers: dict[str, str] = {}
|
|
29
|
-
{{#each authSchemes}}
|
|
30
|
-
{{#if (eq type "http-bearer")}}
|
|
31
|
-
if BEARER_TOKEN:
|
|
32
|
-
headers["Authorization"] = f"Bearer {BEARER_TOKEN}"
|
|
33
|
-
{{/if}}
|
|
34
|
-
{{#if (eq type "apiKey")}}
|
|
35
|
-
if API_KEY:
|
|
36
|
-
headers["{{headerName}}"] = API_KEY
|
|
37
|
-
{{/if}}
|
|
38
|
-
{{/each}}
|
|
39
|
-
return headers
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{{#each tools}}
|
|
43
|
-
@server.tool()
|
|
44
|
-
async def {{functionName}}({{#each pathParams}}{{this}}: str, {{/each}}{{#each queryParams}}{{this}}: str = "", {{/each}}{{#if hasRequestBody}}body: dict | None = None{{/if}}) -> list[TextContent]:
|
|
45
|
-
"""{{pyDocstring description}}"""
|
|
46
|
-
url = f"{BASE_URL}{{{pathTemplate}}}"
|
|
47
|
-
{{#if queryParams.length}}
|
|
48
|
-
params = { {{#each queryParams}}"{{this}}": {{this}}, {{/each}} }
|
|
49
|
-
params = {k: v for k, v in params.items() if v}
|
|
50
|
-
{{/if}}
|
|
51
|
-
try:
|
|
52
|
-
resp = await client.request(
|
|
53
|
-
"{{method}}",
|
|
54
|
-
url,
|
|
55
|
-
headers=_headers(),
|
|
56
|
-
{{#if queryParams.length}}
|
|
57
|
-
params=params,
|
|
58
|
-
{{/if}}
|
|
59
|
-
{{#if hasRequestBody}}
|
|
60
|
-
json=body,
|
|
61
|
-
{{/if}}
|
|
62
|
-
)
|
|
63
|
-
data = resp.json() if resp.headers.get("content-type", "").startswith("application/json") else resp.text
|
|
64
|
-
return [TextContent(type="text", text=json.dumps(data, indent=2) if isinstance(data, (dict, list)) else str(data))]
|
|
65
|
-
except Exception as e:
|
|
66
|
-
return [TextContent(type="text", text=f"Error: {e}")]
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
{{/each}}
|
|
70
|
-
async def main():
|
|
71
|
-
async with stdio_server() as (read_stream, write_stream):
|
|
72
|
-
await server.run(read_stream, write_stream, server.create_initialization_options())
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if __name__ == "__main__":
|
|
76
|
-
import asyncio
|
|
77
|
-
asyncio.run(main())
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scaffolds the project files and shared modules for a site (Playwright-based) MCP server.
|
|
3
|
-
*/
|
|
4
|
-
import type { SiteProjectManifest } from '../types/site.js';
|
|
5
|
-
import type { CodeUnit } from './code-writer.js';
|
|
6
|
-
/**
|
|
7
|
-
* Generate project skeleton files (package.json, tsconfig, Dockerfile, etc.)
|
|
8
|
-
*/
|
|
9
|
-
export declare function scaffoldSiteProjectFiles(manifest: SiteProjectManifest): CodeUnit[];
|
|
10
|
-
/**
|
|
11
|
-
* Generate shared source modules (server entry, config, browser manager).
|
|
12
|
-
*/
|
|
13
|
-
export declare function scaffoldSiteSharedModules(manifest: SiteProjectManifest): CodeUnit[];
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scaffolds the project files and shared modules for a site (Playwright-based) MCP server.
|
|
3
|
-
*/
|
|
4
|
-
import { renderSiteTemplate } from './site-template-loader.js';
|
|
5
|
-
import { renderTemplate } from './template-loader.js';
|
|
6
|
-
/**
|
|
7
|
-
* Generate project skeleton files (package.json, tsconfig, Dockerfile, etc.)
|
|
8
|
-
*/
|
|
9
|
-
export function scaffoldSiteProjectFiles(manifest) {
|
|
10
|
-
const units = [
|
|
11
|
-
{
|
|
12
|
-
filePath: 'package.json',
|
|
13
|
-
content: renderSiteTemplate('package.json', manifest),
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
filePath: 'tsconfig.json',
|
|
17
|
-
// Reuse the existing tsconfig template — it's the same for both server types
|
|
18
|
-
content: renderTemplate('tsconfig.json', manifest),
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
filePath: '.env.example',
|
|
22
|
-
content: renderSiteTemplate('env.example', manifest),
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
filePath: '.gitignore',
|
|
26
|
-
content: renderTemplate('gitignore', manifest),
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
filePath: 'Dockerfile',
|
|
30
|
-
content: renderSiteTemplate('dockerfile', manifest),
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
// Regeneration metadata for `mcpmake rescan` (see SiteRegenMetadata).
|
|
34
|
-
filePath: 'mcpmake.site.json',
|
|
35
|
-
content: JSON.stringify({
|
|
36
|
-
serverName: manifest.serverName,
|
|
37
|
-
serverVersion: manifest.serverVersion,
|
|
38
|
-
transport: manifest.transport,
|
|
39
|
-
baseUrl: manifest.baseUrl,
|
|
40
|
-
envVars: manifest.envVars,
|
|
41
|
-
browserConfig: manifest.browserConfig,
|
|
42
|
-
}, null, 2),
|
|
43
|
-
},
|
|
44
|
-
];
|
|
45
|
-
return units;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Generate shared source modules (server entry, config, browser manager).
|
|
49
|
-
*/
|
|
50
|
-
export function scaffoldSiteSharedModules(manifest) {
|
|
51
|
-
const serverMainTemplate = manifest.transport === 'http' ? 'server-main-http.ts' : 'server-main.ts';
|
|
52
|
-
return [
|
|
53
|
-
{
|
|
54
|
-
filePath: 'src/index.ts',
|
|
55
|
-
content: renderSiteTemplate(serverMainTemplate, manifest),
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
filePath: 'src/config.ts',
|
|
59
|
-
content: renderSiteTemplate('config.ts', manifest),
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
filePath: 'src/browser-manager.ts',
|
|
63
|
-
content: renderSiteTemplate('browser-manager.ts', manifest),
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
filePath: 'src/site-descriptor.json',
|
|
67
|
-
content: JSON.stringify(manifest.siteDescriptor, null, 2),
|
|
68
|
-
},
|
|
69
|
-
];
|
|
70
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template loader for site (Playwright-based) MCP server templates.
|
|
3
|
-
* Parallel to template-loader.ts but reads from site-templates/.
|
|
4
|
-
*/
|
|
5
|
-
import { readFileSync } from 'node:fs';
|
|
6
|
-
import { resolve, dirname } from 'node:path';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import Handlebars from 'handlebars';
|
|
9
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const SITE_TEMPLATE_DIR = resolve(__dirname, 'site-templates');
|
|
11
|
-
const siteTemplateCache = new Map();
|
|
12
|
-
// Register helpers (same as template-loader.ts — Handlebars helpers are global)
|
|
13
|
-
// These may already be registered; Handlebars silently overwrites, which is fine.
|
|
14
|
-
Handlebars.registerHelper('eq', function (a, b) {
|
|
15
|
-
return a === b;
|
|
16
|
-
});
|
|
17
|
-
Handlebars.registerHelper('capitalize', function (str) {
|
|
18
|
-
if (!str)
|
|
19
|
-
return str;
|
|
20
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
21
|
-
});
|
|
22
|
-
// Escape a value for safe interpolation inside a SINGLE-QUOTED JS/TS string
|
|
23
|
-
// literal in generated code. Critical for any string derived from crawled
|
|
24
|
-
// (untrusted) site DOM — selectors, hrefs, field-name keys — so it can never
|
|
25
|
-
// break out of the literal and inject code into the generated server.
|
|
26
|
-
Handlebars.registerHelper('jsString', function (value) {
|
|
27
|
-
const s = value === null || value === undefined ? '' : String(value);
|
|
28
|
-
return (s
|
|
29
|
-
.replace(/\\/g, '\\\\')
|
|
30
|
-
.replace(/'/g, "\\'")
|
|
31
|
-
.replace(/\n/g, '\\n')
|
|
32
|
-
.replace(/\r/g, '\\r')
|
|
33
|
-
// Drop any remaining control chars that could terminate the literal.
|
|
34
|
-
.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ''));
|
|
35
|
-
});
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
-
export function renderSiteTemplate(name, data) {
|
|
38
|
-
if (!siteTemplateCache.has(name)) {
|
|
39
|
-
const templatePath = resolve(SITE_TEMPLATE_DIR, `${name}.hbs`);
|
|
40
|
-
if (!templatePath.startsWith(SITE_TEMPLATE_DIR + '/')) {
|
|
41
|
-
throw new Error(`Invalid site template name: ${name}`);
|
|
42
|
-
}
|
|
43
|
-
const raw = readFileSync(templatePath, 'utf-8');
|
|
44
|
-
siteTemplateCache.set(name, Handlebars.compile(raw, { noEscape: true }));
|
|
45
|
-
}
|
|
46
|
-
return siteTemplateCache.get(name)(data);
|
|
47
|
-
}
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser lifecycle management for the site MCP server.
|
|
3
|
-
* Handles launching, closing, idle timeout, and screenshots.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { chromium } from 'playwright';
|
|
7
|
-
import type { Browser, BrowserContext, Page } from 'playwright';
|
|
8
|
-
import type { BrowserConfig } from './config.js';
|
|
9
|
-
|
|
10
|
-
interface BrowserSession {
|
|
11
|
-
context: BrowserContext;
|
|
12
|
-
page: Page;
|
|
13
|
-
lastAccessedAt: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let browser: Browser | undefined;
|
|
17
|
-
const sessions = new Map<string, BrowserSession>();
|
|
18
|
-
let idleTimer: ReturnType<typeof setInterval> | undefined;
|
|
19
|
-
let config: BrowserConfig;
|
|
20
|
-
|
|
21
|
-
const DEFAULT_SESSION = '__default__';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Initialize the browser manager with configuration.
|
|
25
|
-
*/
|
|
26
|
-
export function initBrowserManager(browserConfig: BrowserConfig): void {
|
|
27
|
-
config = browserConfig;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Resolve the first selector that attaches to the page, trying the primary
|
|
32
|
-
* selector first and then any fallbacks. This gives generated tools runtime
|
|
33
|
-
* resilience: when a site's DOM shifts and the primary selector breaks, a
|
|
34
|
-
* fallback (id, aria-label, name, CSS path, XPath, ...) can still match,
|
|
35
|
-
* instead of the tool failing outright.
|
|
36
|
-
*
|
|
37
|
-
* Throws a descriptive error if none of the candidates resolve.
|
|
38
|
-
*/
|
|
39
|
-
export async function resolveSelector(
|
|
40
|
-
page: Page,
|
|
41
|
-
candidates: Array<string | undefined>,
|
|
42
|
-
options: { timeoutMs?: number } = {},
|
|
43
|
-
): Promise<string> {
|
|
44
|
-
const valid = candidates.filter(
|
|
45
|
-
(c): c is string => typeof c === 'string' && c.trim().length > 0,
|
|
46
|
-
);
|
|
47
|
-
if (valid.length === 0) {
|
|
48
|
-
throw new Error('No selector candidates were provided');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Fast path: return the first candidate already attached to the DOM.
|
|
52
|
-
for (const selector of valid) {
|
|
53
|
-
try {
|
|
54
|
-
const handle = await page.$(selector);
|
|
55
|
-
if (handle) {
|
|
56
|
-
await handle.dispose();
|
|
57
|
-
return selector;
|
|
58
|
-
}
|
|
59
|
-
} catch {
|
|
60
|
-
// Invalid selector syntax — skip and try the next candidate.
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Slow path: the element may not have rendered yet. Wait for the first
|
|
65
|
-
// candidate to attach, racing all of them under a single timeout budget.
|
|
66
|
-
const timeoutMs = options.timeoutMs ?? 5000;
|
|
67
|
-
try {
|
|
68
|
-
return await Promise.any(
|
|
69
|
-
valid.map(async (selector) => {
|
|
70
|
-
await page.waitForSelector(selector, { state: 'attached', timeout: timeoutMs });
|
|
71
|
-
return selector;
|
|
72
|
-
}),
|
|
73
|
-
);
|
|
74
|
-
} catch {
|
|
75
|
-
throw new Error(
|
|
76
|
-
`None of the selectors resolved on the page (the page structure may have changed): ${valid.join(', ')}`,
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Get or create a browser session.
|
|
83
|
-
* If sessionId is provided, reuses an existing session or creates one with that ID.
|
|
84
|
-
* If not, creates a new session with a generated ID.
|
|
85
|
-
* Throws if the session pool is at capacity (maxSessions).
|
|
86
|
-
*/
|
|
87
|
-
export async function getOrCreateSession(sessionId?: string): Promise<{ page: Page; sessionId: string }> {
|
|
88
|
-
const id = sessionId ?? DEFAULT_SESSION;
|
|
89
|
-
|
|
90
|
-
// Reuse existing session
|
|
91
|
-
const existing = sessions.get(id);
|
|
92
|
-
if (existing) {
|
|
93
|
-
existing.lastAccessedAt = Date.now();
|
|
94
|
-
resetIdleTimer();
|
|
95
|
-
return { page: existing.page, sessionId: id };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Check capacity before creating a new session
|
|
99
|
-
const maxSessions = config.maxSessions ?? 10;
|
|
100
|
-
if (sessions.size >= maxSessions) {
|
|
101
|
-
throw new Error(
|
|
102
|
-
`Session pool at capacity (${maxSessions}). ` +
|
|
103
|
-
'Close an existing session before creating a new one.'
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Launch browser if not running
|
|
108
|
-
if (!browser || !browser.isConnected()) {
|
|
109
|
-
// Only disable sandbox when running as root in a container
|
|
110
|
-
const isRoot = process.getuid?.() === 0;
|
|
111
|
-
browser = await chromium.launch({
|
|
112
|
-
headless: config.headless,
|
|
113
|
-
args: isRoot ? ['--no-sandbox', '--disable-setuid-sandbox'] : [],
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Create new session
|
|
118
|
-
const context = await browser.newContext({
|
|
119
|
-
viewport: config.viewport,
|
|
120
|
-
...(config.userAgent ? { userAgent: config.userAgent } : {}),
|
|
121
|
-
});
|
|
122
|
-
const page = await context.newPage();
|
|
123
|
-
|
|
124
|
-
// Generate final session ID upfront to avoid race conditions
|
|
125
|
-
const finalId = id === DEFAULT_SESSION
|
|
126
|
-
? `session_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`
|
|
127
|
-
: id;
|
|
128
|
-
|
|
129
|
-
sessions.set(finalId, {
|
|
130
|
-
context,
|
|
131
|
-
page,
|
|
132
|
-
lastAccessedAt: Date.now(),
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
resetIdleTimer();
|
|
136
|
-
|
|
137
|
-
return { page, sessionId: finalId };
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Close a specific session or the default session.
|
|
142
|
-
*/
|
|
143
|
-
export async function closeSession(sessionId?: string): Promise<void> {
|
|
144
|
-
const id = sessionId ?? DEFAULT_SESSION;
|
|
145
|
-
const session = sessions.get(id);
|
|
146
|
-
if (!session) return;
|
|
147
|
-
|
|
148
|
-
await session.context.close().catch(() => {});
|
|
149
|
-
sessions.delete(id);
|
|
150
|
-
|
|
151
|
-
// If no sessions remain, close the browser
|
|
152
|
-
if (sessions.size === 0) {
|
|
153
|
-
await closeBrowser();
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Close the browser and all sessions.
|
|
159
|
-
*/
|
|
160
|
-
export async function closeBrowser(): Promise<void> {
|
|
161
|
-
clearIdleTimer();
|
|
162
|
-
|
|
163
|
-
for (const [id, session] of sessions) {
|
|
164
|
-
await session.context.close().catch(() => {});
|
|
165
|
-
sessions.delete(id);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (browser) {
|
|
169
|
-
await browser.close().catch(() => {});
|
|
170
|
-
browser = undefined;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Take a screenshot of the current page in a session.
|
|
176
|
-
*/
|
|
177
|
-
export async function takeScreenshot(
|
|
178
|
-
sessionId?: string,
|
|
179
|
-
fullPage = false,
|
|
180
|
-
): Promise<Buffer> {
|
|
181
|
-
const id = sessionId ?? DEFAULT_SESSION;
|
|
182
|
-
const session = sessions.get(id);
|
|
183
|
-
if (!session) {
|
|
184
|
-
throw new Error(`No active session: ${id}`);
|
|
185
|
-
}
|
|
186
|
-
session.lastAccessedAt = Date.now();
|
|
187
|
-
return session.page.screenshot({ type: 'png', fullPage });
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* List active session IDs.
|
|
192
|
-
*/
|
|
193
|
-
export function listSessions(): string[] {
|
|
194
|
-
return Array.from(sessions.keys());
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Check if the browser is running.
|
|
199
|
-
*/
|
|
200
|
-
export function isBrowserRunning(): boolean {
|
|
201
|
-
return browser !== undefined && browser.isConnected();
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// ─── Idle Timer ─────────────────────────────────────────────────────
|
|
205
|
-
|
|
206
|
-
function resetIdleTimer(): void {
|
|
207
|
-
clearIdleTimer();
|
|
208
|
-
idleTimer = setInterval(() => {
|
|
209
|
-
const now = Date.now();
|
|
210
|
-
// Close idle sessions
|
|
211
|
-
for (const [id, session] of sessions) {
|
|
212
|
-
if (now - session.lastAccessedAt > config.idleTimeoutMs) {
|
|
213
|
-
session.context.close().catch(() => {});
|
|
214
|
-
sessions.delete(id);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
// Close browser if no sessions
|
|
218
|
-
if (sessions.size === 0) {
|
|
219
|
-
closeBrowser();
|
|
220
|
-
}
|
|
221
|
-
}, 30_000); // Check every 30 seconds
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function clearIdleTimer(): void {
|
|
225
|
-
if (idleTimer) {
|
|
226
|
-
clearInterval(idleTimer);
|
|
227
|
-
idleTimer = undefined;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Clean up on process exit
|
|
232
|
-
process.on('SIGTERM', () => closeBrowser());
|
|
233
|
-
process.on('SIGINT', () => closeBrowser());
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export interface BrowserConfig {
|
|
2
|
-
headless: boolean;
|
|
3
|
-
idleTimeoutMs: number;
|
|
4
|
-
viewport: { width: number; height: number };
|
|
5
|
-
userAgent?: string;
|
|
6
|
-
maxSessions: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface AppConfig {
|
|
10
|
-
baseUrl: string;
|
|
11
|
-
browser: BrowserConfig;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function loadConfig(): AppConfig {
|
|
15
|
-
return {
|
|
16
|
-
baseUrl: process.env.BASE_URL ?? '{{{baseUrl}}}',
|
|
17
|
-
browser: {
|
|
18
|
-
headless: process.env.HEADLESS !== 'false',
|
|
19
|
-
idleTimeoutMs: parseInt(process.env.IDLE_TIMEOUT_MS ?? '{{browserConfig.idleTimeoutMs}}', 10),
|
|
20
|
-
viewport: {
|
|
21
|
-
width: parseInt(process.env.VIEWPORT_WIDTH ?? '{{browserConfig.viewport.width}}', 10),
|
|
22
|
-
height: parseInt(process.env.VIEWPORT_HEIGHT ?? '{{browserConfig.viewport.height}}', 10),
|
|
23
|
-
},
|
|
24
|
-
userAgent: process.env.USER_AGENT || undefined,
|
|
25
|
-
maxSessions: parseInt(process.env.MAX_SESSIONS ?? '{{browserConfig.maxSessions}}', 10) || 10,
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
FROM mcr.microsoft.com/playwright:v1.50.0-noble AS builder
|
|
2
|
-
|
|
3
|
-
WORKDIR /app
|
|
4
|
-
COPY package.json package-lock.json ./
|
|
5
|
-
RUN npm ci
|
|
6
|
-
COPY tsconfig.json ./
|
|
7
|
-
COPY src/ src/
|
|
8
|
-
RUN npm run build
|
|
9
|
-
|
|
10
|
-
FROM mcr.microsoft.com/playwright:v1.50.0-noble
|
|
11
|
-
|
|
12
|
-
LABEL org.opencontainers.image.title="{{serverName}}"
|
|
13
|
-
ENV NODE_ENV=production
|
|
14
|
-
ENV TRANSPORT=http
|
|
15
|
-
ENV PORT=3000
|
|
16
|
-
ENV HEADLESS=true
|
|
17
|
-
|
|
18
|
-
WORKDIR /app
|
|
19
|
-
COPY package.json package-lock.json ./
|
|
20
|
-
RUN npm ci --omit=dev
|
|
21
|
-
|
|
22
|
-
COPY --from=builder /app/dist dist/
|
|
23
|
-
|
|
24
|
-
USER pwuser
|
|
25
|
-
|
|
26
|
-
EXPOSE 3000
|
|
27
|
-
|
|
28
|
-
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
|
29
|
-
CMD node -e "fetch('http://localhost:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
|
30
|
-
|
|
31
|
-
CMD ["node", "dist/index.js"]
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# {{serverName}} configuration
|
|
2
|
-
|
|
3
|
-
# Target website URL
|
|
4
|
-
BASE_URL={{{baseUrl}}}
|
|
5
|
-
|
|
6
|
-
# Browser settings
|
|
7
|
-
HEADLESS=true
|
|
8
|
-
IDLE_TIMEOUT_MS={{browserConfig.idleTimeoutMs}}
|
|
9
|
-
VIEWPORT_WIDTH={{browserConfig.viewport.width}}
|
|
10
|
-
VIEWPORT_HEIGHT={{browserConfig.viewport.height}}
|
|
11
|
-
# USER_AGENT=
|
|
12
|
-
{{#if (eq transport "http")}}
|
|
13
|
-
|
|
14
|
-
# Transport: "stdio" or "http"
|
|
15
|
-
TRANSPORT=http
|
|
16
|
-
PORT=3000
|
|
17
|
-
# Allowed origin for CORS/DNS-rebinding protection
|
|
18
|
-
ALLOWED_ORIGIN=
|
|
19
|
-
{{/if}}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{serverName}}",
|
|
3
|
-
"version": "{{serverVersion}}",
|
|
4
|
-
"type": "module",
|
|
5
|
-
"private": true,
|
|
6
|
-
"scripts": {
|
|
7
|
-
"build": "tsc",
|
|
8
|
-
"start": "node dist/index.js",
|
|
9
|
-
"dev": "tsx src/index.ts",
|
|
10
|
-
"test": "vitest run"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
14
|
-
"playwright": "^1.50.0",
|
|
15
|
-
"zod": "^3.24.0"
|
|
16
|
-
},
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"@types/node": "^22.0.0",
|
|
19
|
-
"typescript": "^5.8.0",
|
|
20
|
-
"tsx": "^4.19.0",
|
|
21
|
-
"vitest": "^3.1.0"
|
|
22
|
-
},
|
|
23
|
-
"engines": {
|
|
24
|
-
"node": ">=20.0.0"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import http from 'node:http';
|
|
2
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
5
|
-
import { loadConfig } from './config.js';
|
|
6
|
-
import { initBrowserManager, closeBrowser, isBrowserRunning } from './browser-manager.js';
|
|
7
|
-
import { registerAllTools } from './tools/index.js';
|
|
8
|
-
|
|
9
|
-
function log(level: string, message: string, data?: Record<string, unknown>): void {
|
|
10
|
-
const entry = { timestamp: new Date().toISOString(), level, message, ...data };
|
|
11
|
-
process.stderr.write(JSON.stringify(entry) + '\n');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const config = loadConfig();
|
|
15
|
-
initBrowserManager(config.browser);
|
|
16
|
-
|
|
17
|
-
const server = new McpServer({
|
|
18
|
-
name: '{{serverName}}',
|
|
19
|
-
version: '{{serverVersion}}',
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
registerAllTools(server, config);
|
|
23
|
-
|
|
24
|
-
let isReady = false;
|
|
25
|
-
|
|
26
|
-
if (process.env.TRANSPORT === 'http') {
|
|
27
|
-
const port = parseInt(process.env.PORT ?? '3000', 10);
|
|
28
|
-
const allowedOrigin = process.env.ALLOWED_ORIGIN;
|
|
29
|
-
|
|
30
|
-
const httpServer = http.createServer(async (req, res) => {
|
|
31
|
-
const url = new URL(req.url ?? '/', `http://localhost:${port}`);
|
|
32
|
-
|
|
33
|
-
if (url.pathname === '/health') {
|
|
34
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
35
|
-
res.end(JSON.stringify({ status: 'ok', browser: isBrowserRunning() }));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (url.pathname === '/ready') {
|
|
40
|
-
res.writeHead(isReady ? 200 : 503, { 'Content-Type': 'application/json' });
|
|
41
|
-
res.end(JSON.stringify({ ready: isReady }));
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (url.pathname === '/mcp') {
|
|
46
|
-
if (allowedOrigin) {
|
|
47
|
-
// Compare host-to-host exactly (DNS-rebinding protection). A substring
|
|
48
|
-
// match would let "evil-{ALLOWED_ORIGIN}.attacker.com" through.
|
|
49
|
-
const originHeader = req.headers.origin;
|
|
50
|
-
let originHost = '';
|
|
51
|
-
if (originHeader) {
|
|
52
|
-
try {
|
|
53
|
-
originHost = new URL(originHeader).host;
|
|
54
|
-
} catch {
|
|
55
|
-
originHost = '';
|
|
56
|
-
}
|
|
57
|
-
} else if (req.headers.host) {
|
|
58
|
-
originHost = req.headers.host;
|
|
59
|
-
}
|
|
60
|
-
// ALLOWED_ORIGIN may be a full origin (https://app.example.com) or a bare host.
|
|
61
|
-
let allowedHost = allowedOrigin;
|
|
62
|
-
try {
|
|
63
|
-
allowedHost = new URL(allowedOrigin).host;
|
|
64
|
-
} catch {
|
|
65
|
-
// already a bare host
|
|
66
|
-
}
|
|
67
|
-
// Hosts are case-insensitive; normalize both sides (URL.host already lowercases).
|
|
68
|
-
if (originHost.toLowerCase() !== allowedHost.toLowerCase()) {
|
|
69
|
-
res.writeHead(403);
|
|
70
|
-
res.end('Forbidden');
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
76
|
-
await server.connect(transport);
|
|
77
|
-
await transport.handleRequest(req, res);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
res.writeHead(404);
|
|
82
|
-
res.end('Not Found');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
httpServer.listen(port, () => {
|
|
86
|
-
isReady = true;
|
|
87
|
-
log('info', `MCP server listening on port ${port}`);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const shutdown = async () => {
|
|
91
|
-
log('info', 'Shutting down...');
|
|
92
|
-
await closeBrowser();
|
|
93
|
-
httpServer.close();
|
|
94
|
-
setTimeout(() => process.exit(0), 5000);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
process.on('SIGTERM', shutdown);
|
|
98
|
-
process.on('SIGINT', shutdown);
|
|
99
|
-
} else {
|
|
100
|
-
const transport = new StdioServerTransport();
|
|
101
|
-
await server.connect(transport);
|
|
102
|
-
isReady = true;
|
|
103
|
-
|
|
104
|
-
process.on('SIGTERM', async () => {
|
|
105
|
-
await closeBrowser();
|
|
106
|
-
process.exit(0);
|
|
107
|
-
});
|
|
108
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { loadConfig } from './config.js';
|
|
4
|
-
import { initBrowserManager, closeBrowser } from './browser-manager.js';
|
|
5
|
-
import { registerAllTools } from './tools/index.js';
|
|
6
|
-
|
|
7
|
-
const config = loadConfig();
|
|
8
|
-
initBrowserManager(config.browser);
|
|
9
|
-
|
|
10
|
-
const server = new McpServer({
|
|
11
|
-
name: '{{serverName}}',
|
|
12
|
-
version: '{{serverVersion}}',
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
registerAllTools(server, config);
|
|
16
|
-
|
|
17
|
-
const transport = new StdioServerTransport();
|
|
18
|
-
await server.connect(transport);
|
|
19
|
-
|
|
20
|
-
process.on('SIGTERM', async () => {
|
|
21
|
-
await closeBrowser();
|
|
22
|
-
process.exit(0);
|
|
23
|
-
});
|