create-ailk 0.23.0 → 0.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -33
- package/component-catalog.json +3 -3
- package/component-catalog.md +3 -3
- package/dist/add-module.js +10 -3
- package/dist/add-route.d.ts +12 -1
- package/dist/add-route.js +38 -4
- package/dist/cli.js +12 -7
- package/dist/derive-architecture.d.ts +3 -4
- package/dist/derive-architecture.js +75 -22
- package/dist/lib/design-checks.d.ts +9 -4
- package/dist/lib/design-checks.js +13 -7
- package/dist/lib/extract-module-bundle.js +29 -2
- package/dist/lib/fetch-template-snapshot.d.ts +1 -1
- package/dist/lib/fetch-template-snapshot.js +7 -3
- package/dist/lib/import-graph.d.ts +1 -1
- package/dist/lib/import-graph.js +4 -4
- package/dist/lib/module-license-gate.d.ts +12 -1
- package/dist/lib/module-license-gate.js +34 -12
- package/dist/lib/scaffold-render-sweep.d.ts +33 -0
- package/dist/lib/scaffold-render-sweep.js +45 -0
- package/dist/module-architecture.d.ts +20 -3
- package/dist/module-architecture.js +122 -538
- package/dist/paid-sections.d.ts +25 -1
- package/dist/paid-sections.js +88 -3
- package/dist/surfaces.js +10 -2
- package/dist/sync-routes.d.ts +30 -4
- package/dist/sync-routes.js +271 -18
- package/dist/template-record.d.ts +14 -4
- package/dist/template-record.js +23 -5
- package/dist/topology.d.ts +16 -3
- package/dist/topology.js +31 -19
- package/dist/update.d.ts +10 -1
- package/dist/update.js +540 -16
- package/package.json +3 -3
- package/release-scope.json +1 -1
- package/templates/.claude/cloudparity-repo-hooks.json +5 -0
- package/templates/.claude/hooks/template-guard.sh +137 -0
- package/templates/.claude/rules/extending.md +106 -0
- package/templates/.claude/rules/loop-contract.md +2 -2
- package/templates/.claude/settings.json +11 -0
- package/templates/.env.example +9 -5
- package/templates/.github/workflows/ci.yml +7 -4
- package/templates/CLAUDE.md +15 -3
- package/templates/CONVENTIONS.md +8 -10
- package/templates/README.md +14 -13
- package/templates/apps/api/.env.example +9 -2
- package/templates/apps/api/CLAUDE.md +122 -82
- package/templates/apps/api/jest.config.cjs +15 -5
- package/templates/apps/api/package.json +16 -20
- package/templates/apps/api/src/__tests__/trusted-origins.server.test.ts +2 -2
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +4 -4
- package/templates/apps/api/src/index.ts +1 -1
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +7 -4
- package/templates/apps/api/src/modules/commerce.ts +10 -0
- package/templates/apps/api/src/modules/content-write.ts +10 -0
- package/templates/apps/api/src/modules/multi-tenant.ts +10 -0
- package/templates/apps/api/src/modules/scheduling.ts +10 -0
- package/templates/apps/api/src/modules/signal-collectors.ts +10 -0
- package/templates/apps/api/src/modules/workspace.ts +10 -0
- package/templates/apps/api/src/server.ts +117 -1857
- package/templates/apps/api/vercel.json +2 -2
- package/templates/apps/mcp/CLAUDE.md +77 -24
- package/templates/apps/mcp/__tests__/app-entry.test.ts +4 -254
- package/templates/apps/mcp/__tests__/boot-file.test.ts +92 -0
- package/templates/apps/mcp/jest.config.cjs +6 -1
- package/templates/apps/mcp/package.json +5 -12
- package/templates/apps/mcp/src/app.ts +8 -7
- package/templates/apps/mcp/src/index.ts +6 -34
- package/templates/apps/mcp/src/lib/paid-tools.generated.ts +10 -0
- package/templates/apps/mcp/src/lib/topology.generated.ts +2 -2
- package/templates/apps/mcp/src/site.ts +12 -0
- package/templates/apps/web/.env.example +1 -1
- package/templates/apps/web/CLAUDE.md +21 -5
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +9 -3
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +14 -4
- package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +1 -1
- package/templates/apps/web/app/[locale]/contact/intake-script.ts +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +22 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +28 -7
- package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +43 -0
- package/templates/apps/web/app/[locale]/pricing/page.tsx +7 -3
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/__tests__/route.test.ts +222 -0
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +10 -3
- package/templates/apps/web/app/api/revalidate/route.ts +3 -3
- package/templates/apps/web/auth.ts +2 -2
- package/templates/apps/web/jest.config.cjs +1 -0
- package/templates/apps/web/lib/__tests__/magnet-routes.test.ts +7 -0
- package/templates/apps/web/lib/auth-email.ts +1 -1
- package/templates/apps/web/lib/magnet-routes.ts +3 -1
- package/templates/apps/web/lib/newsletter.actions.ts +9 -6
- package/templates/apps/web/lib/relay.ts +2 -2
- package/templates/apps/web/lib/release-scope.ts +47 -0
- package/templates/apps/web/lib/server-api.ts +1 -1
- package/templates/apps/web/src/lib/commerce-claim.ts +1 -1
- package/templates/customizations.yaml +37 -0
- package/templates/database/prisma.config.ts +10 -14
- package/templates/database/seed-test.ts +2 -2
- package/templates/database/seed.ts +32 -5
- package/templates/package.json +12 -6
- package/templates/pnpm-workspace.yaml +0 -1
- package/templates/project.yaml +16 -8
- package/templates/tools/jest/reporters.cjs +65 -0
- package/templates/tools/lint-architecture.ts +757 -7
- package/templates/tsconfig.json +1 -2
- package/versions.json +48 -35
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +0 -409
- package/templates/apps/api/src/__tests__/factories/agent-caller.ts +0 -102
- package/templates/apps/api/src/__tests__/factories/content-fixture.ts +0 -138
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +0 -441
- package/templates/apps/api/src/__tests__/factories/index.ts +0 -70
- package/templates/apps/api/src/__tests__/factories/lead-fixture.ts +0 -82
- package/templates/apps/api/src/__tests__/leads-forwarded-rate-limit.test.ts +0 -135
- package/templates/apps/api/src/__tests__/services/enrichment-shim.test.ts +0 -126
- package/templates/apps/api/src/__tests__/stripe-boot.test.ts +0 -132
- package/templates/apps/api/src/__tests__/support/hub-sign-in-fake-db.ts +0 -362
- package/templates/apps/api/src/bin/__tests__/rate-limit-probe.test.ts +0 -184
- package/templates/apps/api/src/bin/abandon-sweep.ts +0 -42
- package/templates/apps/api/src/bin/rate-limit-probe.ts +0 -53
- package/templates/apps/api/src/bin/spokes.ts +0 -198
- package/templates/apps/api/src/bin/stripe-connections-rekey.ts +0 -45
- package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +0 -196
- package/templates/apps/api/src/config/__tests__/index.test.ts +0 -233
- package/templates/apps/api/src/config/__tests__/modules.test.ts +0 -76
- package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +0 -185
- package/templates/apps/api/src/config/__tests__/trusted-origins.test.ts +0 -238
- package/templates/apps/api/src/config/index.ts +0 -596
- package/templates/apps/api/src/config/modules.ts +0 -83
- package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +0 -581
- package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +0 -143
- package/templates/apps/api/src/lib/__tests__/consent-ledger-double.ts +0 -237
- package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +0 -353
- package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +0 -201
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +0 -224
- package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +0 -120
- package/templates/apps/api/src/lib/__tests__/errors.test.ts +0 -94
- package/templates/apps/api/src/lib/__tests__/file-fallback.test.ts +0 -144
- package/templates/apps/api/src/lib/__tests__/fixtures/HomePage/home.mdx +0 -8
- package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +0 -149
- package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +0 -116
- package/templates/apps/api/src/lib/__tests__/hub-sign-in-store.test.ts +0 -209
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +0 -257
- package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +0 -96
- package/templates/apps/api/src/lib/__tests__/outbound-http.test.ts +0 -125
- package/templates/apps/api/src/lib/__tests__/prisma.test.ts +0 -165
- package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +0 -317
- package/templates/apps/api/src/lib/__tests__/require-paid-tier.test.ts +0 -129
- package/templates/apps/api/src/lib/__tests__/site.test.ts +0 -292
- package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +0 -89
- package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +0 -202
- package/templates/apps/api/src/lib/__tests__/stripe-admission.test.ts +0 -217
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -82
- package/templates/apps/api/src/lib/agent-store.ts +0 -613
- package/templates/apps/api/src/lib/auth-email-budget.ts +0 -161
- package/templates/apps/api/src/lib/consent-store.ts +0 -363
- package/templates/apps/api/src/lib/content-adapter.ts +0 -112
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +0 -96
- package/templates/apps/api/src/lib/error-serializer.ts +0 -164
- package/templates/apps/api/src/lib/errors.ts +0 -95
- package/templates/apps/api/src/lib/file-fallback.ts +0 -87
- package/templates/apps/api/src/lib/flow-completion-store.ts +0 -136
- package/templates/apps/api/src/lib/hub-sign-in-store.ts +0 -455
- package/templates/apps/api/src/lib/lead-capture-store.ts +0 -122
- package/templates/apps/api/src/lib/lead-delivery.ts +0 -52
- package/templates/apps/api/src/lib/license-key-store.ts +0 -45
- package/templates/apps/api/src/lib/locales.ts +0 -201
- package/templates/apps/api/src/lib/outbound-http.ts +0 -127
- package/templates/apps/api/src/lib/prisma.ts +0 -21
- package/templates/apps/api/src/lib/proposal-paid-store.ts +0 -66
- package/templates/apps/api/src/lib/rate-limit-probe.ts +0 -165
- package/templates/apps/api/src/lib/rate-limit-store.ts +0 -142
- package/templates/apps/api/src/lib/require-commerce-claim.ts +0 -250
- package/templates/apps/api/src/lib/require-paid-tier.ts +0 -53
- package/templates/apps/api/src/lib/site.ts +0 -254
- package/templates/apps/api/src/lib/slice-load.ts +0 -90
- package/templates/apps/api/src/lib/spoke-store.ts +0 -180
- package/templates/apps/api/src/lib/stripe.ts +0 -318
- package/templates/apps/api/src/lib/webhook-idempotency.ts +0 -156
- package/templates/apps/api/src/middleware/__tests__/adversarial/bearer-token-format.test.ts +0 -111
- package/templates/apps/api/src/middleware/__tests__/adversarial/cookie-or-bearer-precedence.test.ts +0 -172
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-bearer-skip.test.ts +0 -119
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-cookie-path.test.ts +0 -138
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-trusted-origins.test.ts +0 -142
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +0 -155
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-timeout.test.ts +0 -139
- package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +0 -562
- package/templates/apps/api/src/middleware/__tests__/auth.both-present-precedence.test.ts +0 -76
- package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +0 -220
- package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +0 -97
- package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +0 -557
- package/templates/apps/api/src/middleware/__tests__/auth.neither-present.test.ts +0 -72
- package/templates/apps/api/src/middleware/__tests__/auth.optional-cookie.test.ts +0 -140
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +0 -721
- package/templates/apps/api/src/middleware/__tests__/rate-limit-cross-isolate.db.test.ts +0 -220
- package/templates/apps/api/src/middleware/__tests__/rate-limit-store.test.ts +0 -430
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +0 -1409
- package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +0 -174
- package/templates/apps/api/src/middleware/auth.ts +0 -864
- package/templates/apps/api/src/middleware/default-tenant.ts +0 -166
- package/templates/apps/api/src/middleware/rate-limit.ts +0 -983
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +0 -264
- package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +0 -182
- package/templates/apps/api/src/openapi/index.ts +0 -18
- package/templates/apps/api/src/openapi/spec.ts +0 -1815
- package/templates/apps/api/src/openapi/surface-drift.ts +0 -195
- package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +0 -175
- package/templates/apps/api/src/routes/aeo/index.ts +0 -107
- package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +0 -196
- package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +0 -249
- package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +0 -532
- package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +0 -313
- package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +0 -413
- package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +0 -609
- package/templates/apps/api/src/routes/auth/hub-sign-in.ts +0 -242
- package/templates/apps/api/src/routes/auth/tokens.ts +0 -211
- package/templates/apps/api/src/routes/auth/transactional-email.ts +0 -301
- package/templates/apps/api/src/routes/auth/user.ts +0 -305
- package/templates/apps/api/src/routes/billing/plan-checkout.ts +0 -325
- package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +0 -178
- package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +0 -1300
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +0 -448
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -1742
- package/templates/apps/api/src/routes/checkout/index.ts +0 -34
- package/templates/apps/api/src/routes/checkout/session.ts +0 -171
- package/templates/apps/api/src/routes/checkout/sessions.ts +0 -540
- package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +0 -386
- package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +0 -160
- package/templates/apps/api/src/routes/consent/account.ts +0 -268
- package/templates/apps/api/src/routes/consent/cookie.ts +0 -90
- package/templates/apps/api/src/routes/consent/index.ts +0 -26
- package/templates/apps/api/src/routes/consent/unsubscribe.ts +0 -127
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +0 -853
- package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +0 -230
- package/templates/apps/api/src/routes/content/__tests__/list-types.test.ts +0 -44
- package/templates/apps/api/src/routes/content/__tests__/locale.test.ts +0 -118
- package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +0 -731
- package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +0 -753
- package/templates/apps/api/src/routes/content/__tests__/rate-limit.test.ts +0 -51
- package/templates/apps/api/src/routes/content/__tests__/search.test.ts +0 -450
- package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +0 -762
- package/templates/apps/api/src/routes/content/get-page.ts +0 -268
- package/templates/apps/api/src/routes/content/index.ts +0 -49
- package/templates/apps/api/src/routes/content/list-pages.ts +0 -95
- package/templates/apps/api/src/routes/content/list-types.ts +0 -19
- package/templates/apps/api/src/routes/content/offerings.ts +0 -184
- package/templates/apps/api/src/routes/content/pricing.ts +0 -176
- package/templates/apps/api/src/routes/content/rate-limit.ts +0 -22
- package/templates/apps/api/src/routes/content/search.ts +0 -129
- package/templates/apps/api/src/routes/content/shown-offerings.ts +0 -229
- package/templates/apps/api/src/routes/content/slug.ts +0 -9
- package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +0 -88
- package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +0 -171
- package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +0 -515
- package/templates/apps/api/src/routes/leads/__tests__/spoke.test.ts +0 -392
- package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +0 -165
- package/templates/apps/api/src/routes/leads/index.ts +0 -573
- package/templates/apps/api/src/routes/leads/list.ts +0 -104
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +0 -626
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +0 -1268
- package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +0 -847
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +0 -480
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +0 -465
- package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +0 -872
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +0 -1022
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +0 -484
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +0 -535
- package/templates/apps/api/src/routes/project-listings/index.ts +0 -164
- package/templates/apps/api/src/routes/project-listings/me.ts +0 -87
- package/templates/apps/api/src/routes/project-listings/patch-copy.ts +0 -224
- package/templates/apps/api/src/routes/project-listings/patch-draft.ts +0 -102
- package/templates/apps/api/src/routes/project-listings/public.ts +0 -98
- package/templates/apps/api/src/routes/project-listings/respond.ts +0 -48
- package/templates/apps/api/src/routes/project-listings/resume-token.ts +0 -27
- package/templates/apps/api/src/routes/project-listings/start.ts +0 -140
- package/templates/apps/api/src/routes/project-listings/submit.ts +0 -130
- package/templates/apps/api/src/routes/project-listings/verify-email.ts +0 -150
- package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +0 -686
- package/templates/apps/api/src/routes/requests/confirm.ts +0 -601
- package/templates/apps/api/src/routes/requests/index.ts +0 -54
- package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +0 -306
- package/templates/apps/api/src/routes/test/auth/session.ts +0 -143
- package/templates/apps/api/src/routes/webhooks/README.md +0 -398
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +0 -649
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +0 -749
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -448
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -264
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode-live.test.ts +0 -159
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode.test.ts +0 -158
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +0 -814
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -301
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-proposal-paid.test.ts +0 -369
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -215
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +0 -301
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +0 -808
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -1155
- package/templates/apps/api/src/routes/webhooks/index.ts +0 -141
- package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +0 -384
- package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -2026
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +0 -528
- package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +0 -132
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +0 -1028
- package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +0 -313
- package/templates/apps/api/src/services/__tests__/consent-ledger.test.ts +0 -503
- package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +0 -65
- package/templates/apps/api/src/services/__tests__/consent.test.ts +0 -299
- package/templates/apps/api/src/services/__tests__/default-tenant.test.ts +0 -147
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +0 -867
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +0 -379
- package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +0 -368
- package/templates/apps/api/src/services/__tests__/entitlements.test.ts +0 -142
- package/templates/apps/api/src/services/__tests__/hub-sign-in.test.ts +0 -854
- package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +0 -234
- package/templates/apps/api/src/services/__tests__/lead-accept-user.test.ts +0 -251
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +0 -2660
- package/templates/apps/api/src/services/__tests__/listing-config.test.ts +0 -166
- package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +0 -389
- package/templates/apps/api/src/services/__tests__/person.test.ts +0 -1176
- package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +0 -444
- package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +0 -154
- package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +0 -137
- package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +0 -531
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +0 -372
- package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +0 -348
- package/templates/apps/api/src/services/__tests__/spoke-resolution.test.ts +0 -660
- package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +0 -941
- package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +0 -588
- package/templates/apps/api/src/services/__tests__/verified-email.test.ts +0 -78
- package/templates/apps/api/src/services/abandon-email.ts +0 -313
- package/templates/apps/api/src/services/aeo-score.ts +0 -325
- package/templates/apps/api/src/services/answer-distributions.ts +0 -138
- package/templates/apps/api/src/services/audit-log.ts +0 -961
- package/templates/apps/api/src/services/checkout-buyer.ts +0 -168
- package/templates/apps/api/src/services/consent.ts +0 -834
- package/templates/apps/api/src/services/default-tenant.ts +0 -416
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +0 -755
- package/templates/apps/api/src/services/dunning-policy.ts +0 -294
- package/templates/apps/api/src/services/enrichment-shim.ts +0 -53
- package/templates/apps/api/src/services/entitlements.ts +0 -338
- package/templates/apps/api/src/services/hub-sign-in.ts +0 -472
- package/templates/apps/api/src/services/lead-accept-user.ts +0 -126
- package/templates/apps/api/src/services/lead-promotion.ts +0 -160
- package/templates/apps/api/src/services/lead-routing.ts +0 -1372
- package/templates/apps/api/src/services/leads-inbox.ts +0 -312
- package/templates/apps/api/src/services/listing-config.ts +0 -178
- package/templates/apps/api/src/services/magnet-completion.ts +0 -242
- package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +0 -295
- package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +0 -268
- package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +0 -868
- package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +0 -416
- package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +0 -396
- package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +0 -681
- package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +0 -231
- package/templates/apps/api/src/services/offerings/catalog.ts +0 -308
- package/templates/apps/api/src/services/offerings/checkout-authorization.ts +0 -385
- package/templates/apps/api/src/services/offerings/checkout-session-record.ts +0 -192
- package/templates/apps/api/src/services/offerings/connection.ts +0 -1003
- package/templates/apps/api/src/services/offerings/index.ts +0 -614
- package/templates/apps/api/src/services/offerings/source.ts +0 -140
- package/templates/apps/api/src/services/offerings/store.ts +0 -150
- package/templates/apps/api/src/services/owner-workspaces.ts +0 -132
- package/templates/apps/api/src/services/person.ts +0 -1188
- package/templates/apps/api/src/services/plan-offerings.ts +0 -125
- package/templates/apps/api/src/services/project-listing-resume-email.ts +0 -326
- package/templates/apps/api/src/services/project-listings.ts +0 -2073
- package/templates/apps/api/src/services/public-site-resolver.ts +0 -396
- package/templates/apps/api/src/services/request-confirm.ts +0 -698
- package/templates/apps/api/src/services/revalidation.ts +0 -235
- package/templates/apps/api/src/services/slack-notify.ts +0 -291
- package/templates/apps/api/src/services/usage-aggregation.ts +0 -516
- package/templates/apps/api/src/services/usage-credits.ts +0 -340
- package/templates/apps/api/src/services/verified-email.ts +0 -71
- package/templates/apps/mcp/__tests__/abuse/daily-cap.test.ts +0 -52
- package/templates/apps/mcp/__tests__/abuse/email-validator.test.ts +0 -46
- package/templates/apps/mcp/__tests__/abuse/guard.test.ts +0 -198
- package/templates/apps/mcp/__tests__/abuse/honeypot.test.ts +0 -36
- package/templates/apps/mcp/__tests__/abuse/user-agent-deny.test.ts +0 -54
- package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +0 -116
- package/templates/apps/mcp/__tests__/audit-single-page.test.ts +0 -299
- package/templates/apps/mcp/__tests__/boot.test.ts +0 -280
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +0 -359
- package/templates/apps/mcp/__tests__/config/modules.test.ts +0 -78
- package/templates/apps/mcp/__tests__/get_business_hours.test.ts +0 -82
- package/templates/apps/mcp/__tests__/get_faq.test.ts +0 -80
- package/templates/apps/mcp/__tests__/get_locations.test.ts +0 -65
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +0 -557
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +0 -524
- package/templates/apps/mcp/__tests__/get_site_info.test.ts +0 -81
- package/templates/apps/mcp/__tests__/http-transport.test.ts +0 -1151
- package/templates/apps/mcp/__tests__/index.test.ts +0 -195
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +0 -191
- package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +0 -531
- package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +0 -65
- package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +0 -187
- package/templates/apps/mcp/__tests__/lib/content-root.test.ts +0 -93
- package/templates/apps/mcp/__tests__/lib/errors.test.ts +0 -75
- package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +0 -165
- package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +0 -124
- package/templates/apps/mcp/__tests__/list_pages.test.ts +0 -228
- package/templates/apps/mcp/__tests__/locale.test.ts +0 -135
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +0 -175
- package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +0 -101
- package/templates/apps/mcp/__tests__/read_content.test.ts +0 -474
- package/templates/apps/mcp/__tests__/request_callback.test.ts +0 -225
- package/templates/apps/mcp/__tests__/search_content.test.ts +0 -256
- package/templates/apps/mcp/__tests__/server.test.ts +0 -388
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +0 -448
- package/templates/apps/mcp/__tests__/submit_lead.test.ts +0 -307
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +0 -223
- package/templates/apps/mcp/__tests__/tools/create_project.test.ts +0 -243
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +0 -451
- package/templates/apps/mcp/src/abuse/daily-cap.ts +0 -47
- package/templates/apps/mcp/src/abuse/email-validator.ts +0 -25
- package/templates/apps/mcp/src/abuse/honeypot.ts +0 -21
- package/templates/apps/mcp/src/abuse/index.ts +0 -342
- package/templates/apps/mcp/src/abuse/user-agent-deny.ts +0 -58
- package/templates/apps/mcp/src/boot.ts +0 -167
- package/templates/apps/mcp/src/config/modules.ts +0 -43
- package/templates/apps/mcp/src/http-transport.ts +0 -526
- package/templates/apps/mcp/src/lib/audit-single-page.ts +0 -308
- package/templates/apps/mcp/src/lib/commerce-claim.ts +0 -263
- package/templates/apps/mcp/src/lib/content-adapter.ts +0 -142
- package/templates/apps/mcp/src/lib/empty-content-adapter.ts +0 -139
- package/templates/apps/mcp/src/lib/errors.ts +0 -61
- package/templates/apps/mcp/src/lib/license-key-store.ts +0 -45
- package/templates/apps/mcp/src/lib/locales.ts +0 -171
- package/templates/apps/mcp/src/lib/prisma.ts +0 -39
- package/templates/apps/mcp/src/lib/public-workspace.ts +0 -133
- package/templates/apps/mcp/src/lib/resolve-locale.ts +0 -30
- package/templates/apps/mcp/src/lib/shown-offerings.ts +0 -105
- package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +0 -59
- package/templates/apps/mcp/src/request-context.ts +0 -52
- package/templates/apps/mcp/src/server.ts +0 -343
- package/templates/apps/mcp/src/serverless-app.ts +0 -88
- package/templates/apps/mcp/src/tools/audit.ts +0 -127
- package/templates/apps/mcp/src/tools/capture_lead.ts +0 -64
- package/templates/apps/mcp/src/tools/create_project.ts +0 -134
- package/templates/apps/mcp/src/tools/get_business_hours.ts +0 -68
- package/templates/apps/mcp/src/tools/get_faq.ts +0 -71
- package/templates/apps/mcp/src/tools/get_locations.ts +0 -88
- package/templates/apps/mcp/src/tools/get_offerings.ts +0 -96
- package/templates/apps/mcp/src/tools/get_pricing.ts +0 -94
- package/templates/apps/mcp/src/tools/get_site_info.ts +0 -86
- package/templates/apps/mcp/src/tools/index.ts +0 -137
- package/templates/apps/mcp/src/tools/list_pages.ts +0 -74
- package/templates/apps/mcp/src/tools/person_notes.ts +0 -25
- package/templates/apps/mcp/src/tools/proposals.ts +0 -27
- package/templates/apps/mcp/src/tools/purchase.ts +0 -205
- package/templates/apps/mcp/src/tools/read_content.ts +0 -75
- package/templates/apps/mcp/src/tools/request_callback.ts +0 -59
- package/templates/apps/mcp/src/tools/search_content.ts +0 -67
- package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +0 -57
- package/templates/apps/mcp/src/workspace-server.ts +0 -224
- package/templates/database/CHANGELOG.md +0 -961
- package/templates/database/LICENSE +0 -202
- package/templates/database/NOTICE +0 -2
- package/templates/database/README.md +0 -243
- package/templates/database/__tests__/agents.db.test.ts +0 -320
- package/templates/database/__tests__/appointment.db.test.ts +0 -106
- package/templates/database/__tests__/artifact-content-store.db.test.ts +0 -316
- package/templates/database/__tests__/composite-fence.db.test.ts +0 -206
- package/templates/database/__tests__/consent-ledger.db.test.ts +0 -345
- package/templates/database/__tests__/consent-source-checkout-migration.test.ts +0 -79
- package/templates/database/__tests__/fence-fan-out.db.test.ts +0 -715
- package/templates/database/__tests__/fixtures/appointment-create-probe.ts +0 -79
- package/templates/database/__tests__/fixtures/person-backfill.json +0 -264
- package/templates/database/__tests__/fixtures/person-contact-backfill.json +0 -182
- package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +0 -100
- package/templates/database/__tests__/flow-completion-migration.test.ts +0 -115
- package/templates/database/__tests__/flow-completion.db.test.ts +0 -136
- package/templates/database/__tests__/flow-workspace-key.db.test.ts +0 -263
- package/templates/database/__tests__/flows-listing-diagnostic.test.ts +0 -392
- package/templates/database/__tests__/flows-presets.test.ts +0 -173
- package/templates/database/__tests__/in-page-magnets-migration.test.ts +0 -161
- package/templates/database/__tests__/lead-idempotency-key.db.test.ts +0 -92
- package/templates/database/__tests__/member-area.db.test.ts +0 -257
- package/templates/database/__tests__/member-contract-migration.test.ts +0 -85
- package/templates/database/__tests__/member-contract.db.test.ts +0 -413
- package/templates/database/__tests__/offerings-checkout-session.db.test.ts +0 -270
- package/templates/database/__tests__/offerings-migration.test.ts +0 -197
- package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +0 -86
- package/templates/database/__tests__/offerings.db.test.ts +0 -303
- package/templates/database/__tests__/outbound-message-action.db.test.ts +0 -80
- package/templates/database/__tests__/person-contact.db.test.ts +0 -563
- package/templates/database/__tests__/person-notes.db.test.ts +0 -249
- package/templates/database/__tests__/person.db.test.ts +0 -586
- package/templates/database/__tests__/piece-kind-migration.test.ts +0 -67
- package/templates/database/__tests__/piece-kind.db.test.ts +0 -208
- package/templates/database/__tests__/prefixed-ids.db.test.ts +0 -222
- package/templates/database/__tests__/project-listing-consent-at.db.test.ts +0 -111
- package/templates/database/__tests__/seed.test.ts +0 -240
- package/templates/database/__tests__/spoke-registry.db.test.ts +0 -191
- package/templates/database/__tests__/tenancy.test.ts +0 -141
- package/templates/database/__tests__/user-cascade.test.ts +0 -140
- package/templates/database/_gitignore +0 -8
- package/templates/database/auth/.gitkeep +0 -0
- package/templates/database/auth/schema.prisma +0 -641
- package/templates/database/config/.gitkeep +0 -0
- package/templates/database/config/schema.prisma +0 -441
- package/templates/database/content/.gitkeep +0 -0
- package/templates/database/content/schema.prisma +0 -339
- package/templates/database/eslint.config.mjs +0 -69
- package/templates/database/flows/listing-diagnostic.ts +0 -182
- package/templates/database/flows/presets.ts +0 -315
- package/templates/database/inbox/.gitkeep +0 -0
- package/templates/database/inbox/README.md +0 -3
- package/templates/database/inbox/__tests__/schema.test.ts +0 -44
- package/templates/database/inbox/schema.prisma +0 -1447
- package/templates/database/jest.config.cjs +0 -36
- package/templates/database/migrations/20260508000000_add_inbox_namespace/migration.sql +0 -328
- package/templates/database/migrations/20260513000000_better_auth_migration/migration.sql +0 -100
- package/templates/database/migrations/20260515000000_add_user_billing_fields/migration.sql +0 -28
- package/templates/database/migrations/20260516000000_add_user_tier_care_plan_seat/migration.sql +0 -25
- package/templates/database/migrations/20260518000000_add_telemetry_records/migration.sql +0 -42
- package/templates/database/migrations/20260525000000_theme_wiring/migration.sql +0 -67
- package/templates/database/migrations/20260710000000_retire_commercial_tier/migration.sql +0 -21
- package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +0 -201
- package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +0 -67
- package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +0 -46
- package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +0 -25
- package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +0 -49
- package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +0 -197
- package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +0 -32
- package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +0 -22
- package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +0 -151
- package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +0 -223
- package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +0 -22
- package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +0 -32
- package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +0 -40
- package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +0 -4
- package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +0 -2
- package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +0 -2
- package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +0 -29
- package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +0 -6
- package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +0 -39
- package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +0 -24
- package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +0 -3
- package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +0 -33
- package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +0 -30
- package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +0 -47
- package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +0 -16
- package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +0 -47
- package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +0 -10
- package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +0 -26
- package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +0 -90
- package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +0 -174
- package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +0 -14
- package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +0 -25
- package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +0 -32
- package/templates/database/migrations/20260911180000_consent_grants/migration.sql +0 -71
- package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +0 -30
- package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +0 -49
- package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +0 -123
- package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +0 -15
- package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +0 -158
- package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +0 -40
- package/templates/database/migrations/20260918120000_add_appointment/migration.sql +0 -67
- package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +0 -70
- package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +0 -181
- package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +0 -172
- package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +0 -41
- package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +0 -185
- package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +0 -70
- package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +0 -57
- package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +0 -68
- package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +0 -1067
- package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +0 -210
- package/templates/database/migrations/20261003120000_spoke_registry/migration.sql +0 -80
- package/templates/database/migrations/20261003130000_scheduling_connection/migration.sql +0 -49
- package/templates/database/migrations/20261005120000_agents/migration.sql +0 -84
- package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +0 -35
- package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +0 -20
- package/templates/database/migrations/20261009120000_agent_grants/migration.sql +0 -33
- package/templates/database/migrations/20261010120000_person/migration.sql +0 -106
- package/templates/database/migrations/20261010121000_person_backfill/migration.sql +0 -132
- package/templates/database/migrations/20261010130000_agent_grants_people_read/migration.sql +0 -20
- package/templates/database/migrations/20261011120000_member_area/migration.sql +0 -158
- package/templates/database/migrations/20261011130000_agent_grants_member_pieces_manage/migration.sql +0 -21
- package/templates/database/migrations/20261012120000_member_contract/migration.sql +0 -192
- package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +0 -20
- package/templates/database/migrations/20261014120000_consent_ledger/migration.sql +0 -122
- package/templates/database/migrations/20261014120000_person_contact/migration.sql +0 -231
- package/templates/database/migrations/20261015120000_person_notes/migration.sql +0 -77
- package/templates/database/migrations/20261015120000_rate_limit_counters/migration.sql +0 -43
- package/templates/database/migrations/20261016120000_piece_kind/migration.sql +0 -95
- package/templates/database/migrations/20261017120000_outbound_message_action/migration.sql +0 -30
- package/templates/database/migrations/20261018120000_flow_completion/migration.sql +0 -52
- package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +0 -27
- package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +0 -146
- package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +0 -77
- package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +0 -45
- package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +0 -47
- package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +0 -16
- package/templates/database/migrations/migration_lock.toml +0 -3
- package/templates/database/ops/.gitkeep +0 -0
- package/templates/database/ops/schema.prisma +0 -43
- package/templates/database/package.json +0 -89
- package/templates/database/schema.prisma +0 -71
- package/templates/database/scripts/db-generate-locked.sh +0 -80
- package/templates/database/scripts/person-backfill.sql +0 -132
- package/templates/database/tenancy.ts +0 -183
- package/templates/database/tsconfig.build.json +0 -16
- package/templates/database/tsconfig.flows.json +0 -15
- package/templates/database/tsconfig.json +0 -16
- package/templates/database/tsconfig.tenancy.json +0 -14
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Paid-module presence — the registration-grain signal used by `server.ts`
|
|
3
|
-
* to decide whether a paid module's tools are wired into this build
|
|
4
|
-
* (S1, #5088; defaulted off by D-44, #5368). Mirrors
|
|
5
|
-
* `apps/api/src/config/modules.ts` — apps/* may not import apps/*, so this
|
|
6
|
-
* tiny reader is duplicated rather than shared.
|
|
7
|
-
*
|
|
8
|
-
* `AILK_ENABLED_MODULES` is a comma-separated allowlist (e.g.
|
|
9
|
-
* `"multi-tenant,scheduling"`). Unset means **no paid modules** — installed
|
|
10
|
-
* modules enable themselves explicitly; an explicit empty string (`""`)
|
|
11
|
-
* means the same (the OSS-strip build target); a list enables exactly the
|
|
12
|
-
* modules it names.
|
|
13
|
-
*
|
|
14
|
-
* Recognized module keys: `docs`, `multi-tenant`, `scheduling`,
|
|
15
|
-
* `signal-collectors` (the `ModuleName` vocabulary
|
|
16
|
-
* `packages/create-ailk/src/module-architecture.ts` already models via
|
|
17
|
-
* `MODULE_SLICES`) plus `content-write` — the Pro content-write surface,
|
|
18
|
-
* which owns NO MCP tool since #5853: `create_page`/`update_page`/`delete_page`
|
|
19
|
-
* were retired by deletion along with their untenanted HTTP twins, and their
|
|
20
|
-
* replacement is identity-bound, which the identity-blind MCP surface cannot
|
|
21
|
-
* carry (D7, D15; the authed MCP surface is #3591). The flag is read here only
|
|
22
|
-
* because this reader is shared with `apps/api`, where it still gates the
|
|
23
|
-
* tenant-guarded editor routes. `content-write` is intentionally NOT a
|
|
24
|
-
* `MODULE_SLICES` entry: it is not (yet) a create-ailk `--modules` selector,
|
|
25
|
-
* so it stays a local flag here rather than widening the create-ailk module
|
|
26
|
-
* vocabulary.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
function enabledModules(): Set<string> {
|
|
30
|
-
const raw = process.env.AILK_ENABLED_MODULES;
|
|
31
|
-
if (raw === undefined) return new Set(); // unset = no paid modules
|
|
32
|
-
return new Set(
|
|
33
|
-
raw
|
|
34
|
-
.split(",")
|
|
35
|
-
.map((m) => m.trim())
|
|
36
|
-
.filter(Boolean),
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Whether the named paid module's tools should register on this build/boot. */
|
|
41
|
-
export function isModuleEnabled(moduleName: string): boolean {
|
|
42
|
-
return enabledModules().has(moduleName);
|
|
43
|
-
}
|
|
@@ -1,526 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP HTTP transport for @working-theory/mcp.
|
|
3
|
-
*
|
|
4
|
-
* Wraps the MCP SDK's StreamableHTTPServerTransport in a Fastify server with:
|
|
5
|
-
* - @fastify/cors — open for reads, allowlist-by-config for writes
|
|
6
|
-
* - @fastify/rate-limit — 60/min reads, 10/min writes (env-overridable)
|
|
7
|
-
* - abuse heuristics — UA deny list, honeypot, email validation, daily cap
|
|
8
|
-
*
|
|
9
|
-
* Decision references:
|
|
10
|
-
* Decision 1 — same binary, env-var transport selection (apps/mcp/src/index.ts;
|
|
11
|
-
* the Vercel entry, apps/mcp/src/app.ts, serves HTTP only — #8070)
|
|
12
|
-
* Decision 2 — configurable path, default /mcp, standalone root-mounted
|
|
13
|
-
* Decision 3 — @fastify/rate-limit per-IP token bucket
|
|
14
|
-
* Decision 4 — in-process abuse heuristics for write tools
|
|
15
|
-
* Decision 5 — @fastify/cors open reads, allowlist writes
|
|
16
|
-
*
|
|
17
|
-
* Two modes, chosen by `config.stateless` (#8070 D1; the entries read it from
|
|
18
|
-
* `MCP_HTTP_STATELESS` through `readStatelessEnv`, src/boot.ts):
|
|
19
|
-
* - stateful (the long-running entry's default): an `initialize` creates a
|
|
20
|
-
* StreamableHTTPServerTransport + McpServer pair kept in an in-memory map
|
|
21
|
-
* keyed by its Mcp-Session-Id. Sessions live in ONE process's memory and
|
|
22
|
-
* are not persisted across restarts, so this mode needs a single instance.
|
|
23
|
-
* - stateless (the Vercel entry's default): every POST gets a fresh
|
|
24
|
-
* transport + server and no session ID; GET and DELETE answer 400. Any
|
|
25
|
-
* instance can answer any request.
|
|
26
|
-
*
|
|
27
|
-
* The anonymous `config.path` never reads `Authorization`. Beside it,
|
|
28
|
-
* `${config.path}/workspace` serves the signed-in workspace tools (#3591 D15):
|
|
29
|
-
* POST only and always stateless, each request carrying an agent key that is
|
|
30
|
-
* handed to that request's tools and nothing else — see
|
|
31
|
-
* {@link registerWorkspaceRoute}.
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
import { randomUUID } from "node:crypto";
|
|
35
|
-
import type { IncomingMessage } from "node:http";
|
|
36
|
-
|
|
37
|
-
import cors from "@fastify/cors";
|
|
38
|
-
import rateLimit from "@fastify/rate-limit";
|
|
39
|
-
import type { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js";
|
|
40
|
-
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
41
|
-
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
42
|
-
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
43
|
-
import Fastify, {
|
|
44
|
-
type FastifyInstance,
|
|
45
|
-
type FastifyReply,
|
|
46
|
-
type FastifyRequest,
|
|
47
|
-
} from "fastify";
|
|
48
|
-
|
|
49
|
-
import {
|
|
50
|
-
AbuseGuard,
|
|
51
|
-
AbuseReasonCode,
|
|
52
|
-
type AbuseConfig,
|
|
53
|
-
} from "./abuse/index.js";
|
|
54
|
-
import { trustedCallerAddress } from "./request-context.js";
|
|
55
|
-
import { createMcpServer } from "./server.js";
|
|
56
|
-
import { createWorkspaceMcpServer } from "./workspace-server.js";
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* HTTP status for each {@link AbuseReasonCode} an AbuseGuard refusal can
|
|
60
|
-
* carry (#5994).
|
|
61
|
-
*
|
|
62
|
-
* A refusal of the CALLER — a denied User-Agent, an IP that has spent its
|
|
63
|
-
* daily budget — is **403**: the input was fine, the caller is not allowed to
|
|
64
|
-
* make the call right now. A refusal of the INPUT — a filled honeypot, a
|
|
65
|
-
* malformed email — is **422**.
|
|
66
|
-
*
|
|
67
|
-
* Keying on the code rather than on the refusal prose is what this table is
|
|
68
|
-
* for: the split used to be `reason.includes("daily cap")`, so a reworded
|
|
69
|
-
* reason silently demoted a rate-limit refusal to 422 — and `capture_lead`'s
|
|
70
|
-
* wording only matched by accident, because the tool's name begins with
|
|
71
|
-
* "cap". Typing the record over the full union also means a new reason code
|
|
72
|
-
* fails to compile here until it declares its status.
|
|
73
|
-
*/
|
|
74
|
-
export const ABUSE_REFUSAL_STATUS: Record<AbuseReasonCode, 403 | 422> = {
|
|
75
|
-
[AbuseReasonCode.BLOCKED_USER_AGENT]: 403,
|
|
76
|
-
[AbuseReasonCode.DAILY_CAP_REACHED]: 403,
|
|
77
|
-
[AbuseReasonCode.HONEYPOT_FILLED]: 422,
|
|
78
|
-
[AbuseReasonCode.INVALID_EMAIL]: 422,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* MCP tool names that are write operations (subject to stricter CORS + rate
|
|
83
|
-
* limits, and to the AbuseGuard).
|
|
84
|
-
*
|
|
85
|
-
* `start_purchase` joined in #5971: it has a side effect at Stripe and it is
|
|
86
|
-
* worth money to abuse, so it takes the write branch — the origin allow-list,
|
|
87
|
-
* the write CORS posture, and `abuseGuard.validate()` — rather than the read
|
|
88
|
-
* branch's UA check alone.
|
|
89
|
-
*
|
|
90
|
-
* `create_booking` and `submit_flow_step` joined in #8071: each forwards
|
|
91
|
-
* caller-chosen data to the hub — a real booking on the seller's calendar
|
|
92
|
-
* that emails the attendee an invite, and a flow step whose completing call
|
|
93
|
-
* can create a Lead — so they take the same branch, each on its own daily
|
|
94
|
-
* cap. `__tests__/write-tool-guard.test.ts` fails when a catalog tool that
|
|
95
|
-
* sends a non-GET request to the hub is missing from this set.
|
|
96
|
-
*/
|
|
97
|
-
// Any name added here MUST be enforced through the batch-aware path in
|
|
98
|
-
// writeToolCalls() below — a request body can be a JSON-RPC ARRAY, and a
|
|
99
|
-
// per-request (rather than per-message) check lets N batched calls through on
|
|
100
|
-
// one cap unit. See the #5971 security review and the batch regression tests
|
|
101
|
-
// in __tests__/http-transport.test.ts.
|
|
102
|
-
export const WRITE_TOOLS = new Set([
|
|
103
|
-
"capture_lead",
|
|
104
|
-
"subscribe_newsletter",
|
|
105
|
-
"request_callback",
|
|
106
|
-
"start_purchase",
|
|
107
|
-
"create_booking",
|
|
108
|
-
"submit_flow_step",
|
|
109
|
-
]);
|
|
110
|
-
|
|
111
|
-
export interface HttpTransportCorsConfig {
|
|
112
|
-
/**
|
|
113
|
-
* Origins allowed to call write tools.
|
|
114
|
-
* Empty array = no cross-origin writes (most restrictive; default).
|
|
115
|
-
* ["*"] = allow all origins for writes (use only in dev/testing).
|
|
116
|
-
*/
|
|
117
|
-
writeAllowedOrigins: string[];
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export interface HttpTransportRateLimits {
|
|
121
|
-
/** Max requests per minute per IP for read tools. Default 60. */
|
|
122
|
-
readPerMin: number;
|
|
123
|
-
/** Max requests per minute per IP for write tools. Default 10. */
|
|
124
|
-
writePerMin: number;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export interface HttpTransportConfig {
|
|
128
|
-
/** URL path to mount the MCP endpoint on. Default "/mcp". */
|
|
129
|
-
path?: string;
|
|
130
|
-
/** Rate limit configuration. */
|
|
131
|
-
rateLimits: HttpTransportRateLimits;
|
|
132
|
-
/** CORS configuration. */
|
|
133
|
-
cors: HttpTransportCorsConfig;
|
|
134
|
-
/** Abuse protection configuration. */
|
|
135
|
-
abuse: AbuseConfig;
|
|
136
|
-
/**
|
|
137
|
-
* When true, use stateless transport mode: no session IDs, every request
|
|
138
|
-
* creates a fresh McpServer + transport. Needed on a host that runs several
|
|
139
|
-
* instances (the Vercel entry), and used by tests.
|
|
140
|
-
* Default: false (stateful). The entries set it from `MCP_HTTP_STATELESS`
|
|
141
|
-
* (`readStatelessEnv`, src/boot.ts — #8070 D1).
|
|
142
|
-
*/
|
|
143
|
-
stateless?: boolean;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** One write-tool invocation found in a request body. */
|
|
147
|
-
interface WriteToolCall {
|
|
148
|
-
toolName: string;
|
|
149
|
-
args: unknown;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Every write-tool invocation carried by a request body, in order.
|
|
154
|
-
*
|
|
155
|
-
* JSON-RPC 2.0 allows a BATCH: an array of messages in one POST. The MCP SDK
|
|
156
|
-
* honours it — `webStandardStreamableHttp.js` maps an array body to N parsed
|
|
157
|
-
* messages and dispatches every one. So a body must be normalised to a LIST of
|
|
158
|
-
* messages before any of them is classified; inspecting `body.method` on what
|
|
159
|
-
* may be an array silently answers "not a write" for the whole batch, and the
|
|
160
|
-
* request then takes the read branch: no write-origin check, and no
|
|
161
|
-
* `AbuseGuard` cap unit consumed by ANY of the N calls it goes on to execute.
|
|
162
|
-
*
|
|
163
|
-
* That is a real bypass of the per-IP daily cap, and for `start_purchase` each
|
|
164
|
-
* bypassed call creates a genuine Stripe Checkout Session on the seller's
|
|
165
|
-
* account. It predates `start_purchase` — `capture_lead`,
|
|
166
|
-
* `subscribe_newsletter` and `request_callback` have carried it since this
|
|
167
|
-
* transport was written — and normalising here closes it for all four at once
|
|
168
|
-
* (#5971 security review, High).
|
|
169
|
-
*
|
|
170
|
-
* Returns one entry PER matching message, so the caller can charge the guard
|
|
171
|
-
* once per call rather than once per request.
|
|
172
|
-
*/
|
|
173
|
-
function writeToolCalls(body: unknown): WriteToolCall[] {
|
|
174
|
-
const messages = Array.isArray(body) ? body : [body];
|
|
175
|
-
const calls: WriteToolCall[] = [];
|
|
176
|
-
|
|
177
|
-
for (const message of messages) {
|
|
178
|
-
if (!message || typeof message !== "object") continue;
|
|
179
|
-
const req = message as Record<string, unknown>;
|
|
180
|
-
if (req["method"] !== "tools/call") continue;
|
|
181
|
-
const params = req["params"] as Record<string, unknown> | undefined;
|
|
182
|
-
if (!params) continue;
|
|
183
|
-
const toolName = params["name"];
|
|
184
|
-
if (typeof toolName !== "string" || !WRITE_TOOLS.has(toolName)) continue;
|
|
185
|
-
calls.push({ toolName, args: params["arguments"] ?? {} });
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return calls;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Determine whether an Origin header is in the write allowlist.
|
|
193
|
-
* ["*"] matches any origin. Empty list blocks all cross-origin writes.
|
|
194
|
-
*/
|
|
195
|
-
function isOriginAllowedForWrite(
|
|
196
|
-
origin: string | undefined,
|
|
197
|
-
allowedOrigins: string[],
|
|
198
|
-
): boolean {
|
|
199
|
-
if (!origin) return true; // Same-origin or non-browser clients have no Origin header
|
|
200
|
-
if (allowedOrigins.includes("*")) return true;
|
|
201
|
-
return allowedOrigins.includes(origin);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* An agent key's shape (#3591 D2): `ailk_agt_` plus 32 random bytes in
|
|
206
|
-
* base64url. apps/mcp checks only this; apps/api is where the key is checked.
|
|
207
|
-
*/
|
|
208
|
-
const AGENT_BEARER = /^Bearer (ailk_agt_[A-Za-z0-9_-]{43})$/;
|
|
209
|
-
|
|
210
|
-
/** The agent key an `Authorization` header carries, or null. */
|
|
211
|
-
function agentKeyOf(header: string | string[] | undefined): string | null {
|
|
212
|
-
if (typeof header !== "string") return null;
|
|
213
|
-
return AGENT_BEARER.exec(header)?.[1] ?? null;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* The signed-in workspace surface (#3591 D15) at `path`: POST only, always
|
|
218
|
-
* stateless — a fresh transport and `createWorkspaceMcpServer()` per request,
|
|
219
|
-
* closed when the response ends, and no session map, so GET and DELETE are
|
|
220
|
-
* 405. In order, a POST must:
|
|
221
|
-
*
|
|
222
|
-
* 1. carry `Authorization: Bearer ailk_agt_<43 base64url>` — else 401 in
|
|
223
|
-
* `onRequest`, before the body is read and before any tool runs. A
|
|
224
|
-
* personal token (`tok_…`) is refused here like any other shape;
|
|
225
|
-
* 2. be one JSON-RPC message — a batch (array) body is 400;
|
|
226
|
-
* 3. pass the abuse guard's User-Agent check.
|
|
227
|
-
*
|
|
228
|
-
* Rate limiting is apps/api's per-agent limiter, which every forwarded call
|
|
229
|
-
* meets after the key is verified (D4); the app-wide per-IP limit still
|
|
230
|
-
* applies here. The key is attached as the SDK's `req.auth` for this request
|
|
231
|
-
* only, which the SDK hands each tool as `extra.authInfo`.
|
|
232
|
-
*/
|
|
233
|
-
function registerWorkspaceRoute(
|
|
234
|
-
app: FastifyInstance,
|
|
235
|
-
path: string,
|
|
236
|
-
abuseGuard: AbuseGuard,
|
|
237
|
-
): void {
|
|
238
|
-
const notAllowed = async (_request: FastifyRequest, reply: FastifyReply) =>
|
|
239
|
-
reply
|
|
240
|
-
.code(405)
|
|
241
|
-
.header("allow", "POST")
|
|
242
|
-
.send({ error: "method_not_allowed" });
|
|
243
|
-
app.get(path, notAllowed);
|
|
244
|
-
app.delete(path, notAllowed);
|
|
245
|
-
|
|
246
|
-
app.post(path, {
|
|
247
|
-
onRequest: async (request, reply) => {
|
|
248
|
-
if (agentKeyOf(request.headers["authorization"]) === null) {
|
|
249
|
-
return reply.code(401).send({ error: "unauthorized" });
|
|
250
|
-
}
|
|
251
|
-
},
|
|
252
|
-
preHandler: async (request, reply) => {
|
|
253
|
-
if (Array.isArray(request.body)) {
|
|
254
|
-
return reply.code(400).send({ error: "batch_not_supported" });
|
|
255
|
-
}
|
|
256
|
-
const ua = abuseGuard.validate("__read__", {}, request);
|
|
257
|
-
if (!ua.allowed) {
|
|
258
|
-
return reply.code(403).send({ error: ua.reason });
|
|
259
|
-
}
|
|
260
|
-
},
|
|
261
|
-
handler: async (request, reply) => {
|
|
262
|
-
const raw = request.raw as IncomingMessage & { auth?: AuthInfo };
|
|
263
|
-
raw.auth = {
|
|
264
|
-
token: agentKeyOf(request.headers["authorization"])!,
|
|
265
|
-
clientId: "ailk-agent",
|
|
266
|
-
scopes: [],
|
|
267
|
-
};
|
|
268
|
-
const transport = new StreamableHTTPServerTransport({
|
|
269
|
-
enableJsonResponse: true,
|
|
270
|
-
});
|
|
271
|
-
const server = createWorkspaceMcpServer();
|
|
272
|
-
try {
|
|
273
|
-
// The cast is the same exactOptionalPropertyTypes one the anonymous
|
|
274
|
-
// route carries.
|
|
275
|
-
await server.connect(transport as unknown as Transport);
|
|
276
|
-
await transport.handleRequest(raw, reply.raw, request.body);
|
|
277
|
-
} catch (err) {
|
|
278
|
-
if (!reply.sent) {
|
|
279
|
-
reply.code(500).send({
|
|
280
|
-
jsonrpc: "2.0",
|
|
281
|
-
error: { code: -32603, message: "Internal server error" },
|
|
282
|
-
id: null,
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
throw err;
|
|
286
|
-
} finally {
|
|
287
|
-
await transport.close().catch(() => undefined);
|
|
288
|
-
await server.close().catch(() => undefined);
|
|
289
|
-
}
|
|
290
|
-
},
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Register the MCP HTTP transport at `config.path` on an existing Fastify
|
|
296
|
-
* instance: CORS, the per-IP rate limit, the abuse guard, the anonymous MCP
|
|
297
|
-
* route and the signed-in workspace route (#8070 D2). The caller owns the
|
|
298
|
-
* instance — and so its options, such as the Vercel entry's `trustProxy`
|
|
299
|
-
* (src/serverless-app.ts).
|
|
300
|
-
*
|
|
301
|
-
* @param app - The Fastify instance to register on (not yet listening)
|
|
302
|
-
* @param config - HTTP transport configuration
|
|
303
|
-
*/
|
|
304
|
-
export async function registerHttpTransport(
|
|
305
|
-
app: FastifyInstance,
|
|
306
|
-
config: HttpTransportConfig,
|
|
307
|
-
): Promise<void> {
|
|
308
|
-
const mcpPath = config.path ?? "/mcp";
|
|
309
|
-
const abuseGuard = new AbuseGuard(config.abuse);
|
|
310
|
-
|
|
311
|
-
// Active transports keyed by session ID (stateful mode only).
|
|
312
|
-
const transports = new Map<string, StreamableHTTPServerTransport>();
|
|
313
|
-
|
|
314
|
-
// ── CORS ──────────────────────────────────────────────────────────────────
|
|
315
|
-
// Read tools: open (origin: true — reflect the caller's Origin).
|
|
316
|
-
// Write tools: enforced per-request in the preHandler below.
|
|
317
|
-
// We register a permissive global CORS so browser preflight succeeds for
|
|
318
|
-
// reads; write-tool CORS is enforced by the preHandler, not this plugin.
|
|
319
|
-
await app.register(cors, {
|
|
320
|
-
origin: true,
|
|
321
|
-
methods: ["GET", "POST", "DELETE", "OPTIONS"],
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
// ── Rate limiting ─────────────────────────────────────────────────────────
|
|
325
|
-
// Global ceiling — the lower write limit is enforced per-request via the
|
|
326
|
-
// preHandler by checking the tool name after body parse.
|
|
327
|
-
await app.register(rateLimit, {
|
|
328
|
-
max: config.rateLimits.readPerMin,
|
|
329
|
-
timeWindow: "1 minute",
|
|
330
|
-
keyGenerator: (request: FastifyRequest) => request.ip,
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// ── MCP route ─────────────────────────────────────────────────────────────
|
|
334
|
-
|
|
335
|
-
// GET — SSE stream for an existing session
|
|
336
|
-
app.get(mcpPath, async (request, reply) => {
|
|
337
|
-
const sessionId = request.headers["mcp-session-id"] as string | undefined;
|
|
338
|
-
if (!sessionId || !transports.has(sessionId)) {
|
|
339
|
-
reply.code(400).send("Invalid or missing session ID");
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
const transport = transports.get(sessionId)!;
|
|
343
|
-
await transport.handleRequest(request.raw, reply.raw);
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
// POST — new session initialize or existing session message
|
|
347
|
-
app.post(mcpPath, {
|
|
348
|
-
preHandler: async (request, reply) => {
|
|
349
|
-
const body = request.body as unknown;
|
|
350
|
-
const origin = request.headers["origin"] as string | undefined;
|
|
351
|
-
|
|
352
|
-
// A batch body counts as a write request when ANY of its messages names
|
|
353
|
-
// a write tool — see writeToolCalls().
|
|
354
|
-
const writeCalls = writeToolCalls(body);
|
|
355
|
-
|
|
356
|
-
// Write-tool CORS enforcement
|
|
357
|
-
if (writeCalls.length > 0) {
|
|
358
|
-
if (!isOriginAllowedForWrite(origin, config.cors.writeAllowedOrigins)) {
|
|
359
|
-
reply
|
|
360
|
-
.code(403)
|
|
361
|
-
.send({ error: "CORS: cross-origin write not allowed" });
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Write-tool rate limit (stricter than read ceiling)
|
|
366
|
-
// We re-use the same in-memory store but apply a tighter key.
|
|
367
|
-
// @fastify/rate-limit tracks by keyGenerator; we check manually here.
|
|
368
|
-
// Simple approach: use a second rate-limit store baked into the guard.
|
|
369
|
-
// For now, we rely on the global ceiling for writes (configurable).
|
|
370
|
-
// TODO: per-tool write rate limit when @fastify/rate-limit v11 lands
|
|
371
|
-
// a per-route override API compatible with our stateless route design.
|
|
372
|
-
|
|
373
|
-
// Abuse heuristics — charged ONCE PER CALL, not once per request, so a
|
|
374
|
-
// batch of N cannot spend one cap unit and execute N times. The whole
|
|
375
|
-
// request is refused on the first message that fails, before the SDK
|
|
376
|
-
// dispatches any of them.
|
|
377
|
-
for (const { toolName, args } of writeCalls) {
|
|
378
|
-
const abuseResult = abuseGuard.validate(toolName, args, request);
|
|
379
|
-
if (!abuseResult.allowed) {
|
|
380
|
-
reply
|
|
381
|
-
.code(ABUSE_REFUSAL_STATUS[abuseResult.code])
|
|
382
|
-
.send({ error: abuseResult.reason });
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
} else {
|
|
387
|
-
// Read-tool or protocol message: check UA deny list only
|
|
388
|
-
const abuseGuardResult = abuseGuard.validate("__read__", {}, request);
|
|
389
|
-
if (!abuseGuardResult.allowed) {
|
|
390
|
-
reply.code(403).send({ error: abuseGuardResult.reason });
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
handler: async (request, reply) =>
|
|
396
|
-
// The lead-write tools read this address (#8073) through
|
|
397
|
-
// `visitorForwardHeadersForThisCall()` while the SDK dispatches their
|
|
398
|
-
// call, somewhere inside `transport.handleRequest()` below — the whole
|
|
399
|
-
// handler body runs inside the store so every path reaches it.
|
|
400
|
-
trustedCallerAddress.run(request.ip, async () => {
|
|
401
|
-
const body = request.body as unknown;
|
|
402
|
-
const sessionId = request.headers["mcp-session-id"] as
|
|
403
|
-
| string
|
|
404
|
-
| undefined;
|
|
405
|
-
|
|
406
|
-
try {
|
|
407
|
-
let transport: StreamableHTTPServerTransport;
|
|
408
|
-
|
|
409
|
-
if (config.stateless) {
|
|
410
|
-
// Stateless mode: each request gets a fresh transport + server.
|
|
411
|
-
// enableJsonResponse: true ensures plain JSON responses (no SSE).
|
|
412
|
-
// sessionIdGenerator is intentionally omitted (stateless = no sessions).
|
|
413
|
-
transport = new StreamableHTTPServerTransport({
|
|
414
|
-
enableJsonResponse: true,
|
|
415
|
-
});
|
|
416
|
-
const server = createMcpServer();
|
|
417
|
-
// Cast needed: SDK's StreamableHTTPServerTransport.onclose is typed
|
|
418
|
-
// as `(() => void) | undefined`, which conflicts with Transport.onclose
|
|
419
|
-
// under exactOptionalPropertyTypes: true. The runtime behaviour is
|
|
420
|
-
// identical; the cast is safe.
|
|
421
|
-
await server.connect(transport as unknown as Transport);
|
|
422
|
-
try {
|
|
423
|
-
await transport.handleRequest(request.raw, reply.raw, body);
|
|
424
|
-
} finally {
|
|
425
|
-
// Close the per-request transport + server to release internal
|
|
426
|
-
// handles. Without this, the MCP SDK's transport leaves open
|
|
427
|
-
// sockets that surface as `socket.destroySoon is not a function`
|
|
428
|
-
// during Jest teardown on Node 20.x (@hono/node-server compat).
|
|
429
|
-
await transport.close().catch(() => undefined);
|
|
430
|
-
await server.close().catch(() => undefined);
|
|
431
|
-
}
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (sessionId && transports.has(sessionId)) {
|
|
436
|
-
// Existing session — reuse transport
|
|
437
|
-
transport = transports.get(sessionId)!;
|
|
438
|
-
} else if (!sessionId && isInitializeRequest(body)) {
|
|
439
|
-
// New initialization — create a stateful transport + fresh server
|
|
440
|
-
transport = new StreamableHTTPServerTransport({
|
|
441
|
-
sessionIdGenerator: () => randomUUID(),
|
|
442
|
-
onsessioninitialized: (sid) => {
|
|
443
|
-
transports.set(sid, transport);
|
|
444
|
-
},
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
transport.onclose = () => {
|
|
448
|
-
const sid = transport.sessionId;
|
|
449
|
-
if (sid) transports.delete(sid);
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
const server = createMcpServer();
|
|
453
|
-
// Cast needed: SDK's StreamableHTTPServerTransport.onclose is typed
|
|
454
|
-
// as `(() => void) | undefined`, which conflicts with Transport.onclose
|
|
455
|
-
// under exactOptionalPropertyTypes: true. The runtime behaviour is
|
|
456
|
-
// identical; the cast is safe.
|
|
457
|
-
await server.connect(transport as unknown as Transport);
|
|
458
|
-
} else {
|
|
459
|
-
reply.code(400).send({
|
|
460
|
-
jsonrpc: "2.0",
|
|
461
|
-
error: {
|
|
462
|
-
code: -32000,
|
|
463
|
-
message: "Bad Request: no valid session",
|
|
464
|
-
},
|
|
465
|
-
id: null,
|
|
466
|
-
});
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
await transport.handleRequest(request.raw, reply.raw, body);
|
|
471
|
-
} catch (err) {
|
|
472
|
-
if (!reply.sent) {
|
|
473
|
-
reply.code(500).send({
|
|
474
|
-
jsonrpc: "2.0",
|
|
475
|
-
error: { code: -32603, message: "Internal server error" },
|
|
476
|
-
id: null,
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
throw err;
|
|
480
|
-
}
|
|
481
|
-
}),
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
// DELETE — session termination
|
|
485
|
-
app.delete(mcpPath, async (request, reply) => {
|
|
486
|
-
const sessionId = request.headers["mcp-session-id"] as string | undefined;
|
|
487
|
-
if (!sessionId || !transports.has(sessionId)) {
|
|
488
|
-
reply.code(400).send("Invalid or missing session ID");
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
const transport = transports.get(sessionId)!;
|
|
492
|
-
await transport.handleRequest(request.raw, reply.raw);
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
// ── Signed-in workspace route (#3591 D15) ─────────────────────────────────
|
|
496
|
-
registerWorkspaceRoute(app, `${mcpPath}/workspace`, abuseGuard);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* Build and return a configured Fastify instance that exposes the MCP HTTP
|
|
501
|
-
* transport at `config.path`. The instance is not yet listening — callers
|
|
502
|
-
* call `.listen()` or `.inject()` (tests). Sets no `trustProxy`, so
|
|
503
|
-
* `request.ip` is the socket's address.
|
|
504
|
-
*
|
|
505
|
-
* @param config - HTTP transport configuration
|
|
506
|
-
* @returns Configured Fastify instance (ready to listen or inject)
|
|
507
|
-
*/
|
|
508
|
-
export async function buildHttpTransportApp(
|
|
509
|
-
config: HttpTransportConfig,
|
|
510
|
-
): Promise<FastifyInstance> {
|
|
511
|
-
const app = Fastify({ logger: false });
|
|
512
|
-
await registerHttpTransport(app, config);
|
|
513
|
-
return app;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Start the HTTP transport bound to the configured port.
|
|
518
|
-
* Returns the listening Fastify instance.
|
|
519
|
-
*/
|
|
520
|
-
export async function startHttpTransport(
|
|
521
|
-
config: HttpTransportConfig & { port: number },
|
|
522
|
-
): Promise<FastifyInstance> {
|
|
523
|
-
const app = await buildHttpTransportApp(config);
|
|
524
|
-
await app.listen({ port: config.port, host: "0.0.0.0" });
|
|
525
|
-
return app;
|
|
526
|
-
}
|